I have recently been given the task of getting the Pentaho BIServer to deploy within JBoss5 AS. Classloading has aparently been completely reworked in v5 vs v4 and prior. The new way to customize classloading behavior is to write up a jboss-classloading.xml file and drop it in various spots within your application to be deployed. After many hours of being frustrated reading blog and forum posts and not finding a single source of information on exactly what the jboss-classloading.xml is and does and where to put it, I figured I’d try to save someone else (and myself later after I forget this) the same pain I’ve experienced. Below I answer some of the questions I had on my journey.
Where do I place a jboss-classloading.xml file?
To control classloading behavior of an..
- ..EAR, place it in the META-INF of the ear, e.g. myapp.ear/META-INF/jboss-classloading.xml
- ..WAR, place it in the WEB-INF of the war, e.g. myapp.war/WEB-INF/jboss-classloading.xml
- ..JAR, place it in the META-INF of the jar, e.g. mylib.jar/META-INF/jboss-classloading.xml (I’ve not personally tried this but you can find info on this in JBoss forums)
What are all the attributes and what do they mean?
- name – typically the name of war or ear, e.g. “myapp.war”
- import-all – (“true”/”false”) – true means make all classes exported from other applications visible to this application
- export-all – value “NON_EMPTY” – all classes are exposed to other applications
- domain – a classloading domain, can be an arbitrary name but typically you will want to name it the name of your application, e.g. “myapp.ear” or “myapp.war”. If a domain by that name already exists, your app will join that classloading domain.
- parent-domain – default is “DefaultDomain” which is shared with all other applications that don’t specify a domain. If you prefer to delegate to an explicit parent domain when a class is not found in yours, then specify an existing domain here, as in delegating to an EAR’s domain, e.g. “myapp.ear”
- top-level-classloader – used to enable embedded apps, such as a war within an EAR, to participate in a top-level domain (see JBCL-125)
- parent-first (“true”/”false”) – if false, the app is in non-j2ee compliance mode, where the app’s domain is searched first for a class before searching the parent domain. The default for top-level deployed WARs is “false”, however, I have found that the default behavior for WARs within EARs is “true”
How do I deploy a WAR within an EAR in which the WAR can see all jars in the EAR, but the EAR’s classes trump JBoss’s?
The original problem I had was the Pentaho BIServer application uses a more recent version of commons-collections.jar, more recent than the one in JBoss’s own library. When deploying the Pentaho WAR outside of an EAR, I did not have a problem since as of JBoss5, WARs are deployed with a classloading scheme that looks up classes within the WAR first, before delegating to the parent domain. However, when I deployed the Pentaho EAR I saw the opposite, as if JBoss’s copy of commons-collections.jar was found first, not the EAR’s classes first. I needed to override this behavior such that EAR classes are preferred over JBoss’s. The situation I started with was a pentaho.war within a pentaho.ear and no jboss-classloading.xml files present. As I mentioned earlier, once I deployed this, JBoss libs were preferred over mine. So I had to go to work authoring some custom classloading. Here’s what I wound up with:
Contents of jboss-classloading.xml in pentaho.ear/META_INF:
<classloading xmlns="urn:jboss:classloading:1.0" domain="pentaho.ear" export-all="NON_EMPTY" import-all="true" parent-first="false"> </classloading>
The important parts here: (1) we are specifing that the pentaho.ear application participate in it’s own domain “pentaho.ear”. This is so in the embedded war, we can declare this domain as it’s parent. (2) by setting parent-first to false, we are essentially declaring that EARs classes be preferred over JBoss’s (in the DefaultDomain presumably).
One funny side-effect to mention here is that I was actually able to deploy an EAR in which javax.servlet.HttpServlet was not found during deployment. Amazing that this is even possible, but it happened. The jboss-classloading.xml file that made this happen included only a parent-first=”false” attribute, nothing else. I haven’t eliminated the attrs one-by-one to find out which is responsible for this; I suspect perhaps import-all.
Contents of jboss-classloading.xml in pentaho.ear/pentaho.war/WEB-INF:
<classloading xmlns="urn:jboss:classloading:1.0" domain="pentaho.war" parent-domain="pentaho.ear" export-all="NON_EMPTY" import-all="true"> </classloading>
The most important bit here is the pentaho.war is in it’s own domain “pentaho.war”, but delegates to the EAR’s CL domain, “pentaho.ear”. This enables the WAR to see classes within jars deployed at the EAR level. The pentaho.war in the case of an EAR deployment has no libs in WEB-INF/lib, so it must rely on the EAR for jars.
Result? Once I dropped these files in, the app “just worked”. My app found my commons-collections.jar and not JBoss’s.
FYI, the most helpful article on the subject I found during this effort was http://java.dzone.com/articles/jboss-microcontainer-classloading