Tuesday, November 5, 2013

Is there a standard way (i.e. defined by some Java/J2EE/etc. spec) to provide a custom class loader to a Java Servlet Container which should be used to load a WAR file?
On a new project we are extending a large commercial Java software package (Foo) with web services which requires some flexibility of deployment (as separate services, etc). In particular, we want to avoid the necessity to include in each WAR file all of the Foo software dependency Jar files as they are numerous, large, and will be changing with patch/bugfix releases as we develop. Similarly, it is highly undesirable to have to copy all of the dependencies into each Servlet container's "lib" directory.
Ideally, I would like to tell the Java application server that these WAR files must be loaded using a custom class loader which I will provide which automatically includes the Foo software dependency Jars. Something like this (in Java-pseudocode):
public class MyWarFileClassLoader extends ClassLoader {
  protected URLClassLoader urlcl;
  public MyWarFileClassLoader(File warFile) {
    File installDir = System.getEnv("FOO_HOME");
    List<File> fooEntries = new File(installDir, "jars").listFiles("*.jar");
    fooEntries.add(new File(installDir, "resources"));
    fooEntries.add(warFile);
    this.urlcl = new URLClassLoader(fooEntries);
  }
  public Class findClass(String name) {
    return this.urlcl.findClass(name);
  }
}
If there is no standard way to do this, is there a straightforward way to achieve the same goal for several WAR files, regardless of the target Servlet Container?
[Edit]
To put it another way: is there a common pattern for allowing WAR files to manage their own dependencies at runtime instead of relying on the Servlet Container configuration? I could, of course, have the WAR file manifest include a Class-Path attribute but then the entries are still "hardcoded" at build time rather than detected automatically at runtime.
share|improve this question
There is no standard way to enforce the use of a particular custom classloader in a Java EE application, to load classes from a predefined source. There is however the ability to bundle libraries within a Java EE application, so that multiple modules (including web-modules residing in WARs) can load and access classes in the bundled libraries.
The Java EE specification allows an Enterprise application deployment (a .ear file) to bundle libraries in a library deployment directory; by default this is the lib directory within a .ear file. These libraries may then be used by multiple web-modules (located in different .war files) within the root of the .earfile. The relevant part of the Java EE 6 specification is Section EE 8.2.1, where the following is stated:
.ear file may contain a directory that contains libraries packaged in JAR files. The library-directory element of the .ear file’s deployment descriptor contains the name of this directory. If a library-directory element isn’t specified, or if the .ear file does not contain a deployment descriptor, the directory named lib is used. An empty library-directory element may be used to specify that there is no library directory.
All files in this directory (but not subdirectories) with a .jar extension must be made available to all components packaged in the EAR file, including application clients. These libraries may reference other libraries, either bundled with the application or installed separately, using any of the techniques described herein.
It is important to note that all Java EE compliant application servers (WebLogic/WebSphere/JBoss et al) will support deployment of EAR files with bundled libraries. However, there are servlet containers (like Tomcat and Jetty) which do not comply with the entire Java EE specification; such containers will not support deployment of EAR files.
In the event where the libraries are required to be accessed by multiple web modules in a servlet container (either due to the choice of the container or due to a preference for WAR files), you ought to rely on the servlet container support for shared libraries to deploy the WAR files without the libraries. The Java EE specification does not mandate any requirement in this area concerning the use of installed libraries. Some containers support shared libraries better than others, by supporting versioned shared libraries (where deployed applications may use only one version among several), while others (like Tomcat) do not.
share|improve this answer
 
Thanks for this information, Vineet, very interesting. I will refine my question to mention Servlet Containers specifically, since we don't need a full AppServer. –  maerics Sep 20 '11 at 16:17
 
@maerics, my answer will continue to be the same, save for the fact that you may rely on the Extension-List attribute in tha JAR's manifest to allow for loading of shared libraries (that will still be installed in the server in the manner applicable to each server). You might want to read up the entire section on library support (Section 8.2) in the Java EE 6 specification. –  Vineet Reynolds Sep 20 '11 at 16:35

No comments: