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
- 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 asmy.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.
-
Casting objects between the two classes results in a
- 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!
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
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
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 loadcom.marakana.example.MyClass
version 2) without problems)
-
Help to load different versions of a class (example: application A can load
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 |
---|---|
The servlet specification requires that no other application should have access to classes within a WAR file. But you can modify this behavior.
In |
-
All the classes found in the class path, including JVM classes.
Also includes classes found in the
${jboss.home.dir}/bin
-
All classes for deployed applications and the classes found in the JAR files under
${jboss.server.home.url}/lib
- At the end, classes found in a WAR file are loaded.
Consider the following use cases:
Library class is present in application’s archive and nowhere else.
- The library class will be loaded from application’s archive and cached
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
-
The library class will be loaded from
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
-
By default, the library class will be loaded from
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
-
The library will be loaded from
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:
- System and Bootstrap classes
- $JBOSS_HOME/server/<config>/deploy/ <app>/WEB-INF/classes
- $JBOSS_HOME/server/<config>/deploy/ <app>/WEB-INF/lib
- Root Repository Cache
- $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 changeUseJBossWebLoader
totrue
.<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 |
---|---|
Changing the default setting can have adverse effects. |
- 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
Scoped search order for apps with (default)
java2ParentDelegation=false
:-
$JBOSS_HOME/server/<config>/deploy/<app>/
- System Bootstrap
- Default Repository Cache
-
$JBOSS_HOME/server/<config>/lib/
-
-
For web applications the order is similar: with
WEB-INF/classes
andWEB-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
:- Default Repository Cache
-
System Bootstrap (
$JAVA_HOME/lib/ext
) -
$JBOSS_HOME/server/<config>/deploy/<app>/
-
$JBOSS_HOME/server/<config>/lib/
-
For web applications the order is similar: with
WEB-INF/classes
andWEB-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
- 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
orlog4j.properties
in the archive’s class-root - Use your own Log4j library
- Enable non-delegated scoped class loading for your application
-
Change the name of JBoss' Log4j configuration (to something non-default), e.g.:
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.
-
In
$JBOSS_HOME/server/<config>/conf/
directory, renamelog4j.xml
tojboss-log4j.xml
-
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
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
-
Either manually search for the class in JAR files on your system (
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
orLinkageError
- Same class loaded by different class loaders
- Bad code?
- See http://community.jboss.org/wiki/ClassCastExceptions
NoClassDefFoundError
- Unfound import (similar to above)
- See http://community.jboss.org/wiki/GetClassNotFoundExceptionOrNoClassDefFoundError
ClassCircularityError
- Circular reference or a JDK bug
- See http://community.jboss.org/wiki/ClassPreloadService
See http://community.jboss.org/wiki/ClassLoadingOverview for general overview of class loading issues.
- 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 bothWEB-INF/classes
andWEB-INF/lib
(as a.jar
) locations