こちらのエントリの続きです。
組み込みTomcatでJAX-RS(RESTEasy)とCDIを使う
http://d.hatena.ne.jp/Kazuhira/20150308/1425780313
こちらを書いた時は、とりあえずsbt runで起動して動作確認したところまでですが、なんとなくFat JARにしてみたいものです。
というわけで、やってみました。やるために設定したファイルは、こちらになります。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.littlewings</groupId> <artifactId>embedded-tomcat-jaxrs-cdi</artifactId> <packaging>jar</packaging> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-logging-juli</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-servlet-initializer</artifactId> <version>3.0.10.Final</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-cdi</artifactId> <version>3.0.10.Final</version> </dependency> <dependency> <groupId>org.jboss.weld.servlet</groupId> <artifactId>weld-servlet</artifactId> <version>2.2.9.Final</version> </dependency> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>2.11.6</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.2.2.RELEASE</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>3.2.0</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <scalaVersion>2.11.6</scalaVersion> <args> <arg>-Xlint</arg> <arg>-unchecked</arg> <arg>-deprecation</arg> <arg>-feature</arg> </args> <recompileMode>incremental</recompileMode> </configuration> </plugin> </plugins> </build> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> </project>
というわけで、Mavenになりました。
いや、sbt-assemblyとかちゃんと見てみたのですけど、JARファイルをJARの中に持つみたいな形式はサポートしてなさそうでして。sbt-one-jarというものもあるのですが、こちらは停止して長いようですし。
用途が用途なので、素直にMavenにしました。
で、よくよく見ると使っているのがSpring Boot Maven Pluginです。
<plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.2.2.RELEASE</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>
なんか、ランチャーっぽくて単体でも使えそうな感じだったので、使ってみたらOKそうでした。
Spring Boot Maven plugin
http://docs.spring.io/spring-boot/docs/current/reference/html/build-tool-plugins-maven-plugin.html
executionを指定するところがポイントのようです。
<executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions>
maven-shade-pluginというのも思い浮かびますが、依存するJARは展開したくないということと、unpackしない設定もできるようなのですがうまくやれずに挫折しました…。
コードは基本的に前回のままですが、mainクラスのみ再度載せます。
src/main/scala/org/littlewings/javaee7/TomcatBootstrap.scala
package org.littlewings.javaee7 import scala.io.StdIn import java.io.File import org.apache.catalina.startup.Tomcat import org.apache.tomcat.util.descriptor.web.ContextResource object TomcatBootstrap { def main(args: Array[String]): Unit = { val port = 8080 val tomcat = new Tomcat // ポートはデフォルトで8080 tomcat.setPort(port) try { // ベースのディレクトリ、DocbaseはSpring Bootを参考に tomcat.setBaseDir(createTempDir("tomcat", port).getAbsolutePath) val context = tomcat.addWebapp("", createTempDir("tomcat-docbase", port).getAbsolutePath) // CDIでWEB-INF/classesに配置されていなくても対象とされる、「flat」に設定 context.addParameter("org.jboss.weld.environment.servlet.archive.isolation", "false") // RESTEasyとCDIの統合 context.addParameter("resteasy.injector.factory", "org.jboss.resteasy.cdi.CdiInjectorFactory") // 組み込みTomcatでJNDIを有効に tomcat.enableNaming() // BeanManaerをJNDIリソースとして定義 val resource = new ContextResource resource.setAuth("Container") resource.setName("BeanManager") resource.setType("javax.enterprise.inject.spi.BeanManager") resource.setProperty("factory", "org.jboss.weld.resources.ManagerObjectFactory") context.getNamingResources.addResource(resource) // Tomcatの起動 tomcat.start() // Enter打ったら終了 StdIn.readLine("> Enter stop") // 普通、待機はこっち // tomcat.getServer.await() } finally { // Tomcatの破棄と停止 tomcat.stop() tomcat.destroy() } } def createTempDir(prefix: String, port: Int): File = { val tempDir = File.createTempFile(s"${prefix}.", s".${port}") tempDir.delete() tempDir.mkdir() tempDir.deleteOnExit() tempDir } }
で、こちらをパッケージング。
$ mvn package
実行!
$ java -jar target/embedded-tomcat-jaxrs-cdi-0.0.1-SNAPSHOT.jar
すると、立ち上がってくれそうな挙動をしますが
3 09, 2015 12:14:01 午前 org.apache.coyote.AbstractProtocol init 情報: Initializing ProtocolHandler ["http-nio-8080"] 3 09, 2015 12:14:01 午前 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector 情報: Using a shared selector for servlet write/read
途中でJspServletが見つからないと言って、コケてくれます。
重大: Servlet [jsp] in web application [] threw load() exception java.lang.ClassNotFoundException: org.apache.jasper.servlet.JspServlet
Fat JARの中にはJspServletは含まれているのですが(tomcat-jni-X.Y.Z.jarの中にいます)、これが見えていない様子。ClassLoaderっぽいですね。
Spring Bootではこういうことにならないので、細工があるのだろうと確認してみると、Contextに親ClassLoaderを設定すればよさそうです。
Contextに対して親ClassLoaderを設定するには、1度StandardContextにキャストする必要があります。
なので、import文を追加して
import org.apache.catalina.core.StandardContext
取得したContextに対して、ClassLoaderを設定。
val context = tomcat.addWebapp("", createTempDir("tomcat-docbase", port).getAbsolutePath) // 親ClassLoaderを、ContextClassLoaderに設定 context .asInstanceOf[StandardContext] .setParentClassLoader(Thread.currentThread.getContextClassLoader)
ここまでやって、再度パッケージングして実行すると、今度は起動するようになります。
なんかJava EEとSpring Bootが入り乱れたみたいな感じになりましたけど、とりあえず目的は達成(?)できました。
こちらに置いているソースコードにも、反映してあります。
https://github.com/kazuhira-r/javaee7-scala-examples/tree/master/embedded-tomcat-jaxrs-cdi