One of the major advantages of open-source software is that it allows you to instantly gain benefits "out of the box" that previously would have taken weeks or months to develop on your own. The Apache consortium has been particularly successful in this regard, as many of the open-source projects within the Apache Jakarta project have become de-facto standards within the industry.
In fact, these projects have become so successful and so accepted that IBM has begun taking advantage of them within WebSphere Application Server itself. For instance, it is common knowledge that the administration console in WebSphere Application Server V5.0 and 5.1 is built using Apache Struts. Likewise, WebSphere Application Server uses the Jasper JSP compiler within its Web Container. However, those are not the only Apache open-source projects that are used within WebSphere Application Server V5.0 and 5.1. As a result, classpath conflicts between versions of the JAR files included within a Web or EJB™ project can often cause subtle and hard-to-debug errors. The following tale describes how some detective work on the part of one team led to the development of a general tool for helping resolve those errors.
Diagnosing classpath problems
Recently, the team of developers in which one of the authors works began migrating a Web application from WebSphere Application Server V3.5 to WebSphere Application Server V5.1. This application makes use of the regular expression classes from the Apache Jakarta ORO 2.0.7 framework. One area where these classes are used is in validating that userids are in the correct format before connecting to CICS for the actual authentication.
As the team worked through the migration, they noticed that under WebSphere Application Server V5.1 valid userids were being rejected by the ORO PatternMatcher. This was puzzling because the same code had worked on Version 3.5, and also worked when unit tested by running the "main" method under the WebSphere Studio Application Developer Version 5.1 JVM. Something in the servlet container was causing it to fail.
After much head-scratching, the team began wondering if the servlet container might include another version of the ORO classes in its classpath. We initially decided to use the JAR tool to list the contents of the various runtime JARs and grep for the classes in question. However, WebSphere Application Server V5.1 uses several JARs at run time that are spread across many different directories. Given how difficult this proved to be, the team then began to wonder if there was a way at run time to query which JAR contains the classes in question.
As it happens, the java.security package contains the classes ProtectionDomain and CodeSource that can provide the source location of classes. The basic syntax is:
We quickly added some logging code to dump the CodeSource for the
The result was
C:/Program Files/IBM/WebSphere Studio/Application Developer/v5.1.1/runtimes/base_v51/lib/jython.jar,
WEB-INF/lib/jakarta-oro-2.0.7.jar that we expected.
Apparently the ORO classes in the
jython.jar did not operate
the same way as those in 2.0.7.
A general tool for resolving classpath questions
While discussing the problem of discovering classpath conflicts in WebSphere Application Server, we determined that what was needed was a simple, non-intrusive way to ask a running WebSphere instance from what JAR file a particular class was being loaded at run time. That way, when we suspected a classpath conflict, we could simply run a query and determine if in fact the class is being loaded from the JAR file we think it is being loaded from. We then determined that the easiest way to do this (at least within the Web Container) was to build a simple servlet to answer this question for us. A servlet that does this is shown below:
How to use the servlet
We provide this servlet in Java source form and in compiled .class form
within the WAR file available with this article for download. However, how
you install this servlet depends upon the mechanism by which you include
your open-source JAR files. If your JAR files are included at the root of
an EAR file (which is the recommendation for most projects of this source)
then you can simply include the entire WAR file within your EAR. However,
if you reference JAR files with the /lib directory of your own WAR files,
then you will need to include the servlet .class file within your own WAR
/bin directory and update the WAR file's
web.xml file to include a reference to the servlet drawn from
the one in our provided
web.xml. Remember that this is a
development-time debugging tool only. For security reasons, you should
make sure that this servlet is removed from the WAR or EAR prior to
deploying your application into production.
Dealing with classpath conflicts
Once you have determined that a class is being loaded from a JAR file provided by WebSphere Application Server, rather than from a JAR file provided by your own J2EE component, the next step is to determine how to load the correct version of the class. Sometimes the best approach is simply to remove the classes from your application's classpath (for example, by removing the duplicate open-source JAR files from your EAR files). Often the best solution is to configure the classloader under WebSphere to use PARENT_LAST classloader mode rather than the default PARENT_FIRST mode. The PARENT_LAST classloader mode causes the classloader to first attempt to load classes from its local classpath before delegating the classloading to its parent. This policy allows an application classloader to override and provide its own version of a class that exists in the parent classloader.
In this article we have shown that classpath conflicts are common when dealing with open source Java software, and provided a simple means for identifying when classpath problems occur. Identifying the conflicts is the first step to fixing them, and this tool provides a simple and easy way to move you along that path.
|Code sample||classfinder.ZIP ( HTTP | FTP )||6 KB|
- Browse for books on these and other technical topics.