Menu Close

第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/xml, application/xml, application/\*+xml

javax.xml.bind.JAXBElement

text/xml, application/xml, application/\*+xml

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

text/xml, application/xml, application/\*+xml

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

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

javax.ws.rs.core.StreamingOutput

*/* [b]

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

カスタムリーダー

カスタムエンティティーリーダーは、サービスの実装が操作できる Java 型に受信 HTTP 要求をマッピングする役割を担います。これらは、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

Type

エンティティーを保存するために使用されるオブジェクトの 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

Type

エンティティーを保存するために使用されるオブジェクトの 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 エンティティーを消費し、ソースオブジェクトに保存するエンティティーリーダーを示しています。

例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);
...