Apache Wink、Eclipse、および Maven を使用して RESTful な Web サービスを開発する

Apache Wink は、JAX-RS (Java™ API for RESTful Web Services) 仕様のオープンソース実装です。Apache Wink を Eclipse IDE および Maven プロジェクト管理ツールとともに使用して RESTful な Web サービスを開発、デプロイ、実行する方法を学んでください。

Gabriel Mateescu, Computational Scientist, National Supercomputer Center, Sweden

Gabriel MateescuGabriel Mateescu はバイオインフォマティクスや高エネルギー物理シミュレーターなど、データ中心で計算負荷の重いアプリケーションを管理、実行するための分散システムを構築しています。彼はこれまで、LHC Computing Grid や DEISA 計画 (Distributed European Infrastructure for Supercomputing Applications)、GridCanada、NIH MIDAS など、いくつかのプロジェクトに従事してきました。



2010年 2月 09日

Apache Wink は、REST Web サービスの作成および使用を可能にする Apache のインキュベーター・プロジェクトです。REST Web サービスでは、クライアントとサービスとの間でのやりとりは事前に定義された一連の操作に制限されるため、クライアント・サーバー間の対話の複雑さは、クライアントとサービスとの間で交換されるリソース表現のみに限定されます。この手法により、相互運用可能でスケーラブルかつ信頼性に優れた REST ベースの分散ハイパーメディア・システムの構築が可能になります。

よく使われる頭字語

  • API: Application Programming Interface
  • HTTP: HyperText Transfer Protocol
  • IDE: Integrated Development Environment
  • JSON: JavaScript Object Notation
  • REST: Representational State Transfer
  • URI: Uniform Resource Identifier
  • XML: Extensible Markup Language

この記事では、Apache Wink を Eclipse IDE および Maven プロジェクト管理ツールとともに使用して RESTful な Web サービスを開発、デプロイ、実行する方法を説明します。

Web サービスのための REST による手法

REST による Web サービスの設計手法は、クライアントとサービスとの対話を一連の CRUD (Create, Read, Update, Delete) 操作に制限します。これらの操作は HTTP メソッド、具体的には POSTGETPUT、および DELETE に直接対応したものです。RESTful なスタイルは HTTP プロトコルしか使えないわけではありませんが、この記事ではクライアントとサービスとの間の通信には HTTP を使用することを前提とします。

REST Web サービスはリソースに対して CRUD 操作を実行します。クライアントは REST サービスとの間で、リソース状態の表現を交換します。この表現で使用されるデータ・フォーマットは、HTTP リクエストまたはレスポンスのヘッダーに指定されます。広く使用されているデータ・フォーマットには XML と JSON があります。データ・フォーマットは操作によって異なる場合があり、例えばリソースを作成する場合のデータ・フォーマットとリソースを読み取るためのデータ・フォーマットは異なります。REST サービスはリソースの状態を保持しますが、サーブレットとは異なり、クライアント・セッションの情報は保持しません。

この REST による手法のおかげで、相互運用可能でスケーラブルかつ信頼性に優れた REST ベースの分散システムを構築することが可能になります。例えば、GETPUT、および DELETE メソッドはべき等の操作であるため、これらの操作は何回実行しても、1 回だけ実行したときと結果が変わりません。こうした特徴を利用して GET リクエストの結果をキャッシュに入れるようにすれば、GET 操作はリソースの状態を変更しないため、リクエスト・レスポンス・サイクルの時間を短縮することができます。

JAX-RS では、RESTful な Java Web サービスのための API を HTTP プロトコルで定義しています。JAX-RS 実装には Apache Wink、Sun Jersey、JBoss RESTEasy がありますが、この記事では Apache Wink を使用します。

JAX-RS は Java アノテーションの威力を利用し、アノテーションを使って以下のような操作を実行します。

  • HTTP メソッドと URI を Java クラスのメソッドにバインドする
  • URI または HTTP ヘッダーの要素をメソッドのパラメーターとして注入する
  • HTTP メッセージの本文から Java のデータ型への変換、およびその逆の変換を行う
  • URI パターンを Java クラスおよびメソッドにバインドする (@Path アノテーション)
  • HTTP 操作を Java メソッドにバインドする (@GET@POST@PUT、および @DELETE アノテーション)

JAX-RS では新しい機能を作成するためのフレームワークも用意しています。例えばカスタム・データ・フォーマットの場合、プログラマーはメッセージのリーダーおよびライターを開発し、Java オブジェクトを HTTP メッセージにマーシャリングしたり、HTTP メッセージから Java オブジェクトにアンマーシャリングしたりすることができます。

この記事では、Eclipse と Maven を使用して Apache Wink をダウンロードし、Apache Wink に付属の HelloWorld サンプルを実行した後、独自の REST Web サービスを Eclipse プロジェクトとして作成する手順を説明します。


Eclipse での Apache Wink の入手

このセクションでは Eclipse に Maven Integration for Eclipse (別名 m2eclipse) および Subclipse プラグインをインストールした後、Eclipse を使用して Apache Wink をインストールします (m2eclipse は Eclipse から Maven へのアクセスを提供し、Subclipse は Subversion リポジトリーへのアクセスを提供します)。Eclipse は、Web サービスをビルドおよび実行するためのプラットフォームとしても使用します。

前提条件

Apache Wink を入手する前に、以下のソフトウェア・パッケージをダウンロードしてインストールしてください (それぞれのダウンロード URL については「参考文献」を参照)。

  • Java Software Development Kit (JDK) バージョン 6。JAVA_HOME 環境変数を設定して、パス %JAVA_HOME%\bin (Windows® の場合) または $JAVA_HOME/bin (Linux® の場合) に追加します。
  • Apache Tomcat バージョン 6.0。CATALINA_HOME 環境変数がインストール・ディレクトリーを指すように設定します。
  • Eclipse IDE for Java EE (Java™ Platform, Enterprise Edition) Developers。この記事を執筆している時点での最新リリースは Eclipse Galileo です。

