61.6. エンティティーリーダーインターセプター
概要
本セクションでは、エンティティーリーダーインターセプター を実装し、登録する方法を説明します。これにより、クライアント側またはサーバー側でメッセージボディーを読み取る際に入力ストリームをインターセプトできます。これは通常、暗号化や復号などの要求ボディーの汎用的な変換や圧縮や圧縮解除に役立ちます。
ReaderInterceptor インターフェイス
javax.ws.rs.ext.ReaderInterceptor
インターフェイスは以下のように定義されます。
// Java ... package javax.ws.rs.ext; public interface ReaderInterceptor { public Object aroundReadFrom(ReaderInterceptorContext context) throws java.io.IOException, javax.ws.rs.WebApplicationException; }
ReaderInterceptor
インターフェイスを実装することで、メッセージボディー (Entity
オブジェクト) をサーバー側またはクライアント側で読み取る際にインターセプトできます。エンティティーリーダーインターセプターは、以下のいずれかのコンテキストで使用できます。
- サーバー側: サーバー側のインターセプターとしてバインドされた場合に、エンティティーリーダーインターセプターは、(一致したリソース内の) アプリケーションコードによってアクセスされたタイミングで、要求メッセージのボディーをインターセプトします。REST 要求のセマンティクスによっては、一致したリソースがメッセージ本文にアクセスできない場合があり、そのような場合にはリーダーインターセプターは呼び出されません。
-
クライアント側: クライアント側のインターセプターとしてバインドされた場合に、エンティティーリーダーインターセプターは、クライアントコードによってアクセスされたタイミングで応答メッセージのボディーをインターセプトします。クライアントコードが明示的にレスポンスメッセージにアクセスしない場合 (
Response.getEntity
メソッドの呼び出しなど)、リーダーインターセプターは呼び出されません。
ReaderInterceptorContext インターフェイス
ReaderInterceptor
の aroundReadFrom
メソッドは、メッセージボディー (Entity
オブジェクト) とメッセージメタデータの両方にアクセスするために使用できる javax.ws.rs.ext.ReaderInterceptorContext
型の引数を 1 つ受け取ります。
ReaderInterceptorContext
インターフェイスは以下のように定義されます。
// Java ... package javax.ws.rs.ext; import java.io.IOException; import java.io.InputStream; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MultivaluedMap; public interface ReaderInterceptorContext extends InterceptorContext { public Object proceed() throws IOException, WebApplicationException; public InputStream getInputStream(); public void setInputStream(InputStream is); public MultivaluedMap<String, String> getHeaders(); }
InterceptorContext インターフェイス
ReaderInterceptorContext
インターフェイスは、ベース InterceptorContext
インターフェイスから継承されたメソッドもサポートします。
InterceptorContext
インターフェイスは以下のように定義されます。
// Java ... package javax.ws.rs.ext; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Collection; import javax.ws.rs.core.MediaType; public interface InterceptorContext { public Object getProperty(String name); public Collection<String> getPropertyNames(); public void setProperty(String name, Object object); public void removeProperty(String name); public Annotation[] getAnnotations(); public void setAnnotations(Annotation[] annotations); Class<?> getType(); public void setType(Class<?> type); Type getGenericType(); public void setGenericType(Type genericType); public MediaType getMediaType(); public void setMediaType(MediaType mediaType); }
クライアント側での実装例
クライアント側にエンティティーリーダーインターセプターを実装するには、ReaderInterceptor
インターフェイスを実装するクラスを定義します。
たとえば、以下のコードは、クライアント側のエンティティーリーダーインターセプター (優先度が 10) の例を示しています。これは、受信応答のメッセージボディーで COMPANY_NAME
の全インスタンスを Red Hat
に置き換えます。
// Java package org.jboss.fuse.example; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import javax.annotation.Priority; import javax.ws.rs.WebApplicationException; import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.ReaderInterceptorContext; @Priority(value = 10) public class SampleClientReaderInterceptor implements ReaderInterceptor { @Override public Object aroundReadFrom(ReaderInterceptorContext interceptorContext) throws IOException, WebApplicationException { InputStream inputStream = interceptorContext.getInputStream(); byte[] bytes = new byte[inputStream.available()]; inputStream.read(bytes); String responseContent = new String(bytes); responseContent = responseContent.replaceAll("COMPANY_NAME", "Red Hat"); interceptorContext.setInputStream(new ByteArrayInputStream(responseContent.getBytes())); return interceptorContext.proceed(); } }
サーバー側での実装例
サーバー側のエンティティーリーダーインターセプターを実装するには、ReaderInterceptor
インターフェイスを実装し、@Provider
アノテーションを付けるクラスを定義します。
たとえば、以下のコードは、サーバー側のエンティティーリーダーインターセプター (優先度が 10) の例を示しています。これは、受信リクエストのメッセージボディーで COMPANY_NAME
の全インスタンスを Red Hat
に置き換えます。
// Java package org.jboss.fuse.example; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import javax.annotation.Priority; import javax.ws.rs.WebApplicationException; import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.ReaderInterceptorContext; @Priority(value = 10) @Provider public class SampleServerReaderInterceptor implements ReaderInterceptor { @Override public Object aroundReadFrom(ReaderInterceptorContext interceptorContext) throws IOException, WebApplicationException { InputStream inputStream = interceptorContext.getInputStream(); byte[] bytes = new byte[inputStream.available()]; inputStream.read(bytes); String requestContent = new String(bytes); requestContent = requestContent.replaceAll("COMPANY_NAME", "Red Hat"); interceptorContext.setInputStream(new ByteArrayInputStream(requestContent.getBytes())); return interceptorContext.proceed(); } }
クライアント側でのリーダーインターセプターのバインド
JAX-RS 2.0 クライアント API を使用すると、エンティティーリーダーインターセプターを javax.ws.rs.client.Client
オブジェクトまたは javax.ws.rs.client.WebTarget
オブジェクトに直接登録できます。実質的に、リーダーインターセプターはオプションで異なるスコープに適用できるため、インターセプターの影響を受けるのは、特定の URI パスのみです。
たとえば、以下のコードは、client
オブジェクトを使用して実行されたすべての呼び出しに適用されるように SampleClientReaderInterceptor
インターセプターを登録する方法を示しています。
// Java ... import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Invocation; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; ... Client client = ClientBuilder.newClient(); client.register(SampleClientReaderInterceptor.class);
JAX-RS 2.0 クライアントでインターセプターを登録する方法は、「クライアントエンドポイントの設定」 を参照してください。
サーバー側でのリーダーインターセプターのバインド
サーバー側でリーダーインターセプターを バインド する (つまり Apache CXF ランタイムにインストールする) には、以下の手順を実行します。
以下のコードフラグメントで示されるように、
@Provider
アノテーションをリーダーインターセプタークラスに追加します。// Java package org.jboss.fuse.example; ... import javax.annotation.Priority; import javax.ws.rs.WebApplicationException; import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.ReaderInterceptorContext; @Priority(value = 10) @Provider public class SampleServerReaderInterceptor implements ReaderInterceptor { ... }
リーダーインターセプター実装が Apache CXF ランタイムにロードされると、REST 実装はロードされたクラスを自動的にスキャンし、
@Provider
アノテーション (スキャンフェーズ) の付いたクラスを検索します。XML で JAX-RS サーバーエンドポイントを定義する場合 (例: 「JAX-RS サーバーエンドポイントの設定」)、
jaxrs:providers
要素のプロバイダーリストにリーダーインターセプターを追加します。<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs" xmlns:cxf="http://cxf.apache.org/blueprint/core" ... > ... <jaxrs:server id="customerService" address="/customers"> ... <jaxrs:providers> <ref bean="interceptorProvider" /> </jaxrs:providers> <bean id="interceptorProvider" class="org.jboss.fuse.example.SampleServerReaderInterceptor"/> </jaxrs:server> </blueprint>
注記この手順は、Apache CXF の非標準要件です。厳密に言うと、JAX-RS 標準によれば、インターセプターをバインドするために必要なのは
@Provider
アノテーションのみです。しかし実際には、標準的なアプローチはやや柔軟性がなく、大規模なプロジェクトに多くのライブラリーが含まれている場合は、プロバイダーの衝突につながる可能性があります。