一,概述 我們知道,Websphere有自己的Classloader,它對類的加載做了一些自己的定制,但是往往由于有些定制的特殊性,而造成了人們在上面部署應用的不方面,這其中一個比較大麻煩就是,Webpshere中對jar包中資源的定位URL有自己特殊的格式,例如:
wsjar:file:/C:/dev/ws/default/sample_app/xsd.resources.jar!/org/eclipse/test.xml |
我們知道URL是統(tǒng)一資源定位描述,Sun J2SE標準中,jar包中的資源的URL格式應該是以jar:開頭,即類似如下的樣子
jar:file:/C:/dev/ws/default/sample_app/xsd.resources.jar!/org/eclipse |
而目前大多數(shù)開源技術(shù),在以URL做為參數(shù)來載入實際資源時,并沒有考慮到wsjar開頭的情況,因此當一個較為復雜的應用向Webpshere服務器部署時,往往就會出現(xiàn)關(guān)于這方面的報錯,例如:
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 |
如果你仍然覺得不知道在什么情況下會出現(xiàn)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文件中,執(zhí)行該頁面時就會報如下的錯誤:
[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打印出來,結(jié)果URL是以wsjar:開頭的一個形式。我們此時只需做如下簡單的轉(zhuǎn)換,就可以讓代碼正常工作:
URL url = PropertyGetter.class.getClassLoader().getResource("myconfig.xml");//你將myconfig.xml打在某個jar包中 //加入url轉(zhuǎn)化代碼 String urlStr=url.toString(); if(urlStr.startsWith("wsjar:"){ urlStr=urlString.substring(2); url=new URL(urlStr); } //轉(zhuǎn)轉(zhuǎn)換結(jié)束 factory.setConfigurationURL(url); |
現(xiàn)在你應該明白了,為什么會在websphere中報wsjar有關(guān)的錯誤了吧。也許你會說,只要我們自己寫代碼時將wsjar的情況考慮進去不就可以避免了嗎?是的,如果我們自己的應用考慮到了wsjar的情況,那么只能說我們自己的代碼在websphere運行不會出現(xiàn)類似的問題,但是在許多情況下,我們所使用的開源技術(shù)的在很多情況下也會使用 XXXClass.class.getClassLoader().getResource("xxx.properties")的形式,那么如果他們自己沒有考慮wsjar的情況,那么他們內(nèi)部就會報出wsjar相關(guān)的錯誤,這樣整個應用也往往因此而啟動失敗。
二,如何解決wsjar的問題 通過上面的描述,我們已經(jīng)明白,wsjar的URL格式是Websphere自己特殊的URL格式,解決wsjar的問題,我們需要從幾個方面入手
- 對于我們自己的應用。
- 我們自己在寫與URL相關(guān)的代碼時,將wsjar的格式考慮進去,例如我們可以提供一個URL轉(zhuǎn)換類
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的轉(zhuǎn)換 * * @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轉(zhuǎn)換出錯!"); 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轉(zhuǎn)換出錯!"); return null; } } else { if (logger.isDebugEnabled()) { logger.debug("當前使用的是普通的classloader"); } try { urlObj = new URL(url); } catch (Exception e) { logger.error("URL轉(zhuǎn)換出錯!"); return null; } } return urlObj; }
} |
- 使用上面的轉(zhuǎn)換類,在進行xx.class.getClassLoader().getResource(fileInJar)時,我們加入轉(zhuǎn)換:
... URL url=xx.class.getClassLoader().getResource(fileInJar); url=URLPatternResolver.getStandardURL(url); ... |
- 對于應用中所使用到的開源技術(shù)
- 如果我們要使用某些開源技術(shù)的某個類的方法,而該方法是以URL對象為輸入?yún)?shù),那么我們在輸入URL之前,應該經(jīng)過類似上面說的轉(zhuǎn)換。
- 到目前為止,一些開源技術(shù)例如Spring等,在他們的最新版本中已經(jīng)加入了對wsjar這中URL格式的支持,如果可以,請?zhí)鎿Q掉你原先所使用的jar,使用最新版的jar包即可,可以說,現(xiàn)在比較重要的開源技術(shù)一般都開始對Websphere 的wsjar格式進行了支持,請檢查你所使用的jar是不是最新版的,如果不是而且報錯又來源于這個包,則下載一個最新版的。
- 如果我們使用了尚未支持wsjar的開源技術(shù),而且我們必須使用該技術(shù),那么一個不得已的方法就是,下載該技術(shù)的開源代碼,直接修改其代碼使其支持wsjar的格式。
三,從wsjar問題看Websphere
- 我們知道Sun規(guī)定的J2EE標準是一個基礎(chǔ)標準,應用服務器廠商在實現(xiàn)時,往往都加入了一些甚至許多額外的特性和功能,那么同一個應用程序在向不同的J2EE服務器部署時,可能就會出現(xiàn)不同的問題,當然了一個簡單的標準的J2EE應用,在任何J2EE標準服務器上部署應該都是沒有太大問題的,但是我們實際中的應用往往都是比較或相當復雜的,當我們基于某個服務器開發(fā)時,往往就會在某些方面依賴于該服務器,當移植到其它服務器時,就必須做相應的修改。
- Websphere是IBM的一個重量級J2EE商業(yè)服務器,它所實現(xiàn)的復雜程度要比一般的商業(yè)J2EE服務器和開源的J2EE服務器要大得多,它對服務器中類加載策略、服務器集群以及性能優(yōu)化等方面做了大量的工作,就以其類加載策略為例:
- 類加載策略可以在幾個層次上進行設置:服務器級別(例如在server1中)、應用級別(例如你的application中)、Web模塊級別(應用中如果存在war),在每個級別中,都可以選擇parent last或者parent first等等。
- 這多級別的類加載策略,websphere在管理時肯定會加入自己許多額外的東西。
- 關(guān)于jar方面,websphere中也有多種層次的jar:webpshere自身的jar、應用自身的jar和共享庫中的jar,這多種jar,websphere可能也需要不少額外的東西才能管理的過來,在Websphere中引入wsjar也許就有這方面的原因。
- 但是無論如何,還是要批評Websphere中關(guān)于wsjar的引入,畢竟給人們帶來了相當?shù)牟槐?,尤其是如何解決wsjar的問題,IBM相關(guān)部門、論壇、技術(shù)支持等基本都沒有給出較好的意見和回復,寫上面的文章,主要目的就是為了不讓其它人再為wsjar的問題而困擾,不再為得不到IBM有效的支持而感到絕望,同時也希望IBM相關(guān)的技術(shù)論壇、信息中心(Info Center)能改一改那種官方的面孔,解決問題才是最關(guān)鍵的!