REST、Web サービス、そして RESTful なサービス

Java ビジネス・サービスを REST に対応させるための単純な手法

最近ではバックエンドのエンタープライズ・サービスに統合する Web サービスとして、SOAP よりも REST ベースの Web サービスが選ばれることが次第に多くなってきています。その理由は、REST の単純さです。この記事では、Java ビジネス・サービスを REST 風のサービスとして公開する、単純で拡張可能なフレームワークを提案し、その設計について詳細に説明します。さらに、このフレームワークを拡張するさまざまな手法についても探っていきます。

Vedesh Dambal, Product Technical Architect, Infosys Technologies Ltd.

Colin Beckingham はカナダのオンタリオ州東部に住むフリーランスの研究者であり、ライターであり、プログラマーでもあります。Queen's University, Kingston で学位を取得している彼は、園芸、競馬、教育、行政サービス、小売業、旅行/観光業などにも関わってきました。彼はデータベース・アプリケーションの作成者であり、無数の新聞記事や雑誌記事、オンライン記事を執筆しており、また Linux でのオープンソース・プログラミングや VoIP、音声制御アプリケーションも研究しています。



2010年 6月 14日

はじめに

REST (REpresentational State Transfer) とは、個々の Web サービスをリソースと見なし、Web サービスをその URL によって一意に識別できるアーキテクチャー原則のことです。REST 原則に従った (RESTful な) Web サービスには、それぞれに異なる操作の呼び出しを意味する HTTP メソッドを明示的に使用するという重要な特徴があります。

REST の基本設計原則では、典型的な CRUD 操作に以下の HTTP プロトコル・メソッドを使用します。

  • POST - リソースを作成
  • GET - リソースを取得
  • PUT – リソースを更新
  • DELETE - リソースを削除

RESTful なサービスには、主に以下の利点があります。

  • 基本的な HTTP プロトコルを利用するため、さまざまなプラットフォーム (Java、.NET、PHP など) で幅広く再利用することができます。
  • 複雑な SOAP XML の代わりに基本的な XML を使用するため、簡単に使いこなすことができます。

REST ベースの Web サービスは、バックエンドのエンタープライズ・サービスと統合する Web サービスとして次第に人気が高まっています。その理由として挙げられるのは、SOAP をベースとした Web サービスと比べ、プログラミング・モデルが単純であること、そして SOAP の代わりにネイティブ XML を使用することから、シリアライズとデシリアライズの複雑さも、サード・パーティーのライブラリーを追加する必要性も低減されることです。

現在 RESTful なサービスを作成するための Java ベースのフレームワークには、Apache CXF、RESTlet、REST 対応の JAX-WS API、Spring 3.0 以降で使用可能な Spring MVC REST サポートなどがありますが、これらのフレームワークは開発、XML 構成という点に関して複雑であり、通常は習得するまでに時間がかかります。また、特定バージョンの従属 JAR ファイルに依存するため、異なるアプリケーション・サーバー環境での統合が非常に困難です。その上、一部のフレームワーク (Apache CXF、JAX-WS) は SOAP サービスと REST サービスを両方サポートしようとしていることから、パッケージ化の観点から重くなりがちで、パフォーマンスに影響を与える可能性もあります。

以上の理由からこの記事で提案するのが、Java ビジネス・サービスを REST 風のサービスとして公開する、単純で拡張可能なフレームワークです。このフレームワークは非常に軽量であり、ごく簡単に理解できる標準のフロント・コントローラー・パターンを採用しています。さらに、API やその他の統合パターン (ESB など) を使ってバックエンドのサービスと統合するために拡張することも可能です。データ交換モデルは、カスタム XML シリアライザー、JAXB、あるいはその他のオブジェクト対 XML の変換ツールを使用して簡単に構成することができます。

この記事では、このフレームワークについて詳しく説明します。


アーキテクチャーの概要

J2EE アプリケーションでの Java API あるいはサービスは、ステートレス・セッション Bean API (セッション・ファサード・パターン)、または SOAP Web サービスとして公開されます。これらのサービスを Java 以外の技術 (.NET や PHP など) を使用しているクライアント・アプリケーションと統合する場合、SOAP Web サービスは極めて扱いにくくなり、かなりの開発作業が必要になってきます。

