Jetty:WebAppContext.setClassLoader

2019年5月15日

jettyについての全投稿は/tag/jettyにあるので参照されたい

これはJettyをアプリ組み込みで使う際の話であることに注意。

Jetty:WebAppContext.setParentLoaderPriorityを利用してアプリを構成していたのだが、ある状況においては、アプリ側が確実に存在するはずのライブラリをロードできないことがある。

例えば以下だ。

java.lang.ClassNotFoundException: com.google.inject.servlet.GuiceServletContextListener
    at java.base/java.net.URLClassLoader.findClass(Unknown Source)
    at org.eclipse.jetty.webapp.WebAppClassLoader.findClass(WebAppClassLoader.java:510)
    at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:441)
    at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:403)
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(Unknown Source)
    at java.base/java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.base/java.net.URLClassLoader.defineClass(Unknown Source)
    at java.base/java.net.URLClassLoader.access$100(Unknown Source)
    at java.base/java.net.URLClassLoader$1.run(Unknown Source)
    at java.base/java.net.URLClassLoader$1.run(Unknown Source)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.net.URLClassLoader.findClass(Unknown Source)
    at org.eclipse.jetty.webapp.WebAppClassLoader.findClass(WebAppClassLoader.java:510)

どうもorg.eclipse.jetty.webapp.WebAppClassLoaderにバグがあるような気がするのだが、たしかではない。ともあれ、この解決方法としては、単純にJettyがウェブアプリごとに用意するクラスロードをやめさせ、システムクラスローダを使ってしまうという方法がある。

Jetty Classloadingの最後に記述がある。

単純に、以下のようにすればよい。

class SomeClass {
  public void someMethod() {
    WebAppContext warContext = new WebAppContext();
    warContext.setClassLoader(SomeClass.class.getClassLoader());
  }
}

こうすれば、コンテナ側(アプリ本体)と、その組み込みJetty上のウェブアプリのクラスローダが同一になるようだ。当然のことながら、「warContext.setParentLoaderPriority(true);」は不要になる。