CLOVER🍀

That was when it all began.

WildFly SwarmとJAX-RSに、Jackson Scala Moduleを加えて

個人的にはJava EEとの付き合い方としていろいろ思うところはあるのですが、なんだかんだで
Java EEも気になるしで、スタンドアロンなものについてはEE継続でいいのかなぁとちょっと思ったり。

で、前にちょこっとWildFly SwarmのMicroProfileで遊んだわけですが、あとでいろいろ遊ぼうとすると
やっぱりJPAとかないのは厳しいかもとか思いはじめ、ふつうに使うことにしました。
※ただし、なぜかめちゃくちゃハマりましたが(後述)

手始めには、JAX-RSをやりたいと思います。Jackson Scala Moduleを付けて。

GitHub - FasterXML/jackson-module-scala: Add-on module for Jackson (http://wiki.fasterxml.com/JacksonHome) to support Scala-specific datatypes

ブレずにScalaでいこうと思います。

準備

Mavenの設定。
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/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>

WildFly Swarmのバージョンは、「2016.12.0」です。

サンプルコード

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とかやったら詰みそう…。なにが悪いんでしょう…。