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
これでうまくいきました。