Showing posts with label tomcat. Show all posts
Showing posts with label tomcat. Show all posts

Thursday, May 12, 2011

java.lang.LinkageError in Tomcat 6

Usually the cause of this error is you may unintentionally include following two jars into your war file. 
    el-api.jar jasper-el.jar
They are provided by tomcat 6.

In Tomcat 6, you will get following error:

java.lang.LinkageError: loader constraint violation: loader …

You can solve the problem by adding following snippet into your pom file.

<dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>el-api</artifactId>
        <version>[1,)</version>
        <scope>provided</scope>
</dependency>
<dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>jasper-el</artifactId>
        <version>[1,)</version>
        <scope>provided</scope>
</dependency>

Read this post for more information.

Tuesday, October 26, 2010

JAASRealm + SSL

http://tomcat.apache.org/tomcat-6.0-doc/realm-howto.html#JAASRealm

This is a good article on the topic: http://blog.frankel.ch/custom-loginmodule-in-tomcat. Some of following code is borrowed from the article.

Following steps are listed on the official tomcat doc. I will elaborate each of them.

  1. "Write your own LoginModule, User and Role classes based on JAAS (see the JAAS Authentication Tutorial and the JAAS Login Module Developer's Guide) to be managed by the JAAS Login Context (javax.security.auth.login.LoginContext) When developing your LoginModule, note that JAASRealm's built-in CallbackHandler only recognizes the NameCallback and PasswordCallback at present. "
    package test;
    
    import java.io.IOException;
    import java.util.Map;
    
    import javax.security.auth.Subject;
    import javax.security.auth.callback.Callback;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.callback.NameCallback;
    import javax.security.auth.callback.PasswordCallback;
    import javax.security.auth.callback.UnsupportedCallbackException;
    import javax.security.auth.login.LoginException;
    import javax.security.auth.spi.LoginModule;
    
    /**
     * Login module that simply matches name and password to perform authentication.
     * If successful, set principal to name and credential to "AuthorizedUser".
     *
     * @author Nicolas Fränkel. Modified by Gerald Guo.
     * @since 2 avr. 2009
     */
    public class PlainLoginModule implements LoginModule {
    
        /** Callback handler to store between initialization and authentication. */
        private CallbackHandler handler;
    
        /** Subject to store. */
        private Subject subject;
    
        /** Login name. */
        private String login;
    
        /**
         * This implementation always return false.
         *
         * @see javax.security.auth.spi.LoginModule#abort()
         */
        @Override
        public boolean abort() throws LoginException {
    
            return false;
        }
    
        /**
         * This is where, should the entire authentication process succeeds,
         * principal would be set.
         *
         * @see javax.security.auth.spi.LoginModule#commit()
         */
        @Override
        public boolean commit() throws LoginException {
    
            try {
    
                PlainUserPrincipal user = new PlainUserPrincipal(login);
                PlainRolePrincipal role = new PlainRolePrincipal("AuthorizedUser");
    
                subject.getPrincipals().add(user);
                subject.getPrincipals().add(role);
    
                return true;
    
            } catch (Exception e) {
    
                throw new LoginException(e.getMessage());
            }
        }
    
        /**
         * This implementation ignores both state and options.
         *
         * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject,
         *      javax.security.auth.callback.CallbackHandler, java.util.Map,
         *      java.util.Map)
         */
        @Override
        public void initialize(Subject aSubject, CallbackHandler aCallbackHandler, Map aSharedState, Map aOptions) {
    
            handler = aCallbackHandler;
            subject = aSubject;
        }
    
        /**
         * This method checks whether the name and the password are the same.
         *
         * @see javax.security.auth.spi.LoginModule#login()
         */
        @Override
        public boolean login() throws LoginException {
    
            Callback[] callbacks = new Callback[2];
            callbacks[0] = new NameCallback("login");
            callbacks[1] = new PasswordCallback("password", true);
    
            try {
    
                handler.handle(callbacks);
    
                String name = ((NameCallback) callbacks[0]).getName();
                String password = String.valueOf(((PasswordCallback) callbacks[1]).getPassword());
    
                if (!name.equals(password)) {
    
                    throw new LoginException("Authentication failed");
                }
    
                login = name;
    
                return true;
    
            } catch (IOException e) {
    
                throw new LoginException(e.getMessage());
    
            } catch (UnsupportedCallbackException e) {
    
                throw new LoginException(e.getMessage());
            }
        }
    
        /**
         * Clears subject from principal and credentials.
         *
         * @see javax.security.auth.spi.LoginModule#logout()
         */
        @Override
        public boolean logout() throws LoginException {
    
            try {
    
                PlainUserPrincipal user = new PlainUserPrincipal(login);
                PlainRolePrincipal role = new PlainRolePrincipal("admin");
    
                subject.getPrincipals().remove(user);
                subject.getPrincipals().remove(role);
    
                return true;
    
            } catch (Exception e) {
    
                throw new LoginException(e.getMessage());
            }
        }
    }
  2. "Although not specified in JAAS, you should create seperate classes to distinguish between users and roles, extending javax.security.Principal, so that Tomcat can tell which Principals returned from your login module are users and which are roles (see org.apache.catalina.realm.JAASRealm). Regardless, the first Principal returned is always treated as the user Principal. "
    Also read the API doc http://tomcat.apache.org/tomcat-5.5-doc/catalina/docs/api/org/apache/catalina/realm/JAASRealm.html. If authentication succeeds, your LoginModule must attach at least a user principal and a user role to subject.
    package test;
    
    import java.security.Principal;
    
    public class PlainRolePrincipal implements Principal {
    
        String roleName;
        
        public PlainRolePrincipal(String name) {
            roleName = name;
        }
        public String getName() {
            return roleName;
        }
        
        public String toString() {
            return ("RolePrincipal: " + roleName);
        }   
    
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }   
            if (obj instanceof PlainRolePrincipal) {
                PlainRolePrincipal other = (PlainRolePrincipal) obj;
                return roleName.equals(other.roleName);
            }   
            return false;
        }   
    
        public int hashCode() {
            return roleName.hashCode();
        }   
    }
    You get the idea, you can implement class PlainUserPrincipal in a similar way.
  3. "Place the compiled classes on Tomcat's classpath "
  4. "Set up a login.config file for Java (see JAAS LoginConfig file) and tell Tomcat where to find it by specifying its location to the JVM, for instance by setting the environment variable: JAVA_OPTS=$JAVA_OPTS -Djava.security.auth.login.config==$CATALINA_BASE/conf/jaas.config "
    Create a JAAS config file:
    -----------------------------
    CertBasedCustomLogin {
        test.CertBasedLoginModule
        sufficient;
    };
    -----------------------------
    When you launch tomcat, use  -Djava.security.auth.login.config= to specify where the config file is stored.
  5. "Configure your security-constraints in your web.xml for the resources you want to protect"
    The goal of the whole process is to protect some resources. This step specifies which resources should be protected.
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Secure Content</web-resource-name>
            <url-pattern>/cert-protected-users/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>AuthorizedUser</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <!-- ... -->
    <login-config>
        <auth-method>CLIENT-CERT</auth-method>
        <realm-name>The Restricted Zone</realm-name>
    </login-config>
    <!-- ... -->
    <security-role>
        <description>The role required to access restricted content </description>
        <role-name>AuthorizedUser</role-name>
    </security-role>
    Basically, it says only users with role "AuthorizedUser" can access the resources cert-protected-users/*.
    Note:role-name must match the role attached to subject in step 1) ("AuthorizedUser" in our case) for successful access.
  6. "Configure the JAASRealm module in your server.xml"
    Actually, to put web app specific context config into server.xml is not recommended. Instead, I put a file named context.xml under directory META-INF.
    <Context>
        <Realm className="org.apache.catalina.realm.JAASRealm" appName="CertBasedCustomLogin"
            userClassNames="test.PlainUserPrincipal"
            roleClassNames="test.PlainRolePrincipal">
        </Realm>
    </Context>
    The value of appName must match the name specified in step 4.
  7. Add "-Dsun.security.ssl.allowUnsafeRenegotiation=true" for renegotiation support. (Read http://java.sun.com/javase/javaseforbusiness/docs/TLSReadme.html for more information)

Note

Some versions of Tomcat have problems to support JAASRealm + SSL mutual auth (https://issues.apache.org/bugzilla/show_bug.cgi?id=45576).  I tried 6.0.18, 6.0.20 and 6.0.29. Only 6.0.20 works for me. 6.0.29 gave errors when I tried.

More resources:

http://tomcat.apache.org/tomcat-6.0-doc/realm-howto.html#JAASRealm

http://tomcat.apache.org/tomcat-5.5-doc/catalina/docs/api/org/apache/catalina/realm/JAASRealm.html

http://wiki.metawerx.net/wiki/Web.xml.AuthConstraint

https://issues.apache.org/bugzilla/show_bug.cgi?id=45576

http://java.sun.com/javase/javaseforbusiness/docs/TLSReadme.html

Monday, October 25, 2010

web.xml schema (simplified)

  <xsd:complexType name="web-appType">

    <xsd:choice minOccurs="0" maxOccurs="unbounded">
      <xsd:group ref="j2ee:descriptionGroup"/>
      <xsd:element name="distributable" type="j2ee:emptyType"/>
      <xsd:element name="context-param" type="j2ee:param-valueType">
        <xsd:annotation>
          <xsd:documentation> 
            The context-param element contains the declaration
            of a web application's servlet context
            initialization parameters.  
          </xsd:documentation>
        </xsd:annotation>
      </xsd:element>

      <xsd:element name="filter" type="j2ee:filterType"/>
      <xsd:element name="filter-mapping" type="j2ee:filter-mappingType"/>
      <xsd:element name="listener" type="j2ee:listenerType"/>
      <xsd:element name="servlet" type="j2ee:servletType"/>
      <xsd:element name="servlet-mapping" type="j2ee:servlet-mappingType"/>
      <xsd:element name="session-config" type="j2ee:session-configType"/>
      <xsd:element name="mime-mapping" type="j2ee:mime-mappingType"/>
      <xsd:element name="welcome-file-list" type="j2ee:welcome-file-listType"/>
      <xsd:element name="error-page" type="j2ee:error-pageType"/>
      <xsd:element name="jsp-config" type="j2ee:jsp-configType"/>
      <xsd:element name="security-constraint" type="j2ee:security-constraintType"/>
      <xsd:element name="login-config" type="j2ee:login-configType"/>
      <xsd:element name="security-role" type="j2ee:security-roleType"/>
      <xsd:group ref="j2ee:jndiEnvironmentRefsGroup"/>
      <xsd:element name="message-destination" type="j2ee:message-destinationType"/>
      <xsd:element name="locale-encoding-mapping-list" type="j2ee:locale-encoding-mapping-listType"/>
    </xsd:choice>

    <xsd:attribute name="version" type="j2ee:web-app-versionType" use="required"/>
    <xsd:attribute name="id" type="xsd:ID"/>
  </xsd:complexType>

Saturday, September 26, 2009

Classloaders, shared directory and endorsed directory of Tomcat 5.5 and 6.0

Tomcat 5.5 Class Loader: http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html
Tomcat 6.0 Class Loader: http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html

Tomcat 5.5

Class loader hierarchy
image  (from tomcat 5.5 document)

Class loaders:

  • Boostrap, System
  • Common:
    visible to both Tomcat internal classes and to all web applications.
    Related directories
    • classes and resources in: $CATALINA_HOME/common/classes
    • jar files in: $CATALINA_HOME/commons/endorsed, $CATALINA_HOME/commons/i18n and $CATALINA_HOME/common/lib
  • Catalina
    include all classes and resources required to implement Tomcat 5 itself. These classes and resources are TOTALLY invisible to web applications.
    Related directories:
    • classes and resources in: $CATALINA_HOME/server/classes
    • jars in: $CATALINA_HOME/server/lib
  • Shared
    Shared resources are shared across all web applications. (They are not used by Tomcat internal classes).
    Related directories
    • classes and resources in: $CATALINA_BASE/shared/classes
    • jars in: $CATALINA_BASE/shared/lib
  • WebappX
    Load web app specific resources and jars.
    Related directories
    • classes and resources: <web-app-dir>/WEB-INF/classes
    • jars: <web-app-dir>/WEB-INF/lib

Note: $CATALINA_BASE (not $CATALINA_HOME) is used class loader “Shared”.

#   CATALINA_HOME   May point at your Catalina "build" directory.
#
#   CATALINA_BASE   (Optional) Base directory for resolving dynamic portions
#                   of a Catalina installation.  If not present, resolves to
#                   the same directory that CATALINA_HOME points to.

Tomcat 6.0

Class loader hierarchy
image   (from tomcat 6.6 document)
Similar to the hierarchy in tomcat 5.5. 
The difference is that class loaders Catalina and Shared are not present in tomcat 6.6.
For Class loader Common, the related directories are different. In tomcat 6, classes, resources and jars under directory $CATALINA_HOME/lib (in tomcat 5.5, the directory is $CATALINA_HOME/common/classes, $CATALINA_HOME/common/lib, etc) are loaded by class loader Common.

These directories under which classes and resources are searched for can be changed by modifying configuration file $CATALINA_HOME/conf/catalina.properties.
Default values for class loader search directories are:

common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
:shared.loader=

If you still want to use shared directory in the way specified in tomcat 5.5, you can change value of property shared.loader.
For example: shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar

Endorsed directory

Tomcat 5.5

“Tomcat utilizes this mechanism by including the system property setting
-Djava.endorsed.dirs=$CATALINA_HOME/common/endorsed in the command line that starts the container. Therefore, you can replace the parser that is installed in this directory, and it will get used even on a JDK 1.4 system.”

Tomcat 6.0

Tomcat utilizes this mechanism by including the system property setting
-Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS in the command line that starts the container.

Variable JAVA_ENDORSED_DIRS is specified in file setclasspath.sh: 
    JAVA_ENDORSED_DIRS="$BASEDIR"/endorsed

Variable BASEDIR is specified in file catalina.sh:
    BASEDIR="$CATALINA_HOME"

In other words,
    in tomcat 5.5, the default endorsed directory is $CATALINA_HOME/common/endorsed
    in tomcat 6.0, the default endorsed directory is $CATALINA_HOME/endorsed

Friday, October 10, 2008

JAXP + Java Endorsed Packages

In this post I summarized XML related stuff. After all JAXP was constructed with the hope to unify interface.
Here is a very good FAQ about JAXP.

Pluggability
Pluggability is necessary so that programmers can choose the desired implementations of JAXP.
From the JAXP specification, I found the lookup procedure. For XSLT, the procedure is:

• Use the javax.xml.transform.TransformerFactory system property
• Use the properties file "lib/jaxp.properties" in the JRE directory. This configuration file is in standard java.util.Properties format and contains the fully qualified name of the implementation class with the key being the system property defined above. The jaxp.properties file is read only once by the JSR-000206 Java™API for XML Processing (“Specification”) implementation and its values are then cached for future use. If the file does not exist when the first attempt is made to read from it, no further attempts are made to check for its existence. It is not possible to change the value of any property in jaxp.properties after it has been read for the first time.
• Use the Services API (as detailed in the JAR specification), if available, to determine the classname.
The Services API will look for the classname in the file META-INF/services/javax.xml.transform.TransformerFactory
in jars available to the runtime.
• Platform default TransformerFactory instance.

It seems that the #3 option is used mostly. If you download an implementation jar (xalan.jar or xerces-api.jar...), you should be able to find the corresponding file META-INF/services/javax.xml.xxx.xxx.

Problem with Java SE 1.4
However, there is a problem for Java 1.4. It bundled in an implementation of JAXP 1.1 from Apache. Unfortunately, the developers did not change the package names. Even if you downloads and uses a newer version of package from Apache, none of the plug-in options described above works because the class loader always uses the built-in implementation.

Solution
Then two solutions are provided:
(1) Change the package names to other internal package names (e.g. com.sun.org.apache.*)
This is what Sun does in later Java SE releases.
(2) For old Java SE, Endorsed override mechanism can be used.
Java 5 Endorsed Standard: http://java.sun.com/j2se/1.5.0/docs/guide/standards/index.html.
Users can specify the endorsed package path by setting system property - java.endorsed.dirs.
The default path is: <jre-home>/lib/endorsed
On my machine, the path is /usr/java/jdk1.5.0_09/jre/lib/endorsed.
Information about system properties can be accessed here http://java.sun.com/docs/books/tutorial/essential/environment/sysprop.html.
Also I found a very simple program on the web to list all system properties:

public class DisplaySystemProps {
    public static void main(String[] args) {
         System.getProperties().list(System.out);
    }
}
Then you can check the properties you are interested in, such as user home directory, java home, vm version, java class version, path separator, file separator...

Tomcat
For old tomcat releases which rely on old versions of Java, users can put XML parser  into CATALINA_HOME/common/lib.
But for latest versions, it does not work. Read http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html for more information.

"Classes which are part of the JRE base classes cannot be overriden. For some classes (such as the XML parser components in J2SE 1.4+), the J2SE 1.4 endorsed feature can be used.
In previous versions of Tomcat, you could simply replace the XML parser in the $CATALINA_HOME/common/lib directory to change the parser used by all web applications. However, this technique will not be effective when you are running on JSE 5, because the usual class loader delegation process will always choose the implementation inside the JDK in preference to this one.
Tomcat utilizes this mechanism by including the system property setting -Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS in the command line that starts the container."
In other words, users MUST use Endorsed override mechanism to use another XML parser/XSLT in Tomcat. See this post for more information.

Sunday, March 16, 2008

Configuration Of Tomcat

(Part of this post is cited from Tomcat Offcial site)
    I have been using Tomcat as JSP/Servlet container in current project. However, I have not got full understanding about Tomcat configuration. Every time I need to modify configuration, I search on the web and learn those related elements. I don't have a big picture about it. To fully understand the configuration, I read through official document at this site http://tomcat.apache.org/tomcat-6.0-doc/config/. And as I expect, it helps me a lot.
The configuration file of Tomcat is $TOMCAT_HOME/conf/server.xml.
(1) Server

Root element is server.
(2) Service
    A Service element represents the combination of one or more Connector components that share a single Engine component for processing incoming requests. So it looks like a container to enclose related Connector and Engine.
(3) Connector
    Connector receives requests from clients. There are two kinds of connector:HTTP and AJP.
HTTP Connector enables Tomcat to function as a stand-alone web server. An instance of connector listens for connections on a specific port number. One service can contain more than one connector. Normally, every connector maintains its own thread pool. However, this can be altered by setting property executor. If so, the connector will use the executor and all thread related properties are ignored. One advantage of using executor is that it can shared between components.
    "Each incoming request requires a thread for the duration of that request. If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum (the value of the maxThreads attribute). If still more simultaneous requests are received, they are stacked up inside the server socket created by the Connector, up to the configured maximum (the value of the acceptCount attribute. Any further simultaneous requests will receive "connection refused" errors, until resources are available to process them."
    If SSL on HTTP is needed, corresponding connector element should be modified. Property redirectPort can be used to redirect SSL request received by a plain connector to another connector which supports SSL.
(4) Engine
    Following all connector elements in a service component is engine element. Engine element represents processing machinery associated with a service. It gets and processes all requests from connectors and returns the response to Connector. Note: exactly one engine element should be defined inside a service element.
(5) Host
    Inside engine element, multiple host elements can be nested. Each host element represents a virtual host. At least one host is required, and one of the hosts MUST match value of property defaultHost of engine element. Element alias can be used to give alias. Property appBase is used to specify application base directory of the virtual host. Properties deployOnStartUp and autoDeploy can be configured to set functionality of auto-deployment.
(6) Context
Context element represents a web application which is run inside a certain virtual host. "Each web application is based on a Web Application Archive (WAR) file, or a corresponding directory containing the corresponding unpacked contents, as described in the Servlet Specification (version 2.2 or later)." However, in high version of Tomcat, Context elements do not need to written in server.xml. Detailed information is:
    "For Tomcat 6, unlike Tomcat 4.x, it is NOT recommended to place <Context> elements directly in the server.xml file. This is because it makes modifing the Context configuration more invasive since the main conf/server.xml file cannot be reloaded without restarting Tomcat.
Context elements may be explicitly defined:
  • in the $CATALINA_HOME/conf/context.xml file: the Context element information will be loaded by all webapps
  • in the $CATALINA_HOME/conf/[enginename]/[hostname]/context.xml.default file: the Context element information will be loaded by all webapps of that host
  • in individual files (with a ".xml" extension) in the $CATALINA_HOME/conf/[enginename]/[hostname]/ directory. The name of the file (less the .xml extension) will be used as the context path. Multi-level context paths may be defined using #, e.g. context#path.xml. The default web application may be defined by using a file called ROOT.xml.
  • if the previous file was not found for this application, in an individual file at /META-INF/context.xml inside the application files
  • inside a Host element in the main conf/server.xml

How to map incoming request to specific web app?
    "The web application used to process each HTTP request is selected by Catalina based on matching the longest possible prefix of the Request URI against the context path of each defined Context. Once selected, that Context will select an appropriate servlet to process the incoming request, according to the servlet mappings defined in the web application deployment descriptor file (which MUST be located at /WEB-INF/web.xml within the web app's directory hierarchy)."

Deployment
    If you want to develop and deploy your JSP/Servlet applications, consult this page: http://tomcat.apache.org/tomcat-6.0-doc/appdev/deployment.html. It contains information about: Standard directory layout, where to put shared libraries, web.xml and deployment. This link also contains some useful information: http://tomcat.apache.org/tomcat-6.0-doc/config/host.html#Automatic%20Application%20Deployment.

Configuration of virtual host:
http://hi.baidu.com/zouziting/blog/item/7e3ad7808f3491d79123d936.html