4.3. Java オブジェクトとの間のマーシャリング

HTTP で送信するための Java オブジェクトのマーシャリング

REST プロトコルを使用する最も一般的な方法の 1 つは、Java Bean の内容をメッセージボディーで送信することです。これを実現させるには、Java オブジェクトを適切なデータフォーマットとの間でマーシャリングするメカニズムが必要です。Java オブジェクトのエンコードに適した以下のデータフォーマットが REST DSL でサポートされています。

JSON

JSON (JavaScript Object Notation) は、Java オブジェクトとの間で簡単にマッピングできる軽量なデータフォーマットです。JSON 構文はコンパクトで、緩く型指定され、人間が読み書きがしやすい構文です。これらの理由から、JSON は REST サービスのメッセージ形式として人気があります。

たとえば、以下の JSON コードは、idname の 2 つのプロパティーフィールドを持つ User Bean を表現できます。

{
    "id" : 1234,
    "name" : "Jane Doe"
}
JAXB

JAXB(Java Architecture for XML Binding) は、Java オブジェクトと XML の間で簡単にマッピングできるデータフォーマットです。XML を Java オブジェクトにマーシャリングするには、使用する Java クラスにアノテーションを付ける必要があります。

たとえば、以下の JAXB コードは、idname の 2 つのプロパティーフィールドを持つ User Bean を表現できます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<User>
  <Id>1234</Id>
  <Name>Jane Doe</Name>
</User>
注記

Camel 2.17.0 以降、JAXB データフォーマットと型コンバーターは、XmlRootElement の代わりに ObjectFactory を使用するクラスの XML から POJO への変換をサポートします。また、Camel コンテキストでは、値が true の CamelJaxbObjectFactory プロパティーを含める必要があります。ただし、後方互換のためにデフォルトは false になっています。

REST DSL による JSON と JAXB の統合

メッセージボディーを Java オブジェクトへ変換するために必要なコードを自分で書くこともできます。しかし、REST DSL は、この変換を自動的に実行する利便性を提供します。特に、JSON と JAXB を REST DSL と統合すると、以下のようなメリットがあります。

  • Java オブジェクトとの間のマーシャリングは自動的に実行されます (適切な設定がある場合)。
  • REST DSL は、データフォーマット (JSON または JAXB のいずれか) を自動的に検出し、適切な変換を行うことができます。
  • REST DSL は 抽象化レイヤー を提供するので、開発するコードは JSON または JAXB 実装に固有のものではありません。そのため、アプリケーションコードへの影響を最小限に抑えながら、後から実装を切り替えることができます。

サポートされるデータフォーマットコンポーネント

Apache Camel は JSON と JAXB データフォーマットの多くの実装を提供しています。現在、REST DSL では以下のデータフォーマットがサポートされています。

  • JSON

    • Jackson データフォーマット (camel-jackson) (デフォルト)
    • gson データフォーマット (camel-gson)
    • XStream データフォーマット (camel-xstream)
  • JAXB

    • JAXB データフォーマット (camel-jaxb)

オブジェクトマーシャリングを有効にする方法