Subclipse インストールする

Eclipse で Maven 対応プロジェクトを管理するには、Subclipse および m2eclipse の 2 つの Eclipse プラグインをインストールする必要があります。Subclipse プラグインをインストールする手順は以下のとおりです。

  1. Eclipse を起動します。
  2. メニュー・バーの「Help (ヘルプ)」をクリックし、「Install New Software (新規ソフトウェアのインストール)」を選択します。
  3. Available Software (使用可能なソフトウェア)」ウィンドウで「Add (追加)」をクリックします。
  4. Add Site (サイトの追加)」ウィンドウに、以下の内容を入力します。

         Name:     Subclipse 
         Location: http://subclipse.tigris.org/update_1.6.x/

    入力し終わったら「OK」をクリックします。

  5. Available Software (使用可能なソフトウェア)」ウィンドウで、「Subclipse」配下に表示された「Subclipse (Required) (Subclipse (必須))」および「SVNKit Client Adapter (Not required) (SVNKit Client Adapter (必要ありません))」チェック・ボックスを選択し ( 図 1 を参照)、「Next (次へ)」をクリックします。
    図 1. Subclipse プラグインのインストール
    「Available Software (使用可能なソフトウェア)」ウィンドウが表示されたインストール画面のスクリーン・ショット。「Subclipse」と「SVNKit Client Adapter」のチェック・ボックスが選択されています。
  6. Install Details (インストール詳細)」ウィンドウで「Next (次へ)」をクリックします。
  7. Review Licenses (ライセンスのレビュー)」ウィンドウで、ライセンス項目を確認し、使用条件に同意して「Finish (完了)」をクリックします。
  8. Yes (はい)」をクリックして Eclipse を再始動します。

m2eclipse をインストールする

m2eclipse プラグインをインストールする手順は Subclipse プラグインのインストール手順と同じですが、以下のステップは異なります。

  • Add Site (サイトの追加)」ウィンドウで、以下を入力します。

         Name:     Maven Integration for Eclipse
         Location: http://m2eclipse.sonatype.org/update/

    入力し終わったら「OK」をクリックします。

  • Available Software (使用可能なソフトウェア)」ウィンドウで、「Maven Integration for Eclipse (Required) (Eclipse 用 Maven 統合 (必須))」および「Maven SCM handler for Subclipse (Optional) (Subclipse 用 Maven SCM ハンドラー (オプション))」チェック・ボックスを選択し (図 2 を参照)、「Next (次へ)」をクリックします。
    図 2. m2eclipse プラグインのインストール
    使用可能なソフトウェアが表示されたインストール画面のスクリーン・ショット。「Maven Integration for Eclipse (Required) (Eclipse 用 Maven 統合 (必須))」と「Maven SCM handler for Subclipse (Optional) (Subclipse 用 Maven SCM ハンドラー (オプション))」のチェック・ボックスが選択されています。

Apache Wink を入手する

これで、Eclipse を使用してリポジトリーの Apache Wink サンプルをチェックアウトする準備が整いました。必要な Java アーカイブ (JAR) ファイル (Apache Wink JAR ファイルも含む) をローカル・リポジトリーにダウンロードして、Apache Wink の HelloWorld サンプルをビルドし、実行してください。この一連の作業を行う手順は以下のとおりです。

  1. Eclipse で「File (ファイル)」 > 「Import (インポート)」の順に選択して「Import (インポート)」ウィザードを立ち上げます。
  2. ウィザードの「Select (選択)」ページで、「Select and import source (インポート・ソースの選択)」ボックスに「maven」と入力します。
  3. Maven」配下に表示された「Materialize Maven Projects (Maven プロジェクトの具体化)」を選択し、「Next (次へ)」をクリックします。
  4. ウィザードの「Select Maven artifacts (Maven アーティファクトの選択)」ページで「Add (追加)」をクリックします。
  5. Add Dependency (依存関係の追加)」ページの「Enter groupId, artifactId or sha1 prefix or pattern (*) (グループ ID、アーティファクト ID または sha1 接頭部またはパターン (*) の入力)」ボックスに、「org.apache.wink.example」と入力します。
    注: Maven で使用する「アーティファクト (Artifact)」という用語は、バージョン管理対象としてリポジトリーに保管されているソフトウェア・パッケージの階層構造を指します。
  6. Search Results (検索結果)」領域で「org.apache.wink.example apps」を選択し (図 3 を参照)、「OK」をクリックします。
    図 3. org.apache.wink.example グループの apps アーティファクト
    「Add dependency (依存関係の追加)」ウィンドウのスクリーン・ショット。「org.apache.wink.example apps」が選択されています。
  7. ウィザードの「Select Maven artifacts (Maven アーティファクトの選択)」ページで「Next (次へ)」をクリックし、「Finish (完了)」をクリックします。
  8. ウィザードの「Maven Projects (Maven プロジェクト)」ページでは「/pom.xml」チェック・ボックスのみを選択し、「Finish (完了)」をクリックします。

Maven は、アーティファクトのすべての依存関係を解決するために、リモート・リポジトリーから依存関係をダウンロードし、ローカル・リポジトリーを組み立てます。Maven の長所の 1 つは、変化していく依存関係を扱えることです。したがって、Maven POM (Project Object Model) ファイルでアーティファクトの直接的な依存関係を宣言するだけで、Maven はそれよりも上位の依存関係を自動的に解決してくれます。

上記のステップ 8 が完了すると、Apache Wink サンプルの apps モジュールのコードが含まれる Eclipse プロジェクトが作成されます。Eclipse の「Project Explorer (プロジェクト・エクスプローラー)」ビューで、プロジェクト・ファイルを調べてください。


