|
|
YOUR FEEDBACK
SOA World Conference
Virtualization Conference $200 Savings Expire May 16, 2008... – Register Today! Did you read today's front page stories & breaking news?
SYS-CON.TV |
TOP THREE LINKS YOU MUST CLICK ON Feature
Making the Most of WebLogic Classloaders
By: John Musser
Digg This!
As a Java developer, have you ever found yourself running into what might be politely called 'issues' related to the CLASSPATH and class loading? If you haven't, you're one of the few. This is one of the most notorious sources of developer aggravation in Java development. Now J2EE has added a new set of wrinkles to this phenomenon. This article dispels some of the mystery of what's going on behind the classloader curtain and provides insights into how you can use this knowledge to your advantage when designing, packaging, and deploying your WebLogic Server applications. Important items we'll cover include a refresher on class-loading fundamentals you need to be aware of; how WebLogic's own custom classloaders build on these basics; the often misunderstood relationship between classloader hierarchies and EARs, EJBs, and WARs; techniques for packaging and deploying utility classes; and a simple but handy tool for diagnosing how classes are loaded within your applications.
Quick Classloader Review
Nonetheless, there are certain rules all classloaders follow, many of which can ultimately impact your WebLogic applications. In particular, they:
Classloaders in a WebLogic Application
Always keep in mind that class loading and deployment are inseparable. When you deploy your modules to WebLogic, each is typically loaded in its own dynamic classloader. If you're deploying a single EJB JAR file, it will get its own classloader. When you deploy a single WAR file, it gets one too. And when deploying an EAR file, WebLogic will create, depending on what's in your EAR file, an entire hierarchy of classloaders. How does this work? What rules does WebLogic follow? The principal concept is that of application deployment units such as an EAR, an EJB, or a WAR. When any of these are deployed separately they are loaded in separate sibling classloaders. If EJBs and WARs are grouped into an EAR, they get a hierarchy of classloaders: at the top is the application classloader, which loads the EJBs and JARs referenced in its manifest file's classpath (more on this later), and then classloaders for each WAR file. Figure 1 illustrates a sample WebLogic Server deployment. Each shadowed box in the WebLogic section represents an independent classloader instance. At the top level we have two EARs, one EJB JAR, and one WAR. Each of these is loaded by a sibling classloader. Within the first EAR are three EJB JARs (each with potentially multiple EJBs) and manifest-referenced utility files that share a single classloader and two Web applications that each get another classloader. Because of the established classloader hierarchy, the JSPs, servlets, and utility classes in both WARs can directly use any of those EJBs. In the second EAR, a simpler hierarchy allows the same interactions as the first, but these components are isolated from those in the first EAR. In this arrangement, if either of the independently deployed modules, the EJB or the WAR, needs to access the other, this must be done via remote interfaces. This means that the EJB home and remote interfaces must be packaged into the calling modules and all communication may require more overhead than if they were packaged together (in which case WebLogic can perform various call optimizations such as using call-by-reference as it does for new EJB 2.0 local interfaces). Table 1 lists the primary classloaders in a typical WebLogic EAR application. These are listed in order of creation from the primordial bootstrap classloader through various child classloaders down to the Web classloader. A couple of notes here: each of these classloaders may or may not be a distinct ClassLoader subtype or may be multiple instances of the same class configured differently. WebLogic may internally use additional classloaders (the details of which don't matter to us here); the native JVM classloaders are not set up to be reloadable but WebLogic's are (thus the familiar problem of having to reboot the application server if classes loaded by the non-reloadable classloaders have changed).
What This Means
Handling Utility Classes
To elaborate a bit on the last item in Table 2: because they are JAR files, all J2EE modules have a MANIFEST.MF file in their META-INF folder. The J2EE specification allows classes in one JAR - such as an EJB or WAR archive - to access other JARs by explicitly adding their names to a special "Class-Path:" entry in the manifest file (this is built upon the "extensions mechanism" introduced in JDK 1.2). By using this entry you can tailor EAR class-loading behavior and can modularly deal with many of the classpath issues mentioned earlier. In order to make this work, first create a text file specifying a list of JAR filenames as relative URLs separated by spaces and then include this file when building your deployment. Here's what a typical entry might look like: Manifest-Version: 1.0And here's an example of how you might add it when creating an EJB JAR (presuming you have the above line in a file called mymanifest.txt): jar cmd mymanifest.txt myejb.jar com META-INF Now the classes in myejb.jar have access to myparser.jar and vendorlib.jar because they've been loaded with this EJB JAR into its same classloader. Within the context of an EAR file, the JARs referenced within EJBs and WARs are loaded at the same level as EJBs and are available to all beans and Web modules within the application. (Note what happens: WebLogic takes the transitive closure of all manifest classpath entries specified in the EJB and WAR archives for a given EAR and loads them at the same top level within that EAR.) Regardless of which classloaders and packaging technique you use, there's one other caveat worth mentioning and that's the issue of classloading order. Based upon the interdependencies among your utilities, JARs, EJBs, etc., you may find yourself needing to tailor what's placed where solely to satisfy load-time ordering. For example, utility classes needed by WebLogic itself, such as JDBC drivers, must be specified on the system classpath so that they're available when it starts. Complications can also arise in the case of startup classes and their dependencies (this is covered in more detail on BEA's site at http://edocs.bea.com/wls/docs61/programming/packaging.html). Overall, here are a few general guidelines on utility classes: packaging in JAR files is generally more manageable than using individual class files. If the utilities are needed only by a single Web module then put them in a jar in its WEB-INF/lib folder; if the utilities are to be shared by multiple components or modules within a single application then keep them as a JAR at the application level; and finally, if you need to use the utility classes across applications, first try to use the manifest classpath approach and if that's not feasible, then use the global classpath. You can find a good discussion of this as well as J2EE packaging in Tyler Jewell's articles on this topic at www.onjava.com/pub/a/onjava/2001/06/26/ejb.html.
Debugging Class-Loading Issues
- Walk through your shell environment, startup scripts, and configuration files to get a handle on what's being set where. Often it makes sense to consolidate setting file locations and paths in a single place such as one startup script (which itself does not assume any environment variables have been set or explicitly overrides them). - Given that some things won't be apparent until runtime, have your run script print out to the console all settings prior to server launch. - Write a simple Java debug routine that displays relevant values (including system properties such as "sun.boot.class.path"). - Use a tool like the JSP page described below. As an example, say you have a utility class, MyHandler, which for some reason (deliberate or accidental) appears more than once in your environment; it could be that the version loaded at runtime may not be the one you expect. If in this scenario your servlet (in a WAR file) instantiates MyHandler and the only physical location of MyHandler.class is under the WEB-INF/classes directory of that WAR, then this will be the one that gets loaded. But, if you have another version of MyHandler.class somewhere on the system classpath, it will be this one that gets loaded, regardless of the fact that there's one right there in the same archive as your program. Why does this happen? Because of classloader delegation. As noted earlier, all classloaders first delegate load requests to their parent, recursively up the tree, allowing each parent the opportunity to handle the request. In this case, because the system classloader was able to resolve this class reference, the child WAR classloader never had the chance to resolve and load this class itself. The nuances of such behaviors can appear to be quite idiosyncratic even if they're not and certainly vary across application servers and server versions. If you have multiple versions of the same libraries (such as might happen with common utilities such as XML parsers), be prepared to do a little investigating. Also keep in mind that because standard classloaders (including WebLogic's) delegate all load requests to their parents before their own searches, anything deployed on the system classpath can't be redeployed. As just shown, these classes will always be loaded by the JDK system classloader, which does not support reloading. Thus, anything you want to be redeployable without having to restart the server must only be contained within one of the dynamically loadable units such as EARs, EJB-JARs, and WARs. One last debugging note: Classes with no visibility modifier before the class declaration are deemed to be "package level" or "package private". In order for another class to access these classes, they must be in the same package and loaded by the same classloader. If this isn't the case, the caller will get an IllegalAccessException.
J2EE Class-Loading Diagnostic Tool
The second function is to outline in a table a number of the standard locations searched by the various classloaders. This includes the locations searched by the standard Java classloaders (such as the CLASSPATH or, more specifically, the locations identified in the system properties). If you are running the server on your local machine and can click on the names of any JARs, you might want to open them and view the contents. You can get a sense of what this tool provides from Figure 2. In this example, the page has been reloaded after I specified that I wanted my utility class, MyHandler, to be loaded. At the top it shows how and where it found this class, and below it shows an outline of each set of files and directories searched by various Java and WebLogic classloaders. [Two notes: 1) the ClassLoader method getParent() will return null to represent the bootstrap classloader, which explains why a specific classname isn't given in this case; and 2) this utility doesn't try to dig into things like the manifest files or other bean references so it may not display all references in the lower table]. When the tool is unable to locate one of the standard locations (such as WEB-INF/lib), it will print a "not found" message. In this case, the missing directory is perfectly acceptable given that those locations are optional. On the other hand, for items derived from your CLASSPATH, this can indicate a problem such as a JAR file that's specified in the environment but doesn't really exist at that location. This happens as JARs are moved around and can be a handy way to catch these errors (in the example pictured, under Application Classes the file Weblogic_sp.jar is not found - the right location for this should be determined or it should be removed from the classpath).
Conclusion
BEA WEBLOGIC LATEST STORIES
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK BREAKING NEWS FROM THE WIRES
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||