REST DSL でオブジェクトのマーシャリングを有効にする場合は、以下の点に注意してください。

  1. bindingMode オプションを設定してバインディングモードを有効にします (バインディングモードを設定できるレベルはいくつかあり、詳細は 「バインディングモードの設定」 を参照してください)。
  2. 受信メッセージでは type オプション (必須)、送信メッセージでは outType オプション (任意) を使用して、変換先 (または変換元) の Java 型を指定します。
  3. Java オブジェクトを JAXB データフォーマットとの間で変換する場合、Java クラスに適切な JAXB アノテーションを付ける必要があります。
  4. jsonDataFormat オプションや xmlDataFormat オプション (restConfiguration ビルダーで指定可能) を使用して、ベースとなるデータフォーマットの実装を指定します。
  5. ルートが JAXB 形式の戻り値を提供する場合、通常、エクスチェンジボディーの Out メッセージを JAXB アノテーションを持つクラスのインスタンス (JAXB 要素) に設定することが期待されます。ただし、JAXB の戻り値を XML 形式で直接提供する場合は、キー xml.out.mustBeJAXBElementdataFormatPropertyfalse に設定します (restConfiguration ビルダーで指定可能)。たとえば、XML DSL の構文では以下になります。

    <restConfiguration ...>
      <dataFormatProperty key="xml.out.mustBeJAXBElement"
                          value="false"/>
      ...
    </restConfiguration>
  6. 必要な依存関係をプロジェクトのビルドファイルに追加します。たとえば、Maven ビルドシステムを使用し、Jackson データフォーマットを使用している場合、次の依存関係を Maven POM ファイルに追加します。

    <?xml version="1.0" encoding="UTF-8"?>
    <project ...>
      ...
      <dependencies>
        ...
        <!-- use for json binding --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jackson</artifactId> </dependency>
        ...
      </dependencies>
    </project>
  7. アプリケーションを OSGi コンテナーにデプロイする際には、必ず選択したデータフォーマットに必要な機能をインストールしてください。たとえば、Jackson データフォーマット (デフォルト) を使用している場合、以下の Karaf コンソールコマンドを入力して camel-jackson 機能をインストールします。

    JBossFuse:karaf@root> features:install camel-jackson

    また、Fabric 環境にデプロイする場合は、Fabric プロファイルにその機能を追加します。たとえば、プロファイル MyRestProfile を使用している場合は、次のコンソールコマンドを入力して機能を追加できます。

    JBossFuse:karaf@root> fabric:profile-edit --features camel-jackson MyRestProfile

バインディングモードの設定

bindingMode オプションはデフォルトで off になっているため、Java オブジェクトのマーシャリングを有効にするには、明示的に設定する必要があります。表は、サポートされているバインディングモードの一覧を示しています。

注記

Camel 2.16.3 以降では、POJO から JSon/JAXB へのバインディングは、content-type ヘッダーに json または xml が含まれている場合にのみ発生します。これにより、カスタムのコンテンツタイプを指定することで、メッセージボディーがバインディングを使用してマーシャリングしなくなります。これは、メッセージボディーがカスタムバイナリーのペイロードである場合などに便利です。

表4.2 REST DSL のバインディングモード

バインディングモード説明

off

バインディングはオフになります (デフォルト)

auto

バインディングは JSON または XML に対して有効になります。このモードでは、Camel は受信メッセージのフォーマットに基づいて JSON または XML (JAXB) を自動選択します。必ずしも両方のデータフォーマットの実装を有効にする必要はありません。JSON の実装と XML の実装のどちらかまたは両方をクラスパスで提供します。

json

バインディングは JSON でのみ有効になります。クラスパスに JSON の実装を用意 しなければなりません (デフォルトでは、Camel は camel-jackson の実装を有効にしようとします)。

xml

バインディングは XML でのみ有効です。クラスパスに XML の実装を用意 しなければなりません (デフォルトでは、Camel は camel-jaxb の実装を有効にしようとします)。

json_xml

バインディングは JSON と XML の両方に対して有効になります。このモードでは、Camel は受信メッセージのフォーマットに基づいて JSON または XML (JAXB) を自動選択します。クラスパスに 両方 のデータフォーマットの実装を用意する必要があります。

Java では、これらのバインディングモード値は、以下の enum 型のインスタンスとして表されます。

org.apache.camel.model.rest.RestBindingMode

bindingMode が設定できるレベルは、以下のように複数あります。

REST DSL の設定

以下のように、restConfiguration ビルダーから bindingMode オプションを設定できます。

restConfiguration().component("servlet").port(8181).bindingMode(RestBindingMode.json);
サービス定義のベースパート

rest() キーワードの直後 (Verb 句の前) に、以下のように bindingMode オプションを設定することができます。

rest("/user").bindingMode(RestBindingMode.json).get("/{id}").VerbClause
Verb 句

Verb 句で bindingMode オプションを設定する場合は、以下のようになります。

rest("/user")
    .get("/{id}").bindingMode(RestBindingMode.json).to("...");

