一,概述 我们知道,Websphere有自己的Classloader,它对类的加载做了一些自己的定制,但是往往由于有些定制的特殊性,而造成了人们在上面部署应用的不方面,这其中一个比较大麻烦就是,Webpshere中对jar包中资源的定位URL有自己特殊的格式,例如:
wsjar:file:/C:/dev/ws/default/sample_app/xsd.resources.jar!/org/eclipse/test.xml |
我们知道URL是统一资源定位描述,Sun J2SE标准中,jar包中的资源的URL格式应该是以jar:开头,即类似如下的样子
jar:file:/C:/dev/ws/default/sample_app/xsd.resources.jar!/org/eclipse |
而目前大多数开源技术,在以URL做为参数来载入实际资源时,并没有考虑到wsjar开头的情况,因此当一个较为复杂的应用向Webpshere服务器部署时,往往就会出现关于这方面的报错,例如:
No configuration found. Configuring ehcache from ehcache-failsafe.xml found in the classpath: wsjar:file:/F:/WebSphere/AppServer/lib/workplaceoa/ehcache-1.1.jar!/ehcache-failsafe.xml |
如果你仍然觉得不知道在什么情况下会出现wsjar的情况,那么给你举一个例子:下面我们使用apache commons中的commons configuration来写一个代码片断
Configuration configuration = (Configuration) confMap.get(name); if (configuration == null) { ConfigurationFactory factory = new ConfigurationFactory(); URL url = PropertyGetter.class.getClassLoader().getResource("myconfig.xml");//你将myconfig.xml打在某个jar包中 factory.setConfigurationURL(url); try { configuration = factory.getConfiguration(); } catch (Exception e) { logger.error("could not find the configuration ", e); } } String value=configuration.getString(propertyName); |
将该代码片断放在websphere某个应用的某个jsp文件中,执行该页面时就会报如下的错误:
[06-4-18 14:31:03:500 CST] 73068638 Digester E org.apache.commons.digester.Digester Begin event threw exception [06-4-18 14:31:03:516 CST] 73068638 Digester E org.apache.commons.digester.Digester TRAS0014I: 下列异常已记录 org.apache.commons.configuration.ConfigurationException: java.lang.NullPointerException at org.apache.commons.configuration.AbstractFileConfiguration.load(AbstractFileConfiguration.java:283) |
我们在出错之前,如果将此时的url打印出来,结果URL是以wsjar:开头的一个形式。我们此时只需做如下简单的转换,就可以让代码正常工作:
URL url = PropertyGetter.class.getClassLoader().getResource("myconfig.xml");//你将myconfig.xml打在某个jar包中 //加入url转化代码 String urlStr=url.toString(); if(urlStr.startsWith("wsjar:"){ urlStr=urlString.substring(2); url=new URL(urlStr); } //转转换结束 factory.setConfigurationURL(url); |
现在你应该明白了,为什么会在websphere中报wsjar有关的错误了吧。也许你会说,只要我们自己写代码时将wsjar的情况考虑进去不就可以避免了吗?是的,如果我们自己的应用考虑到了wsjar的情况,那么只能说我们自己的代码在websphere运行不会出现类似的问题,但是在许多情况下,我们所使用的开源技术的在很多情况下也会使用 XXXClass.class.getClassLoader().getResource("xxx.properties")的形式,那么如果他们自己没有考虑wsjar的情况,那么他们内部就会报出wsjar相关的错误,这样整个应用也往往因此而启动失败。
二,如何解决wsjar的问题 通过上面的描述,我们已经明白,wsjar的URL格式是Websphere自己特殊的URL格式,解决wsjar的问题,我们需要从几个方面入手
- 对于我们自己的应用。
- 我们自己在写与URL相关的代码时,将wsjar的格式考虑进去,例如我们可以提供一个URL转换类
public class URLPatternResolver {
/** URL protocol for an entry from a jar file: "jar" */ private static final String URL_PROTOCOL_JAR = "jar";
/** URL protocol for an entry from a zip file: "zip" */ private static final String URL_PROTOCOL_ZIP = "zip";
/** URL protocol for an entry from a WebSphere jar file: "wsjar" */ private static final String URL_PROTOCOL_WSJAR = "wsjar";
/** Separator between JAR URL and file path within the JAR */ private static final String JAR_URL_SEPARATOR = "!/";
private static Log logger = LogFactory.getLog(URLPatternResolver.class);
public URLPatternResolver() { super(); }
/** * TODO 增加对zip的转换 * * @param url * 输入的url * @return */ public static URL getStandardURL(String url) { if (url == null) { return null; } if (logger.isDebugEnabled()) { logger.debug("解析URL:" + url); } URL urlObj = null; if (url.startsWith(URL_PROTOCOL_WSJAR)) { if (logger.isDebugEnabled()) { logger.debug("当前使用的是WAS的classloader"); } try { url = url.substring(2); urlObj = new URL(url); } catch (Exception e) { logger.error("URL转换出错!"); return null; } } else if (url.startsWith(URL_PROTOCOL_JAR)) { if (logger.isDebugEnabled()) { logger.debug("当前使用的是普通的classloader"); } try { urlObj = new URL(url); } catch (Exception e) { logger.error("URL转换出错!"); return null; } } else { if (logger.isDebugEnabled()) { logger.debug("当前使用的是普通的classloader"); } try { urlObj = new URL(url); } catch (Exception e) { logger.error("URL转换出错!"); return null; } } return urlObj; }
} |
- 使用上面的转换类,在进行xx.class.getClassLoader().getResource(fileInJar)时,我们加入转换:
... URL url=xx.class.getClassLoader().getResource(fileInJar); url=URLPatternResolver.getStandardURL(url); ... |
- 对于应用中所使用到的开源技术
- 如果我们要使用某些开源技术的某个类的方法,而该方法是以URL对象为输入参数,那么我们在输入URL之前,应该经过类似上面说的转换。
- 到目前为止,一些开源技术例如Spring等,在他们的最新版本中已经加入了对wsjar这中URL格式的支持,如果可以,请替换掉你原先所使用的jar,使用最新版的jar包即可,可以说,现在比较重要的开源技术一般都开始对Websphere 的wsjar格式进行了支持,请检查你所使用的jar是不是最新版的,如果不是而且报错又来源于这个包,则下载一个最新版的。
- 如果我们使用了尚未支持wsjar的开源技术,而且我们必须使用该技术,那么一个不得已的方法就是,下载该技术的开源代码,直接修改其代码使其支持wsjar的格式。
三,从wsjar问题看Websphere
- 我们知道Sun规定的J2EE标准是一个基础标准,应用服务器厂商在实现时,往往都加入了一些甚至许多额外的特性和功能,那么同一个应用程序在向不同的J2EE服务器部署时,可能就会出现不同的问题,当然了一个简单的标准的J2EE应用,在任何J2EE标准服务器上部署应该都是没有太大问题的,但是我们实际中的应用往往都是比较或相当复杂的,当我们基于某个服务器开发时,往往就会在某些方面依赖于该服务器,当移植到其它服务器时,就必须做相应的修改。
- Websphere是IBM的一个重量级J2EE商业服务器,它所实现的复杂程度要比一般的商业J2EE服务器和开源的J2EE服务器要大得多,它对服务器中类加载策略、服务器集群以及性能优化等方面做了大量的工作,就以其类加载策略为例:
- 类加载策略可以在几个层次上进行设置:服务器级别(例如在server1中)、应用级别(例如你的application中)、Web模块级别(应用中如果存在war),在每个级别中,都可以选择parent last或者parent first等等。
- 这多级别的类加载策略,websphere在管理时肯定会加入自己许多额外的东西。
- 关于jar方面,websphere中也有多种层次的jar:webpshere自身的jar、应用自身的jar和共享库中的jar,这多种jar,websphere可能也需要不少额外的东西才能管理的过来,在Websphere中引入wsjar也许就有这方面的原因。
- 但是无论如何,还是要批评Websphere中关于wsjar的引入,毕竟给人们带来了相当的不便,尤其是如何解决wsjar的问题,IBM相关部门、论坛、技术支持等基本都没有给出较好的意见和回复,写上面的文章,主要目的就是为了不让其它人再为wsjar的问题而困扰,不再为得不到IBM有效的支持而感到绝望,同时也希望IBM相关的技术论坛、信息中心(Info Center)能改一改那种官方的面孔,解决问题才是最关键的!