2008-08-23

How to use the WebLogic® FilteringClassLoader mechanism ?

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.

Classloader Order

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.

  1. « System classpath loader » (« Bootstrap classloader »)
  2. « WebLogic server classpath loader »
  3. « Application classpath loader (« library-directory » then « APP-INF/lib »)
  4. « EJB classpath loader »
  5. « 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".

Normal Way

That's the default way WLS loads classes.


image

  1. The demand is made in the LOW classloader
  2. The classloader asks for its parent if it has the class loaded
  3. If it's the case, the class is given. If not, this last step is repeated until the highest classloader has been reached.
  4. 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.


image

  1. The demand is made in the LOW classloader
  2. The classloader first looks for the class in its scope.
  3. If it finds it, the class is returned, else the classloader asks its parent for the class.
  4. 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.


image

  1. The demand is still made in the LOW classloader
  2. The classloader asks its parent for the class (like in the original mechanism) but this time the filter acts as the parent classloader
  3. The filter looks for the class package
  4. If the filter finds the package, it throws a ClassNotFoundException, simulating that the parent tree didn't find the class
  5. 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 >

    <package-name>org.apache.log4j.*</package-name>

    <package-name>antlr.*</package-name>

</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).


image


I first deployed my EAR on the server without any special configuration.


image


The result is as expected (class loaded from the web app, since it is not declared elsewhere).


image


Then I export the DomainLib project as a JAR file, and I place it in the domain/lib directory :


image


I restart the server to have the lib taken into account.

And when I access the JSP, this time I've got :


image


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 :


image


I resynchronize (= redeploy) the application and this time, I've got :


image


It works perfectly well ! It will save you some time thinking about mixing up your libraries, trust me ! :)


12 comments:

r3d said...

Big thanks for this post.

Maxence Button said...

My pleasure !

Luciano said...
This comment has been removed by the author.
chris said...

Very good explanation, friend ! Thanks for this.

disaakdisaak said...

Hey, can you send post or send me your source code? I've tried exactly what you suggest here in a project I have and I still get the ClassNotFoundException: org.hibernate.hql.ast.HqlToken.

Pierre said...

very usefull for me. It solved my problem with antlr library.

Pierre said...

Very usefull for me, it solved my problem with antlr definitly

Donald Isaak said...

Actually I ended by succeeding.

Thanks for posting this.

You inspired me to right my own multi-part blog about 'How to Migrate a Web App from WebSpere to WebLogic (11g)' as I could find lots off sites with info on migrating from WebLogic to WebSphere, but I could not find much on migration to WebLogic.

These technologies are new to me so I took a lot of screen-shots along the say and kept my blog content simple. Feel free to check it out and reference anything you find useful.

Donald Isaak said...

Whoops I don't think I added my Migrating a Web App from Websphere to WebLogic Blog URL. It is http://disaak.blogspot.com/2009/11/migrating-web-application-from.html

juve said...

I have a appfuse hibernate spring web application which create a .war file i dont have .ear where should i place weblogic-application.xml

Hemant said...

Is this filtering classloader available for web application as well?

weblogic-application.xml is in EAR.
but how about WAR file?

Thanks in advance,
Hemant

SUMIT BLOG said...

Max ,

I have just started using weblogic 10.3.3 . Ours is web application.I am seeing an issue during server startup . The same application is working fine in 9.2MP3.


<2010-10-20 03:08:40:425> <> [fault (self-tuning)'] SessionFactoryImpl ERROR: - Error in named query: deleteAllowableSettin
gsTempAll
org.hibernate.QueryException: ClassNotFoundException: org.hibernate.hql.ast.HqlToken [delete from com.airvana.anp.model.db.domainobjects.
AllowableSettingsTemp]
at org.hibernate.hql.ast.HqlLexer.panic(HqlLexer.java:57)
at antlr.CharScanner.setTokenObjectClass(CharScanner.java:340)
at org.hibernate.hql.ast.HqlLexer.setTokenObjectClass(HqlLexer.java:31)
at antlr.CharScanner.(CharScanner.java:51)
at antlr.CharScanner.(CharScanner.java:60)
at org.hibernate.hql.antlr.HqlBaseLexer.(HqlBaseLexer.java:56)
at org.hibernate.hql.antlr.HqlBaseLexer.(HqlBaseLexer.java:53)
at org.hibernate.hql.antlr.HqlBaseLexer.(HqlBaseLexer.java:50)
at org.hibernate.hql.ast.HqlLexer.(HqlLexer.java:26)
at org.hibernate.hql.ast.HqlParser.getInstance(HqlParser.java:44)
at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:242)
at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:157)
at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:111)
at org.hibernate.engine.query.HQLQueryPlan.(HQLQueryPlan.java:77)
at org.hibernate.engine.query.HQLQueryPlan.(HQLQueryPlan.java:56)
at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:72)
at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:413)
at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:361)