読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

XercesのおかげでWebアプリケーションリロードが失敗

Java界におけるXMLパーサーの標準実装といえばXerces
最近はXMLを扱わないWebアプリケーションは滅多にない(設定ファイルとかは大抵XML)と思うので、非常に高い確率でxercesImpl.jarを(間接的に)使っていると思います。


ところがこのXerces、Webアプリケーションのリロード時にちょっとした問題を引き起こしてくれやがります orz

ClassCastException

Xercesはクラスローダーとの絡みで問題があるらしくTomcat上でアプリケーションのリロードを行う(通常、クラスファイルの更新を検知するとEclipseが自動的に行う)と、次のようなエラーが出ます。

java.lang.ClassCastException:
    org.apache.xerces.parsers.XIncludeAwareParserConfiguration cannot be cast to org.apache.xerces.xni.parser.XMLParserConfiguration
    at org.apache.xerces.parsers.SAXParser.(Unknown Source)
    at org.apache.xerces.parsers.SAXParser.(Unknown Source)
    at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.(Unknown Source)
    at org.apache.xerces.jaxp.SAXParserImpl.(Unknown Source)
    at org.apache.xerces.jaxp.SAXParserFactoryImpl.newSAXParser(Unknown Source)

これは、どうやらリロード時にWebアプリケーションコンテキストクラスローダが入れ替えになることから、
本来ならキャストに成功するはずのクラスなのにキャストに失敗しているみたいです。


ということは、WebアプリケーションコンテキストクラスローダでXercesのクラスをロードさせなければよいので、
Tomcatの起動構成を次のように設定しました。『ユーザエントリ』へ『外部Jarの追加』をすればよいです。



あと、/WEB-INF/lib配下にxercesImpl.jarがコピーされないようにする必要があります。
これはEclipseでどう設定すればよいのかわからなかったので、
皆様方におかれましては、気合いで乗り切って頂きますようお願い申し上げます(-_-)

(追記)
/WEB-INF/libにxercesImpl.jarがコピーされても、それは読まれないので問題ないです。

OutOfMemoryError

さて、これでアプリケーションのリロードは出来るようになったのですが、何度かリロードしていると次のようなエラーが出ます。
どうやらパーマネント領域のメモリが足りてないようです。

java.lang.OutOfMemoryError: PermGen space 
    java.lang.ClassLoader.defineClass1(Native Method)
    java.lang.ClassLoader.defineClass(ClassLoader.java:621)
    java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
    org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1850)
    org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:890)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1354)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1233)
    java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
    org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:156)
    org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:134)
    org.eclipse.persistence.internal.jpa.EJBQueryImpl.(EJBQueryImpl.java:91)
    org.eclipse.persistence.internal.jpa.EJBQueryImpl.(EJBQueryImpl.java:78)
    org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1065)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    java.lang.reflect.Method.invoke(Method.java:597)
    org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:198)
    ...


そこで、Tomcatの起動パラメータにこれを追加します。

(注: 値は自分の環境に合わせて増減する必要があるかもしれません)

-XX:MaxPermSize=256m


これでうまくいきました。