A question (mostly original with some slight editing on my part) came up on our internal mailing list today that I thought might be interesting to those that have cloassloader questions. For a quick refresher on the options for classloading in WebLogic, click the image to see a larger image or get the presentation from the bottom of the post on slideshare. The key for this scenario is the DOMAIN_HOME/lib directory.
Scenario: We have a embeddable java component which gets shipped within every webapp in our suite of products. This embeddable java component has a cache object which is loaded every time the component is initialized by the webapp. This cache object is a singleton and has the same content across all the webapps. We have one managed server in which many ear files are deployed to. Each ear initialize's this embeddable java component on start up.
Issue: When I profile the objects loaded (using Classloader Analysis Tool) in each of these web apps I see that this cache singleton object is loaded in each one of the web app class loaders. I would like to load just one of these singleton objects and reference the same object across all the ear's. This saves heap space and all the calls that it takes to create this cache object in each ear. Even though its a singleton I see that since the class loaders are different for each ear that it creates a new cache object. I have an understanding of the weblogic class loader hierarchies (bootstrap, system, webapp etc). I don't want to move this embeddable java component and its dependencies to the system class loader by modifying the scripts or environment variables. Are there other options?
My answer: Have a look at the DOMAIN_HOME/lib directory.
To try it, just drop the jars you want to be shared in the DOMAIN_HOME/lib directory.
Here’s an example.
* To change this template, choose Tools | Templates
* and open the template in the editor.
* @author jbayer
public class SharedCache
public static final Map<String,String> mapCache = new java.util.HashMap<String, String> ();
System.out.println( SharedCache.class.toString() + " loaded into a classloader" + SharedCache.class.getClassLoader().toString() );
Now I use this class in two webapps named WebApplication1 and WebApplication2 that refer to the SharedCache class in their identical index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8" import="test.*"%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<h1>Hello World! Cache size:
SharedCache myCache = new SharedCache();
out.print( myCache.mapCache.size() );
When the test.SharedCache class is loaded from each web application respectively, you should see the following in the System.out showing that 2 versions of the class were loaded into separate classloaders for each web module:
class test.SharedCache loaded into a classloaderweblogic.utils.classloaders.ChangeAwareClassLoader@bde4ac finder: weblogic.utils.classloaders.CodeGenClassFinder@1ff9980 annotation: WebApplication1@web
class test.SharedCache loaded into a classloaderweblogic.utils.classloaders.ChangeAwareClassLoader@4a4acd finder: weblogic.utils.classloaders.CodeGenClassFinder@3f08e3 annotation: WebApplication2@web
Once you drop the jar with the test.SharedCache in the DOMAIN_HOME/lib and restart the server, and access both web apps, you'll see the class is now shared from one system classloader:
class test.SharedCache loaded into a classloaderjava.net.URLClassLoader@88e2dd
And if you use CAT, you can see that both applications use the same System Classloader. Click to enlarge. The hash code shows that the same classloader is used for both applications.
Check out Jeff West’s recording on WebLogic Server classloaders and CAT.