この記事で説明する手法が対象とするのは、組織内に再利用可能なサービスが多数あるものの、SOAP を使用した場合の相互運用性および開発コストが妨げとなって、簡単には統合に踏み切れない組織におけるサービス統合です。さらに、サービスをエンタープライズ ESB や EAI で公開する意図が内部ガバナンス組織にない場合、そのサービスと別の技術を使用しているサービスとをポイント・ツー・ポイントで統合するのは難しい話となります。

例えば、通信 IT 環境では以下のいずれかのサービスが考えられます。

  • SOAP Web サービスまたは EJB API として公開されたグループ固有の SMSC に SMS を送信する。
  • データベース・ストアード・プロシージャー (Oracle CRM など) として ESB で公開された CRM アプリケーション内で、MQ または JMS バインディングを使ってサービス・リクエストを作成する
  • SMS ゲートウェイを使用してモバイル SMS からディストリビューターへの販売注文リクエストを作成する

上記のサービスを非 Java アプリケーションで使用するとなると、SOAP Web サービスを使った統合は煩雑な作業になり、さらなる開発が必要となってきます。

ここで紹介する新しい手法は、フレームワークという形で実装されています。そのため、Java サービスを REST のようなリソースとして公開することのできる他の分野で再利用することができます。この手法は Struts フレームワークの手法と似ており、以下の図に示すコンポーネントで構成されています。

図 1. アーキテクチャーの概要
アーキテクチャーの概要

上記のアーキテクチャーでは、フロント・コントローラー (Front controller) がリクエストを受信し、クライアントにレスポンスを返す中心点的な役割を果たします。フロント・コントローラーは、このフレームワークの処理ロジックが含まれる ActionController にリクエストの処理を委任します。ActionController はリクエストを検証した後、そのリクエストを適切なアクションにマッピングし、そのアクションを呼び出してレスポンスを生成します。また、リクエストの処理、ロギング、および例外処理を行う各種のヘルパー・サービスも用意されており、ActionController と個々のアクションはこれらのサービスを使用することができます。


フレームワークのコンポーネント

以下の図に、フレームワークを構成する各種のコンポーネントと、コンポーネント間の関係を示します。

図 2. フレームワークのコンポーネント
フレームワークのコンポーネント

以下で、上記に示されている各種のコンポーネントについて説明します。

サービス・クライアント (ServiceClient)

このコンポーネントは、サービスを呼び出す必要のあるクライアント・アプリケーションです。サービス・クライアントは Java ベースのクライアントにすることも、あるいは HTTP メソッドをサポートできる限り、Java 以外をベースとしたクライアントにすることもできます。

共通コンポーネント (Common components)

共通コンポーネントは、フレームワークが必要とするユーティリティー・サービスです。これらのユーティリティー・サービスには、ロギング、例外処理、そして実装に必要な共通関数あるいは定数などがあります。サンプル・コードでは、Log4j 実装を使用した Apache Commons ロギングを使用します。

RESTServiceServlet

フレームワークでは、1 ヶ所でリクエストを処理するために、フロント・コントローラー・パターンを使用し、実際にこの Java サーブレット・コンポーネントを使用してリクエストを処理します。このコンポーネントは、GET、POST、PUT、DELETE などの共通 HTTP メソッドをサポートします。

RESTActionController

このコンポーネントがコアのフレームワーク・コントローラーとして、サービスとフレームワーク構成のロード、リクエストの検証、リクエストと構成済み REST アクションとのマッピング、そしてアクションの実行というコア機能を管理します。

RESTConfiguration

このコンポーネントは、実行時にフレームワーク構成および各種の REST サービス構成をロードしてキャッシュに入れます。RESTActionController はこのコンポーネントを使用して、リクエストに応じて呼び出す必要のある適切なアクションを特定するともに、入力リクエストを検証します。

RESTMapping

このコンポーネントは、構成ファイルに指定された REST アクションのマッピングを保管します。マッピングの主な構成要素は、クライアントが呼び出す URI、そして処理を行うアクション・クラスです。

ActionContext

このコンポーネントは、REST アクションを実行するために必要なすべての機能をカプセル化します。このコンポーネントのおかげで、開発者はリクエストとレスポンスの処理機能を提供する際に、実際のビジネス・ロジック実装をコーディングするだけで済みます。このコンポーネントはプロトコルに固有のリクエスト・オブジェクトおよびレスポンス・オブジェクトをアクション・コンポーネントから隠し、POJO のような単独のテストを実行することを可能にします。さらに、XML バインディング・サービスへのハンドルを提供し、構成済み XML バインディング API を利用して Java ビジネス・オブジェクトと XML との間の変換を容易にできるようにします。RESTActionController はこのコンポーネントを動的に構成して、アクション・コンポーネントに提供します。

