Jboss Admin Tutorial: Class Loading on JBoss

14. Class Loading on JBoss

14.1. Class Namespace Isolation

  • Java EE application servers need to provide an environment where any application can use any class (or a library of classes)

    • Of a particular "version"
    • And statically initialize it in its own way
  • Classes loaded (and initialized) by other deployed applications and/or the server itself must not conflict
  • Mandated by the Java EE spec
  • This requirements is known as Class Namespace Isolation
  • Provided by Java EE app servers

14.2. Java Class Runtime Identity

  • But, Java classes do not have "versions"
  • The runtime identity (a pseudo version) of a class is defined by its fully qualified class name and its defining class loader
  • Consequently, my.package.MyClass loaded by loader L1 is not the same as my.package.MyClass loaded by loader L2

    • Casting objects between the two classes results in a ClassCastException even though they look like they are the same
    • Fully qualified class name includes the name of the package that the class belongs to as well as its actual class name (as stored on disk) - e.g. : java.lang.String, java.net.URL, com.myorg.mypackage.MyClass, etc.
  • Prior to Java 1.2, Java employed a flat class namespace.
  • As of Java 1.2, the defining class loader becomes a part of the class identity
  • Impossible for a "rogue" class loader to redefine an existing class, like java.lang.String
  • Makes it possible to have multiple class loading domains within the same JVM
  • But, impossible to pass the object references between the domains (results ClassCastException)
  • To fix, fall back to object serialization (call by value): slow!

14.3. Class Loading in Java EE

  • To provide Class Namespace Isolation each Java EE application can get its own class loader

    • Each app gets its own version of classes
    • Allows independent static configuration of classes
    • Hot-redeployment is easy - restarting an application class loader reloads all of its classes
    • But too slow if this application needs to share objects with other applications
  • Sharing classes between applications - no duplicate libraries, fast call-by-reference

    • But if the shared classes change, all applications that use them must be restarted
  • The trick is to isolate the classes that need to be shared and isolate the rest

14.4. Class Loading On JBoss

  • JBoss 5.x introduces of a new class loader based on the new Virtual File System (VFS): VFS Class Loader

    • This new class loader is fully backwards compatible (previous configuration from JBoss 3.x/4.x still works)
    • Uses VFS to locate JAR and class files
    • VFS was implemented to simplify the internal file-handling within the app server, but the class-loading semantics are the same as before

14.5. The Class Loader

  • JBoss AS uses multiple class loaders, each of which loads a specific set of classes

    • Help to load different versions of a class (example: application A can load com.marakana.example.MyClass version 1 and application B can load com.marakana.example.MyClass version 2) without problems)
  • JBoss AS implements rules:

    • To define which class loader loads a given class
    • For visibility (access to classes loaded by another class loader) - i.e. class sharing
[Important]Important

The servlet specification requires that no other application should have access to classes within a WAR file. But you can modify this behavior. In deployers/jbossweb.deployer/META-INF/war-deployers-jboss-beans.xml, set useJBossWebLoader property to true for the WarDeployer bean.

14.6. Default Class Search Order

  1. All the classes found in the class path, including JVM classes. Also includes classes found in the ${jboss.home.dir}/bin
  2. All classes for deployed applications and the classes found in the JAR files under ${jboss.server.home.url}/lib
  3. At the end, classes found in a WAR file are loaded.

Consider the following use cases:

  1. Library class is present in application’s archive and nowhere else.

    • The library class will be loaded from application’s archive and cached
  2. Library class is present as a (as a .jar) $JBOSS_HOME/server/<config>/lib/ and nowhere else.

    • The library class will be loaded from $JBOSS_HOME/server<config>/lib/ and cached
  3. Library class is present in both the application’s archive and $JBOSS_HOME/server/<config>/lib/ (as a .jar)

    • By default, the library class will be loaded from $JBOSS_HOME/server/<config>/lib/ and cached
  4. Library class is present in all of the following locations: $JAVA_HOME/lib/ext/ (as a .jar), in application’s archive, and in $JBOSS_HOME/server/<config>/lib/ (in a .jar)

    • The library will be loaded from $JAVA_HOME/lib/ext/ and cached

