Hello, WildFly Swarm!
まずは動作確認がてら、Hello World レベルの JAX-RS アプリケーションを作成します。
JAX-RS とは Java EE の仕様の 1 つであり、RESTful な API をアノテーションベースで作成することができます
完成版は以下リポジトリにありますので、適宜参照ください。
https://github.com/emag/wildfly-swarm-tour/tree/2017.1.1/code/helloworld
まずは適当なディレクトリに移動し、こちらで用意した雛形のプロジェクト(helloworld_initial)を helloworld プロジェクトとしてコピーします。
$ curl -sL https://github.com/emag/wildfly-swarm-tour/archive/2017.1.1.zip -o /tmp/wildfly-swarm-tour.zip \
  && unzip -q /tmp/wildfly-swarm-tour.zip -d /tmp/ \
  && cp -rp /tmp/wildfly-swarm-tour-2017.1.1/code/helloworld_initial helloworld
IDE を利用される方はこの helloworld プロジェクトをインポートしてください。
次に、以下のように 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>wildflyswarmtour</groupId>
  <artifactId>helloworld</artifactId>
  <version>2017.1.1</version>
  <!-- (1) jar としてパッケージング -->
  <packaging>jar</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>
    <version.wildfly-swarm>${project.version}</version.wildfly-swarm>
  </properties>
  <dependencyManagement>
    <dependencies>
      <!-- (2) bom-all を指定する -->
      <dependency>
        <groupId>org.wildfly.swarm</groupId>
        <artifactId>bom-all</artifactId>
        <version>${version.wildfly-swarm}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <!-- (3) JAX-RS を使う -->
    <dependency>
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jaxrs</artifactId>
    </dependency>
  </dependencies>
  <build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
      <!-- (4) 実行可能 jar(uber jar)を作成するためのプラグイン -->
      <plugin>
        <groupId>org.wildfly.swarm</groupId>
        <artifactId>wildfly-swarm-plugin</artifactId>
        <version>${version.wildfly-swarm}</version>
        <configuration>
          <!-- (5) main() を持つクラスを指定 -->
          <mainClass>wildflyswarm.Bootstrap</mainClass>
        </configuration>
        <executions>
          <execution>
            <goals>
              <!-- (6)  package ゴールで動くようにする -->
              <goal>package</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
