IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  WebSphere  >

IBM WebSphere 开发者技术期刊: 类路径冲突的鉴别

最佳实践

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 初级

Shannon Kendrick, IT 专家, IBM Global Services
Kyle Brown, 高级技术人员, IBM Software Services for WebSphere

2004 年 9 月 01 日

类路径冲突在处理开放源代码 Java™ 软件时比较常见。本文将介绍出现类路径问题时鉴别类路径冲突的简易方法。

引言

开放源代码软件主要优势之一就是它能让您“即装即用(out of the box)”,这在以前需要靠您自个花上数周甚至数月的时间。Apache 协会在这一点上成就显著,在 Apache Jakarta 程序中的大多数开放源代码程序都成为行业内事实上的标准。

事实上,由于这些程序如此成功并被一致受到好评,IBM 开始在 WebSphere 应用服务器(WebSphere Application Server )内引入它们。例如,众所周知 WebSphere 应用服务器 V5.0(WebSphere Application Server V5.0)和 WebSphere 应用服务器 V5.1(WebSphere Application Server V5.1)的管理控制台都是使用 Apache Struts 构建的。同样地,WebSphere Application Server 在它的 Web 容器内使用的是 Jasper JSP 编译程序。然而,在 WebSphere Application Server V5.0 和 5.1 中使用的还不仅仅是这些 Apache 开放源代码程序。这样,在 Web 或 EJB™程序 所含的 JAR 文件不同版本之间的类路径冲突常常引起细微又难于调试的错误。以下将向您描述在团队中如何开展检测工作以及帮助解决通用工具开发过程中的这些错误。





回页首


对类路径问题的诊断

最近,开发者团队开始将 WebSphere Application Server V3.5 中的 Web 应用程序迁移至 WebSphere Application Server V5.1。该应用程序利用了 Apache Jakarta ORO 2.0.7 框架中的正规表达式类。这些类的使用范围之一就是在连接到 CICS 以进行实际身份验证之前确认用户名的正确格式。

当团队完成迁移工作时,他们发现在 WebSphere Application Server V5.1 下,有效的用户名会被 ORO PatternMatcher 拒绝。奇怪的是同样的代码能在版本 3.5 上运行,也能在 WebSphere Studio Application Developer Version 5.1 JVM 下用“main”方法进行单元测试时运行。失败的原因和 servlet 容器有关。

冥思苦想之后,该团队开始怀疑 servlet 容器是不是在自身的类路径里包含了其它版本的 ORO 类。我们最初决定使用 JAR 工具列出 JAR 的各种运行时间的目录并查找出现问题的类。然而,WebSphere Application Server V5.1 使用的几种 JAR 在运行时通过不同的目录分散开来。这样一来就太困难了,团队于是考虑在运行时是否存在一种方式可以及时对 JAR 文件所包含的出现问题的类进行查询。

当情况出现时,java.security 包中的 ProtectionDomain 和 CodeSource 类可以指出类的源位置。基本的语法是:

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

我们很快地添加一些日志代码将 CodeSource 转储为

org.apache.oro.text.regex.Perl5Compiler

其结果是 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 。显然, jython.jar 里的 ORO 类并没有按照 2.0.7 中的方式运作。





回页首


解决类路径问题的常规工具

在讨论 WebSphere Application Server 中类路径冲突的发现问题上,我们需要一种简单的且无干扰的方式,可以用来查询 WebSphere 运行时特殊的类正从哪个 JAR 文件里装载。这样,当我们觉察到类路径冲突时,只需简单查询并确定从 JAR 文件装载的类是否和我们料想到的 JAR 文件相同。具有该功能的 servlet 如下所示:

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();
	}





回页首


怎样使用 servlet

我们提供了本文用到的 WAR 文件中 Java 源代码形式和 compiled .class 形式的 servlet 的下载。但是,怎样安装该 servlet 将取决于包含开放源代码 JAR 文件的机制。如果 JAR 文件包含在 EAR 文件的根目录下(该类项目大都这样建议),那么,您可以直接将整个 WAR 文件包含在 EAR 文件内。然而,如果您将 JAR 文件存储于 WAR 文件的 /lib 目录下,那么您需要将 servlet .class 文件包含在 WAR 文件的 /bin 目录下,并更新 WAR 文件的 web.xml 文件,以使其包含我们提供的 web.xml 对 servlet 的引用。请记住这只是一个程序调试期的调试工具而已。为安全起见,您应该在将应用程序配置到产品中之前,确保该 servlet 已在 WAR 或者 EAR 文件中删除。





回页首


解决类路径冲突

一旦您确定类正在从 WebSphere Application Server 提供的 JAR 文件装载,而不是从您的 J2EE 组件提供的 JAR 文件装载,下一步要做的就是确定怎样装载类的正确版本。有时最好的办法只需删除应用程序的类路径(例如,通过删除从 EAR 文件中复制的开放源代码 JAR 文件)。通常最佳解决方案是在 WebSphere 下设定类装载器(classloader)以使用 PARENT_LAST classloader 模式而不是默认的 PARENT_FIRST 模式。PARENT_LAST classloader 模式会导致类装载器在装载到父类之前首先尝试从自身的类路径装载类。该方案允许应用程序类装载器优先于父类,并提供存于父类中的自身的类版本。





回页首


结束语

在本文中,我们向您说明在处理开放源代码 Java 软件时,类路径冲突是很常见的。并且向您介绍了类路径问题发生时如何确认的通用方法。鉴别这些冲突正是解决它们的第一步,本方法为您解决这些问题提供了简易的途径。






回页首


下载

名字大小下载方法
classfinder.ZIP6 KBHTTP
关于下载方法的信息


参考资料



作者简介

Shannon Kendrick是 IBM Global Services 的 Java/J2EE 应用程序开发部门的 IT 专家。


Kyle Brown是 IBM Software Services for WebSphere 的高级技术人员。Kyle为 500 名客户提供咨询服务、培训以及面向对象主题和 J2EE 技术的指导。他是 IBM WebSphere 企业 Java 程序设计WebSphere AEs 4.0 Workbook for Enterprise Java Beans,第三版The Design Patterns Smalltalk Companion三本书的作者。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款