XMLBinding

このコンポーネントは Java XML バインディング・メカニズムをカプセル化し、Java ビジネス・オブジェクトと XML との間での変換に統一したインターフェースを使えるようにします。標準フレームワーク・インターフェースを実装するこのコンポーネントにより、JAXB、SDO、Castor などの任意の XML バインディング・メカニズムを構成することができます。デフォルトでは、XMLEncoder および XMLDecoder のサポートをそのまますぐに使用することができます。

構成 XML (Configuration.xml)

このコンポーネントには、フレームワーク構成とサービスの構成が含まれます。REST サービスの開発である限り、サービス構成ファイルにサービスを追加することができます。フレームワーク構成には、定期的に変更する必要のないロギング・サービスおよび XML バインディング・サービスが含まれます。

これらのコンポーネントが標準的な POST リクエストを処理する際に行うやりとりを以下の図に示します。

図 3. コンポーネント間のやりとり
コンポーネント間のやりとり

上記に示されているように、REST サービス構成は最初にロードされて、RESTConfiguration コンポーネントにキャッシュされます。REST サービスの HTTP リクエストに関してはすべて、RESTServiceServlet コンポーネントがそのリクエストの処理を RESTActionController に委任します。すると、RESTActionController は対応するマッピングを取得してリクエストを検証し、パスおよびクエリー入力を含めた ActionContext コンポーネントを作成してアクション・クラス (createUserAction など) を呼び出します。呼び出されたアクション・クラスは、バックエンドの Java ビジネス・サービスを呼び出してリクエストを処理します。


フレームワークとして機能させる方法

このセクションでは、記事に付属しているこのフレームワークのサンプル実装について説明します。ここではクラス図、さまざまな構成ファイル、そしてコード・スニペットを記載して、上記の設計を実現する方法を示します。

注: サンプル・コードは一例に過ぎないため、Java コーディングのすべてのベスト・プラクティスに従っているわけではありません。

サンプル実装

以下の図に、サンプル実装に含まれるクラスを示します。「青」で示されているクラスはフレームワーク外部のクラスですが、フレームワークとの構造的な関連を示すために記載しています。

図 4. クラス図
クラス図

構成ファイル

rest-services-config.xml」という名前の構成ファイルには、REST サービス表現とその表現に対応する Java アクションとの間のマッピングが含まれています (以下のリストを参照)。

リスト 1. REST サービスの構成
<?xml version="1.0" ?>
<rest-config>
  <rest-api id="CreateUserProfile" uri="/Registration/CreateUser" method="POST">
     <handler id="RegAction" class="ws.registration.restactions.CreateProfile"/>
  </rest-api>
  <rest-api id="GetUserProfile" uri="/Registration/GetUser" method="GET">
     <handler id="RegAction" class=" ws.registration.restactions.GetProfile"/>
  </rest-api>
...
</rest-config>

サンプル実装で XML バインディング・サービス実装が構成されているフレームワーク構成ファイルは、以下に記載する「rl-config.xml」ファイルです。XMLBindingService インターフェースが実装される限りは、このファイルを変更することによって、あらゆるカスタム実装を追加することができます。

リスト 2. フレームワークの構成
# XML Binding Implementation Service
# Default implementation
ws.rest.xmlbinding.service.impl=ws.rest.xmlbinding.service.impl.XMLEncDecServiceImpl

ログ構成ファイル「ws_log.properties」が指定しているのは、log4j プロパティー、そしてログ・ファイルの場所です。これらの内容は、要件に応じて変更することができます。

コントローラー・サーブレット

