ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Java における web アプリケーション開発ことはじめ (JAX-RS 2.0 / Jersey 2.4)

JAX-RS を使って Java で web アプリケーションを開発したい、と思っても、Java EE 周りに詳しくないと何をどうすればいいか全然わかんないですよね! そんなことないですか? 少なくとも私はよくわかんなかったです。

そんなわけなので、JAX-RS を使って web アプリケーションを開発しようと思った人のために、「Hello world!」 ってテキストを返すだけの web アプリケーションを作るところまで説明したいと思います。

サンプルプロジェクト

GitHub 上にサンプルプロジェクトをおいています。

Git と JDK がインストールされていれば、次のように 3 つのコマンドを実行するだけでサンプルプロジェクトを手元のサーバーで動かすことができます。

# プロジェクトをクローン
git clone -b hello_world git@github.com:nobuoka/jax-rs-jersey-application-sample.git

# ディレクトリ移動
cd jax-rs-jersey-application-sample

# ビルドしてサーバーを起動
./gradlew jettyRun

Web ブラウザなどで http://localhost:8080/jax-rs-jersey-application-sample/ にアクセスすると、「Hello world!」 というテキストが返ってくるはずです。 このサンプルプロジェクトの詳細をこれ以降説明していきます。

Hello world!」 を返す JAX-RS/Jersey アプリケーションの作り方

そもそも JAX-RS とは?

REST アーキテクチャにのっとって web アプリケーションを記述するための JavaAPI です。 Java EE の一部として定義されています。

JAX-RS 2.0 の仕様に書いている目標 (1.3 Goals) を簡単に紹介すると次のようなものがあります。

  • POJO-based: APIアノテーションの集合とそれに関連しており POJO と一緒に使えるようなクラスとインターフェイスを提供する *1
  • HTTP-centric: HTTP は基礎的なネットワークプロトコルであると考えており、JAX-RS は HTTP および URI 要素とそれに対応する API クラスとアノテーションとのマップを提供する。
  • Format independence: 様々な HTTP エンティティボディコンテントの種類に対応する。
  • Container independence: 様々な種類の web 層のコンテナにデプロイ可能である。 サーブレットコンテナや JAX-WS プロバイダへのデプロイ方法を定義する
  • Inclusion in Java EE: The specification will define the environment for a Web resource class hosted in a Java EE container and will specify how to use Java EE features and components within a Web resource class

つまり、原則的には JAX-RS で定義されているアノテーションなどを使って JAX-RS アプリケーションを書くことになります。 そして、JAX-RS に対応した HTTP サーバーやサーブレットコンテナでそのアプリケーションを実行させることができるわけです。 JAX-RS アプリケーションは (理想的には) サーバーの実装に依存しないので、移植性が高くなります。

RubyPerl でも Rack や PSGI といった仕様が定義されていますが、大体そんな感じです *2

本記事ではサーバーサイドの話のみを記述しますが、JAX-RS 2.0 ではクライアントサイド用の API も定義されました。

Hello world!」 を返すシンプルな JAX-RS アプリケーション

Servlet コンテナで実行される JAX-RS アプリケーションを単純に実装すると上のコミットのような感じになります。 JAX-RS アプリケーションとして重要な部分は、次の 2 つのクラスです。

上のコミットでは、どちらのクラスも JAX-RS 仕様に含まれるアノテーションやクラスのみを使用しています。

最近の Servlet 仕様では、ある条件下において web.xml デプロイメント記述子を省略できるらしくて *4JAX-RS アプリケーションを Servlet コンテナで実行させる場合も特定条件下では web.xml を省略できます。

例えば、上のコミットの例のように、Application クラス のサブクラスが含まれていて、そのクラスに @ApplicationPath アノテーション が付けられている場合で、Servlet 3 framework pluggability mechanism が使われるコンテナで使用する場合は web.xml を省略できます *5。 よって、上のコミットの内容で WAR ファイルを作って GlassFish サーバーなどにデプロイすれば JAX-RS アプリケーションが動くはずです。

