Jason Greene [
http://community.jboss.org/people/jason.greene] created the document:
"Module Compatible Classloading Guide"
To view the document, visit:
http://community.jboss.org/docs/DOC-16701
--------------------------------------------------------------
h3. Introduction
Modular classloading has a much greater focus on isolation and separation than traditional
heirarchical models, so it is far less forgiving about using the "wrong"
classloader. Unfortunately, this means that any dynamic classloading code you have written
in the past may need to be updated to play nicely in a modular environment. However, the
good news is that code that is module compatible is more correct, and therefore less error
prone in traditional environments. This article provides some background infromation along
with some pointers on how to make this transition.
h3. Common Terms
This article assumes that the following terms are well understood.
*Defining Classloade*r - The classloader which loaded the calling class. This is what is
used when you use Class.forName() without specifying a classloader. It's also what
loads classes that you reference statically (e.g. via an import). It can be obtained by
calling *getClass().getClassLoader()* on the respective class.
*Thread Context Classloader (TCCL)* - The classloader which has been assigned to the
calling thread. It's meaning is specific to the environment it was assigned in. In
Java EE code, the TCCL is always the classloader of the relevant deployment. *+The TCCL is
the most commonly misused classloader.+*
*Dynamic Classloading* - Loading a class by name using a ClassLoader. This is typically
used for pluggable architectures that discover some kind of extension. An example is an
API framework that supports multiple implementations/providers (e.g. JAXP). It dynamically
loads the provider by name using some kind of service discovery mechanism (a file in
META-INF/services, a system property, etc). Another example is application server code
which needs to operate against a deployment's classes. Since they are logically
isolated, and may even be hot-deployed, static linking (imports) can not be used.
*JDK Service Provider Loading* - The common pattern used by Java SE and Java EE frameworks
to load different providers / implementations of an API. This is done by checking a system
property, a special properties file, and more commonly looking for files in
META-INF/services. See the
http://download.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html ServiceLoader
javadoc for more info on how the process works.
*Module* - A single consistent class loading namespace. It encompases classes/resources
directly available in the module (contents of one or more jar(s)) local to the module, as
well as all module dependencies defined on the module. Every module has it's own
ClassLoader which it uses to load all classes that can be loaded from the module.
*Deployment* - A deployment is a user provided package of components. In AS7, every
deployment has a Module. In some cases a deployment may contain nested deployments or
other artifacts. Those are often represented by a module of their own.
*System Classloader* - Also commonly referred to as the "application
classloader". It represents everything that is defined on java.class.path, and
commonly includes java extensions and some jdk classes.
h3. Picking the "Right" Classloader
The key to loading classes in a modular environment is to use the correct classloader. In
the past many frameworks, including many Java standard APIs, expect that the users classes
(or the deployment classes) and the framework classes are accessible via the same
classloader.* +The problem with this assumption is that it prevents proper isolation of
the framework implementation classes and a user's classes.+* In many cases though it
would work in a heirarchical model (as long as there was no name conflicts), since class
sharing is done by inheritance rather than explicit links as is the case in a modular
environment. This single bad assumption makes it impossible to achieve capabilities like
allowing a user to use their own XML parser.
The correct way to pick a ClassLoader is to always use the one that is directly associated
with the class you are trying to load. So if a framework wants to load a class from one of
it's dependencies +*then it should use it's defining classloader.*+ Likewise, If
the framework wants to load a class from a deployment, then it should use the
deployment's classloader. In some cases, like in the case of service provider
discovery, a framework might want to look for a particular resource in both it's
classloader (say a default provider), and the user Classloader. Under this scenario the
framework should check both ClassLoaders following the order that best suits it's
needs.
h3. *FRAMEWORKS, NEVER LOAD YOUR CLASSES FROM THE TCCL!!*
If you expect portability between different classloading environments, it is always wrong
for a framework to load it's classes from the TCCL. +*Instead use the right
classloader, which is most often the defining classloader.*+ If the defining classloader
is not the right classloader, then pick it and use it to load classes. Even if you have an
inheritance model with a heirarchy that sees the classes you want, it is always more
correct to pick the classloader that "owns" the classes.
The TCCL should be used with extreme care. EE code is required to see the defining
deployments classloader as the TCCL. Beware though that other frameworks may have
different rules, and may set it to another value when executing in their context. I+*f you
have access to the classloader you want it's always better to use it over the TCCL.*+
Note that AS7 subsystems that are executing within any kind of EE context can safely
asssume that the TCCL will point to the deployment classloader. They should, however,
never attempt to load AS7 code from the deployment classloader.
h3. What To Export/Expose
The only thing that a module should ever expose is classes that make up it's API
contracts. In most cases this means that an AS7 module should not export it's
dependencies. This allows for the module and it's calling module to use completely
different versions of the same dependency. Deployments can, for example, use commons-bah
1.2 and AS7 code can happily use commons-blah 1.0.
h3. How to deal with "Bad" dependencies
If your module/deployment/framework depends on something that isn't compatible with a
modular environment. Then you have a couple of options that may workaround the issue.
However in some cases the only workable solution is to patch the bad framework.
Common workarounds include:
* *Swapping TCCL* - If a call to a framework method incorrectly uses the TCCL to load
it's classes/resources, and the same call does not need to see deployment classes,
then temporarily set the TCCL to the framework's defining classloader. (e.g.
Thread.currentThread().setContextClassLoader(frameworkClass.getClass().getClassLoader()
* *Prefer calls which have a CL argument* - If you can pass a classloader, prefer that to
calls which try to guess (usually picking the TCCL) if possible.
* *Heirarchical Emulation -* This is a worst case workaround that should only be used as a
last resort. In the unfortunate case where a framework expects both deployment classes and
it's own classes to be on the same classloader, a custom classloader can be created
which delegates first to the deployment classsloader and second to frameworks defining
classloader. This classloader can the be swapped to the TCCL as described above, or
somehow passed to the framework via a mechnism it exposes. This has the undesirable effect
of a possible conflict between a user's implementation choices and the frameworks.
However, it at least does not pollute the deployment module.
If you have a bad dependency then file a bug to get it fixed/replaced. In particularly if
you have to use heirarchical emulation.
h3. *How to use JAXP and other service provider APIs from AS/Framework code*
EE applications can safely use the no-arg newInstance() methods provided by JAXP. However
framework code must either not use them, or use the swapping TCCL workaround described in
the "bad" dependencies section above. This ensures that either the
container's xml parser is used or the parser explictly imported by the frameworks
module (requires a service import). Not doing this will result in the deployments xml
parser being used (since the deployment CL is often the TCCL), which may be incompatible.
--------------------------------------------------------------
Comment by going to Community
[
http://community.jboss.org/docs/DOC-16701]
Create a new document in JBoss AS7 Development at Community
[
http://community.jboss.org/choose-container!input.jspa?contentType=102&am...]