IBM WebSphere Developer Technical Journal: Identifying classpath conflicts

A best practice

Classpath conflicts are not uncommon when dealing with open source Java™ software. This article offers a simple means for identifying when classpath problems occur.

Share:

Shannon Kendrick, IT Specialist, IBM Global Services

Shannon Kendrick is an IT Specialist in Java/J2EE application development with IBM Global Services.



Kyle Brown, Senior Technical Staff Member, IBM

Kyle Brown is a Senior Technical Staff Member with IBM Software Services for WebSphere. Kyle provides consulting services, education, and mentoring on object-oriented topics and J2EE technologies to Fortune 500 clients. He is a co-author of Enterprise Java Programming with IBM WebSphere, the WebSphere AEs 4.0 Workbook for Enterprise Java Beans, 3rd Edition , and The Design Patterns Smalltalk Companion . He is also a frequent conference speaker on the topics of Enterprise Java, OO design, and design patterns.



23 June 2004

Also available in Chinese

Introduction

Get the products and tools used in this article

If you are a developerWorks subscriber, you have a single user license to use WebSphere® Application Server, and other DB2®, Lotus®, Rational®, Tivoli®, and WebSphere products -- including the Eclipse-based WebSphere Studio IDE -- to develop, test, evaluate, and demonstrate your applications. If you are not a subscriber, you can subscribe today.

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:

Class.forName(className).getProtectionDomain().getCodeSource()

We quickly added some logging code to dump the CodeSource for the

org.apache.oro.text.regex.Perl5Compiler

The result was C:/Program Files/IBM/WebSphere Studio/Application Developer/v5.1.1/runtimes/base_v51/lib/jython.jar, not the 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:

Click to see code listing

package com.ibm.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.security.CodeSource;
import java.security.ProtectionDomain;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * This servlet will attempt to load a user-specified class and, if successful,
 * query the classes' CodeSource for the location of the class.
 *
 * @author <a href="mailto:kendrick@us.ibm.com">Shannon Kendrick</a>
 * @version $Id$
 */
public class ClassFinderServlet extends HttpServlet {
	//~ Static fields/initializers ---------------------------------------------

	private static final String CLASS_NAME = "className";

	//~ Methods ----------------------------------------------------------------

	/**
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
	 * 		javax.servlet.http.HttpServletResponse)
	 */
	protected void doGet(
		HttpServletRequest request,
		HttpServletResponse response)
		throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
	 * 		javax.servlet.http.HttpServletResponse)
	 */
	protected void doPost(
		HttpServletRequest request,
		HttpServletResponse response)
		throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * Process the request.
	 *
	 * @param request
	 * @param response
	 *
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void processRequest(
		HttpServletRequest request,
		HttpServletResponse response)
		throws ServletException, IOException {
		String error = null;
		String classLocation = null;
		String className = request.getParameter(CLASS_NAME);

		if ((className != null)
			&& ((className = className.trim()).length() != 0)) {
			// Attempt to load class and get its location.
			try {
				ProtectionDomain pd =
					Class.forName(className).getProtectionDomain();

				if (pd != null) {
					CodeSource cs = pd.getCodeSource();

					if (cs != null) {
						classLocation = cs.toString();
					} else {
						error = "No CodeSource found!";
					}
				} else {
					error = "No ProtectionDomain found!";
				}
			} catch (Throwable t) {
				error = t.toString();
				log("error=" + t, t);
			}
		}

		// Set content type and get the writer.
		response.setContentType("text/html");

		PrintWriter out = response.getWriter();

		// Write out content.
		out.println(
			"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
		out.println("<html>");
		out.println("<head>");
		out.println("<title>Servlet Container Class Finder</title>");
		out.println("</head>");
		out.println("<body bgcolor=\"#d1d7b3\">");
		out.println(
			"<h2><font color=\"#0000a0\">Servlet Container Class Finder</font></h2>");
		out.println(
			"<p><font color=\"#000000\">Enter the fully-qualified name of a Java class");
		out.println(
			"(e.g. org.apache.oro.text.regex.Perl5Compiler) in the field below. The");
		out.println(
			"servlet will attempt to load the class and, if successful, query the");
		out.println(
			"classes' <em>java.security.CodeSource</em> for the location of the class");
		out.println("using the following methods: <pre>");
		out.println(
			"Class.forName(className).getProtectionDomain().getCodeSource()");
		out.println("</pre> </font></p>");
		out.println(
			"<form method=\"post\" action=\""
				+ request.getRequestURI()
				+ "\">");
		out.println(
			"<p>Class Name: <input type=\"text\" name=\"" + CLASS_NAME + "\"");
		out.println(
			"\tvalue=\""
				+ ((className != null) ? className : "")
				+ "\" size=\"40\" /> <input type=\"submit\"");
		out.println("\tvalue=\"Submit\" /></p>");
		out.println("</form>");

		if ((classLocation != null) || (error != null)) {
			out.println("<table border=\"1\" bgcolor=\"#8080c0\">");
			out.println(
				"\t<caption align=\"top\"><font color=\"#000000\">Search Results</font></caption>");
			out.println("\t<tbody>");
			out.println("\t\t<tr>");
			out.println(
				"\t\t\t<td align=\"right\"><font color=\"#000000\">Class Name:</font></td>");
			out.println(
				"\t\t\t<td><font color=\"#000000\">"
					+ className
					+ "</font></td>");
			out.println("\t\t</tr>");
			out.println("\t\t<tr>");

			if (error != null) {
				out.println(
					"\t\t\t<td align=\"right\"><font color=\"#a00000\">Error:</font></td>");
				out.println(
					"\t\t\t<td><font color=\"#a00000\">"
						+ error
						+ "</font></td>");
			} else {
				out.println(
					"\t\t\t<td align=\"right\"><font color=\"#000000\">Class Location:</font></td>");
				out.println(
					"\t\t\t<td><font color=\"#000000\">"
						+ classLocation
						+ "</font></td>");
			}

			out.println("\t\t</tr>");
			out.println("\t</tbody>");
			out.println("</table>");
		}

		out.println("</body>");
		out.println("</html>");

		// Finished.
		out.flush();
		out.close();
	}

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 file's /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.


Conclusion

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.


Download

DescriptionNameSize
Code sampleclassfinder.ZIP  ( HTTP | FTP )6 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=14523
ArticleTitle=IBM WebSphere Developer Technical Journal: Identifying classpath conflicts
publish-date=06232004