Servlet コンポーネントを REST 実装として使用して、REST DSL を使用する方法を示す完全なコード例は、Apache Camel の camel-example-servlet-rest-blueprint の例を参照してください。この例は、スタンドアロンの Apache Camel ディストリビューション apache-camel-2.23.2.fuse-7_10_0-00018-redhat-00001.zip をインストールして見ることができます。これは、Fuse インストールの extras/ サブディレクトリーにあります。

スタンドアロン Apache Camel ディストリビューションのインストール後に、以下のディレクトリーでサンプルコードを確認できます。

ApacheCamelInstallDir/examples/camel-example-servlet-rest-blueprint

REST 実装として Servlet コンポーネントを設定

camel-example-servlet-rest-blueprint の例では、REST DSL のベースとなる実装は Servlet コンポーネントによって提供されます。Servlet コンポーネントは、例4.1「REST DSL の Servlet コンポーネントの設定」 に示されているように、Blueprint XML ファイルで設定されます。

例4.1 REST DSL の Servlet コンポーネントの設定

<?xml version="1.0" encoding="UTF-8"?>
<blueprint ...>

  <!-- to setup camel servlet with OSGi HttpService -->
  <reference id="httpService" interface="org.osgi.service.http.HttpService"/>

  <bean class="org.apache.camel.component.servlet.osgi.OsgiServletRegisterer"
        init-method="register"
        destroy-method="unregister">
    <property name="alias" value="/camel-example-servlet-rest-blueprint/rest"/>
    <property name="httpService" ref="httpService"/>
    <property name="servlet" ref="camelServlet"/>
  </bean>

  <bean id="camelServlet" class="org.apache.camel.component.servlet.CamelHttpTransportServlet"/>
  ...
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">

    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>
    ...
  </camelContext>

</blueprint>

Servlet コンポーネントを REST DSL で設定するには、以下の 3 つのレイヤーを設定する必要があります。

REST DSL レイヤー
REST DSL レイヤーは restConfiguration 要素によって設定され、component 属性を servlet という値に設定することで Servlet コンポーネントと統合されます。
Servlet コンポーネントレイヤー
Servlet コンポーネントレイヤーは、クラス CamelHttpTransportServlet のインスタンスとして実装され、このサンプルインスタンスには Bean ID camelServlet があります。
HTTP コンテナーレイヤー

Servlet コンポーネントは HTTP コンテナーにデプロイする必要があります。Karaf コンテナーには通常、ポート 8181 の HTTP リクエストをリッスンするデフォルトの HTTP コンテナー (Jetty HTTP コンテナー) が提供されています。Servlet コンポーネントをデフォルトの Jetty HTTP コンテナーにデプロイするには、以下を行います。

  1. OSGi サービス (org.osgi.service.http.HttpService) への OSGi 参照を取得します。このサービスは、OSGi のデフォルト HTTP サーバーへのアクセスを提供する標準化された OSGi インターフェイスです。
  2. ユーティリティークラスのインスタンス (OsgiServletRegisterer) を作成して、HTTP コンテナーに Servlet コンポーネントを登録します。OsgiServletRegisterer クラスは、Servlet コンポーネントのライフサイクル管理を簡素化するユーティリティーです。このクラスのインスタンスが作成されると、HttpService OSGi サービス上の registerServlet メソッドが自動的に呼び出され、インスタンスが破棄されると、unregister メソッドが自動的に呼び出されます。

必要な依存関係

この例には、REST DSL にとって重要な以下の 2 つの依存関係があります。

Servlet コンポーネント

REST DSL のベースとなる実装を提供します。以下のように Maven POM ファイルで指定します。

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-servlet</artifactId>
  <version>${camel-version}</version>
</dependency>

また、OSGi コンテナーにデプロイする場合、以下のように Servlet コンポーネント機能をインストールする必要があります。

JBossFuse:karaf@root> features:install camel-servlet
Jackson データフォーマット

JSON データフォーマットの実装を提供します。以下のように Maven POM ファイルで指定します。

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-jackson</artifactId>
  <version>${camel-version}</version>