JAX-RS 実装 Jersey

JAX-RS アプリケーションを動作させるための実装の 1 つに Jersey があります。 Jersey は JAX-RS の参照実装でもあります。

Jersey は アプリケーションサーバー GlassFish の中に組み込まれているので、GlassFish 上で JAX-RS アプリケーションを動かす場合には Jersey を使用することになるでしょう。 また、Jersey は JAX-RS アプリケーションを実行することのできる Servlet 実装も提供しているので、Jersey が提供している ServletJAX-RS アプリケーションをラップさせて、任意の Servlet コンテナで JAX-RS アプリケーションを実行するということもできます。

また、単に JAX-RS アプリケーションを動かす側の実装だけでなく、JAX-RS アプリケーションを実装するために便利な API を提供してくれたりもします。 今回のサンプルプロジェクトでも Jersey の便利な機能を使っているので、この下で紹介します。

Jersey を使って Application のサブクラスをより簡易に実装する

Jersey には ResourceConfig という Application クラス のサブクラスが含まれています。

これを使用することで、例えば getClasses メソッドを自分で実装する必要がなくなったりします。 今回のサンプルプロジェクトでは、packages メソッドを使ってリソースクラスが入っているパッケージ名を指定し、リソースクラスをスキャニングさせるように変更しました。

Jersey のテストフレームワークを使用する

Jersey には、テスト用のフレームワークも含まれています。

JUnit ベースなので、Jersey のテストフレームワークを使う障壁は低いでしょう。 テスト時に使用される JAX-RS アプリケーションのコンテナはいくつか用意されているので、その点でも扱いやすいです。 また、外部コンテナも使用できるようです。

今回のサンプルプロジェクトでは、「Hello world!」 という文字列がレスポンスとして返されるかどうかを調べるテストを書いてみました。

Servlet 3.x コンテナの中で Jersey アプリケーションを動かす

上で書いたように、GlassFish などの一部のサーブレットコンテナでは明示的な Servlet の指定をしなくても JAX-RS アプリケーションを認識してくれるっぽいのですが、web.xml にちゃんと Servlet を明示しないとだめな Servlet 3.x コンテナもあるようです。 (Jetty 9 で試したところ web.xmlServlet を明示しないと動いてくれませんでした。 が、web 上を見てたら Jetty 9 でも web.xml 不要とか書いてる記事もあるので私の使い方がおかしいのかも。)

今回の例では、Jersey が提供する ServletContainer *6 を使って、任意の Servlet 3.x コンテナにデプロイできるようにしました。

Servlet 3.x コンテナ用のサーバー側実装だけでなく、Servlet 2.x コンテナ用の実装など、他の JAX-RS アプリケーションコンテナの実装もあります。

HTTP サーバー Jetty で開発時の状況を確認する

開発中は手早く開発用のサーバーを起動したりホットデプロイしたりしたいですよね。 Gradle には jetty プラグインが標準でついているので、開発用サーバーとして Jetty を起動したりデプロイしたりということがしやすくなっています。

クラスファイルが更新されたときにはホットデプロイもされるようです。 IDE と併用して、IDE 側でリアルタイムコンパイルを走らせて Jetty にホットデプロイする、というような使い方が考えられます。

もちろん IDE 側に開発サーバーと連携する機能があるならそっちを使えばいいかもしれませんが。

開発環境の話

ここまでは特に IDE には依存しないような感じで書いてきましたが、実際の開発では IDE を使うことになると思います。 Web 上で調べてみたところお、JAX-RS アプリケーションを書くなら NetBeans 一択というような感じがしました。 実際、自分でも EclipseNetBeansIntelliJ を試してみましたが、JAX-RS アプリケーションを書くなら NetBeans が使いやすいように感じました。

NetBeans を使う場合は開発用のサーバーは GlassFish が標準なので、GlassFish を使うことになると思います。 NetBeans + GlassFish の開発環境を準備する話は次の記事が参考になりました。

