第51章 エンティティーサポート

概要

Apache CXF ランタイムは、追加設定なしで MIME タイプと Java オブジェクトとの間のマッピングを少数サポートします。開発者はカスタムリーダーとライターを実装してマッピングを拡張できます。カスタムリーダーとライターは、起動時にランタイムに登録されます。

概要

ランタイムは、JAX-RS MessageBodyReader と MessageBodyWriter 実装に依存して、HTTP メッセージと Java 表現間でデータをシリアライズおよびデシリアライズします。リーダーとライターは、処理可能な MIME タイプを制限できます。

ランタイムは、共通マッピングのリーダーおよびライターを多数提供します。アプリケーションにより高度なマッピングが必要な場合に、開発者は MessageBodyReader インターフェイスおよび MessageBodyWriter インターフェイスのカスタム実装を指定できます。カスタムリーダーとライターは、アプリケーションの起動時にランタイムに登録されます。

ネイティブでサポートされるタイプ

表51.1「ネイティブでサポートされるエンティティーマッピング」 では、追加設定なしの Apache CXF によって提供されるエンティティーマッピングを一覧表示します。

表51.1 ネイティブでサポートされるエンティティーマッピング

Java タイプMIME タイプ

プリミティブ型

text/plain

java.lang.Number

text/plain

byte[]

*/*

java.lang.String

*/*

java.io.InputStream

*/*

java.io.Reader

*/*

java.io.File

*/*

javax.activation.DataSource

*/*

javax.xml.transform.Source

text/xmlapplication/xmlapplication/\*+xml

javax.xml.bind.JAXBElement

text/xmlapplication/xmlapplication/\*+xml

JAXB アノテーションが付けられたオブジェクト

text/xmlapplication/xmlapplication/\*+xml

javax.ws.rs.core.MultivaluedMap<String, String>

application/x-www-form-urlencoded [a]

javax.ws.rs.core.StreamingOutput