web.xml には、コンテキスト・パスが <web-app>/restservices/* となっているすべてのリクエストを処理するように RESTServiceServlet が構成されています (以下のリストを参照)。

リスト 3. サーブレットの構成
<servlet>
    <description></description>
    <display-name>RESTServletService</display-name>
    <servlet-name>RESTServletService</servlet-name>
    <servlet-class>ws.rest.servlet.RESTServiceServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>RESTServletService</servlet-name>
    <url-pattern>/restservices/*</url-pattern>
  </servlet-mapping>

REST アクション

それぞれの REST リソース (GetUserProfile など) ごとに、ActionInterface を実装するアクション・クラスが作成されます。このインターフェースは、該当するアクション・クラスに対して実装する必要のある「doExecute(ActionContext ctx)」メソッドを定義します。ActionContext はアクションに固有のプロトコルを公開することなく、REST パスの入力またはクエリー・パラメーターを取得するサービス、XMLBindingService インスタンスを取得するサービス、そして XML 出力をクライアントに送信するサービスを提供します。PathInputs は、URL に指定されている順番でパスの値を含む List オブジェクトです。

リスト 4. アクションのコード・スニペット
public class GetProfile implements ActionInterface {
	
  public void doExecute(ActionContext context) throws Exception {
   // Get the value from URL path
   String userName = context.getPathInputs().get(0);
   // Invoke backend service to retrieve user profile
   UserProfileBean bean = getUser(userName);
   
   
   // Serialize the bean using framework service and send response
   String xml = context.getXMLBindingService().serialize(bean);
   // Use the ActionContext to generate XML and 
   context.sendResponse(response, xml);
}

このアクション・クラスは、スーパークラスにある XMLBindingService を使用して、出力を XML として生成します。サンプル実装の ws.registration.restactions.GetProfile クラスを見てください。カスタム処理に必要な場合、ActionContext はプロトコルに固有の HttpServletRequest オブジェクトと HttpServletResponse オブジェクトを指定することもできます。さらに Path 値の他、URL パラメーターも指定します。

XML バインディング

サンプル・コードの Java XML バインディング実装では、java.beans.XMLEncoder クラスと java.beans.XMLDecoder クラスを使用しています。XML バインディング・サービス実装は JavaBean オブジェクトを受け入れて、このオブジェクトを上記のエンコーダーとデコーダーに対応する XML 表現に変換します。JAXB 実装が必要な場合には、ws.rest.xmlbinding.service.XMLBindingService インターフェースを実装する実装クラスを開発してください。

サンプル・サービスの実行

サンプル・コードの配布には、Tomcat コンテナーにデプロイできるサンプル WAR ファイル「RESTWS.war」が含まれています (Apache Tomcat バージョン 6.0.20 でテスト済みです)。JDK の要件としては、JDK 1.5 以上が必要です。

アプリケーションのデプロイが正常に完了したら、ブラウザーに URL として、http://localhost:8080/RESTWS/html/CreateProfile.html と入力してください。

図 5. Create Profile サービスの入力
Create Profile サービスの入力

上記のページによって、REST サービスが呼び出されます。

POST <url-prefix>/Registration/CreateProfile

<string></string> タグの中に指定されている XML 値は変更しても構いません。

注: XML 構造は、どの JavaBean オブジェクトと Java XML シリアライズ手法を使用しているかによって異なることに注意してください。

「Submit」ボタンをクリックすると、アクション・クラスからバックエンド・サービスの呼び出しに成功したことを伝えるメッセージが表示されます。デバッグ・メッセージについては、ws_log.log ファイルで調べてください。

図 6. Create Profile サービスの出力
Create Profile サービスの出力

同様に、プロファイルを取得するためのサンプル GET <url-prefix>/Registration/GetProfile/{username} サービスも実装されています (以下のリストを参照)。

図 7. Get Profile サービスの実行
Get Profile サービスの入力

まとめ

これまで説明してきたように、このフレームワークを使用すれば、あらゆる Java サービスを REST API として簡単に公開することができます。クライアント・アプリケーションが HTTP プロトコルをサポートしているのであれば、XML を出力データとして使うことで、この REST API として公開された Java サービスと Java 以外の言語で作成されたクライアント・アプリケーションを統合することができます。さらに、アクション・クラスを実装するための開発作業も大幅に少なくなります。

このフレームワークの実装は、適切な例外処理サービスとキャッシュ・サービスによって、さらに強化することが可能です。また、アノテーションのサポートを実装すれば、開発者がデフォルト構成を作成する際に、構成ファイルにその構成を指定する必要がなくなります。


謝辞

この記事をレビューし、貴重な情報とコメントを提供してくれた同僚の Rajesh Mishra 氏に心から感謝いたします。彼の協力によって、フレームワークのユーザビリティーを改善できただけでなく、その体裁も整えることができました。


ダウンロード

内容ファイル名サイズ
A sample implementation code for the frameworkRESTWS.war10KB

参考文献

学ぶために

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

議論するために

コメント

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=SOA and web services
ArticleID=500060
ArticleTitle=REST、Web サービス、そして RESTful なサービス
publish-date=06142010