NetBeansJAX-RS に従った web アプリケーションを作る方法については次のチュートリアルが参考になります。

しかし、Gradle や Maven を使ってるプロジェクトを NetBeans にインポートするとき、プロジェクト管理をそのまま Gradle や Maven を使って行うのは難しいっぽい感じがしますね。 IDE によるプロジェクト管理と IDE に依存しないプロジェクト管理をどうするか、みたいなの悩ましい感じがします。

まとめ

単に 「Hello world!」 を返すような web アプリケーションを例に、JAX-RS アプリケーションの基本的な作成方法を説明しました。 今回は次のようなものを使いました。

  • JAX-RS アプリケーションの実装に Jersey が提供している JAX-RS アプリケーション用の便利機能を使った
  • JAX-RS アプリケーションを Servlet 3.x コンテナで実行するために、Jersey が提供している Servlet 実装を使った
  • JAX-RS アプリケーションを WAR に固めるために Gradle の war プラグインを使った
  • JAX-RS アプリケーションを開発時に簡単に動かせるように Gradle の jetty プラグインを使った

一口に JAX-RS アプリケーションといっても、どのサーバーで動かすかなどによって作法が変わってくるところもあるのでどうすればいいのか結構悩んでいたのですが、今のところ IDE に依存しない感じでプロジェクトを作るなら上のような感じが良さそうだなーと思っています。

参考文献

JAX-RS の基本的なことを知るなら次の本がオススメです。

JavaによるRESTfulシステム構築

JavaによるRESTfulシステム構築

あとは次のような記事が参考になります。

  • JAX-RSとかの話 — 裏紙 : Java EE 周りをある程度理解している人なら JAX-RS のとっかかりとしてこの記事がわかりやすいと思います。 本記事のサンプルコードでは Jersey 2.4 を使っていますが、この記事では Jersey 1 系が使われていることには注意が必要です。 (Jersey のパッケージ名が違うしコピペして動かない可能性がある)
  • [Java] Jetty9(Servlet3.0)でJAX-RS | ルクサエンジニアのブログ : Jetty 9 (Servlet 3.x) で JAX-RS アプリケーションを動かすという記事。 ちょっと参考にしたので参考文献として挙げましたが、間違ってる部分もあるということで削除しておきます。 詳しくは本記事のコメント参照。
  • JAX-RS実装のデプロイメントについて | OPENSQUARE.jp - BLOG : JAX-RS アプリケーションのデプロイに web.xml デプロイメント記述子は省略できるのかどうか、ということを試している記事。 「基本的に web.xml は必要」 という結論っぽいけど、必ずしも web.xml が必要というわけではないので注意が必要。 これに関してもコメント頂いたので、本記事のコメントの方がご覧ください。
  • eXtreme JAX-RS : HTTP と JAX-RS の関係といった基礎的なことから、デプロイ方法についてや JAX-RS における認証・認可についての話などが書かれたスライド。 わかりやすいです。
  • JAX-RS 2.0 ことはじめ - Programming Studio : JAX-RS 2.0 仕様についての概観。 上のスライドの方がオススメだと著者の Hasunuma さんからコメント頂きました。

*1:つまり、POJO (= 通常の Java クラス) としてリソースを表すクラスを定義できる

*2:厳密にいうと JAX-RS は HTTP リクエストやレスポンスそのものには触らないようになっているので、Rack や PSGI と比べると少し高度ではあります。

*3:Application のサブクラスがアーカイブファイルに含まれていない場合や、getClasses が空の Set を返す場合にはアーカイブファイル内のリソースファイルを全て検索されたりするが、ここでは説明しない。

*4:詳しいことは知らない。

*5:JAX-RS 2.0 仕様を読むとそんな風に読み取れるけど違うかもしれない。

*6:ServletContainer という名前ですが、Servlet コンテナではなくて JAX-RS アプリケーションのコンテナの役割をする Servlet です。