First of all, I'd like to point out some basic resources to understand how the class loading works.
If you're not comfortable with that, then you should first read Understanding WebLogic Server Application Classloading.
First notice that the "APP-INF/lib" mechanism is not standard : until the arrival of Java EE 5, there was no default mechanism to load libraries in an EAR.
BEA decided to implement such a mechanism, copying the way libs are loaded in a web application.
Now, a default behavior has been introduced by Sun under "application-type > library-directory" in the application.xml. (http://java.sun.com/xml/ns/javaee/application_5.xsd)
The default lib directory, if no "library-directory" has been defined, is "lib".
Here's the order in which classes are loaded.
- « System classpath loader » (« Bootstrap classloader »)
- « WebLogic server classpath loader »
- « Application classpath loader (« library-directory » then « APP-INF/lib »)
- « EJB classpath loader »
- « Webapp classpath » (WEB-INF/lib)
Reminder on classloading
This behavior of classloading can be summed up in one sentence : "The first loaded is the one" :)
To make a quick summary on that, let's consider a web application, looking for a class "Sample".
That's the default way WLS loads classes.
- The demand is made in the LOW classloader
- The classloader asks for its parent if it has the class loaded
- If it's the case, the class is given. If not, this last step is repeated until the highest classloader has been reached.
- If the class is nowhere to be found, a ClassNotFoundException is raised.
You can see that even if the LOW classloader has the class, it will first ask the parents if they have it loaded.
From WebLogic Server 6.1
An interesting schema has been introduced in WLS 6.1 : the child-first-delegation model, embodied through the option "prefer-web-inf-classes" in the weblogic.xml file.
- The demand is made in the LOW classloader
- The classloader first looks for the class in its scope.
- If it finds it, the class is returned, else the classloader asks its parent for the class.
- The previous step is played again till it reaches the top classloader and if it didn't find the class, then a ClassNotFoundException is raised.
Well, it gives an answer to our problem, but brings some new potential issues.
Actually, the tree is entirely reversed which can cause some major trouble : considering that a classloader isn't aware of the class loaded by its children,
it can lead to some linkage errors ("Loader Constraints Violated").
You may take a look at an interesting doc from JBoss about linkage errors.
From WebLogic Server 9.2
BEA found another mechanism, much more clever : the filtering class loader.
- The demand is still made in the LOW classloader
- The classloader asks its parent for the class (like in the original mechanism) but this time the filter acts as the parent classloader
- The filter looks for the class package
- If the filter finds the package, it throws a ClassNotFoundException, simulating that the parent tree didn't find the class
- If no corresponding package is defined, then the filter let the request pass through and the original behavior is played.
This mechanism offers the advantages of both previous mechanisms.
It's the best way to isolate the classloaders.
How to declare a FilteringClassLoader
You simply have to add a declaration in your "weblogic-application.xml", such as :
<prefer-application- packages >
And that's all !
FilteringClassLoader in action :)
For my demonstration, I used Workshop 10.3 (excellent version which supports EJB3, JAX-WS webservice and much more, by the way !).
I have two Java projects (DomainLib & WebAppLib) which each contains a class fr.mbutton.filteringclassloader.SampleClass
As you may have guessed, I will use these classes to determine from which classloader the class is loaded.
I've got a simple WebApp project (ClassCallerWAR) with one JSP which makes the call for the class.
And an EAR project (ClassCallerEAR).
I first deployed my EAR on the server without any special configuration.
The result is as expected (class loaded from the web app, since it is not declared elsewhere).
Then I export the DomainLib project as a JAR file, and I place it in the domain/lib directory :
I restart the server to have the lib taken into account.
And when I access the JSP, this time I've got :
As expected, the class is loaded from the highest classloader.
To be able to get the class loaded by the web app classloader, I configure a filteringclassloader in the weblogic-application.xml, such as :
I resynchronize (= redeploy) the application and this time, I've got :
It works perfectly well ! It will save you some time thinking about mixing up your libraries, trust me ! :)