個人的にはJava EEとの付き合い方としていろいろ思うところはあるのですが、なんだかんだで
Java EEも気になるしで、スタンドアロンなものについてはEE継続でいいのかなぁとちょっと思ったり。
で、前にちょこっとWildFly SwarmのMicroProfileで遊んだわけですが、あとでいろいろ遊ぼうとすると
やっぱりJPAとかないのは厳しいかもとか思いはじめ、ふつうに使うことにしました。
※ただし、なぜかめちゃくちゃハマりましたが(後述)
手始めには、JAX-RSをやりたいと思います。Jackson Scala Moduleを付けて。
ブレずにScalaでいこうと思います。
準備
<?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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.littlewings</groupId> <artifactId>resteasy-jackson2-scala-module</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <scala.major.version>2.11</scala.major.version> <scala.version>${scala.major.version}.8</scala.version> <scala.maven.plugin.version>3.2.2</scala.maven.plugin.version> <failOnMissingWebXml>false</failOnMissingWebXml> <wildfly.swarm.version>2016.12.0</wildfly.swarm.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>bom</artifactId> <version>${wildfly.swarm.version}</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>${scala.version}</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>jaxrs</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-scala_${scala.major.version}</artifactId> <version>2.7.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>${scala.maven.plugin.version}</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <scalaVersion>${scala.version}</scalaVersion> <args> <arg>-Xlint</arg> <arg>-unchecked</arg> <arg>-deprecation</arg> <arg>-feature</arg> </args> <recompileMode>incremental</recompileMode> </configuration> </plugin> <plugin> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-plugin</artifactId> <version>${wildfly.swarm.version}</version> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
RESTEasyにJacksonのScala Moduleを追加。RESTEasyが依存しているJacksonとのバージョンの関係で、Jacksonの
Scala Moduleは2.7系にしました。これに伴い、Scalaは2.11に…。
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>jaxrs</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-scala_${scala.major.version}</artifactId> <version>2.7.4</version> </dependency>
いろいろあって、WARになりました。
<packaging>war</packaging>
あと、wildfly-swarm-pluginにmainClassは指定しませんでした。
<plugin> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-plugin</artifactId> <version>${wildfly.swarm.version}</version> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin>
サンプルコード
JAX-RSリソースクラス。せっかくなので、リクエストとレスポンスにはScalaのCase Classを使用します。
src/main/scala/org/littlewings/wildflyswarm/jaxrs/CalcResource.scala
package org.littlewings.wildflyswarm.jaxrs import javax.ws.rs._ import javax.ws.rs.core.MediaType @Path("calc") class CalcResource { @POST @Path("add") @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) def add(request: AddRequest): AddResponse = AddResponse(request.a + request.b) } case class AddRequest(a: Int, b: Int) case class AddResponse(result: Int)
Applicationのサブクラスは、WildFly Swarmが提供するクラス(org.wildfly.swarm.generated.WildFlySwarmDefaultJAXRSApplication)にお任せします。
src/main/scala/org/littlewings/wildflyswarm/jaxrs/ScalaObjectMapperProvider.scala
package org.littlewings.wildflyswarm.jaxrs import javax.ws.rs.core.MediaType import javax.ws.rs.ext.{ContextResolver, Provider} import javax.ws.rs.{Consumes, Produces} import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.scala.DefaultScalaModule @Provider @Consumes(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON)) class ScalaObjectMapperProvider extends ContextResolver[ObjectMapper] { override def getContext(typ: Class[_]): ObjectMapper = { val objectMapper = new ObjectMapper objectMapper.registerModule(DefaultScalaModule) objectMapper } }
動作確認
起動。
$ mvn wildfly-swarm:run
または
$ mvn package && java -jar target/resteasy-jackson2-scala-module-0.0.1-SNAPSHOT-swarm.jar
確認。
$ curl -XPOST -H 'Content-Type: application/json' 'http://localhost:8080/calc/add' -d '{"a": 5, "b": 3}' {"result":8}
OKそうです。
まとめ
とりあえず、JAX-RS(RESTEasy)にJackson Scala Moduleを追加して動かしてみました。Jackson Scala Moduleの
組み込み自体は、割と簡単に動きました。META-INF/services/javax.ws.rs.ext.Providersにクラス名を
書いたりしなくていいんでしたっけ?という気がしないでもないですが…。
今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/wildfly-swarm-scala-examples/tree/master/resteasy-jackson2-scala-module
ハマったこと
実は、起動するだけでかなりハマりました。
実行はUbuntu Linux 14.04 LTS + Oracle JDK 8なのですが、とにかくwildfly-swarm-pluginのmainClassを
指定すると、以下のようなエラーを引きます。
12 08, 2016 11:59:17 午後 org.wildfly.swarm.container.runtime.cli.CommandLineArgsServiceActivator activate INFO: WFSWARM0029: Install MSC service for command line args: [] 12 08, 2016 11:59:17 午後 org.jboss.as.controller.AbstractOperationContext executeStep ERROR: WFLYCTL0013: Operation ("parallel-extension-add") failed - address: ([]) java.lang.RuntimeException: WFLYCTL0079: Failed initializing module org.jboss.as.logging at org.jboss.as.controller.extension.ParallelExtensionAddHandler$1.execute(ParallelExtensionAddHandler.java:115) at org.jboss.as.controller.AbstractOperationContext.executeStep(AbstractOperationContext.java:890) at org.jboss.as.controller.AbstractOperationContext.processStages(AbstractOperationContext.java:659) at org.jboss.as.controller.AbstractOperationContext.executeOperation(AbstractOperationContext.java:370) at org.jboss.as.controller.OperationContextImpl.executeOperation(OperationContextImpl.java:1322) at org.jboss.as.controller.ModelControllerImpl.boot(ModelControllerImpl.java:468) at org.jboss.as.controller.AbstractControllerService.boot(AbstractControllerService.java:387) at org.jboss.as.controller.AbstractControllerService.boot(AbstractControllerService.java:349) at org.jboss.as.server.ServerService.boot(ServerService.java:397) at org.jboss.as.server.ServerService.boot(ServerService.java:366) at org.jboss.as.controller.AbstractControllerService$1.run(AbstractControllerService.java:299) at java.lang.Thread.run(Thread.java:745) Caused by: java.util.concurrent.ExecutionException: java.lang.IllegalStateException: WFLYLOG0078: The logging subsystem requires the log manager to be org.jboss.logmanager.LogManager. The subsystem has not be initialized and cannot be used. To use JBoss Log Manager you must add the system property "java.util.logging.manager" and set it to "org.jboss.logmanager.LogManager" at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:192) at org.jboss.as.controller.extension.ParallelExtensionAddHandler$1.execute(ParallelExtensionAddHandler.java:107) ... 11 more
この内容…。
org.jboss.as.logging issue with really simple jaxrs project
packagingをJARにしてもWARにしても、mainClassを指定するととにかく失敗する…。
WARにしてmainClassを指定しないようにすると、起動してくれたので今回はこちら。
とはいえ、それだけだとIDEからの起動が厳しいので、簡易的にこういうのを用意。
src/main/scala/org/littlewings/wildflyswarm/jaxrs/App.scala
package org.littlewings.wildflyswarm.jaxrs import org.wildfly.swarm.Swarm object App { def main(args: Array[String]): Unit = { Swarm.main(args: _*) } }
でもこの形態だと、いざという時にカスタマイズ(DeploymentにFractionの追加)とかできないですよねー。
Service Discoveryとかやったら詰みそう…。なにが悪いんでしょう…。