Apache Wink の HelloWorld サービス

ここで、apps モジュールに含まれる HelloWorld Java クラスについて調べてみましょう。「Project Explorer (プロジェクト・エクスプローラー)」ビューで、「apps」 > 「HelloWorld」 > 「src」 > 「main」の順にクリックし、HelloWorld.java ファイルを開いてください。このファイルのスケルトンをリスト 1 に記載します。

リスト 1. HelloWorld.java ファイル
package org.apache.wink.example.helloworld;
...

@Path("/world")
public class HelloWorld {

   public static final String ID = "helloworld:1"; 
   
   @GET
   @Produces(MediaType.APPLICATION_ATOM_XML)
   public SyndEntry getGreeting() {
      SyndEntry synd = new SyndEntry(new SyndText("Hello World!"), ID, new Date());
      return synd;
   }
}

HelloWorld は、クラス定義の前にある @Path("/world") アノテーションによって示されているように、JAX-RS のリソース (サービス) です。「/world」というストリングはリソースの相対ルート URI で、JAX-RS はこの相対 URI「/world」と一致する HTTP リクエストを、HelloWorld クラスのメソッドにルーティングします。

HelloWorld クラスが持つ唯一のメソッド、getGreeting には、@GET アノテーションが付いています。これは、このメソッドは HTTP GET リクエストを処理するように指定されていることを意味します。

@Produces(MediaType.APPLICATION_ATOM_XML) アノテーションは、HTTP レスポンスのメディア・タイプ、つまり HTTP レスポンスのヘッダーに含まれる Content-Type フィールドの値を指定します。

相対ルート URI の「/world」に関連付けられる絶対 URI は、デプロイメント記述子ファイル web.xml での設定によって決まります。このデプロイメント記述子ファイルは HelloWorld/src/main/webapp/WEB-INF の下にあり、図 4 に示す内容となっています。

図 4. HelloWorld アプリケーションのデプロイメント記述子
web.xml ファイルのスクリーン・ショット

