Hibernate使用的文法分析器是antlr,WebLogic一樣也是。java
很多用戶碰到ClassNotFoundException: org.hibernate.hql.ast.HqlToken的典型問題,這個典型問題已經經過配置weblogic.xml,要求Web應用優先加載WEB-INF的Jar(即應用Classloader)而非WebLogic的System Classloader得以勉強解決:web
<weblogic-web-app> <container-descriptor> <prefer-web-inf-classes>true</prefer-web-inf-classes> </container-descriptor> </weblogic-web-app>
Hibernate 3.0的用戶發現上述的方法依然不能解決問題,由於Hibernate使用了Class.forName去Load一個類,而JVM加載類的方式是先 System Classloader(WebLogic),後Application Classloader(Web應用),因而,應用即便採用prefer-web-inf-classes策略,但Class.forName仍是 Load了WebLogic的Antlr!!app
Hibernate 3.1迅速解決了這個問題,Hibernate會使用context classloader,Class.forName這種罪惡的代碼已經被消除掉。jsp
無獨有偶,2005年,澳大利亞的Suncorp-Metway公司使用Hibernate3.0.3 + WebLogic8.1SP4開發他們的應用 , 當時的WebLogic 8.1SP4內置了antlr 2.7.1,跟Hibernate 3.0.3捆綁的antlr-2.7.5H3.jar衝突,Suncorp-Metway公司但願改變WebLogic的SystemClassPath(加入antlr-2.7.5H3.jar), 效果等於替換了WebLogic的Antlr,這固然可以解決Hibernate的問題,但天知道會帶來什麼問題,由於WebLogic自身會使用 Antlr去分析EJBQL(若是不適用CMP,用戶徹底能夠無論3721,置換WebLogic的Antlr版本了之),Suncorp-Metway公司擔憂這種替換對系統帶來其餘影響,但願BEA仍然可以提供技術支持。hibernate
當時,做爲BEA在澳大利亞昆士蘭州最大的BEA客戶,他們的建議固然受到足夠的尊重。 爲此,BEA 8.1 SP4/SP5都提供了一個補丁,等同於升級了WebLogic 8.1自帶的Antlr的版本,WebLogic 8.1SP6以及WebLogic 9.1開始都沿用這個變動。code
很明顯,這種辦法不可能再次沿用到之後的SP,由於Antlr以及Hibernate的版本的更新速度遠遠高於WebLogic Service pack的更新速度,BEA不可能不斷地重構Antlr來適應開源突飛猛進的API變化。xml
WebLogic 8.1 SP6以後,當使用Hibernate 3(策略是<prefer-web-inf-classes>true</prefer-web-inf-classes>)的 Antlr 2.7.6時候,輪到WebLogic自身的Servlet容器出問題(Hibernate好了,WebLogic出事了)拋出以下的Antlr異常:blog
java.lang.ClassCastException: antlr.CommonToken
at antlr.CharScanner.makeToken(CharScanner.java:175)
at weblogic.servlet.jsp.JspLexer.mWORD(JspLexer.java:4723)
at weblogic.servlet.jsp.JspLexer.mXML_ATTRIBUTES(JspLexer.java:4309)
at weblogic.servlet.jsp.JspLexer.mTAGLIB_DIRECTIVE_BODY(JspLexer.java:5034)
at weblogic.servlet.jsp.JspLexer.mTAGLIB_DIRECTIVE(JspLexer.java:4905)
at weblogic.servlet.jsp.JspLexer.mDIRECTIVE(JspLexer.java:4751)
at weblogic.servlet.jsp.JspLexer.mSTANDARD_THING(JspLexer.java:2161)
at weblogic.servlet.jsp.JspLexer.mTOKEN(JspLexer.java:1947)
at weblogic.servlet.jsp.JspLexer.nextToken(JspLexer.java:1820)
at weblogic.servlet.jsp.JspLexer.nextToken(JspLexer.java:1820)
at weblogic.servlet.jsp.JspLexer.parse(JspLexer.java:963)
at weblogic.servlet.jsp.JspParser.doit(JspParser.java:106)
at weblogic.servlet.jsp.JspParser.parse(JspParser.java:234)
at weblogic.servlet.jsp.Jsp2Java.outputs(Jsp2Java.java:125) token
這個問題是WebLogic Servlet容器沒有意識到Hibernate用戶對ANTLR的版本需求:ip
1) 在老的Antlr 2.7.1中,WebLogic的Servlet容器使用antlr.CharScanner經過下面的方法 Class.forName(String className) 加 載token,由於jsp編譯的classes自己是基於WebLogic系統Classloader,因而,Classloader固然使用 WebLogic的Antlr版本(2.7.1),WebLogic沒有被Hibernate的Antlr(2.7.6)所影 響,Class.forName轉型成System CL所規約的 antlr.CommonToken不會有問題。
2) 而 當WebLogic Antlr 2.7.1+Hibernate Antlr2.7.6一塊兒使用的時候,Servlet容器在CharScanner使用 Class.forName(String, boolean, ClassLoader)去加載token,而第三個參數是注入了當前的應用的Classloader,即WebLogic被迫使用了Hibernate 的Antlr 2.7.6,而最終會致使容器在將Antlr 2.7.6的 CommonToken轉型成Antlr 2.7.1的CommonToken出現java.lang.ClassCastException。
這種狀況,只能作兩件事情:
1)向BEA索取此Case的補丁
2)前置System ClassPath,讓新版本的Antlr永遠放在前面。
講述了這麼多東西,其實,最終想揭示的問題是,不管是Hibernate和 WebLogic,在一開始的時候都沒有意識到開源代碼重構的重要性。早期的WebLogic確實已經XML Parser付出版本衝突的代價,因此後期,不少XML包都被WebLogic從新Rename Package(重構代碼的一種方式)。 若是一開始,WebLogic就使用com.bea.opensource.antlr來讓本身的容器使用Antlr,ANTLR衝突的這些問題確定不會出現。
WebLogic 10確實開始大規模重構Jar包,不幸的是,這個世界上有一種潛規則叫作向後兼容,因此,<prefer-web-inf-classes>true</prefer-web-inf-classes>是咱們J2EE初級階段仍然須要接受的現實,固然,你能夠期待另一種技術解決此問題——OSGI,事實上,這個版本可能比咱們想象的要快,或者就在WebLogic 12g。