For all these use-cases, if another application is deployed that uses the same library, the library class will be retrieved from the cache.

  • Default serach order for web-apps:

    1. System and Bootstrap classes
    2. $JBOSS_HOME/server/<config>/deploy/ <app>/WEB-INF/classes
    3. $JBOSS_HOME/server/<config>/deploy/ <app>/WEB-INF/lib
    4. Root Repository Cache
    5. $JBOSS_HOME/server/<config>/lib/
  • The search order is different, to comply with the servlet spec - keeps JSP (and other classes) isolated from other web apps
  • Observe that classes in WEB-INF/classes/ take precedance over classes stored with .jar’s under WEB-INF/lib/.
  • The JSP/class isolation in web applications is controlled by Tomcat’s class loader. To switch to JBoss’s own class loading, edit $JBOSS_HOME/server/<config>/deployers/jbossweb.deployer/META-INF/ war-deployers-jboss-beans.xml, and change UseJBossWebLoader to true.

    <deployment xmlns="urn:jboss:bean-deployer:2.0">
       ...
       <bean name="WarDeployer" class="org.jboss.web.tomcat.service.deployers.TomcatDeployer">
          ...
          <property name="useJBossWebLoader">true</property>
          ...
       </bean>
       ...
    </deployment>
[Note]Note

Changing the default setting can have adverse effects.

14.7. Scoping Classes

  • To provide true class namespace isolation, we need to use deployment-based scoping
  • Each scoped deployment creates its own class loader repository

    • HierarchicalLoaderRepository3
  • First looks at local UCLs before delagating to default repository
  • Enabled through JBoss deployment descriptors: <loader-repository> whose value is a valid and unique JMX ObjectName

For .ear files, add to META-INF/jboss-app.xml:

<jboss-app>
  <loader-repository>
    com.example:loader=my.ear
  </loader-repository>
</jboss-app>

For .war files, add to WEB-INF/jboss-web.xml:

<jboss-web>
  <class-loading>
    <loader-repository>
          com.example:loader=my.war
    </loader-repository>
  </class-loading>
  ...
</jboss-web>

For .sar files, add to META-INF/jboss-service.xml:

<service>
  <loader-repository>
    com.example:loader=some-unique-archive-name
  </loader-repository>
  ...
</service>

For more info on the scoping configuration, please see: http://community.jboss.org/wiki/classloadingconfiguration

14.8. Scoped Class Search Order

  • Scoped search order for apps with (default) java2ParentDelegation=false:

    1. $JBOSS_HOME/server/<config>/deploy/<app>/
    2. System Bootstrap
    3. Default Repository Cache
    4. $JBOSS_HOME/server/<config>/lib/
  • For web applications the order is similar: with WEB-INF/classes and WEB-INF/lib being checked before going to bootstrap extensions or JBoss-wide libraries

Consider a use case where a library class is present in both the scoped application’s archive and $JBOSS_HOME/server/<config>/lib/ (as a .jar). The java2ParentDelagation is set to false (default). Because the application is scoped, the library class will be loaded from the application’s archive.

  • Scoped search order for apps with (non-default) java2ParentDelegation=true:

    1. Default Repository Cache
    2. System Bootstrap ($JAVA_HOME/lib/ext)
    3. $JBOSS_HOME/server/<config>/deploy/<app>/
    4. $JBOSS_HOME/server/<config>/lib/
  • For web applications the order is similar: with WEB-INF/classes and WEB-INF/lib being checked before going to JBoss-wide libraries

To enable Java 2 parent delegation while still using scoped deployment, add <loader-repository-config> to <loader-repository> in the JBoss deployment descriptors:

...
  <loader-repository>
    com.example:unique-archive-name
    <loader-repository-config>
      java2ParentDelegation=true
    </loader-repository-config>
  </loader-repository>
...