/rest/* という URI パターンは、restSdkService サーブレットにバインドされることに注目してください。このサーブレットのコンテキスト・パスである HelloWorld は、HelloWorld/pom.xml ファイル内の <finalName> 要素によって定義されます。したがって、HelloWorld リソースの絶対 URI は以下のようになります。

     http://host:port/HelloWorld/rest/world

図 4 に示されているサーブレット名、restSdkService は、この図の <servlet> 要素によって示されているように、Apache Wink の org.apache.wink.server.internal.servlet.RestServlet クラスを参照します。RestServlet クラスに HelloWorld クラスの名前を渡すために使用される初期化パラメーター applicationConfigLocation は、「HelloWorld/src/main/webapp/WEB-INF」フォルダーに web.xml と一緒に配置されたアプリケーション・ファイルを指します。このアプリケーション・ファイルの内容は 1 行しかありません。それは、HelloWorld リソースの修飾名です。

org.apache.wink.example.helloworld.HelloWorld

RestServlet サーブレットは JAX-RS 対応でないサーブレット・コンテナーでも実行可能です。こうすることによって、非 JAX-RS 対応コンテナーにも容易に JAX-RS サービスをデプロイできるようにしています。

ここからは、HelloWorld サービスをビルドして Tomcat 6.0 サーブレット・コンテナーにデプロイし、実行する作業に移ります。

HelloWorld をビルドする

Eclipse で m2eclipse プラグインを使用して、HelloWorld をビルド、デプロイ、実行します。まず初めに行う作業は、ソース・コードをコンパイルし、HelloWorld サービスの Web アーカイブ (WAR) ファイルをビルドすることです。サービスをビルドする場合は、m2eclipse にライフサイクルのインストール・フェーズを実行するように指示する必要があります (Maven ライフサイクル・フェーズを実行すると、そのプロジェクトのライフサイクルにおける、そのフェーズ以前のフェーズも自動的に実行されます)。インストール・フェーズを実行するには、「Project Explorer (プロジェクト・エクスプローラー)」ビューの「HelloWorld」を右クリックし、「Run As (実行)」 > 「Maven install (Maven インストール)」の順に選択します (図 5 を参照)。

図 5. Maven インストール・フェーズの実行
「Run As (実行)」 > 「Maven install (Maven インストール)」のパスが選択された状態のスクリーン・ショット

Maven はすべての依存関係を (中央リポジトリーの http://repo1.maven.org/maven2、またはミラー・リポジトリーから) ダウンロードし、ローカル・リポジトリーを %HOMEPATH%\.m2\repository (Windows® の場合) または $HOME/.m2/repository (Linux® の場合) の配下に組み立てます。Maven が実行するアクションは、Eclipse ウィンドウのコンソール・ビューに記録されます。

インストール・フェーズが正常に完了すると、WAR ファイル HelloWorld.war がターゲット・ディレクトリーの下に作成され、ローカル・リポジトリーにデプロイされます。この時点で、コンソール・ビューには「Build successful (ビルド成功)」というメッセージが表示されます。

HelloWorld を Tomcat にデプロイする

この手順では Maven の Tomcat プラグインを使用します (Maven は Eclipse と同じく、プラグインによって多数の機能を提供します)。Maven には Tomcat プラグインの場所を指示する必要があるので、プラグインの groupIdartifactId を指定してください (図 6 に示す pom.xml の行 45 から行 46 を参照)。

図 6. Tomcat Maven プラグイン
pom.xml ファイルのスクリーン・ショット

さらに、Maven には Tomcat マネージャー・アプリケーションにアクセスする際に使用するユーザー名とパスワードを渡す必要もあります。Maven は Tomcat マネージャー・アプリケーションを使用して、Tomcat に対して Web アプリケーションのデプロイ、アンデプロイを指示するからです。以下の手順に従って、Maven に認証情報を提供してください。

  1. tomcat-maven-plugin アーティファクトの Tomcat サーバー構成 (ここでは tomcat-localhost という名前を設定) を宣言します (図 6 に示した pom.xml の行 47 から行 49 を参照)。
  2. この構成を Maven 設定ファイル settings.xml (Windows では %HOMEPATH%\.m2\、Linux では $HOME/.m2 にあります) に定義します (リスト 2 を参照)。

    リスト 2. Maven の settings.xml ファイル
    <?xml version="1.0" encoding="UTF-8"?>
    
    <settings 
        xmlns="http://maven.apache.org/SETTINGS/1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 
                            http://maven.apache.org/xsd/settings-1.0.0.xsd">
      <servers>
        <server>
          <id>tomcat-localhost</id>
          <username>admin</username>
          <password>admin</password>
        </server>
      </servers>
    
    </settings>

これで、サービスを Tomcat にデプロイする準備が整いました。サービスをデプロイするには、Tomcat プラグインの redeploy ゴール (訳注: 「ゴール」は Maven での処理の単位を表す用語です) を実行します (ライフサイクル・フェーズを実行すると、そのフェーズ以前のフェーズも自動的に実行されますが、それとは異なり、Maven のゴールは単独で実行されます)。redeploy ゴールを実行する手順は以下のとおりです。

  1. Tomcat 構成ファイル conf/tomcat-users.xml (CATALINA_HOME にあります) に認証情報を挿入します (図 7 を参照)。ただしパスワードには、admin 以外のパスワードを使用してください。
    図 7. Tomcat 認証構成
    Tomcat 認証構成
  2. 以下のコマンドを実行して Tomcat を起動します。

    In Windows:   %CATALINA_HOME%\bin\catalina.bat start
    In Linux:     $CATALINA_HOME/bin/catalina.sh start
  3. maven tomcat:redeploy を実行します。これにより、Tomcat マネージャー・アプリケーションによって HelloWorld.war が Tomcat にデプロイされます。このコマンドを実行する手順は以下のとおりです。
    1. 「Project Explorer (プロジェクト・エクスプローラー)」ビューで、「HelloWorld」を右クリックして「Run As (実行)」 > 「Run configurations (実行の構成)」の順に選択します。
    2. 「Create, manage, and run configurations (構成の作成、管理、および実行)」ウィンドウで、「Maven Build (Maven ビルド)」 > 「New_configuration」の順に選択し、「Main (メイン)」タブをクリックします (図 8 を参照)。
      図 8. tomcat:redeploy ゴールの実行
      「Run Configurations (実行の構成)」画面のスクリーン・ショット
    3. 「Browse Workspace (ワークスペースの参照)」をクリックし、「apps」 > 「HelloWorld」の順に選択してから「OK」をクリックします。
    4. 「Goals (ゴール)」ボックスに「tomcat:redeploy」と入力し、「Run (実行)」をクリックします。

      redeploy ゴールの実行が正常に完了すると、「Console (コンソール)」ビューに「Build successful (ビルド成功)」というメッセージが表示されます。

以上の手順が完了すると、Curl などの HTTP クライアントで HelloWorld サービスを呼び出せるようになります (リスト 3 を参照)。

リスト 3. Curl での HelloWorld サービスの呼び出し
  $ curl -X GET  http://localhost:8080/HelloWorld/rest/world
  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <entry xmlns="http://www.w3.org/2005/Atom" 
         xmlns:ns2="http://a9.com/-/spec/opensearch/1.1/" 
         xmlns:ns3="http://www.w3.org/1999/xhtml">
    <id>helloworld:1</id>
    <updated>2010-01-06T13:26:43.924+01:00</updated>
    <title type="text">Hello World!</title>
  </entry>

REST Web サービスの開発

このセクションでは、書籍リソースのコレクションを管理する REST Web サービスを一から開発します。この REST Web サービスのソース・コードは、記事の最後にある「ダウンロード」セクションから入手することができます。

以下は、この REST Web サービスを開発する際の主な手順です。

  1. リソースの URI、リソースの操作に使用するメソッド、各メソッドのデータ・フォーマットを定義します。
  2. 書籍リソースを表す Java オブジェクトを定義し、その Java オブジェクトをマーシャリングおよびアンマーシャリングする Java コードまたはアノテーションを提供します。
  3. URI および HTTP メソッドを Java メソッドにバインドする Java サービスを定義します。
  4. 抽象クラス javax.ws.rs.core.Application の具象サブクラスを指定します。

それでは早速、上記の手順を実行に移します。

パスおよび HTTP メソッドを Java メソッドにバインドする

ここで作成するサービスは、GETPOSTPUT、および DELETE の HTTP メソッドをサポートし、以下のように HTTP リクエストを Java メソッドにマッピングします。

  • POST /books リクエストは、シグニチャー createBook(@Context UriInfo, Book) を持つJava メソッドにマッピングします (アノテーション @Context については、「JAX-RS Web サービス」で説明します)。書籍リソースを作成する場合のデータ・フォーマットは以下のとおりです。

         POST /books HTTP/1.1
         Content-Type: application/xml
    
         <book>
              <title> ... </title>
              <isbn> .... </isbn>
         </book>
  • GET /books リクエストは、Java メソッド getBook() にマッピングします。書籍リソースを取得する場合のデータ・フォーマットは以下のとおりです。

         GET /books HTTP/1.1
         Content-Type: application/xml
  • GET /books/{id} リクエストは、シグニチャー getBook(@PathParam("id") int) を持つ Java メソッドにマッピングします (アノテーション @PathParam については、「JAX-RS Web サービス」で説明します)。URI パターン /books/{id} で書籍リソースを取得する場合のデータ・フォーマットは以下のとおりです。

         GET /books/{id} HTTP/1.1
         Content-Type: application/xml
  • PUT /books/{id} リクエストは、シグニチャー updateBook(@PathParam("id") int, Book) を持つ Java メソッドにマッピングします。URI パターン /books/{id} で書籍リソースを更新する場合のデータ・フォーマットは以下のとおりです。

         PUT /books/{id} HTTP/1.1
         Content-Type: application/xml
         
         <book>
             <title> ... </title>
             <isbn> .... </isbn>
         </book>
  • DELETE /books/{id} リクエストは、シグニチャー deleteBook(@PathParam("id") int) を持つ Java メソッドにマッピングします。URI パターン /books/{id} で書籍リソースを削除する場合のデータ・フォーマットは以下のとおりです。

         DELETE /books/{id} HTTP/1.1
         Content-Type: application/xml

書籍のオブジェクト・モデル

書籍のコレクションを実装するために使用する Java オブジェクト・モデルには、以下の 3 つのクラスがあります。

リスト 4. Book クラス
package com.ibm.devworks.ws.rest.books;
import javax.xml.bind.annotation.*;

@XmlRootElement(name="book")
@XmlAccessorType(XmlAccessType.FIELD)
public class Book {

	@XmlAttribute(name="id")
	private int id;

	@XmlElement(name="title")
	private String title;

	@XmlElement(name="isbn")
	private String ISBN;

	@XmlElement(name = "link")
	private Link link;

	public Book() { }

	public int getId() { return id; }
	public void setId(int id) { this.id = id; }

	public String getTitle() { return title; }
	public void setTitle(String title) { this.title = title; }

	public String getISBN() { return ISBN; }
	public void setISBN(String ISBN) { this.ISBN = ISBN; }
	
	public Link getLink() { return link; }
	public void setLink(String uri) { this.link = new Link(uri); }
}

Book クラスでは、JAXB (Java Architecture for XML Binding) アノテーションを使用して Java オブジェクトのマーシャリング、アンマーシャリングを行います。Apache Wink は、JAXB アノテーションを持つ Java オブジェクトのマーシャリングおよびアンマーシャリングを、これらの操作を実行する Marshaller および Unmarshaller インスタンスの作成も含め、自動的に実行します。

書籍オブジェクトのコレクションは、BookList クラスを使用して表します。このクラスは内部で、オブジェクトを ArrayList 型のフィールドに保管します。このフィールドには、XML フォーマットへの変換対象として JAXB アノテーションが付けられます。

リスト 5. BookList クラス
package com.ibm.devworks.ws.rest.books;

import java.util.ArrayList;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElementRef;

@XmlRootElement(name="books")
public class BookList {

	@XmlElementRef
	ArrayList<Book> books;	

	public BookList() {  }

	public BookList( Map<Integer, Book> bookMap ) {
		books = new ArrayList<Book>( bookMap.values() );
	}	
}

Link クラスは、書籍オブジェクトの XML 表現にリンク要素を挿入するために使用します (リスト 6 を参照)。

リスト 6. Link クラス
package com.ibm.devworks.ws.rest.books;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

@XmlRootElement(name="link")
@XmlAccessorType(XmlAccessType.FIELD)
public class Link {

	@XmlAttribute
	String href = null;
	
	@XmlAttribute
	String rel = "self";
	
	public Link() { }

	public Link( String uri) { 
		this.href = uri; 	
	}
	
	public Link( String href, String rel) { 
		this.href = href; 	
		this.rel = rel; 
	}
}

JAX-RS Web サービス

続いて、URI および HTTP メソッドを Java メソッドにバインドする Java サービスを定義します。このサービスは Java インターフェースとして定義することも、クラスとして定義することもできます。前者の場合には、インターフェースを実装するクラスも併せて定義します。インターフェースでは、JAX-RS アノテーションをローカライズし、そのインターフェースを実装するクラスを追加します。

BookService Java インターフェースをリスト 7 に記載します。@javax.ws.rs.Path アノテーションによって、インターフェースは JAX-RS サービスとして指定されます。サービスの相対ルート URI を定義するのは、このアノテーションの値である /books です。@javax.ws.rs.POST@javax.ws.rs.GET@javax.ws.rs.PUT@javax.ws.rs.DELETE の各アノテーションはそれぞれ HTTP POSTGETPUTDELETE 操作を、その後に続く Java メソッドにマッピングしています。

リスト 7. BookService インターフェース
package com.ibm.devworks.ws.rest.books;
import javax.ws.rs.*;
import javax.ws.rs.core.*;

@Path("/books")
public interface BookService {
	
	@POST
	@Consumes(MediaType.APPLICATION_XML)
	@Produces(MediaType.APPLICATION_XML)
	public Response createBook(@Context UriInfo uriInfo, Book book);
		
	@GET
	@Produces(MediaType.APPLICATION_XML)
	public BookList getBook();
	
	@Path("{id}")
	@GET
	@Produces(MediaType.APPLICATION_XML)
	public Book getBook(@PathParam("id") int id);
	
	@Path("{id}")
	@PUT
	@Consumes(MediaType.APPLICATION_XML)
	public void updateBook(@PathParam("id") int id, Book book_updated);	
	
	@Path("{id}")
	@DELETE
	public void deleteBook(@PathParam("id") int id);	
}

インターフェースの @PATH("/books") アノテーションが指定する URI とは異なる URI を持つメソッドには、それぞれに固有の @PATH アノテーションを追加します。@PATH アノテーションは累積型のアノテーションで、パス式に別のパス式が続く場合、その別のパス式を前の式の末尾に追加します。したがって、deleteBook メソッドにバインドするパス・パターンは /books/{id} となります。

JAX-RS には、HTTP リクエストから情報を抽出し、その情報を Java オブジェクトにキャストして Java メソッド・パラメーターに注入するためのメカニズムがあります。このメカニズムは、「インジェクション」と呼ばれます。インジェクションを指定するには、Java アノテーションを使用します。この BookService インターフェースの場合には、以下の 2 つのインジェクション用アノテーションを挿入します。

  • @Context。このアノテーションは、HTTP リクエストから URI 情報を抽出して UriInfo オブジェクトに変換し、そのオブジェクトをメソッド・パラメーター uriInfo として注入します。
  • @PathParam("id")。先行する @Path({"id"}) アノテーションと突き合わせるアノテーションです。このアノテーションは URI パターン /books/{id} から {id} を抽出し、それを getBook メソッドの id パラメーターの値として注入します。

BookService のメソッドは、Book などの Java オブジェクトをパラメーターとして取ること (そして返すこと) に注目してください。これが可能な理由は、これらのオブジェクトには JAXB アノテーションが付けられていることから、Apache Wink が自動的に Java オブジェクトのマーシャリングおよびアンマーシャリングを処理するためです。

リスト 8 に、BookService を実装するクラスのスケルトンを記載します。この BookServiceImpl は、メモリー内のマップを使用して書籍オブジェクトのコレクションを保守するシングルトン・クラスです。シングルトン・インスタンスは、マルチスレッド化によって同時リクエストを処理するため、スレッドセーフでなければなりません。

リスト 8. BookService 実装クラス
package com.ibm.devworks.ws.rest.books;
import javax.ws.rs.*;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Path("/books")
public class BookServiceImpl implements BookService {
	
	private static BookServiceImpl instance = null;
	
	private BookServiceImpl() {  }
	
	public synchronized static BookServiceImpl getInstance() {
		if(instance == null) {
			instance = new BookServiceImpl();
		}
		return instance;
	}
	
	private Map <Integer, Book> bookMap =
		new ConcurrentHashMap <Integer, Book>();
	
	private AtomicInteger seqNumber = new AtomicInteger();

	public Response createBook(@Context UriInfo uriInfo, Book book) 
	throws WebApplicationException {	
	  ...
	}
	
	public BookList getBook() {
	   ...	
	}
	
	public Book getBook(@PathParam("id") int id)
	throws WebApplicationException {
	   ...
	}
		
	public void updateBook(@PathParam("id") int id, Book book_updated) 
	throws WebApplicationException {	
	  ...
	}
	
	public void deleteBook(@PathParam("id") int id) 
	throws WebApplicationException {
	    ...			
	}	
}

書籍オブジェクトのコレクションはマップに保管します。マップへのスレッドセーフなアクセスを提供するには、ConcurrentHashMap を使用します。また、固有の書籍 ID を生成するには、インクリメント操作がアトミックであり、それによってスレッドセーフであることを確実にする AtomicInteger を使用します。

抽象クラス Application をサブクラス化する

JAX-RS Web アプリケーションには一連のリソースが含まれます (REST Web サービス)。これらのリソースは JAX-RS 抽象クラス、javax.ws.rs.core.Application のサブクラスとして公開されます。この Application クラスを継承するのが、リスト 9 に記載する BookWebApp クラスです。

リスト 9. javax.ws.rs.core.Application の具体的なサブクラス
package com.ibm.devworks.ws.rest.books;

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;

public class BookWebApp extends Application {

	private Set<Object> svc_singletons = new HashSet<Object>();	
	private Set<Class<?> svc_classes  = new HashSet<Class<?>();

	public BookWebApp() {
		svc_singletons.add(BookServiceImpl.getInstance());
	}
	
	@Override
	public Set<Object> getSingletons() {
		return svc_singletons;
	}
	 
	@Override
	public Set<Class<?> getClasses() {
		return svc_classes;
	}

}

BookWebApp のコンストラクターは、書籍サービス実装オブジェクトのシングルトン・インスタンスを取得します。getSingletons メソッドは、このシングルトンを唯一の要素として持つセットを返します。JAX-RS はリクエストに対処した後、シングルトンを破棄することはしません。


REST Web サービスのビルドとデプロイメント

今度は Eclipse を使用して動的 Web プロジェクトを作成します。このプロジェクトに、前のセクションで説明した Java クラスの他、Web アプリケーション・デプロイメント記述子や必須 JAR ファイルなどの他のリソースを含めます。

注: Eclipse で動的 Web プロジェクトを作成する代わりに、maven-archetype-webappwebapp-jee5 などの原型から Maven プロジェクトを作成することもできます (原型とは、Maven プロジェクト・テンプレートのことです)。しかし、Maven の現行のバージョンでは、Eclipse が使用するプロジェクト・メタデータを生成する際にいくつかの問題が生じます。そのため、IDE でコードが自動的にビルドされるようにするには、一部の Eclipse プロジェクト・ファイルを手動で変更しなければなりません。この理由から、ここでは Eclipse の動的 Web プロジェクトを使用します。

サンプル Web サービスのための Eclipse プロジェクト

サーブレット・コンテナーで実行される Web アプリケーションについて、その開発をサポートする Eclipse の動的 Web プロジェクトは、Apache Wink サービスを開発するのにふさわしいプロジェクト・テンプレートです。この REST による書籍サービスの動的 Web プロジェクトを作成するには、以下の手順に従ってください。

  1. Eclipse で「File (ファイル)」 > 「New (新規)」 > 「Dynamic Web Project (動的 Web プロジェクト)」の順にクリックします。
  2. 「Dynamic Web Project (動的 Web プロジェクト)」ウィザードで、「Project name (プロジェクト名)」ボックスに「Books」と入力します。
  3. 「Target runtime (ターゲット・ランタイム)」ボックスの隣にある「New (新規)」をクリックして、Tomcat ランタイムをセットアップします。
  4. ウィザードの「New Server Runtime Environment (新規サーバー・ランタイム環境)」ページで、「Apache」 > 「Apache Tomcat v6.0」の順にクリックし、「Next (次へ)」をクリックします。
  5. ウィザードの「Tomcat Server (Tomcat サーバー)」ページで「Browse (参照)」をクリックし、「前提条件」セクションで定義した CATALINA_HOME ディレクトリーまでナビゲートします。
  6. 「OK」をクリックし、次に「Finish (完了)」をクリックします。
  7. 「Dynamic Web Project (動的 Web プロジェクト)」ページで「Finish (完了)」をクリックします。

新しく作成したこの Books プロジェクトに、前のセクションで説明した Java クラスを追加します。それには、以下の手順に従います。

  1. 「Project Explorer (プロジェクト・エクスプローラー)」ビューで「Books」フォルダーを展開します。「Java Resources:src (Java リソース: src)」を右クリックして「New (新規)」 > 「Package (パッケージ)」の順に選択します。
  2. 「Java Package (Java パッケージ)」ウィンドウで、「Name (名前)」ボックスにパッケージの名前として「com.ibm.devworks.ws.rest.books」と入力し、「Finish (完了)」をクリックします。
  3. 以下のようにして、Java クラスを com.ibm.devworks.ws.rest.books パッケージに追加します。
    1. 「Project Explorer (プロジェクト・エクスプローラー)」ビューでパッケージを右クリックし、「New (新規)」 > 「Class (クラス)」の順に選択します。
    2. 「Name (名前)」ボックスにクラスの名前を入力し (例えば、Book.java の場合には「Book」と入力)、「Finish (完了)」をクリックします。
    3. Eclipse Java エディターを使用してクラスを作成します。

プロジェクトには次に、Apache Wink サービスをビルドして実行するために必要な JAR ファイルを追加します。これらの JAR ファイルは、前に apps プロジェクトを作成したときに、Maven によってローカル・リポジトリーにダウンロード済みです。ローカル・リポジトリーは、Windows の場合は %HOMEPATH%\.m2\repository、Linux の場合は $HOME/.m2/repository にあります。

JAR ファイルをプロジェクトに追加する手順は以下のとおりです。

  1. 「Project Explorer (プロジェクト・エクスプローラー)」ビューで、「Books/Web Content/WEB-INF」までナビゲートします。
  2. 「lib」を右クリックして「Import (インポート)」 > 「General (一般)」 > 「File System (ファイル・システム)」の順に選択し、「Next (次へ)」をクリックします。
  3. 「File System (ファイル・システム)」ウィンドウで、ローカル Maven リポジトリーにある JAR ファイル jsr311-api-1.0.jar (Apache Wink の apps モジュールをインポートしたときに、m2eclipse によって取り込まれています) をインポートするために、JAR ファイルが配置されたディレクトリーまでナビゲートし、「OK」をクリックします。「File System (ファイル・システム)」ウィンドウで、このインポート対象の JAR ファイルを選択してから、「Finish (完了)」をクリックします。
  4. 上記のステップ 2 と 3 を、各 JAR ファイル (wink-server-1.0-incubating.jar、wink-common-1.0-incubating.jar、slf4j-api-1.5.8.jar、および slf4j-simple-1.5.8.jar) について繰り返します。

すべてのライブラリーをインポートし終わった時点で、「lib」ディレクトリーには図 9 に示す JAR ファイルが含まれているはずです。

図 9. Apache Wink サービスに必要なライブラリー
「Project Explorer (プロジェクト・エクスプローラー)」ウィンドウで展開された状態の「WEB-INF/lib」フォルダーのスクリーン・ショット。Wink JAR ファイルの一覧が表示されています。

この書籍サービス用のこの Eclipse プロジェクトを作成するには、「ダウンロード」セクションに用意されているサンプル・コードからプロジェクトをインポートして作成するという方法もあります。その場合には、ダウンロード・ファイルをダウンロードして解凍した後、Eclipse で「File (ファイル)」 > 「Import (インポート)」 > 「Existing Project Into Workspace (既存プロジェクトをワークスペースへ)」の順にクリックします。この操作によって表示される「Import Projects (プロジェクトのインポート)」ウィザードで、「Books」フォルダーまでナビゲートして「OK」をクリックし、次に「Finish (完了)」をクリックすれば、書籍サービス用プロジェクトが作成されます。

サービスを Tomcat にデプロイする

リスト 10 に記載するデプロイメント記述子ファイル web.xml では、相対ルート・パス /* を持つ URI を Wink サーブレット org.apache.wink.server.internal.servlet.RestServlet にマッピングしています。RestServlet には init-param として、書籍サービスが提供する Application サブクラスの名前が渡されます。

リスト 10. BookService デプロイメント記述子 web.xml
<?xml version="1.0" encoding="UTF-8"?>

<web-app 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns="http://java.sun.com/xml/ns/javaee" 
   xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                       http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
  id="WebApp_ID" 
  version="2.5">

  <display-name>Book Web Application</display-name>

  <servlet>
    <servlet-name>restSdkService</servlet-name>
	<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
	<init-param>
		<param-name>javax.ws.rs.Application</param-name>
		<param-value>com.ibm.devworks.ws.rest.books.BookWebApp</param-value>
	</init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>restSdkService</servlet-name>
	<url-pattern>/*</url-pattern>
  </servlet-mapping>

</web-app>

デプロイメント記述子をセットアップした後は、書籍サービスを Tomcat にデプロイする番です。テストとして、Eclipse が管理するTomcat インスタンス内で Books サービスをデプロイし、実行してください。その手順は以下のとおりです。

  1. 「Project Explorer (プロジェクト・エクスプローラー)」ビューで、「Books」を右クリックして「Run As (実行)」 > 「Run on Server (サーバーで実行)」の順に選択します。
  2. 「Run on Server (サーバーで実行)」ウィンドウで「Finish (完了)」をクリックします。すると、Books プロジェクトを作成したときに構成した Tomcat ランタイムに書籍サービスがデプロイされて、Tomcat が起動します。

テストが完了したら、書籍サービスを WAR ファイルにパッケージ化して、本番環境にデプロイできるようにします。それには、「Project Explorer (プロジェクト・エクスプローラー)」ビューで「Books」を右クリックし、「Export (エクスポート)」 > 「WAR file (WAR ファイル)」の順に選択します。「WAR Export (WAR エクスポート)」ウィザードで「Browse (参照)」をクリックし、WAR ファイルを作成するフォルダーを選択してから「Save (保存)」をクリックし、続いて「Finish (完了)」をクリックします。


REST Web サービスの呼び出し

この書籍サービスを呼び出すには、HTTP 操作の GETPOSTPUTDELETE をサポートする HTTP クライアントを使用します。リスト 11 に示しているのは、このサービスを Curl で呼び出す方法です。このリストでは、最初にすべての書籍の表現を取得しています (最初の時点では、書籍はありません)。次に、jaxrs.xml および rest.xml の表現を使用して 2 つの書籍を作成し、最後にすべての書籍の表現と、id=2 の書籍の表現を取得しています。

リスト 11. 書籍サービスの呼び出し
$ curl   -X GET  http://localhost:8080/Books/books
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<books/>


$ more Books\xml\jaxrs.xml
<?xml version="1.0" encoding="UTF-8"?>
<book>
   <titleRESTful Java with JAX-RS</title>
   <isbn>978-0-596-15804-0</isbn>
</book>

$ more Books\xml\rest.xml
<?xml version="1.0" encoding="UTF-8"?>
<book>
   <title>RESTful Web Services</title>
   <isbn>978-0-596-52926-0</isbn>
</book>


$  curl -H "Content-Type: application/xml" -T Books\xml\jaxrs.xml \
        -X POST http://localhost:8080/Books/books

$  curl -H "Content-Type: application/xml" -T Books\xml\rest.xml \
        -X POST http://localhost:8080/Books/books


$ curl   -X GET  http://localhost:8080/Books/books
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<books>
  <book id="2">
     <title>RESTful Web Services</title>
     <isbn>978-0-596-52926-0</isbn>
     <link rel="self" href="http://localhost:8080/Books/books/2"/>
  </book>
  <book id="1">
    <title>RESTful Java with JAX-RS</title>
    <isbn>978-0-596-15804-0</isbn>
    <link rel="self" href="http://localhost:8080/Books/books/1"/>
  </book>
</books>

$ curl   -X GET  http://localhost:8080/Books/books/2
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book id="2">
  <title>RESTful Web Services</title>
  <isbn>978-0-596-52926-0</isbn>
  <link rel="self" href="http://localhost:8080/Books/books/2"/>
</book>

まとめ

この記事では、一連の技術を組み合せて Apache Wink の REST Web サービを開発、ビルド、デプロイする方法を説明しました。この開発は、Java アノテーションの威力を利用することで単純になります。

REST Web サービスでは、アプリケーションが Linked Data を交換することができます。Sir Tim Berners-Lee は、Linked Data によって Web の形が変わってくると予測しました。Bill Burke は彼の著書『RESTful Java with JAX-RS』のなかで、REST Web サービスは、サービスにおける対話の複雑さをデータ表現だけに抑えることによって、サービスの構成および再利用をこれまでになく容易にすると述べています。

この記事は Apache Wink サービスの開発について、その表面を説明しただけに過ぎません。Apache Wink に備わった他の重要な機能には、リンク・ビルダーやメッセージ本文のカスタム・リーダー、そしてサポートされていないデータ・フォーマット用のライターなどがあります。Apache Wink フレームワークについて詳しく学ぶには、Apache Wink に付属の他のサンプルも必ず調べてください。


ダウンロード

内容ファイル名サイズ
Source code for this articlewink_code.zip645KB

参考文献

学ぶために

  • Apache Wink: Apache Wink プロジェクトのページをよく読んでください。
  • RESTful Web サービスの基本」(developerWorks、2008年11月): Alex Rodriguez が書いたこの記事を読んで、REST の概念を理解してください。
  • RESTful Java with JAX-RS』: JAX-RS 対応サービスの開発について取り上げた Bill Burke の著書 (O'Reilly Media、2009年) を読んでください。
  • 「Practical data binding: Looking into JAXB」: この連載では Brett McLaughlin がデータ・バインディングについてわかりやすく説明しています。第 1 回 (developerWorks、2004年5月) と第 2 回 (developerWorks、2004年6月) を読んでください。
  • double-checked locking と Singleton パターン」(Peter Haggar 著、developerWorks、2002年5月): シングルトンをスレッドセーフにする方法を学んでください。
  • Linked Data: Linked Data とセマンティック Web に関する Sir Tim Berners-Lee の記事を読んでください。
  • developerWorks Web architecture ゾーン: Web 2.0 開発のツールと情報が満載の Web development ゾーンにアクセスしてください。
  • IBM Technical events and webcasts: developerWorks Technical events and webcasts で最新情報を入手してください。
  • My developerWorks: developerWorks のエクスペリエンスを自分流にカスタマイズしてください。

製品や技術を入手するために

  • Apache Wink フレームワーク: RESTful な Web サービスを構築するためのフレームワークをダウンロードしてください。
  • JDK 6: JDK 6 Update 17 (jdk-1.6.0_17) をダウンロードしてください。
  • Apache Tomcat 6.0: Apache Tomcat 6.0.20 をダウンロードしてください。
  • Eclipse: Eclipse IDE for Java EE Developers をダウンロードしてください。
  • IBM 製品の評価版: IBM 製品の評価版をダウンロードして、DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を使ってみてください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=472097
ArticleTitle=Apache Wink、Eclipse、および Maven を使用して RESTful な Web サービスを開発する
publish-date=02092010