ここでは jar でパッケージングするようにしています(1)。デプロイするアプリケーションの設定や WildFly 自体の設定(データソースなど)を 1 から行いたい場合に jar パッケージングを選択します。
WildFly Swarm には必要なモジュール(Fraction と呼ばれます)だけ選んで使うことができ、各 Fraction をバージョン指定することもできます。しかし、上記の (2) のように用意されている bill of materials(BOM)を利用すると Fraction のバージョンを意識しないですむようになるので、こちらの利用を推奨します。
なお、ここでは bom-all を選択しましたが、その他に bom や bom-experimental などが存在します。各 Fraction には安定度が設定されており、安定度によってどの bom-* に含まれるかが決まっています。安定度の見方については以下を参照してください。
http://wildfly-swarm.io/posts/announcing-wildfly-swarm-2016-8-1/#_fraction_stability_indicators
JAX-RS Fraction をここでは利用します(3)。上述の BOM により、version 指定は不要です。
WildFly Swarm は実行可能 jar(uber jar)を作成するプラグインを提供しており、アプリケーションのエントリポイントとなる main() メソッドを持つクラスを指定します(5)。また、このプラグインは Maven の package 時に実行されるようにするとよいでしょう。
次に JAX-RS のリソースクラス(helloworld.HelloWorld)を作成します。
package helloworld;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/hello") //(1)
public class HelloWorld {
  @GET //(2)
  @Produces(MediaType.APPLICATION_JSON) // (3)
  public String hello() { // (4)
    return "{\"message\" : \"Hello, WildFly Swarm!\"}";
  }
}
上記クラスはアノテーションによってリソースパスとして (1) を、リクエストする際のメソッドとして (2) を定義しており、GET /hello と HTTP リクエストすると、(4) のhello() メソッドが実行されます。ここでは return に指定されている JSON フォーマットの文字列をレスポンスします。また、(3) によってレスポンスヘッダに Content-Type: application/json がつけられます。これらは WildFly Swarm とは関係ない、JAX-RS を利用したふつうのコードです。
次にもろもろの設定をする wildflyswarm.Bootstrap クラスを以下のように作ります。これが WildFly Swarm 利用時の固有クラスです。pom.xml で mainClass に指定したクラスですね。
package wildflyswarm;
import helloworld.HelloWorld;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.wildfly.swarm.Swarm;
import org.wildfly.swarm.jaxrs.JAXRSArchive;
public class Bootstrap {
  public static void main(String[] args) throws Exception {
    Swarm swarm = new Swarm(args); // (1)
    // (4) ShrinkWrap の API を使ってデプロイできる形にパッケージング
    JAXRSArchive archive = ShrinkWrap.create(JAXRSArchive.class);
    archive.addClass(HelloWorld.class); // (5)
    swarm
      .start() // (2)
      .deploy(archive); // (3)
  }
}
org.wildfly.swarm.Swarm インスタンス(1)を介して WildFly の設定や起動(2)、アプリケーションのデプロイ(3)を行えます。この Hello World アプリケーションでは特に設定することはないのですが、次章ではデータベースを使うためデータソースの設定をします。
(4) がデプロイするアプリケーションの設定です。これは ShrinkWrap という指定したクラスやリソースファイルを jar や war といった形にオンデマンドでアーカイブするライブラリを利用しています。ここでは先ほど作成した JAX-RS の HelloWorld クラスを追加しています(5)。
なお、
org.wildfly.swarm.Swarm#start(Archive<?> deployment)もあるため、(2) の部分はswarm.start(archive)とすることもできます。
ここまででだいたい以下のようなディレクトリ構成になっているかと思います。
.
├── pom.xml
└── src
    └── main
        └── java
            ├── helloworld
            │   └── HelloWorld.java
            └── wildflyswarm
                └── Bootstrap.java