Consider again the use case where a library class is present in both the scoped application’s archive and $JBOSS_HOME/server/<config>/lib/ (as a .jar). This time java2ParentDelagation is set to true (non-default). If the library class was already loaded (e.g., through another application), and cached in the default repository, then it will be accessed from the cache. Otherwise it will be loaded from the application’s archive. For more info,please see: http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossClassLoadingUseCases

14.9. App-specific Log4J Config

  • JBoss comes with its own Log4J library and the configuration file
  • To use your own version of Log4J lib/config:

    • Change the name of JBoss' Log4j configuration (to something non-default), e.g.: jboss-log4j.xml
    • Define your own log4j.xml or log4j.properties in the archive’s class-root
    • Use your own Log4j library
    • Enable non-delegated scoped class loading for your application

Log4J by default searches for ‘log4j.xml` and then for log4j.properties. Even with scoped deployemnt, Log4J will find JBoss’ default ‘log4j.xml` file before it finds a local log4j.properties. A simple solution is to rename JBoss’ own log4j.xml file.

  1. In $JBOSS_HOME/server/<config>/conf/ directory, rename log4j.xml to jboss-log4j.xml
  2. In $JBOSS_HOME/server/<config>/conf/jboss-service.xml file, change Log4JService’s ConfigurationURL to resource: jboss-log4j.xml.

For simple cases, simply piggy-back off JBoss' own Log4J setup, by configuring your application’s specific logging needs in $JBOSS_HOME/server/<config>/conf/log4j.xml file directly.

For more info, please see: http://wiki.jboss.org/wiki/Wiki.jsp?page=ScopedLoggingConfig

14.10. Problems With Class Loading

  • ClassNotFoundException

    • Missing Jar File

      • Either manually search for the class in JAR files on your system (jar -tvf /path/to/file.jar)
      • Use another utility (like jarFinder, http://www.isocra.com/2006/02/jarfinder/) to find the missing class

        java –classpath classes/ com.isocra.utils.jarSearch.DirectorySearcher $JBOSS_HOME javax.servlet.http.HttpServlet
    • Not visible to the class loader

      • Check scoping
      • Move the JAR containing the class file to a higher-level shared lib/ folder
  • Duplicate JAR files

    • Depending on scoping, your application may be loading a wrong "version" of a JAR file
  • Zip file errors

    • On large deployments (i.e. big JAR/WAR/EAR/SAR files) it’s possible for the deployer to try to load a partially uncompressed zip file
    • Prefer uncompressed and cold re/deployments
  • ClassCastException or LinkageError

  • NoClassDefFoundError

  • ClassCircularityError

See http://community.jboss.org/wiki/ClassLoadingOverview for general overview of class loading issues.

14.11. Lab: Class Loading

  • Experiment with class loading using provided apps: PrintService SAR and Fortune WAR
  • Install multiple versions of a simple utility library in different locations and observe which one gets loaded by applications deployed on JBoss
  • Try deploying applications in different order to observe how caching works
  • Also notice that not only are the classes cached, but so is their static configuration

Under the lab folder you will find the following:

  • util/util-java-ext.jar: the utility library meant to be installed under $JAVA_HOME/lib/ext
  • util/util-jboss-lib.jar: the utility library meant to be installed under $JBOSS_HOME/lib (never gets used)
  • util/util-jboss-server-lib.jar: the utility library meant to be installed under $JBOSS_HOME/server/<config>/lib
  • sar/print-service_default-with-util.sar: app with built-in utility class (already packaged)
  • sar/print-service_default-without-util.sar: app without the utility class (requires external library)
  • sar/print-service_scoped-with-util.sar: app with built-in utility class (scoping enabled)
  • sar/print-service_scoped-without-util.sar: app without built-in utility class (scoping enabled)
  • sar/print-service_delegated-scoped-with-util.sar: app with built-in utility class (scoping and delegation enabled)
  • sar/print-service_delegated-scoped-without-util.sar: app without built-in utility class (scoping and delegation enabled)
  • war/fortune-without-util.war: web app without utility class
  • war/fortune-with-util-both.war: web app with utility class in both WEB-INF/classes and WEB-INF/lib (as a .jar) locations