</dependency>

また、OSGi コンテナーにデプロイする場合、以下のように Jackson データフォーマット機能をインストールする必要があります。

JBossFuse:karaf@root> features:install camel-jackson

レスポンス用 の Java 型

サンプルアプリケーションは、HTTP リクエストメッセージとレスポンスメッセージで User 型のオブジェクトを渡し合います。Java クラス User は、例4.2「JSON レスポンス用ユーザークラス」 で示すように定義されます。

例4.2 JSON レスポンス用ユーザークラス

// Java
package org.apache.camel.example.rest;

public class User {

    private int id;
    private String name;

    public User() {
    }

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

クラス User は、JSON データ形式で比較的シンプルに表現されます。たとえば、JSON 形式で表現されたこのクラスのインスタンスは次のようになります。

{
    "id" : 1234,
    "name" : "Jane Doe"
}

JSON バインディングを使用した REST DSL の例

この例の REST DSL 設定と REST サービス定義を 例4.3「JSON バインディングでの REST DSL の例」 に示します。

例4.3 JSON バインディングでの REST DSL の例

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           ...>
  ...
  <!-- a bean for user services -->
  <bean id="userService" class="org.apache.camel.example.rest.UserService"/>

  <camelContext xmlns="http://camel.apache.org/schema/blueprint">

    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>

    <!-- defines the REST services using the  base path, /user -->
    <rest path="/user" consumes="application/json" produces="application/json">
      <description>User rest service</description>

      <!-- this is a rest GET to view a user with the given id -->
      <get uri="/{id}" outType="org.apache.camel.example.rest.User">
        <description>Find user by id</description>
        <to uri="bean:userService?method=getUser(${header.id})"/>
      </get>

      <!-- this is a rest PUT to create/update a user -->
      <put type="org.apache.camel.example.rest.User">
        <description>Updates or create a user</description>
        <to uri="bean:userService?method=updateUser"/>
      </put>

      <!-- this is a rest GET to find all users -->
      <get uri="/findAll" outType="org.apache.camel.example.rest.User[]">
        <description>Find all users</description>
        <to uri="bean:userService?method=listUsers"/>
      </get>

    </rest>

  </camelContext>

</blueprint>

REST オペレーション

例4.3「JSON バインディングでの REST DSL の例」 からの REST サービスは、以下の REST オペレーションを定義します。

GET /camel-example-servlet-rest-blueprint/rest/user/{id}
{id} で識別されたユーザーの詳細を取得します。HTTP レスポンスは JSON 形式で返されます。
PUT /camel-example-servlet-rest-blueprint/rest/user
新しいユーザーを作成します。ユーザーの詳細は PUT メッセージのボディーに含まれ、JSON 形式でエンコードされます (User オブジェクトタイプと一致するように)。
GET /camel-example-servlet-rest-blueprint/rest/user/findAll
すべてのユーザーの詳細を取得します。HTTP レスポンスはユーザーの配列で、JSON 形式で返されます。

REST サービスを呼び出すための URL

例4.3「JSON バインディングでの REST DSL の例」 から REST DSL の定義を調べることで、各 REST オペレーションを呼び出すのに必要な URL をまとめることができます。たとえば、指定した ID を持つユーザーの詳細を返す最初の REST オペレーションを呼び出す場合、URL は以下になります。

http://localhost:8181
restConfiguration では、プロトコルのデフォルトは http で、ポートは明示的に 8181 に設定されます。
/camel-example-servlet-rest-blueprint/rest
restConfiguration 要素の contextPath 属性によって指定されます。
/user
rest 要素の path 属性によって指定されます。
/{id}
verb 要素 geturi 属性によって指定されます。

したがって、コマンドラインで以下のコマンドを入力することで、curl ユーティリティーでこの REST 操作を呼び出すことができます。

curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/123

同様に、残りの REST オペレーションは、以下のサンプルコマンドを入力することにより、curl で呼び出すことができます。

curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/findAll

curl -X PUT -d "{ \"id\": 666, \"name\": \"The devil\"}" -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user