必要なものはそろったので、以下コマンドを実行しビルドします。
$ ./mvnw clean package
スーパー jar ダウンロードタイムが始まるのでコーヒーでも用意して気長にお待ちください。 たぶん、初回は 10 分くらいかかるかもしれません。-T2 とかつけるとちょっとマシかも。
ビルドが成功すると target 以下に helloworld-swarm.jar という uber jar ができています。
では、さっそくこのアプリケーションを動かしてみましょう。
$ java -jar target/helloworld-swarm.jar
WildFly をお使いの方にはおなじみの WildFly の起動およびアプリケーションのデプロイといったログが出力されます。
ではアクセス。
$ curl localhost:8080/hello
{"message" : "Hello, WildFly Swarm!"}
やりましたね。通常はここで WildFly をダウンロード、アプリをビルドしてから WildFly にデプロイといった手順が入りますが、WildFly Swarm では不要です。
Spring Boot をはじめて触った時の感動が蘇ってきますね。
アプリケーションを作って実行するところまではなんとなくつかめたでしょうか? では次の章では CRUD なアプリケーションに取りかかっていきます。
以降は補足なので、読まなくても差し支えありません。
補足1 java -jar 以外の実行方法
このドキュメントでは ./mvnw package でビルドを行い、java -jar で生成された uber jar を指定して実行するようにしていますが、その他にも実行方法があります。
wildfly-swarm:run
WildFly Swarm Plugin は wildfly-swarm:run というゴールが用意されています。
$ ./mvnw wildfly-swarm:run
IDE からの実行
Bootstrap クラスは main() メソッドを持つクラスですので、各 IDE からこの main() メソッドを指定して実行することもできます。
補足2 javax.ws.rs.core.Application を extends したクラスは?
今までに JAX-RS を触ったことがある方は javax.ws.rs.core.Application を extends したクラスを用意していないことに気づかれたかと思います。
通常、JAX-RS を Java EE アプリケーションサーバで利用する場合は、JAX-RS の有効化のために以下のようなクラス(または web.xml での定義)が必要です。
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/")
public class JaxRsActivator extends Application {}
JAX-RS Fraction は上記のようなクラスが見つからない場合、org.wildfly.swarm.generated.WildFlySwarmDefaultJAXRSApplication というクラスを生成します。このクラスの @ApplicationPath には "/" が設定されています。
@ApplicationPath に別の値を設定したい場合は別途上記のようなクラスを自分で用意します。ここでは横着して、さっき作った HelloWorld に @ApplicationPath をつけたうえで javax.ws.rs.core.Application を継承させてしまいましょう。
@ApplicationPath("/api") // 追加
@Path("/hello")
public class HelloWildFlySwarm extends Application { // 追加
では再ビルドしてアクセス。
$ ./mvnw clean package && java -jar target/helloworld-swarm.jar
$ curl localhost:8080/api/hello
{"message" : "Hello, WildFly Swarm!"}%
いいですね。
補足3 コンテキストルートは "/" 固定なの?
例によって WildFly Swarm が勝手に設定しているのですが、コンテキストルートが "/" であるのが不都合な場合もあるかと思います。変更方法は 3 種類あります。
- システムプロパティ swarm.context.path を渡す
 - jboss-web.xml で設定
 - WildFly Swarm の API を利用
 
システムプロパティ swarm.context.path を渡す
まずシステムプロパティで設定する方法です。渡し方は 2 種類あります。
1 つは実行時に渡す方法です。
$ java -Dswarm.context.path=helloworld -jar target/helloworld-swarm.jar
or
$ java -jar target/helloworld-swarm.jar -Dswarm.context.path=helloworld
後者のようにコマンドライン引数として渡す場合は、Bootstrap クラスにおいて Swarm インスタンスを作成する際、new Swarm(args) とコマンドライン引数を渡しておく必要があります。
もう 1 つは wildfly-swarm-plugin に指定する方法です。
<configuration>
  <mainClass>wildflyswarm.Bootstrap</mainClass>
  <properties>
    <swarm.context.path>helloworld</swarm.context.path> <!-- ここ -->
  </properties>
</configuration>
その他に利用できるシステムプロパティは以下に記載されています。
jboss-web.xml で設定
以下のような jboss-web.xml というファイルをアーカイブに含める方法もあります。
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
  <context-root>/helloworld</context-root>
</jboss-web>
そして Bootstrap クラスでこのファイルを含めるように設定します。addAsWebInfResource(...) はその名の通り、war パッケージングでの WEB-INF 以下に含めてね、というメソッドです。
import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;
[...]
archive.addAsWebInfResource(
      new ClassLoaderAsset("jboss-web.xml", Bootstrap.class.getClassLoader()), "jboss-web.xml");
ここでは以下のように jboss-web.xml がクラスパス(src/main/resources 以下)にあるとします。
.
├── pom.xml
└── src
    └── main
        ├── java
        │   ├── helloworld
        │   │   └── HelloWorld.java
        │   └── wildflyswarm
        │       └── Bootstrap.java
        └── resources
            └── jboss-web.xml
WildFly Swarm の API を利用
以下のように指定することもできます。
archive.setContextRoot("/helloworld");
これは前述の jboss-web.xml での設定をプログラムでできるようにしています。その他のメソッドについては javadoc を参照ください。
動作確認
では上記いずれかの方法で修正し、必要であれば再ビルドしてからアクセス。
$ curl localhost:8080/helloworld/hello
{"message" : "Hello, WildFly Swarm!"}
いいかんじです。
補足4 war でのパッケージング
実は WildFly Swarm では以下サンプルのように war でもパッケージングできます。
https://github.com/wildfly-swarm/wildfly-swarm-examples/tree/2017.1.1/jaxrs/jaxrs
使い分けの指針については以下公式ブログを参照ください。