*/* [b]

[a] このマッピングは、HTML フォームデータの処理に使用されます。
[b] このマッピングは、データをコンシューマーに返す場合にのみサポートされます。

カスタムリーダー

カスタムエンティティーリーダーは、受信 HTTP 要求を、サービスの実装が操作できる Java タイプにマッピングします。javax.ws.rs.ext.MessageBodyReader インターフェイスを実装します。

例51.1「メッセージリーダーインターフェイス」 に示されるインターフェイスには、実装が必要な 2 つのメソッドがあります。

例51.1 メッセージリーダーインターフェイス

package javax.ws.rs.ext;

public interface MessageBodyReader<T>
{
  public boolean isReadable(java.lang.Class<?> type,
                            java.lang.reflect.Type genericType,
                            java.lang.annotation.Annotation[] annotations,
                            javax.ws.rs.core.MediaType mediaType);

  public T readFrom(java.lang.Class<T> type,
                    java.lang.reflect.Type genericType,
                    java.lang.annotation.Annotation[] annotations,
                    javax.ws.rs.core.MediaType mediaType,
                    javax.ws.rs.core.MultivaluedMap<String, String> httpHeaders,
                    java.io.InputStream entityStream)
  throws java.io.IOException, WebApplicationException;
}
isReadable()

この isReadable() メソッドは、リーダーがデータストリームを読み取り、適切な型のエンティティー表現を作成できるかどうかを判断します。リーダーが適切な型のエンティティーを作成できる場合は、メソッドが true を返します。

表51.2「リーダーがエンティティーを生成できるかどうかを決定するために使用されるパラメーター」では isReadable() メソッドのパラメーターについて説明します。

表51.2 リーダーがエンティティーを生成できるかどうかを決定するために使用されるパラメーター

パラメータータイプ説明

type

Class<T>

エンティティーを保存するために使用されるオブジェクトの実際の Java クラスを指定します。

genericType

エンティティーを保存するために使用されるオブジェクトの Java タイプを指定します。たとえば、メッセージボディーがメソッドパラメーターに変換される場合、値は Method.getGenericParameterTypes() メソッドによって返されるメソッドパラメーターの型になります。

annotations

Annotation[]

エンティティーの保存用に作成されたオブジェクトの宣言に、アノテーションの一覧を指定します。たとえば、メッセージボディーがメソッドパラメーターに変換される場合は、Method.getParameterAnnotations() メソッドによって返されるパラメーターのアノテーションになります。

mediaType

MediatType

HTTP エンティティーの MIME タイプを指定します。

readFrom()

readFrom() メソッドは HTTP エンティティーを読み取り、目的の Java オブジェクトに変換します。読み取りに成功すると、メソッドはエンティティーを含め、作成された Java オブジェクトを返します。入力ストリームの読み取り中にエラーが発生した場合に、メソッドは IOException 例外を出力する必要があります。HTTP エラー応答を必要とするエラーが発生した場合は、HTTP 応答のある WebApplicationException が発生します。

表51.3「エンティティーの読み取りに使用されるパラメーター」では readFrom() メソッドのパラメーターについて説明します。

表51.3 エンティティーの読み取りに使用されるパラメーター

パラメータータイプ説明

type

Class<T>

エンティティーを保存するために使用されるオブジェクトの実際の Java クラスを指定します。

genericType

エンティティーを保存するために使用されるオブジェクトの Java タイプを指定します。たとえば、メッセージボディーがメソッドパラメーターに変換される場合、値は Method.getGenericParameterTypes() メソッドによって返されるメソッドパラメーターの型になります。

annotations

Annotation[]

エンティティーの保存用に作成されたオブジェクトの宣言に、アノテーションの一覧を指定します。たとえば、メッセージボディーがメソッドパラメーターに変換される場合は、Method.getParameterAnnotations() メソッドによって返されるパラメーターのアノテーションになります。

mediaType

MediatType

HTTP エンティティーの MIME タイプを指定します。

httpHeaders

MultivaluedMap<String, String>

エンティティーに関連付けられた HTTP メッセージヘッダーを指定します。

entityStream

InputStream

HTTP エンティティーが含まれる入力ストリームを指定します。

重要

このメソッドでは入力ストリームは終了されないはずです。

MessageBodyReader 実装をエンティティーリーダーとして使用するには、javax.ws.rs.ext.Provider アノテーションを付ける必要があります。@Provider アノテーションは、提供された実装が追加の機能を提供することをランタイムに警告します。この実装は、「リーダーおよびライターの登録」 の説明に従ってランタイムにも登録する必要があります。

デフォルトでは、カスタムエンティティープロバイダーはすべての MIME タイプを処理します。javax.ws.rs.Consumes アノテーションを使用して、カスタムエンティティーリーダーが処理する MIME タイプを制限できます。@Consumes アノテーションは、カスタムエンティティープロバイダーが読み取る MIME タイプのコンマ区切りリストを指定します。エンティティーが指定された MIME タイプでない場合に、エンティティープロバイダーはリーダー候補として選択されません。

例51.2「XML ソースエンティティーリーダー」 では、消費された XML エンティティーを使用し、それらを Source オブジェクトに保存するエンティティーリーダーを紹介しています。

例51.2 XML ソースエンティティーリーダー

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.apache.cxf.jaxrs.ext.xml.XMLSource;

@Provider
@Consumes({"application/xml", "application/*+xml", "text/xml", "text/html" })
public class SourceProvider implements MessageBodyReader<Object>
{
  public boolean isReadable(Class<?> type,
                            Type genericType,
                            Annotation[] annotations,
                            MediaType mt)
  {
    return Source.class.isAssignableFrom(type) || XMLSource.class.isAssignableFrom(type);
  }

  public Object readFrom(Class<Object> source,
                         Type genericType,
                         Annotation[] annotations,
                         MediaType mediaType,
                         MultivaluedMap<String, String> httpHeaders,
                         InputStream is)
  throws IOException
  {
    if (DOMSource.class.isAssignableFrom(source))
    {
      Document doc = null;
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder;
      try
      {
        builder = factory.newDocumentBuilder();
        doc = builder.parse(is);
      }
      catch (Exception e)
      {
        IOException ioex = new IOException("Problem creating a Source object");
        ioex.setStackTrace(e.getStackTrace());
        throw ioex;
       }

       return new DOMSource(doc);
    }
    else if (StreamSource.class.isAssignableFrom(source) || Source.class.isAssignableFrom(source))
    {
      return new StreamSource(is);
    }
    else if (XMLSource.class.isAssignableFrom(source))
    {
      return new XMLSource(is);
    }

    throw new IOException("Unrecognized source");
  }
}

カスタムライター

カスタムエンティティーライターは、Java タイプを HTTP エンティティーにマッピングします。javax.ws.rs.ext.MessageBodyWriter インターフェイスを実装します。

例51.3「メッセージライターインターフェイス」 に示されるインターフェイスには、実装が必要な 3 つのメソッドがあります。

例51.3 メッセージライターインターフェイス

package javax.ws.rs.ext;

public interface MessageBodyWriter<T>
{
  public boolean isWriteable(java.lang.Class<?> type,
                             java.lang.reflect.Type genericType,
                             java.lang.annotation.Annotation[] annotations,
                             javax.ws.rs.core.MediaType mediaType);

  public long getSize(T t,
                      java.lang.Class<?> type,
                      java.lang.reflect.Type genericType,
                      java.lang.annotation.Annotation[] annotations,
                      javax.ws.rs.core.MediaType mediaType);

  public void writeTo(T t,
                      java.lang.Class<?> type,
                      java.lang.reflect.Type genericType,
                      java.lang.annotation.Annotation[] annotations,
                      javax.ws.rs.core.MediaType mediaType,
                      javax.ws.rs.core.MultivaluedMap<String, Object> httpHeaders,
                      java.io.OutputStream entityStream)
  throws java.io.IOException, WebApplicationException;
}
isWriteable()

isWriteable() メソッドは、エンティティーライターが Java 型を適切なエンティティー型にマップできるかどうかを判断します。ライターがマッピングを実行できる場合、メソッドは true を返します。

表51.4「エンティティーの読み取りに使用されるパラメーター」では isWritable() メソッドのパラメーターについて説明します。

表51.4 エンティティーの読み取りに使用されるパラメーター

パラメータータイプ説明

type

Class<T>

書き込まれるオブジェクトの Java クラスを指定します。

genericType

リソースメソッドの戻り型の反映または返されたインスタンスの検査のいずれかによって取得される、書き込まれるオブジェクトの Java 型を指定します。「汎用型情報を含むエンティティーの返答」で説明されている GenericEntity クラスは、この値を制御するためのサポートを提供します。

annotations

Annotation[]

エンティティーを返すメソッドのアノテーションの一覧を指定します。

mediaType

MediatType

HTTP エンティティーの MIME タイプを指定します。

getSize()

getSize() メソッドは writeTo() の前に呼び出されます。このメソッドでは、書き込まれているエンティティーの長さ (バイト単位) を返します。正の値が返された場合、値は HTTP メッセージの Content-Length ヘッダーに書き込まれます。

表51.5「エンティティーの読み取りに使用されるパラメーター」では getSize() メソッドのパラメーターについて説明します。

表51.5 エンティティーの読み取りに使用されるパラメーター

パラメータータイプ説明

t

generic

書き込まれるインスタンスを指定します。

type

Class<T>

書き込まれるオブジェクトの Java クラスを指定します。

genericType

リソースメソッドの戻り型の反映または返されたインスタンスの検査のいずれかによって取得される、書き込まれるオブジェクトの Java 型を指定します。「汎用型情報を含むエンティティーの返答」で説明されている GenericEntity クラスは、この値を制御するためのサポートを提供します。

annotations

Annotation[]

エンティティーを返すメソッドのアノテーションの一覧を指定します。

mediaType

MediatType

HTTP エンティティーの MIME タイプを指定します。

writeTo()

writeTo() メソッドは Java オブジェクトを目的のエンティティー型に変換し、エンティティーを出力ストリームに書き込みます。エンティティーを出力ストリームに書き込むときにエラーが発生した場合に、メソッドは IOException 例外を出力する必要があります。HTTP エラー応答を必要とするエラーが発生した場合は、HTTP 応答のある WebApplicationException が発生します。

表51.6「エンティティーの読み取りに使用されるパラメーター」では writeTo() メソッドのパラメーターについて説明します。

表51.6 エンティティーの読み取りに使用されるパラメーター

パラメータータイプ説明

t

generic

書き込まれるインスタンスを指定します。

type

Class<T>

書き込まれるオブジェクトの Java クラスを指定します。

genericType

リソースメソッドの戻り型の反映または返されたインスタンスの検査のいずれかによって取得される、書き込まれるオブジェクトの Java 型を指定します。「汎用型情報を含むエンティティーの返答」で説明されている GenericEntity クラスは、この値を制御するためのサポートを提供します。

annotations

Annotation[]

エンティティーを返すメソッドのアノテーションの一覧を指定します。

mediaType

MediatType

HTTP エンティティーの MIME タイプを指定します。

httpHeaders

MultivaluedMap<String, Object>

エンティティーに関連付けられた HTTP 応答ヘッダーを指定します。

entityStream

OutputStream

エンティティーが書き込まれる出力ストリームを指定します。

MessageBodyWriter 実装をエンティティーライターとして使用するには、javax.ws.rs.ext.Provider アノテーションを付ける必要があります。@Provider アノテーションは、提供された実装が追加の機能を提供することをランタイムに警告します。この実装は、「リーダーおよびライターの登録」 の説明に従ってランタイムにも登録する必要があります。

デフォルトでは、カスタムエンティティープロバイダーはすべての MIME タイプを処理します。javax.ws.rs.Produces アノテーションを使用して、カスタムエンティティーライターが処理する MIME タイプを制限できます。@Produces アノテーションは、カスタムエンティティープロバイダーが生成する MIME タイプのコンマ区切りリストを指定します。エンティティーが指定された MIME タイプでない場合に、エンティティープロバイダーはライター候補として選択されません。

例51.4「XML ソースエンティティーライター」 では、ソースオブジェクトを取得して XML エンティティーを生成するエンティティーライターを紹介しています。

例51.4 XML ソースエンティティーライター

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

import org.apache.cxf.jaxrs.ext.xml.XMLSource;

@Provider
@Produces({"application/xml", "application/*+xml", "text/xml" })
public class SourceProvider implements MessageBodyWriter<Source>
{

  public boolean isWriteable(Class<?> type,
                             Type genericType,
                             Annotation[] annotations,
                             MediaType mt)
  {
    return Source.class.isAssignableFrom(type);
  }

  public void writeTo(Source source,
                      Class<?> clazz,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mediatype,
                      MultivaluedMap<String, Object> httpHeaders,
                      OutputStream os)
  throws IOException
  {
    StreamResult result = new StreamResult(os);
    TransformerFactory tf = TransformerFactory.newInstance();
    try
    {
      Transformer t = tf.newTransformer();
      t.transform(source, result);
    }
    catch (TransformerException te)
    {
      te.printStackTrace();
      throw new WebApplicationException(te);
    }
  }

  public long getSize(Source source,
                      Class<?> type,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mt)
  {
    return -1;
  }
}

リーダーおよびライターの登録

JAX-RS アプリケーションがカスタムエンティティープロバイダーを使用できるようにするには、カスタムプロバイダーをランタイムに登録する必要があります。プロバイダーは、アプリケーションの設定ファイルの jaxrs:providers 要素を使用するか、JAXRSServerFactoryBean クラスを使用してランタイムに登録されます。

jaxrs:providers 要素は jaxrs:server 要素の子で、bean 要素のリストが含まれます。各 bean 要素は 1 つのエンティティープロバイダーを定義します。

例51.5「ランタイムを使用したエンティティープロバイダーの登録」 では、カスタムエンティティープロバイダーのセットを使用するよう設定された JAX-RS サーバーを示しています。

例51.5 ランタイムを使用したエンティティープロバイダーの登録

<beans ...>
  <jaxrs:server id="customerService" address="/">
    ...
    <jaxrs:providers>
      <bean id="isProvider" class="com.bar.providers.InputStreamProvider"/>
      <bean id="longProvider" class="com.bar.providers.LongProvider"/>
    </jaxrs:providers>
  </jaxrs:server>
</beans>

JAXRSServerFactoryBean クラスは、設定 API へのアクセスを提供する Apache CXF エクステンションです。インスタンス化されたエンティティープロバイダーをアプリケーションに追加できるようにする setProvider() メソッドがあります。例51.6「エンティティープロバイダーのプログラムによる登録」 は、エンティティープロバイダーをプログラムで登録するためのコードを示します。

例51.6 エンティティープロバイダーのプログラムによる登録

import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
...
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
...
SourceProvider provider = new SourceProvider();
sf.setProvider(provider);
...