Web サービスアプリケーションの開発
Red Hat JBoss Enterprise Application Platform の Web サービスアプリケーションの開発手順
概要
第1章 Web サービスの概要
Web サービスは、異なるソフトウェアアプリケーション間の標準的な対話手段を提供します。各アプリケーションは、さまざまなプラットフォームやフレームワークで実行できます。
Web サービスは、内部の異種サブシステムの通信を容易にします。さまざまな環境に対して機能を再作成する必要がないため、相互運用性によりサービスの再利用が増加します。
第2章 JAX-RS Web サービスの開発
JAX-RS は RESTful Web サービスの Java API です。これは、アノテーションを使用して REST を使用した Web サービスを構築することをサポートします。これらのアノテーションは、Java オブジェクトを Web リソースにマッピングするプロセスを簡素化します。
RESTEasy は JAX-RS の Red Hat JBoss Enterprise Application Platform 7 実装で、JSR 370: Java™ API for RESTful Web Services (JAX-RS 2.1) 仕様に完全準拠しています。また、この仕様に追加機能も提供します。
Red Hat JBoss Enterprise Application Platform の 7.3 リリースは、Jakarta Enterprise Web Services 1.4 および Jakarta RESTful Web Services 2.1 Jakarta EE 仕様にも準拠しています。
JAX-RS を初めて使用する場合は、Red Hat JBoss Enterprise Application Platform 7 に同梱される helloworld
、および -rs
、jaxrs-clientkitchensink
クイックスタートを参照してください。
JBoss EAP は resteasy-crypto
、resteasy-yaml-provider
、および jose-jwt
モジュールをサポートしません。
2.1. JAX-RS アプリケーション
プロバイダーと Web リソースを作成する際に、それらを宣言するには以下のオプションを使用できます。
-
web.xml
ファイルなしでjavax.ws.rs.core.Application
の簡単なサブクラス。 -
web.xml
ファイルの使用。 -
Javax.ws.rs.core.Application
のサブクラス化とカスタム実装の提供。
2.1.1. javax.ws.rs.core.Application
の単純なサブクラス化
javax.ws.rs.core.Application
クラスを使用して、これらのプロバイダーおよび Web リソースを宣言するサブクラスを作成できます。このクラスは、JBoss EAP に含まれる RESTEasy ライブラリーによって提供されます。
javax.ws.rs.core.Application
を使用してリソースまたはプロバイダーを設定するには、拡張するクラスを作成して @ApplicationPath
アノテーションを追加します。
例: アプリケーションクラス
import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("/root-path") public class MyApplication extends Application { }
2.1.2. web.xml
の使用
または、javax.ws.rs.core.Application
を拡張するクラスを作成しない場合は、web.xml
ファイルに以下を追加します。
例: web.xml
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <servlet> <servlet-name>javax.ws.rs.core.Application</servlet-name> </servlet> <servlet-mapping> <servlet-name>javax.ws.rs.core.Application</servlet-name> <url-pattern>/root-path/*</url-pattern> </servlet-mapping> ... </web-app>
2.1.3. カスタム実装による javax.ws.rs.core.Application
のサブクラス化
javax.ws.rs.core.Application
のサブクラス化を行うとき、既存のメソッドにカスタム実装を提供することを選択できます。getClasses
および getSingletons
メソッドは、公開された JAX-RS アプリケーションに含める必要があるクラスまたはシングルトンのコレクションを返します。
-
getClasses
およびgetSingletons
のいずれかが空でないコレクションを返す場合、これらのクラスとシングルトンのみが JAX-RS アプリケーションで公開されます。 -
getClasses
とgetSingletons
の両方が空のコレクションを返す場合、web アプリケーションでパッケージ化された root リソースクラスおよびプロバイダーはすべて JAX-RS アプリケーションに含まれます。その後、RESTEasy はこれらのリソースを自動的に検出します。
2.2. JAX-RS Client
2.2.1. JAX-RS Client API
JAX-RS 2.0 では、HTTP リクエストをリモート RESTful Web サービスに送信する新しいクライアント API が導入されました。3 つの主要クラスを持つ fluent request-building API です。
-
Client
-
WebTarget
-
Response
Client
インターフェイスは WebTarget インスタンスのビルダーです。WebTarget
は、サブリソース WebTargets をビルドしたり、リクエストを呼び出すための固有の URL または URL テンプレートを表します。
クライアントを作成する方法は、標準の方法か、ResteasyClientBuilder
クラスを使用する方法です。ResteasyClientBuilder
クラスを使用する利点は、クライアントを設定するヘルパーメソッドが他にもいくつか提供されることです。
ResteasyClientBuilder
クラス はこれらのヘルパーメソッドを提供しますが、クラスは JBoss EAP API に固有のものです。アプリケーションを新しいサーバーに移行する場合は、アプリケーションを再構築する必要があります。ResteasyClientBuilder
クラスは RESTEasy に依存し、クラスは移植できません。
JAX-RS および Java EE API 仕様の両方に準拠するクライアントを作成する標準的な方法は、JAX-RS 実装間で移植可能です。
JAX-RS アプリケーションが移植可能な状態に保つには、Java EE API 仕様に従い、可能な場合は Java EE API アプリケーションを使用します。ユースケースが Java EE API の使用をサポートしない場合は、JBoss に固有の API のみを使用してください。
これらのガイドラインに従うと、アプリケーションを別のサーバーに移行する場合や、新しい Java EE と互換性のある JBoss 実装に移行する際に発生する可能性のある問題の数を減らすことができます。
標準の方法を使用したクライアントの作成
以下の例は、クライアントを作成するための標準的な方法のいずれかを示しています。
Client client = ClientBuilder.newClient();
また、別の標準的な方法を使用して、以下の例のようにクライアントを作成することもできます。
Client client = ClientBuilder.newBuilder().build(); WebTarget target = client.target("http://foo.com/resource"); Response response = target.request().get(); String value = response.readEntity(String.class); response.close(); // You should close connections!
ResteasyClientBuilder クラスを使用したクライアントの作成
以下の例は、ResteasyClientBuilder
クラスを使用してクライアントを作成します。
ResteasyClient client = new ResteasyClientBuilder().build(); ResteasyWebTarget target = client.target("http://foo.com/resource");
JAX-RS 2.1 では、2 つのタイムアウトメソッドを ClientBuilder
クラスに追加できます。タイムアウトメソッドは仕様に準拠したメソッドであり、RESTEasy メソッドを使用する代わりに使用することができます。
以下の ClientBuilder
仕様に準拠したメソッドは、非推奨となった RESTEasy メソッドを置き換えます。
connectTimeout
メソッドはestablishConnectionTimeout
メソッドに置き換わります。connectTimeout
メソッドは、新しいサーバー接続を行うときにクライアントが待機する時間を決定します。readTimeout
メソッドはsocketTimeout
メソッドに置き換わります。readTimeout
メソッドは、クライアントがサーバーからの応答を待つ時間を決定します。
以下の例は、connectTimeout
メソッドおよび readTimeout
メソッドの指定された値を示しています。
import javx.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Client; Client client = ClientBuilder.newBuilder() .connectTimeout(100, TimeUnit.SECONDS) .readTimeout(2, TimeUnit.SECONDS) .build();
readTimeout
は、既存の接続で実行されたリクエストに適用されることに注意してください。
timeout パラメーターの値をゼロに設定すると、サーバーは永久に待機します。
RESTEasy は、META-INF/services/javax.ws.rs.ext.Providers
ファイルにリストされているすべてのクラスを含むデフォルトのプロバイダーのセットを自動的に読み込みます。また、Client.configuration()
メソッド呼び出しによって提供される設定オブジェクトを使用して、他のプロバイダー、フィルター、インターセプターを手動で登録できます。設定では、必要になる可能性のある設定プロパティーを設定することもできます。
各 WebTarget
には、親インスタンスに登録されているコンポーネントおよびプロパティーを継承する設定インスタンスがあります。これにより、ユーザー名やパスワードなど、各ターゲットリソースに特定の設定オプションを設定できます。
その他のリソース
-
ResteasyClientBuilder
クラスおよびそのメソッドの詳細 は、Class ResteasyClientBuilder を参照してください。
RESTEasy クライアントクラスの使用
RESTEasy クライアントの以下の依存関係を Maven pom.xml
ファイルに追加する必要があります。
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>VERSION_IN_EAP</version>
</dependency>
RESTEasy クライアントクラスを使用する作業例は、JBoss EAP に同梱される jaxrs-client
および resteasy-jaxrs-client
クイックスタートを参照してください。
クライアント側のフィルター
クライアント側のフィルターには、以下のタイプがあります。
ClientRequestFilter
-
ClientRequestFilter
は、HTTP リクエストがサーバーへネットワーク経由で送信される前に実行されます。ClientRequestFilter
は、要求の実行を中止し、サーバーへの接続を行わずに、実行可能な応答を提供することもできます。 ClientResponseFilter
-
ClientResponseFilter
は、サーバーから応答を受信した後、応答本体がマーシャリング解除される前に実行されます。ClientResponseFilter
は、応答オブジェクトをアプリケーションコードに移動する前に変更できます。以下の例は、これらの概念を示しています。
// execute request filters for (ClientRequestFilter filter : requestFilters) { filter.filter(requestContext); if (isAborted(requestContext)) { return requestContext.getAbortedResponseObject(); } } // send request over the wire response = sendRequest(request); // execute response filters for (ClientResponseFilter filter : responseFilters) { filter.filter(requestContext, responseContext); }
クライアントリクエストへのクライアント側のフィルターの登録
以下の例は、クライアント側のフィルターをクライアント要求に登録する方法を表しています。
client = ClientBuilder.newClient(); WebTarget base = client.target(generateURL("/") + "get"); base.register(ClientExceptionsCustomClientResponseFilter.class).request("text/plain").get();
クライアント側のキャッシュ
RESTEasy には、クライアント側のキャッシュを設定する機能があります。このキャッシュは、サーバーの応答と共に送信されるキャッシュ制御ヘッダーを検索します。cache-control ヘッダーがクライアントが応答のキャッシュを許可するように指定すると、RESTEasy はローカルメモリー内にこれをキャッシュします。
ResteasyWebTarget target = client.target(generateBaseUrl()); target.register(BrowserCacheFeature.class);
チャンク化されたエンコーディングサポート
RESTEasy は、リクエストを チャンク 転送モードで送信するよう指定する機能をクライアント API に提供します。以下のように、チャンク された転送モードを指定する方法を使用できます。
org.jboss.resteasy.client.jaxrs.ResteasyWebTarget
を設定して、すべてのリクエストをチャンクモードで送信することができます。ResteasyClient client = new ResteasyClientBuilder().build(); ResteasyWebTarget target = client.target("http://localhost:8081/test"); target.setChunked(b.booleanValue()); Invocation.Builder request = target.request();
または、特定のリクエストをチャンクモードで送信するように設定することもできます。
ResteasyClient client = new ResteasyClientBuilder().build(); ResteasyWebTarget target = client.target("http://localhost:8081/test"); ClientInvocationBuilder request = (ClientInvocationBuilder) target.request(); request.setChunked(b);
javax.ws.rs.client.Invocation.Builder
クラスとは異なりorg.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder
は RESTEasy クラスです。
チャンクモードでリクエストを送信する機能は、基盤のトランスポート層によって異なります。特に、使用されている org.jboss.resteasy.client.jaxrs.ClientHttpEngine
クラスの実装によって異なります。現時点では、デフォルトの実装 ApacheHttpClient43Engine
と以前の実装の ApacheHttpClient4Engine
のみが、チャンクモードをサポートします。これらはいずれも org.jboss.resteasy.client.jaxrs.engines
パッケージで利用できます。詳細は、Implementing RESTEasy with HTTP Client を参照してください。
2.2.2. HTTP クライアントによる RESTEasy の実装
クライアントとサーバー間のネットワーク通信は、RESTEasy でデフォルトで処理されます。Apache HttpComponents
プロジェクトの HttpClient HttpClient
を使用します。RESTEasy クライアントフレームワークとネットワークの間のインターフェイスは、ClientHttpEngine
インターフェイスによって定義されます。
RESTEasy には、このインターフェイスの 4 つの実装が同梱されています。デフォルトの実装は ApacheHttpClient43Engine
です。この実装は Apache 4.3 を使用します。
ApacheHttpClient4Engine
は、Apache 4.3 よりも前のバージョンを使用する実装です。このクラスは後方互換性を提供します。RESTEasy は、Apache バージョンの検出に基づいて、これらの 2 つの ClientHttpEngine
実装のいずれかを自動的に選択します。InMemoryClientEngine
は、同じ JVM のサーバーにリクエストをディスパッチする実装で、URLConnectionEngine
は java.net.HttpURLConnection
を使用する実装です。
クライアントエグゼキューターは、特定の ClientRequest
に渡すことができます。
ResteasyClient client = new ResteasyClientBuilder().httpEngine(engine).build();
RESTEasy および HttpClient
は、HttpClient
を参照せずに、デフォルトのクライアントフレームワークを使用するように決定します。ただし、一部のアプリケーションでは、HttpClient
の詳細を閲覧する必要がある場合があります。ApacheHttpClient43Engine
および ApacheHttpClient4Engine
は、org.apache.http.client.HttpClient
と org.apache.http.protocol.HttpContext
のインスタンスで提供できます。これは、HttpClient
レイヤーに追加の設定詳細を実行できます。たとえば、認証は以下のように設定できます。
// Configure HttpClient to authenticate preemptively // by prepopulating the authentication data cache. // 1. Create AuthCache instance AuthCache authCache = new BasicAuthCache(); // 2. Generate BASIC scheme object and add it to the local auth cache AuthScheme basicAuth = new BasicScheme(); authCache.put(new HttpHost("sippycups.bluemonkeydiamond.com"), basicAuth); // 3. Add AuthCache to the execution context BasicHttpContext localContext = new BasicHttpContext(); localContext.setAttribute(ClientContext.AUTH_CACHE, authCache); // 4. Create client executor and proxy HttpClient httpClient = HttpClientBuilder.create().build(); ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient, localContext); ResteasyClient client = new ResteasyClientBuilder().httpEngine(engine).build();
HttpContextProvider
は RESTEasy が提供するインターフェイスで、これを使用してカスタム HttpContext
を ApacheHttpClient43Engine
および ApacheHttpClient4Engine
実装に提供できます。
コネクションの解放とコネクションのクローズの違いを理解しておくことが重要となります。接続を解放すると、接続が再利用できるようになります。接続を閉じると、そのリソースが解放され、使用できなくなります。
RESTEasy は通知なしで接続をリリースします。唯一のカウンター例は、応答が InputStream
のインスタンスであるケースです。これは明示的に閉じる必要があります。
一方、呼び出しの結果が Response
のインスタンスである場合は、Response.close()
メソッドを使用して接続を解放する必要があります。
WebTarget target = client.target("http://localhost:8081/customer/123"); Response response = target.request().get(); System.out.println(response.getStatus()); response.close();
これは try-finally
ブロックで実行できます。接続を解放すると、接続を別のことに利用できるようになります。通常、ソケットを閉じることはありません。
ApacheHttpClient4Engine.finalize()
は、使用中の HttpClient
を作成した場合に、開いているソケットを閉じます。finalize()
を呼び出すために JDK を使用するのは安全ではありません。HttpClient
が ApacheHttpClient4Executor
に渡される場合、ユーザーは以下のように接続を閉じる必要があります。
HttpClient httpClient = new HttpClientBuilder.create().build(); ApacheHttpClient4Engine executor = new ApacheHttpClient4Engine(httpClient); ... httpClient.getConnectionManager().shutdown();
ApacheHttpClient4Engine
が HttpClient
の独自のインスタンスを作成した場合は finalize()
がオープンソケットを閉じるのを待つ必要はありません。ClientHttpEngine
インターフェイスには、この目的のために close()
メソッドがあります。
最後に、javax.ws.rs.client.Client
クラスがエンジンを自動的に作成している場合は、Client.close()
を呼び出します。この呼び出しにより、ソケット接続がクリーンアップされます。
2.2.2.1. HTTP リダイレクト
Apache HttpClient をベースとする ClientHttpEngine
実装 は HTTP リダイレクトをサポートします。この機能はデフォルトで無効になっています。これを有効にするには、以下のように setFollowRedirects
メソッドを true
に設定します。
ApacheHttpClient43Engine engine = new ApacheHttpClient43Engine(); engine.setFollowRedirects(true); Client client = new ResteasyClientBuilder().httpEngine(engine).build();
2.3. JAX-RS 要求処理
2.3.1. 非同期 HTTP リクエストの処理
非同期リクエスト処理により、非ブロッキング入力および出力を使用して単一の HTTP リクエストを処理し、必要に応じて別々のスレッドで処理できます。
クライアントとサーバーの両方からプッシュおよびプルする AJAX クライアントについて考えてみましょう。このシナリオでは、サーバーのソケットでクライアントが長時間ブロックし、新しいメッセージを待ちます。サーバーが着信および発信の入出力でブロックしている同期 HTTP 処理の場合は、クライアント接続ごとに別々のスレッドが消費されます。リクエスト処理のこのモデルでは、大量のメモリーおよび有意なスレッドリソースを消費します。
非同期処理は、受け入れる接続とリクエスト処理操作を切り分けます。クライアント接続を受け入れるスレッドと、長時間の時間がかかる操作を処理させるスレッドと、異なるスレッドが割り当てられます。このモデルでは、コンテナーは以下のように動作します。
- アクセプターであるクライアント接続を受け入れるためにスレッドをディスパッチします。
- その後、リクエストを処理スレッド (worker) に渡します。
- 最後に、アクセプタースレッドを解放します。
結果が、worker (ワーカー) スレッドによってクライアントに戻されます。そのため、クライアントの接続は開いたままであるため、サーバーのスループットとスケーラビリティーが向上します。
2.3.1.1. 非同期 NIO 要求処理
RESTEasy のデフォルトの非同期エンジン実装クラスは ApacheHttpAsyncClient4Engine
です。これは Apache
httpcomponents の HttpAsyncClient
でビルドされ、内部で非ブロッキング IO モデルを使用してリクエストをディスパッチします。
ResteasyClientBuilder
クラスで useAsyncHttpEngine
メソッドを呼び出すと、非同期エンジンをアクティブなエンジンとして設定できます。
Client asyncClient = new ResteasyClientBuilder().useAsyncHttpEngine() .build(); Future<Response> future = asyncClient .target("http://locahost:8080/test").request() .async().get(); Response res = future.get(); Assert.assertEquals(HttpResponseCodes.SC_OK, res.getStatus()); String entity = res.readEntity(String.class);
2.3.1.2. サーバー非同期の応答処理
サーバー側では、非同期処理は、元のリクエストスレッドを一時停止し、別のスレッドでリクエスト処理を開始します。これにより、元のサーバー側のスレッドが解放され、他の受信リクエストが許可されます。
2.3.1.2.1. AsyncResponse API
JAX-RS 2.0 仕様は、@Suspended
アノテーションと AsyncResponse
インターフェイスという 2 つのクラスを使用して非同期 HTTP サポートを追加しました。
AsyncResponse
をパラメーターとして JAX-RS メソッドに挿入すると、現在実行されているスレッドから HTTP リクエストと応答を切り離すよう RESTEasy が要求します。これにより、現在のスレッドが応答を自動的に処理しないようにします。
AsyncResponse
はコールバックオブジェクトです。resume()
メソッドのいずれかを呼び出す動作により、応答がクライアントに返送され、HTTP リクエストも終了します。以下は非同期処理の例になります。
import javax.ws.rs.container.Suspended; import javax.ws.rs.container.AsyncResponse; @Path("/") public class SimpleResource { @GET @Path("basic") @Produces("text/plain") public void getBasic(@Suspended final AsyncResponse response) throws Exception { Thread t = new Thread() { @Override public void run() { try { Response jaxrs = Response.ok("basic").type(MediaType.TEXT_PLAIN).build(); response.resume(jaxrs); } catch (Exception e) { e.printStackTrace(); } } }; t.start(); } }
2.3.1.3. Async Missionr Client API
同様に、クライアント側の非同期処理は、サーバーからの応答待ちがないため、リクエストスレッドのブロックを阻止します。たとえば、リクエストを発行したスレッドは、ユーザーインターフェイスコンポーネントも更新することがあります。応答を待つスレッドがブロックされると、ユーザーが認識したアプリケーションのパフォーマンスに影響が出ます。
2.3.1.3.1. future の使用
以下のコードスニペットでは、get()
メソッドはリクエストではなく async()
メソッド上で呼び出されます。これにより、呼び出しメカニズムが同期から非同期に変更されます。同期的に応答するのではなく、async()
メソッドは future
オブジェクトを返します。get()
メソッドを呼び出すと、応答の準備ができるまで呼び出しがブロックされます。future.get()
メソッドは、応答の準備ができると必ず返されます。
import java.util.concurrent.Future; import javax.ws.rs.client.Client; ... @Test public void AsyncGetTest() throws Exception { Client client = ClientBuilder.newClient(); Future<String> future = client.target(generateURL("/test")).request().async().get(String.class); String entity = future.get(); Assert.assertEquals("get", entity); }
2.3.1.3.2. InvocationCallback の使用
AsyncInvoker
インターフェイスを使用すると、非同期呼び出しの処理の準備ができたときに呼び出されるオブジェクトを登録できます。InvocationCallback
インターフェイスは completed()
および failed()
のメソッドを提供します。completed()
メソッドは、処理が正常に完了し、応答を受信するたびに呼び出されます。逆に、要求の処理に成功していないと、failed()
メソッドが呼び出されます。
import javax.ws.rs.client.InvocationCallback; ... @Test public void AsyncCallbackGetTest() throws Exception { Client client = ClientBuilder.newClient(); final CountDownLatch latch = new CountDownLatch(1); Future<Response> future = client.target(generateURL("/test")).request().async().get(new InvocationCallback<Response>() { @Override public void completed(Response response) { String entity = response.readEntity(String.class); Assert.assertEquals("get", entity); latch.countDown(); } @Override public void failed(Throwable error) { } }); Response res = future.get(); Assert.assertEquals(HttpResponseCodes.SC_OK, res.getStatus()); Assert.assertTrue("Asynchronous invocation didn't use custom implemented Invocation callback", latch.await(5, imeUnit.SECONDS)); }
2.3.2. カスタム RESTEasy アノテーション
バイトコードにパラメーター名が追加されたため、@PathParam
、@QueryParam
、@FormParam
、@CookieParam
、@HeaderParam
、および @MatrixParam
アノテーションでパラメーター名を指定する必要がなくなりました。これには、任意の value パラメーターを持つ異なるパッケージで、同じ名前を持つ新しいアノテーションに切り替える必要があります。これは、以下の手順で実現できます。
-
org.jboss.resteasy.annotations.jaxrs
パッケージをインポートし、JAX-RS 仕様のアノテーションを置き換えます。 ビルドシステムがバイトコードにメソッドパラメーター名を記録するように設定します。
Maven ユーザーは、
maven.compiler.parameters
をtrue
に設定すると、バイトコードにメソッドパラメーター名を記録できます。<properties> <maven.compiler.parameters>true</maven.compiler.parameters> </properties>
名前がアノテーション変数の名前に一致する場合は、アノテーション値を削除します。
注記アノテーション付きのパラメーター、アノテーション付きのフィールド、または JavaBean プロパティーのアノテーション名は省略できます。
以下の使用例を見てみましょう。
import org.jboss.resteasy.annotations.jaxrs.*; @Path("/library") public class Library { @GET @Path("/book/{isbn}") public String getBook(@PathParam String isbn) { // search my database and get a string representation and return it } }
アノテーションが付けられた変数に path パラメーターと同じ名前がない場合は、以下のように名前を指定できます。
import org.jboss.resteasy.annotations.jaxrs.*; @Path("/library") public class Library { @GET @Path("/book/{isbn}") public String getBook(@PathParam("isbn") String id) { // search my database and get a string representation and return it } }
2.4. RESTEasy エンドポイントの表示
jaxrs
サブシステムの read-resource
操作を使用して、各 RESTEasy エンドポイントの構造化出力を表示できます。以下に、管理 CLI コマンドの例と期待される結果を示します。
/deployment=DEPLOYMENT_NAME/subsystem=jaxrs/rest-resource=org.jboss.as.quickstarts.rshelloworld.HelloWorld:read-resource(include-runtime=true)
{
"outcome" => "success",
"result" => {
"resource-class" => "org.jboss.as.quickstarts.rshelloworld.HelloWorld",
"rest-resource-paths" => [
{
"resource-path" => "/hello/json",
"consumes" => undefined,
"produces" => [
"application/json",
"text/plain"
],
"java-method" => "java.lang.String org.jboss.as.quickstarts.rshelloworld.HelloWorld.getHelloWorldJSON()",
"resource-methods" => [
"POST /wildfly-helloworld-rs/rest/hello/json",
"GET /wildfly-helloworld-rs/rest/hello/json"
]
},
{
"resource-path" => "/hello/xml",
"consumes" => undefined,
"produces" => ["application/xml"],
"java-method" => "java.lang.String org.jboss.as.quickstarts.rshelloworld.HelloWorld.getHelloWorldXML(@QueryParam java.lang.String name = 'LGAO')",
"resource-methods" => ["GET /wildfly-helloworld-rs/rest/hello/xml"]
}
],
"sub-resource-locators" => [{
"resource-class" => "org.jboss.as.quickstarts.rshelloworld.SubHelloWorld",
"rest-resource-paths" => [
{
"resource-path" => "/hello/subMessage/",
"consumes" => undefined,
"produces" => undefined,
"java-method" => "java.lang.String org.jboss.as.quickstarts.rshelloworld.SubHelloWorld.helloInSub()",
"resource-methods" => ["GET /wildfly-helloworld-rs/rest/hello/subMessage/"]
},
{
"resource-path" => "/hello/subMessage/subPath",
"consumes" => undefined,
"produces" => undefined,
"java-method" => "java.lang.String org.jboss.as.quickstarts.rshelloworld.SubHelloWorld.subPath()",
"resource-methods" => ["GET /wildfly-helloworld-rs/rest/hello/subMessage/subPath"]
}
],
"sub-resource-locators" => undefined
}]
}
}
上記の例では、出力情報が resource-class
でグループ化され、resource-path
ごとに順序づけされます。
-
resource-path
はエンドポイントにアクセスするためのアドレスです。 -
resource-class
はエンドポイントが定義されているクラスを定義します。 -
rest-resource-paths
には、リソースパス、HTTP メソッドを定義し、エンドポイントを消費して生成する Java メソッドが含まれます。 -
java-method
は Java メソッドの名前とそのパラメーターを指定します。また、定義される場合、JAX-RS アノテーションについての以下の情報も含まれます。@PathParam
、@QueryParam
、@HeaderParam
、@MatrixParam
、@CookieParam
、@FormParam
、@DefaultValue
。
または、以下の例のように、rest-resource
パラメーターを定義せずに read-resource
操作を使用し、すべてのエンドポイントに関する情報を取得できます。
/deployment=DEPLOYMENT_NAME/subsystem=jaxrs:read-resource(include-runtime=true,recursive=true)
2.5. RegistryStatsResource を使用した RESTEasy エンドポイントの表示
RegistryStatsResource
リソースからアプリケーションの RESTEasy エンドポイントについての情報を取得できます。
手順
アプリケーションのデプロイメント記述子
web.xml
ファイルに以下の XML スニペットを追加して、RegistryStatsResource
を登録します。<context-param> <param-name>resteasy.resources</param-name> <param-value>org.jboss.resteasy.plugins.stats.RegistryStatsResource</param-value> </context-param>
アプリケーションの RESTEasy エンドポイントを表示します。
CLI の使用
XML で結果を取得する
$ curl http://localhost:8080/{APPLICATION_PREFIX_URL}/resteasy/registry
JSON で結果を取得する
$ curl http://localhost:8080/{APPLICATION_PREFIX_URL}/resteasy/registry
-H "Accept: application/json"
Web ブラウザーの使用
http://localhost:8080/{APPLICATION_PREFIX_URL}/resteasy/registry
2.6. URL ベースのネゴシエーション
2.6.1. メディアタイプへの拡張機能のマッピング
ブラウザーなどの一部のクライアントは、Accept
および Accept-Language
ヘッダーを使用して、表現メディアタイプまたは言語をネゴシエートできません。RESTEasy は、この問題に対処するために、ファイル名接尾辞をメディアタイプおよび言語にマップできます。
web.xml
ファイルを使用してメディアタイプをファイル拡張子にマッピングするには、resteasy.media.type.mappings
コンテキストパラメーターとマッピングのリストを param-value
として追加する必要があります。この一覧はコンマで区切られ、コロン (:
) を使用してファイル拡張子とメディアタイプを区切ります。
web.xml
のメディアタイプへのマッピングファイル拡張機能の例
<context-param> <param-name>resteasy.media.type.mappings</param-name> <param-value>html : text/html, json : application/json, xml : application/xml</param-value> </context-param>
この例では、http://localhost:8080/my-application/test
の以下の URL バリアントがマッピングされます。
-
http://localhost:8080/my-application/test.html
-
http://localhost:8080/my-application/test.json
-
http://localhost:8080/my-application/test.xml
2.6.2. 言語への拡張機能のマッピング
ブラウザーなどの一部のクライアントは、Accept
および Accept-Language
ヘッダーを使用して、表現メディアタイプまたは言語をネゴシエートできません。RESTEasy は、この問題に対処するために、ファイル名接尾辞をメディアタイプおよび言語にマップできます。以下の手順に従い、web.xml
ファイルのファイル拡張子に言語をマッピングします。
web.xml
ファイルを使用してメディアタイプをファイル拡張子にマッピングするには、resteasy.language.mappings
コンテキストパラメーターとマッピングのリストを param-value
として追加する必要があります。この一覧はコンマで区切られ、コロン (:
) を使用してファイル拡張子と言語タイプを区切ります。
言語タイプへの web.xml
マッピングファイル拡張機能の例
<context-param> <param-name>resteasy.language.mappings</param-name> <param-value> en : en-US, es : es, fr : fr</param-name> </context-param>
この例では、http://localhost:8080/my-application/test
の以下の URL バリアントがマッピングされます。
-
http://localhost:8080/my-application/test.en
-
http://localhost:8080/my-application/test.es
-
http://localhost:8080/my-application/test.fr
2.7. コンテンツマーシャリングおよびプロバイダー
2.7.1. デフォルトのプロバイダーとデフォルトの Jakarta RESTful Web サービスのコンテンツ
RESTEasy は、いくつかの異なるメッセージ本文を自動的にマーシャリングおよびマーシャリング解除できます。
メディアタイプ | Java タイプ |
---|---|
| JAXB アノテーション付きクラス |
| org.w3c.dom.Document |
* / * | java.lang.String |
* / * | java.io.InputStream |
| プリミティブ、java.lang.String、または String コンストラクターを持つタイプ、または入力用の静的 valueOf(String) メソ背戸、出力用の toOf() メソッド。 |
* / * | javax.activation.DataSource |
* / * | java.io.File |
* / * | byte |
| javax.ws.rs.core.MultivaluedMap |
2.7.1.1. テキストメディアタイプおよび文字セット
JAX-RS 仕様によると、実装は応答作成時にアプリケーションが提供する文字セットのメタデータに準拠する必要があります。文字セットがアプリケーションによって指定されていない場合、またはサポートされていない文字セットをアプリケーションが指定する場合は、実装で UTF-8 文字セットを使用する必要があります。
反対に、HTTP 仕様によると、送信者が明示的な charset パラメーターを提供しない場合、HTTP 経由で受信すると text
タイプのメディアサブタイプは、デフォルトの charset の値である ISO-8859-1
を持つように定義されます。ISO-8859-1
以外の文字セットまたはそのサブセットのデータは、適切な文字セット値でラベル付けする必要があります。
リソースまたはリソースメソッドで指定された文字セットがない場合、RESTEasy は UTF-8 をテキストメディアタイプの文字セットとして使用します。これを行うため、RESTEasy は明示的な charset パラメーターを content-type 応答ヘッダーに追加します。
テキストメディアタイプに UTF-8 が使用されますが、明示的な charset パラメーターが追加されない場合は、コンテキストパラメーター resteasy.add.charset
を false
に設定します。このパラメーターのデフォルト値は true
です。
以下は、テキストメディアタイプです。
-
タイプ
text
と任意のサブタイプを含むメディアタイプ -
タイプ
application
を持つメディアタイプ、xml
で始まるサブタイプ。これには、application/xml-external-parsed-entity
およびapplication/xml-dtd
が含まれます。
2.7.2. @Provider クラスを使用したコンテンツマーシャルリング
JAX-RS 仕様では、独自の要求/応答本体のリーダーおよびライターをプラグインできます。そのためには、クラスに @Provider
アノテーションを付け、ライターに @Produces
を指定し、リーダーに @Consumes
タイプを指定します。MessageBodyReader/Writer
インターフェイスも実装する必要があります。
@Provider
を使用してアノテーションが付けられたクライアントプロバイダーは、JAX-RS コンテナーランタイムのクライアントインスタンスごとに登録してアノテーションを処理する必要があります。不要または重複したクライアントプロバイダー登録の問題を回避するために、システムプロパティー resteasy.client.providers.annotations.disabled
は、@Provider
アノテーションが付けられたクライアントプロバイダーのデフォルトの処理を無効にます。
RESTEasy ServletContextLoader
は自動的に、@Provider
アノテーションが付けられたクラスの WEB-INF/lib
およびクラスディレクトリーを自動的にスキャンします。または、web.xml
ファイルで手動で設定することができます。
2.7.3. Providers ユーティリティークラス
javax.ws.rs.ext.Providers
は、MessageBodyReaders
、Writers
、ContextResolvers
、ExceptionMappers
を検索できるようにする単純なインジェクト可能なインターフェイスです。これは、他のランダムなコンテンツタイプを埋め込みする multipart プロバイダーおよびコンテンツタイプの実装に非常に便利です。
public interface Providers { <T> MessageBodyReader<T> getMessageBodyReader(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType); <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type, Type genericType, Annotation annotations[], MediaType mediaType); <T extends="" throwable=""> ExceptionMapper<T> getExceptionMapper(Class<T> type); <T> ContextResolver<T> getContextResolver(Class<T> contextType, MediaType mediaType); }
Providers
インスタンスは MessageBodyReader
または Writers
にインジェクトできます。
@Provider @Consumes("multipart/fixed") public class MultipartProvider implements MessageBodyReader { private @Context Providers providers; ... }
2.7.4. ドキュメントマーシャリングの設定
XML ドキュメントパーサーは XXE (XML eXternal Entity) 攻撃と呼ばれる攻撃の形式に依存し、外部エンティティーを拡張すると安全でないファイルが読み込まれます。たとえば、次のドキュメントでは、/etc/passwd
ファイルが読み込まれる可能性があります。
<!--?xml version="1.0"?--> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> <search> <user>bill</user> <file>&xxe;<file> </search>
デフォルトでは、org.w3c.dom.Document
ドキュメントの RESTEasy ビルドインアンマーシャルは外部エンティティーを拡張しません。それらは空の文字列に置き換えられます。これは、外部エンティティーを DTD で定義された値に置き換えるように設定できます。そのためには web.xml
ファイルで resteasy.document.expand.entity.references
コンテキストパラメーターを true
に設定します。
例: resteasy.document.expand.entity.references
コンテキストパラメーターの設定
<context-param> <param-name>resteasy.document.expand.entity.references</param-name> <param-value>true</param-value> </context-param>
この問題に対処するもう 1 つの方法として、RESTEasy がデフォルトで行う DTD を禁止します。この動作を変更するには、resteasy.document.secure.disableDTDs
コンテキストパラメーターを false
に設定します。
例: resteasy.document.secure.disableDTDs
コンテキストパラメーターの設定
<context-param> <param-name>resteasy.document.secure.disableDTDs</param-name> <param-value>false</param-value> </context-param>
ドキュメントは、バッファーが大規模なエンティティーやあまりにも多くの属性によって引き継がれる場合にドス攻撃の対象となります。たとえば、以下のエンティティーを定義すると、&foo6;
の拡張が 1,000,000 foos になります。
<!--ENTITY foo 'foo'--> <!--ENTITY foo1 '&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;&foo;'--> <!--ENTITY foo2 '&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;&foo1;'--> <!--ENTITY foo3 '&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;&foo2;'--> <!--ENTITY foo4 '&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;&foo3;'--> <!--ENTITY foo5 '&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;&foo4;'--> <!--ENTITY foo6 '&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;&foo5;'-->
デフォルトでは、RESTEasy は展開の数とエンティティーごとの属性数を制限します。正確な動作は、基礎となるパーサーによって異なります。この制限を無効にするには、resteasy.document.secure.processing.feature
コンテキストパラメーターを false
に設定します。
例: resteasy.document.secure.processing.feature
コンテキストパラメーターの設定
<context-param> <param-name>resteasy.document.secure.processing.feature</param-name> <param-value>false</param-value> </context-param>
2.7.5. MapProvider の使用
MapProvider
を使用することで、JAX-RS リソースでマップを受け入れて返すことができます。
例: マップを許可して返すリソース
@Path("manipulateMap") @POST @Consumes("application/x-www-form-urlencoded") @Produces("application/x-www-form-urlencoded") public MultivaluedMap<String, String> manipulateMap(MultivaluedMap<String, String> map) { //do something return map; }
クライアントを使用して JAX-RS リソースにマップを送受信することもできます。
例: クライアント
MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>(); //add values to the map... Response response = client.target(generateURL("/manipulateMap")) .request(MediaType.APPLICATION_FORM_URLENCODED_TYPE) .post(Entity.entity(map, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); String data = response.readEntity(String.class); //handle data...
2.7.6. 文字列ベースのアノテーションのオブジェクトへの変換
@QueryParam
、@MatrixParam
、@HeaderParam
、@PathParam
、@FormParam
を含む JAX-RS @*Param
アノテーションは、raw HTTP リクエストで文字列として表されます。これらのタイプのインジェクトされたパラメーターは、それらのオブジェクトに valueOf(String)
静的メソッドまたは String
パラメーターを取るコンストラクターがある場合に、オブジェクトに変換できます。
valueOf()
メソッドまたは文字列コンストラクターが存在しないクラスや、HTTP リクエストに適さないクラスがある場合、JAX-RS は javax.ws.rs.ext.ParamConverterProvider
および javax.ws.rs.ext.ParamConverter
を提供して、メッセージパラメーターの値を対応するカスタム Java タイプに変換します。ParamConverterProvider
は、JAX-RS ランタイムにプログラムで登録されているか、@Provider
アノテーションを付け、プロバイダースキャンフェーズで JAX-RS ランタイムが自動的に検出されるようにする必要があります。
たとえば、以下の手順では、カスタム pos オブジェクトを作成する方法を示します。@QueryParam
、@PathParam
、@MatrixParam
、@HeaderParam
などのメッセージパラメーターから POJO オブジェクトへの変換は、ParamConverter
インターフェイスおよび ParamConverterProvider
インターフェイスの実装によって実行されます。
カスタム POJO クラスを作成します。
public class POJO { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
カスタム POJO Converter クラスを作成します。
public class POJOConverter implements ParamConverter<POJO> { public POJO fromString(String str) { System.out.println("FROM STRNG: " + str); POJO pojo = new POJO(); pojo.setName(str); return pojo; } public String toString(POJO value) { return value.getName(); } }
カスタム POJO Converter Provider クラスを作成します。
public class POJOConverterProvider implements ParamConverterProvider { @Override public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) { if (!POJO.class.equals(rawType)) return null; return (ParamConverter<T>)new POJOConverter(); } }
カスタム MyResource クラスを作成します。
@Path("/") public class MyResource { @Path("{pojo}") @PUT public void put(@QueryParam("pojo") POJO q, @PathParam("pojo") POJO pp, @MatrixParam("pojo") POJO mp, @HeaderParam("pojo") POJO hp) { ... } }
ParamConverter の機能の拡張
JAX-RS セマンティックでは、ParamConverter
は各オブジェクトを表す単一の文字列を変換します。RESTEasy はセマンティックを拡張して、ParamConverter
が複数オブジェクトの文字列表現を解析できるようにし、List<T>
、Set<T>
、SortedSet<T>
、array
、またはその他の複数値のデータ構造の生成を可能にします。
たとえば、以下のリソースについて見てみましょう。
@Path("queryParam") public static class TestResource { @GET @Path("") public Response conversion(@QueryParam("q") List<String> list) { return Response.ok(stringify(list)).build(); } } private static <T> String stringify(List<T> list) { StringBuffer sb = new StringBuffer(); for (T s : list) { sb.append(s).append(','); } return sb.toString(); }
以下のように、標準の表記を使用して TestResource
を呼び出します。
@Test public void testQueryParamStandard() throws Exception { ResteasyClient client = new ResteasyClientBuilder().build(); Invocation.Builder request = client.target("http://localhost:8081/queryParam?q=20161217&q=20161218&q=20161219").request(); Response response = request.get(); System.out.println("response: " + response.readEntity(String.class)); }
結果: response: 20161217,20161218,20161219,
.
代わりにコンマ区切りの表記を使用する場合は、以下を追加できます。
public static class MultiValuedParamConverterProvider implements ParamConverterProvider @SuppressWarnings("unchecked") @Override public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) { if (List.class.isAssignableFrom(rawType)) { return (ParamConverter<T>) new MultiValuedParamConverter(); } return null; } } public static class MultiValuedParamConverter implements ParamConverter<List<?>> { @Override public List<?> fromString(String param) { if (param == null || param.trim().isEmpty()) { return null; } return parse(param.split(",")); } @Override public String toString(List<?> list) { if (list == null || list.isEmpty()) { return null; } return stringify(list); } private static List<String> parse(String[] params) { List<String> list = new ArrayList<String>(); for (String param : params) { list.add(param); } return list; } }
これで、以下のように TestResource
を呼び出すことができます。
@Test public void testQueryParamCustom() throws Exception { ResteasyClient client = new ResteasyClientBuilder().build(); Invocation.Builder request = client.target("http://localhost:8081/queryParam?q=20161217,20161218,20161219").request(); Response response = request.get(); System.out.println("response: " + response.readEntity(String.class)); }
取得: : 20161217,20161218,20161219,
この場合では、MultiValuedParamConverter.fromString()
関数は ArrayList
を作成して返すため、TestResource.conversion()
関数を書き換えることができます。
@Path("queryParam") public static class TestResource { @GET @Path("") public Response conversion(@QueryParam("q") ArrayList<String> list) { return Response.ok(stringify(list)).build(); } }
または MultiValuedParamConverter
は再書き込みして LinkList
を返すことができます。また、TestResource.conversion()
のパラメーターリストは、List
または LinkedList
のいずれかになります。
最後に、この拡張はアレイにも機能することに注意してください。以下に例を示します。
public static class Foo { private String foo; public Foo(String foo) { this.foo = foo; } public String getFoo() { return foo; } } public static class FooArrayParamConverter implements ParamConverter < Foo[] > { @Override public Foo[] fromString(String value) { String[] ss = value.split(","); Foo[] fs = new Foo[ss.length]; int i = 0; for (String s: ss) { fs[i++] = new Foo(s); } return fs; } @Override public String toString(Foo[] values) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < values.length; i++) { sb.append(values[i].getFoo()).append(","); } if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } } @Provider public static class FooArrayParamConverterProvider implements ParamConverterProvider { @SuppressWarnings("unchecked") @Override public < T > ParamConverter < T > getConverter(Class < T > rawType, Type genericType, Annotation[] annotations) { if (rawType.equals(Foo[].class)); return (ParamConverter < T > ) new FooArrayParamConverter(); } } @Path("") public static class ParamConverterResource { @GET @Path("test") public Response test(@QueryParam("foos") Foo[] foos) { return Response.ok(new FooArrayParamConverter().toString(foos)).build(); } }
java.util.Optional
パラメータータイプ
RESTEasy は、複数の追加の java.util.Optional
パラメータータイプを提供します。これらのパラメータータイプは、ラッパーオブジェクトタイプとして機能します。これにより、ユーザーは任意のタイプパラメーターを入力でき、Optional.orElse()
などのメソッドを使用してすべての null チェックを削除できます。
@Path("/double") @GET public String optDouble(@QueryParam("value") OptionalDouble value) { return Double.toString(value.orElse(4242.0)); }
上記の例は、OptionalDouble
をパラメータータイプとして使用できることを示しています。値が @QueryParam
で指定されていない場合は、デフォルト値が返されます。以下のパラメータータイプでは、オプションのパラメーターがサポートされています。
-
@QueryParam
-
@MatrixParam
-
@FormParam
-
@HeaderParam
-
@CookieParam
2.7.7. シリアライズ可能なプロバイダー
信用できないソースから Java オブジェクトをデシリアライズすることは危険です。そのため、org.jboss.resteasy.plugins.providers.SerializableProvider
はデフォルトで無効になっています。このプロバイダーの使用は推奨されません。
2.7.8. JSON プロバイダー
2.7.8.1. RESTEasy Jackson2 での JsonFilter サポート
JsonFilter
は、@JsonFilter
でクラスにアノテーションを付けることで、動的なフィルタリングを容易にします。以下の例では、nameFilter
クラスからフィルターインスタンスへのマッピングを定義し、インスタンスを JSON 形式にシリアライズする際に Bean プロパティーを除外します。
@JsonFilter(value="nameFilter") public class Jackson2Product { protected String name; protected int id; public Jackson2Product() { } public Jackson2Product(final int id, final String name) { this.id = id; this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
@JsonFilter
はリソースクラスにアノテーションを付け、JSON 応答でシリアライズすべきでないプロパティーをフィルターアウトします。フィルター ID とインスタンスをマップするには、別の Jackson クラスを作成し、以下の例のようにその ID とフィルターインスタンスマップを追加する必要があります。
public class ObjectFilterModifier extends ObjectWriterModifier { public ObjectFilterModifier() { } @Override public ObjectWriter modify(EndpointConfigBase<?> endpoint, MultivaluedMap<String, Object> httpHeaders, Object valueToWrite, ObjectWriter w, JsonGenerator jg) throws IOException { FilterProvider filterProvider = new SimpleFilterProvider().addFilter( "nameFilter", SimpleBeanPropertyFilter.filterOutAllExcept("name")); return w.with(filterProvider); } }
上記の例では、メソッド modify()
は応答を記述する前に、name
プロパティー以外のすべてのプロパティーをフィルタリングします。これを機能させるには、RESTEasy はこのマッピング情報を知っておく必要があります。以下の例が示すように、WriterInterceptor
またはサーブレットフィルターのいずれかに設定できます。
例: WriterInterceptor を使用した ObjectFilterModifier の設定
@Provider public class JsonFilterWriteInterceptor implements WriterInterceptor{ private ObjectFilterModifier modifier = new ObjectFilterModifier(); @Override public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { //set a threadlocal modifier ObjectWriterInjector.set(modifier); context.proceed(); } }
例: サーブレットフィルターを使用した ObjectFilterModifier の設定
public class ObjectWriterModifierFilter implements Filter { private static ObjectFilterModifier modifier = new ObjectFilterModifier(); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ObjectWriterInjector.set(modifier); chain.doFilter(request, response); } @Override public void destroy() { } }
これで、RESTEasy は ObjectFilterModifier
を ThreadLocal
変数から取得して、応答の作成前に ObjectWriter
を変更するように設定できます。
2.7.8.2. JSON Binding
RESTEasy は JSON Binding と JSON-P の両方に対応しています。仕様によると、JsonValue
とそのサブタイプを除き、すべてのタイプのエンティティーで、JSON Binding のエンティティープロバイダーの優先度は JSON-P のエンティティープロバイダーよりも高くなります。
resteasy-json-binding-provider
モジュールからの JsonBindingProvider
は、JSON-B のサポートを提供します。JAX-RS 2.1 の要件を満たすため、JsonBindingProvider
プロバイダーの優先度は、Jackson ペイロードなどの JSON ペイロードを処理する他のプロバイダーよりも高くなります。JSON Binding の Jakarta EE と同等の Jakarta EE は、Jakarta JSON Binding Specification 1.0 仕様 に含まれています。
同じ入力では、Jackson および Jakarta JSON Binding リファレンス実装からの JSON 出力は異なる場合があります。後方互換性を維持するには、resteasy.preferJacksonOverJsonB
コンテキストプロパティーを true
に設定し、現在のデプロイメントの JsonBindingProvider
設定を無効にします。
JBoss EAP は、同じ名前のシステムプロパティーを設定して、resteasy.preferJacksonOverJsonB
コンテキストプロパティーのデフォルト値を指定することをサポートします。コンテキストおよびシステムプロパティーに値が設定されていない場合は、Jackson アノテーションの JAX-RS または Jakarta RESTful Web Services デプロイメントをスキャンし、これらのアノテーションのいずれかが見つかった場合にプロパティーを true
に設定します。
2.7.9. アクセッティングプロバイダー
2.7.9.1. アイザーおよび XML プロバイダー
RESTEasy は、XML のメソッドプロバイダーサポートを提供します。
@XmlHeader および @Stylesheet
RESTEasy は、@org.jboss.resteasy.annotations.providers.jaxb.XmlHeader
アノテーションを使用した XML ヘッダーの設定を提供します。
例: @XmlHeader
アノテーションの使用
@XmlRootElement public static class Thing { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } @Path("/test") public static class TestService { @GET @Path("/header") @Produces("application/xml") @XmlHeader("<?xml-stylesheet type='text/xsl' href='${baseuri}foo.xsl' ?>") public Thing get() { Thing thing = new Thing(); thing.setName("bill"); return thing; } }
@XmlHeader
は、XML 出力に XML スタイルシートヘッダーがあることを確認します。
RESTEasy にはスタイルシートヘッダーの便利なアノテーションがあります。
例: @Stylesheet
アノテーションの使用
@XmlRootElement public static class Thing { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } @Path("/test") public static class TestService { @GET @Path("/stylesheet") @Produces("application/xml") @Stylesheet(type="text/css", href="${basepath}foo.xsl") @Junk public Thing getStyle() { Thing thing = new Thing(); thing.setName("bill"); return thing; } }
2.7.9.2. JAXB および JSON プロバイダー
RESTEasy を使用すると、JSON プロバイダーを使用して JSON との間でアノテーションが付けられた pos をマーシャリングできます。このプロバイダーは、このタスクを実行するために Jackson JSON ライブラリーをラップします。Java Beans ベースのモデルと、JAXB に類似した API があります。
Jackson にすでに Jakarta RESTful Web Services 統合が含まれていますが、RESTEasy によって拡張されました。これをプロジェクトに組み込むには、Maven 依存関係を更新する必要があります。
Jackson の Maven 依存関係
<dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jackson2-provider</artifactId> <version>${version.org.jboss.resteasy}</version> <scope>provided</scope> </dependency>
RESTEasy のデフォルトの JSON プロバイダーは Jackson2 です。JBoss EAP の以前のバージョンには Jackson1 JSON プロバイダーが含まれていました。Jackson1 プロバイダーから既存のアプリケーションを移行する方法の詳細は、JBoss EAP Migration Guide を参照してください。Jackson1 プロバイダーを引き続き使用する場合は 、Maven の依存関係を明示的に更新して取得する必要があります。
以前のバージョンの JBoss EAP の RESTEasy のデフォルト JSON プロバイダーは Jettison でしたが、JBoss EAP 7 では非推奨になっています。詳細は、JBoss EAPMigration Guide を参照してください。
JSON プロバイダーの例
@XmlRootElement public static class Thing { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } @Path("/test") public static class TestService { @GET @Path("/thing") @Produces("application/json") public Thing get() { Thing thing = new Thing(); thing.setName("the thing"); return thing; } }
2.7.9.2.1. Java 8 の Jackson モジュールサポート
ここでは、Maven の依存関係を説明します。また、コアの Jackson モジュールが Java 8 ランタイム環境を必要としない場合に Java 8 機能をサポートするために必要な Jackson モジュールを登録する方法について説明します。これらの Jackson モジュールには以下が含まれます。
- Java 8 データ型
- Java 8 日付/時刻
以下の Maven 依存関係を追加します。
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jdk8</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency>
以下の例のように、findAndRegisterModules()
または ObjectMapper.registerModule()
を使用してすべてのモジュールを検索し、登録することができます。
ObjectMapper mapper = new ObjectMapper(); mapper.findAndRegisterModules();
ObjectMapper mapper = new ObjectMapper() .registerModule(new ParameterNamesModule()) .registerModule(new Jdk8Module()) .registerModule(new JavaTimeModule());
例: 期間のデータタイプ
@GET @Path("/duration") @Produces(MediaType.APPLICATION_JSON) public Duration getDuration() { return Duration.ofSeconds(5, 6); }
例: 任意のデータタイプ
@GET @Path("/optional/{nullParam}") @Produces(MediaType.APPLICATION_JSON) public Optional<String> getOptional(@PathParam("nullParam") boolean nullParameter) { return nullParameter ? Optional.<String>empty() : Optional.of("info@example.com"); }
RESTEasy でこれらの Jackson モジュールを使用するには、ContextResolver
のカスタム実装を使用する必要があります。
@Provider @Produces(MediaType.APPLICATION_JSON) public class JacksonDatatypeJacksonProducer implements ContextResolver<ObjectMapper> { private final ObjectMapper json; public JacksonDatatypeJacksonProducer() throws Exception { this.json = new ObjectMapper() .findAndRegisterModules() .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } @Override public ObjectMapper getContext(Class<?> objectType) { return json; } }
2.7.9.2.2. デフォルトの Jackson プロバイダーの切り替え
JBoss EAP 7 には Jackson 2.6.x 以上が含まれ、resteasy-jackson2-provider
がデフォルトの Jackson プロバイダーになりました。
以前のリリースの JBoss EAP に含まれていたデフォルトの resteasy-jackson-provider
に切り替えるには、新しいプロバイダーを除外し、jboss-deployment-structure.xml
アプリケーションデプロイメント記述子ファイルに以前のプロバイダーの依存関係を追加します。
<?xml version="1.0" encoding="UTF-8"?> <jboss-deployment-structure> <deployment> <exclusions> <module name="org.jboss.resteasy.resteasy-jackson2-provider"/> </exclusions> <dependencies> <module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/> </dependencies> </deployment> </jboss-deployment-structure>
2.7.10. デコレーターの作成
RESTEasy の資料プロバイダーには、Marshaller および Un Marketplaceler インスタンスを切り離すプラグ可能な方法があります。メソッドを切り離すために使用できる Marshaller または Un markler インスタンスのいずれかをトリガーできるアノテーションを作成できます。
RESTEasy を使用したデコレーターの作成
Processor クラスを作成します。
-
DecoratorProcessor<Target, Annotation>
を実装するクラスを作成します。ターゲットは JAXB Marshaller または Unmarshaller クラスのいずれかになります。アノテーションは、ステップ 2 で作成されます。 -
クラスに
@DecorateTypes
アノテーションを付け、デコレーターがデコレートする必要のある MIME タイプを宣言します。 decorate 関数内にプロパティーまたは値を設定します。
例: Processor クラス
import org.jboss.resteasy.core.interception.DecoratorProcessor; import org.jboss.resteasy.annotations.DecorateTypes; import javax.xml.bind.Marshaller; import javax.xml.bind.PropertyException; import javax.ws.rs.core.MediaType; import javax.ws.rs.Produces; import java.lang.annotation.Annotation; @DecorateTypes({"text/*+xml", "application/*+xml"}) public class PrettyProcessor implements DecoratorProcessor<Marshaller, Pretty> { public Marshaller decorate(Marshaller target, Pretty annotation, Class type, Annotation[] annotations, MediaType mediaType) { target.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); } }
-
アノテーションを作成します。
-
@Decorator
アノテーションが付けられたカスタムインターフェイスを作成します。 @Decorator
アノテーションのプロセッサーおよびターゲットを宣言します。プロセッサーは、ステップ 1 で作成されます。ターゲットは JAXBMarshaller
またはUnmarshaller
クラスのいずれかになります。例:
@Decorator
アノテーションのあるカスタムインターフェイスimport org.jboss.resteasy.annotations.Decorator; @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Decorator(processor = PrettyProcessor.class, target = Marshaller.class) public @interface Pretty {}
-
- 手順 2 で作成したアノテーションを関数に追加して、入力または出力のいずれかがマーシャリングされるときにデコレートされるようにします。
Jakarta RESTful Web Services Web サービス内に適用できる JAXB デコレーターを作成できるようになりました。
2.7.11. JAX-RS の multipart プロバイダー
multipart MIME 形式は、単一のメッセージに埋め込まれたコンテンツボディーのリストを渡すために使用されます。multipart MIME 形式の一例として、multipart/form-data
MIME タイプが挙げられます。これは Web アプリケーションの HTML 形式のドキュメントにあり、通常はファイルをアップロードするために使用されます。この MIME タイプの form-data
形式は、インラインコンテンツのそれぞれに関連する名前がある以外は、他の multipart 形式と同じです。
RESTEasy は、multipart/form-data
および multipart/*
MIME タイプを許可します。RESTEasy は、multipart タイプの読み書きや、任意の List
(任意の multipart タイプ) および Map
(multipart/form-data のみ) オブジェクトのマーシャリングを行うためのカスタム API も提供します。
Seam の org.jboss.seam.web.MultipartFilter
や Spring の org.springframework.web.multipart.MultipartResolver
など、フィルターやインターセプターの支援により、multipart 解析を自動的に行うフレームワークが多数あります。ただし、着信 multipart 要求ストリームは 1 回のみ解析できます。multipart を使用する RESTEasy ユーザーは、RESTEasy がそれを取得する前にストリームを解析しないようにする必要があります。
2.7.11.1. multipart データでの入力
JAX-RS サービスを記述する際に、RESTEasy は org.jboss.resteasy.plugins.providers.multipart.MultipartInput
MultipartInput インターフェイスを提供し、任意の multipart MIME タイプで読み取ることができます。
package org.jboss.resteasy.plugins.providers.multipart; public interface MultipartInput { List<InputPart> getParts(); String getPreamble(); // You must call close to delete any temporary files created // Otherwise they will be deleted on garbage collection or on JVM exit void close(); } public interface InputPart { MultivaluedMap<String, String> getHeaders(); String getBodyAsString(); <T> T getBody(Class<T> type, Type genericType) throws IOException; <T> T getBody(org.jboss.resteasy.util.GenericType<T> type) throws IOException; MediaType getMediaType(); boolean isContentTypeFromMessage(); }
MultipartInput
は、multipart メッセージの各パーツにアクセスできる簡単なインターフェイスです。各パーツは InputPart
インターフェイスによって表示され、各パートにはそれに関連するヘッダーのセットがあります。getBody()
メソッドのいずれかを呼び出すと、この部分のマーシャルを解除できます。genericType
パラメーターは null
にすることができますが、type
パラメーターは設定する必要があります。RESTEasy は、パーツのメディアタイプおよび渡すタイプ情報に基づいて MessageBodyReader
を検出します。
2.7.11.1.1. multipart/mixed
での入力
例: パーツのマーシャリング解除
@Path("/multipart") public class MyService { @PUT @Consumes("multipart/mixed") public void put(MultipartInput input) { List<Customer> customers = new ArrayList...; for (InputPart part : input.getParts()) { Customer cust = part.getBody(Customer.class, null); customers.add(cust); } input.close(); } }
上記の例では、Customer
クラスに Mission のアノテーションが付けられていることを前提としています。
汎用型のメタデータの影響を受けるボディー部分をマーシャリング解除する必要があることも考えられます。この場合は、org.jboss.resteasy.util.GenericType
クラスを使用できます。
例: 汎用型メタデータに影響を受けやすいタイプのマーシャリング解除
@Path("/multipart") public class MyService { @PUT @Consumes("multipart/mixed") public void put(MultipartInput input) { for (InputPart part : input.getParts()) { List<Customer> cust = part.getBody(new GenericType<List<Customer>>() {}); } input.close(); } }
GenericType
の使用は、ランタイム時に汎用型情報を取得する唯一の方法であるため、必要になります。
2.7.11.1.2. multipart/mixed
および java.util.List
による入力
ボディーの部分が均一である場合は、各部分と各パーツを手動でマーシャリング解除する必要はありません。入力パラメーターとして java.util.List
を指定できます。List
タイプ宣言の汎用パラメーターを使用してマーシャリング解除されている型が必要です。
例: Customers
の List
のマーシャリング解除
@Path("/multipart") public class MyService { @PUT @Consumes("multipart/mixed") public void put(List<Customer> customers) { ... } }
上記の例では、Customer
クラスに Mission のアノテーションが付けられていることを前提としています。
2.7.11.1.3. multipart/form-data
による入力
JAX-RS サービスを記述する際に、RESTEasy はマ multipart/form-data
MIME タイプでの読み込みを可能にするインターフェイスを提供します。multipart/form-data
は、Web アプリケーションの HTML 形式のドキュメントにあり、通常はファイルをアップロードするために使用されます。form-data
形式は、インラインコンテンツのそれぞれに関連する名前がある以外は、他の multipart 形式と同じです。from-data 入力に使用されるインターフェイスは org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput
です。
例: MultipartFormDataInput インターフェイス
public interface MultipartFormDataInput extends MultipartInput { @Deprecated Map<String, InputPart> getFormData(); Map<String, List<InputPart>> getFormDataMap(); <T> T getFormDataPart(String key, Class<T> rawType, Type genericType) throws IOException; <T> T getFormDataPart(String key, GenericType<T> type) throws IOException; }
これは、前述の MultipartInput
と同じように機能します。
2.7.11.1.4. ultipart/form-data
を含む java.utol.Map
form-data では、ボディーの部分が均一である場合は、あらゆるパーツを手動でマーシャリング解除する必要はありません。入力パラメーターとして java.util.Map
を指定できます。List
タイプ宣言の汎用パラメーターを使用してマーシャリング解除されている型が必要です。
例: Customer
オブジェクトの Map
のマーシャリング解除
@Path("/multipart") public class MyService { @PUT @Consumes("multipart/form-data") public void put(Map<String, Customer> customers) { ... } }
上記の例では、Customer
クラスに Mission のアノテーションが付けられていることを前提としています。
2.7.11.2. multipart データでの出力
RESTEasy は、multipart データを出力するための簡単な API を提供します。
package org.jboss.resteasy.plugins.providers.multipart; public class MultipartOutput { public OutputPart addPart(Object entity, MediaType mediaType) public OutputPart addPart(Object entity, GenericType type, MediaType mediaType) public OutputPart addPart(Object entity, Class type, Type genericType, MediaType mediaType) public List<OutputPart> getParts() public String getBoundary() public void setBoundary(String boundary) } public class OutputPart { public MultivaluedMap<String, Object> getHeaders() public Object getEntity() public Class getType() public Type getGenericType() public MediaType getMediaType() }
multipart データを出力するには、MultipartOutput
オブジェクトを作成し、addPart()
メソッドを呼び出します。RESTEasy は、エンティティーオブジェクトをマーシャリングするための MessageBodyWriter
を自動的に検出します。MultipartInput
と同様に、汎用型メタデータに影響しやすいマーシャリングがある場合があります。この場合は、GenericType
を使用します。通常は、オブジェクトとその MediaType
を渡すだけで十分です。
例: multipart/mixed
形式の戻り値
@Path("/multipart") public class MyService { @GET @Produces("multipart/mixed") public MultipartOutput get() { MultipartOutput output = new MultipartOutput(); output.addPart(new Customer("bill"), MediaType.APPLICATION_XML_TYPE); output.addPart(new Customer("monica"), MediaType.APPLICATION_XML_TYPE); return output; } }
上記の例では、Customer
クラスに Jakarta XML Binding のアノテーションが付けられていることを前提としています。
2.7.11.2.1. java.util.List
でのマルチ出力
ボディーの部分が均一である場合は、各パーツおよび各パーツを手動でマーシャリングしたり、MultipartOutput
オブジェクトを使用したりする必要はありません。java.util.List
を指定できます。これは、List
型宣言の汎用パラメーターでマーシャリングされる汎用型である必要があります。また、各パーツのメディアタイプを指定するには @PartType
アノテーションを使用してメソッドにアノテーションを付ける必要もあります。
例: Customer
オブジェクトの List
戻り値
@Path("/multipart") public class MyService { @GET @Produces("multipart/mixed") @PartType("application/xml") public List<Customer> get(){ ... } }
上記の例では、Customer
クラスに Jakarta XML Binding のアノテーションが付けられていることを前提としています。
2.7.11.2.2. multipart/form-data
による出力
RESTEasy は、multipart/form-data
を出力する簡単な API を提供します。
package org.jboss.resteasy.plugins.providers.multipart; public class MultipartFormDataOutput extends MultipartOutput { public OutputPart addFormData(String key, Object entity, MediaType mediaType) public OutputPart addFormData(String key, Object entity, GenericType type, MediaType mediaType) public OutputPart addFormData(String key, Object entity, Class type, Type genericType, MediaType mediaType) public Map<String, OutputPart> getFormData() }
multipart/form-data
を出力するには、MultipartFormDataOutput
オブジェクトを作成し、addFormData()
メソッドを呼び出します。RESTEasy は、エンティティーオブジェクトをマーシャリングするための MessageBodyWriter
を自動的に検出します。MultipartInput
と同様に、汎用型メタデータに影響しやすいマーシャリングがある場合があります。この場合は、GenericType
を使用します。通常は、オブジェクトとその MediaType
を渡すだけで十分です。
例: multipart/form-data
形式の戻り値
@Path("/form") public class MyService { @GET @Produces("multipart/form-data") public MultipartFormDataOutput get() { MultipartFormDataOutput output = new MultipartFormDataOutput(); output.addPart("bill", new Customer("bill"), MediaType.APPLICATION_XML_TYPE); output.addPart("monica", new Customer("monica"), MediaType.APPLICATION_XML_TYPE); return output; } }
上記の例では、Customer
クラスに Jakarta XML Binding のアノテーションが付けられていることを前提としています。
2.7.11.2.3. java.util.Map
による Multipart FormData Output
ボディーの部分が均一である場合は、各パーツを手動でマーシャリングしたり、MultipartFormDataOutput
オブジェクトを使用したりする必要はありません。java.util.Map
のみを指定できます。この Map には、Map
タイプ宣言の汎用パラメーターを使用してマーシャリングする汎用型が必要です。また、各パーツのメディアタイプを指定するには @PartType
アノテーションを使用してメソッドにアノテーションを付ける必要もあります。
例: Customer
オブジェクトの Map
戻り値
@Path("/multipart") public class MyService { @GET @Produces("multipart/form-data") @PartType("application/xml") public Map<String, Customer> get() { ... } }
上記の例では、Customer
クラスに Mission のアノテーションが付けられていることを前提としています。
2.7.11.3. Multipart フォームの POJO へのマッピング
multipart/form-data パッケージについて正確な知識がある場合は、POJO クラスとマップすることができます。これは、org.jboss.resteasy.annotations.providers.multipart.MultipartForm
アノテーション (@MultipartForm
) および JAX-RS @FormParam
アノテーションを使用して実行できます。これを行うには、少なくともデフォルトのコンストラクターを使用して POJO を定義し、@FormParams
でフィールドとプロパティーにアノテーションを付ける必要があります。これらの @FormParams
にも org.jboss.resteasy.annotations.providers.multipart.PartType
(@PartType
) のアノテーションを付ける必要があります (出力を作成する場合)。
例: multipart フォームの POJO へのマッピング
public class CustomerProblemForm { @FormParam("customer") @PartType("application/xml") private Customer customer; @FormParam("problem") @PartType("text/plain") private String problem; public Customer getCustomer() { return customer; } public void setCustomer(Customer cust) { this.customer = cust; } public String getProblem() { return problem; } public void setProblem(String problem) { this.problem = problem; } }
POJOI クラスを定義したら、それを使用して multipart/form-data
を表すことができます。
例: CustomerProblemForm
の送信
@Path("portal") public interface CustomerPortal { @Path("issues/{id}") @Consumes("multipart/form-data") @PUT public void putProblem(@MultipartForm CustomerProblemForm, @PathParam("id") int id); } // Somewhere using it: { CustomerPortal portal = ProxyFactory.create(CustomerPortal.class, "http://example.com"); CustomerProblemForm form = new CustomerProblemForm(); form.setCustomer(...); form.setProblem(...); portal.putProblem(form, 333); }
@MultipartForm
アノテーションは、オブジェクトに @FormParam
があり、そこからマーシャリングが必要であることを RESTEasy に指示します。また、同じオブジェクトを使用して multipart データを受信することもできます。
例: CustomerProblemForm
の受信
@Path("portal") public class CustomerPortalServer { @Path("issues/{id}) @Consumes("multipart/form-data") @PUT public void putIssue(@MultipartForm CustomerProblemForm, @PathParam("id") int id) { ... write to database... } }
2.7.11.4. XML-binary Optimized Packaging (XOP)
一部のバイナリーコンテンツも保持する JAXB アノテーションが付けられた POJO がある場合は、バイナリーを base64 や hex などのどの方法でもエンコードする必要がない方法で送信することを選択できます。これは XOP を使用して実現でき、便利な POJO を使用しながらトランスポートが高速になります。
RESTEasy は、multipart/related
としてパッケージ化された XOP メッセージを許可します。
XOP を設定するには、まずに JAXB アノテーションが付けられたアノテーションが付いた属性が必要です。
例: Jakarta XML Binding POJO
@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public static class Xop { private Customer bill; private Customer monica; @XmlMimeType(MediaType.APPLICATION_OCTET_STREAM) private byte[] myBinary; @XmlMimeType(MediaType.APPLICATION_OCTET_STREAM) private DataHandler myDataHandler; // methods, other fields ... }
@XmlMimeType はバイナリーコンテンツの mime タイプを指示します。これは XOP パッケージ化を行う必要はありませんが、正確なタイプが分かっている場合は設定することが推奨されます。
上記の myBinary
および myDataHandler
はバイナリー添付として処理され、XOP オブジェクト全体が XML として送信されます。バイナリーの代わり、それらの参照のみが生成されます。javax.activation.DataHandler
は、最も一般的なサポートされているタイプです。java.io.InputStream
または javax.activation.DataSource
が必要な場合は、DataHandler
を使用する必要があります。java.awt.Image
および javax.xml.transform.SourceSome
も利用できます。
例: XOP でバイナリーコンテンツを送信するクライアント
// our client interface: @Path("mime") public static interface MultipartClient { @Path("xop") @PUT @Consumes(MultipartConstants.MULTIPART_RELATED) public void putXop(@XopWithMultipartRelated Xop bean); } // Somewhere using it: { MultipartClient client = ProxyFactory.create(MultipartClient.class, "http://www.example.org"); Xop xop = new Xop(new Customer("bill"), new Customer("monica"), "Hello Xop World!".getBytes("UTF-8"), new DataHandler(new ByteArrayDataSource("Hello Xop World!".getBytes("UTF-8"), MediaType.APPLICATION_OCTET_STREAM))); client.putXop(xop); }
上記の例では、Customer
クラスに JAXB のアノテーションが付けられていることを前提としています。
@Consumes(MultipartConstants.MULTIPART_RELATED)
は、multipart/related
パッケージを送信するように RESTEasy に指示するために使用されます。@XopWithMultipartRelated
は、XOP メッセージ を作成するように RESTEasy に指示するために使用されます。
例: XOP を受信する RESTEasy サーバー
@Path("/mime") public class XopService { @PUT @Path("xop") @Consumes(MultipartConstants.MULTIPART_RELATED) public void putXopWithMultipartRelated(@XopWithMultipartRelated Xop xop) { // do very important things here } }
@Consumes(MultipartConstants.MULTIPART_RELATED)
は、multipart/related
パッケージを読み取るように RESTEasy に指示するために使用されます。@XopWithMultipartRelated
は、XOP メッセージを読み取ることを RESTEasy に伝えるために使用されます。RESTEasy サーバーは、@Produces
アノテーションを追加し、適切な型を返すことで、同様の方法で XOP 値を生成するように設定できます。
2.7.11.5. multipart メッセージのデフォルトのフォールバックコンテンツタイプの上書き
デフォルトでは、Content-Type
ヘッダーが一部にない場合、text/plain; charset=us-ascii
がフォールバックとして使用されます。これは MIME RFC によって定義されます。ただし、多くのブラウザーなどの一部の Web クライアントはファイルパーツの Content-Type
ヘッダーを送信しますが、multipart/form-data
のすべてのフィールドについては送信しません。これにより、サーバー側で文字エンコーディングおよびマーシャリング解除エラーが発生する可能性があります。この問題を修正するには、RESTEasy の PreProcessInterceptor
インフラストラクチャーを使用できます。これを使用して、リクエストごとに、RFC に準拠していない別のフォールバック値を動的に定義することができます。
例: * / *; charset=UTF-8
をデフォルトフォールバックとして設定
import org.jboss.resteasy.plugins.providers.multipart.InputPart; @Provider @ServerInterceptor public class ContentTypeSetterPreProcessorInterceptor implements PreProcessInterceptor { public ServerResponse preProcess(HttpRequest request, ResourceMethod method) throws Failure, WebApplicationException { request.setAttribute(InputPart.DEFAULT_CONTENT_TYPE_PROPERTY, "*/*; charset=UTF-8"); return null; } }
2.7.11.6. 複数パートメッセージのコンテンツタイプの上書き
インターセプターおよび InputPart.DEFAULT_CONTENT_TYPE_PROPERTY
属性を使用すると、デフォルトの Content-Type
を設定できます。org.jboss.resteasy.plugins.providers.multipart.InputPart.setMediaType()
を呼び出して、任意の入力部分で Content-Type
をオーバーライドすることもできます。
例: Content-Type
の上書き
@POST @Path("query") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.TEXT_PLAIN) public Response setMediaType(MultipartInput input) throws IOException { List<InputPart> parts = input.getParts(); InputPart part = parts.get(0); part.setMediaType(MediaType.valueOf("application/foo+xml")); String s = part.getBody(String.class, null); ... }
2.7.11.7. multipart メッセージのデフォルトのフォールバック charset
の上書き
multipart メッセージの一部が、charset
パラメーターのない Content-Type
ヘッダーを持つ場合があります。InputPart.DEFAULT_CONTENT_TYPE_PROPERTY
プロパティーが設定され、値に charset
パラメーターがある場合、その値は charset
パラメーターのない既存の Content-Type
ヘッダーに追加されます。
また、デフォルトの charset
は、定数 InputPart.DEFAULT_CHARSET_PROPERTY
(resteasy.provider.multipart.inputpart.defaultCharset
) を使用して指定することもできます。
例: デフォルトの charset
の指定
import org.jboss.resteasy.plugins.providers.multipart.InputPart; @Provider @ServerInterceptor public class ContentTypeSetterPreProcessorInterceptor implements PreProcessInterceptor { public ServerResponse preProcess(HttpRequest request, ResourceMethod method) throws Failure, WebApplicationException { request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, "UTF-8"); return null; } }
InputPart.DEFAULT_CONTENT_TYPE_PROPERTY
と InputPart.DEFAULT_CHARSET_PROPERTY
の両方が設定されている場合は、InputLookup .DEFAULT_CHARSET_PROPERTY
の値は InputPart.DEFAULT_CONTENT_TYPE_PROPERTY
の値の charset を上書きします。
2.7.11.8. RESTEasy クライアントでの multipart エンティティーの送信
multipart プロバイダーを設定する他に、multipart データを送信するように RESTEasy クライアントを設定することもできます。
RESTEasy クライアントクラスの使用
アプリケーションで RESTEasy クライアントクラスを使用するには、Maven 依存関係をプロジェクトの POM ファイルに追加する必要があります。
例: Maven 依存関係
<dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-client</artifactId> <version>${version.org.jboss.resteasy}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-multipart-provider</artifactId> <version>${version.org.jboss.resteasy}</version> <scope>provided</scope> </dependency>
RESTEasy クライアントを使用した multipart データの送信
multipart データを送信するには、まず RESTEasy Client を設定し、org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput
オブジェクトを構築して、multipart データを含めるようにします。次に、クライアントを使用してその MultipartFormDataOutput
オブジェクトを javax.ws.rs.core.GenericEntity
として送信することができます。
例: RESTEasy クライアント
ResteasyClient client = new ResteasyClientBuilder().build(); ResteasyWebTarget target = client.target("http://foo.com/resource"); MultipartFormDataOutput formOutputData = new MultipartFormDataOutput(); formOutputData.addFormData("part1", "this is part 1", MediaType.TEXT_PLAIN); formOutputData.addFormData("part2", "this is part 2", MediaType.TEXT_PLAIN); GenericEntity<MultipartFormDataOutput> data = new GenericEntity<MultipartFormDataOutput>(formOutputData) { }; Response response = target.request().put(Entity.entity(data, MediaType.MULTIPART_FORM_DATA_TYPE)); response.close();
2.7.12. RESTEasy Atom サポート
RESTEasy Atom API およびプロバイダーは、RESTEasy が Atom を表すように定義する簡単なオブジェクトモデルです。API の主なクラスは org.jboss.resteasy.plugins.providers.atom
パッケージにあります。RESTEasy は JAXB を使用して API のマーシャリングおよびマーシャリング解除に使用します。プロバイダーは JAXB ベースであり、XML を使用した Atom オブジェクトの送信に限定されません。RESTEasy が持つすべての JAXB プロバイダーは、JSON を含む Atom API およびプロバイダーによって再利用できます。
import org.jboss.resteasy.plugins.providers.atom.Content; import org.jboss.resteasy.plugins.providers.atom.Entry; import org.jboss.resteasy.plugins.providers.atom.Feed; import org.jboss.resteasy.plugins.providers.atom.Link; import org.jboss.resteasy.plugins.providers.atom.Person; @Path("atom") public class MyAtomService { @GET @Path("feed") @Produces("application/atom+xml") public Feed getFeed() throws URISyntaxException { Feed feed = new Feed(); feed.setId(new URI("http://example.com/42")); feed.setTitle("My Feed"); feed.setUpdated(new Date()); Link link = new Link(); link.setHref(new URI("http://localhost")); link.setRel("edit"); feed.getLinks().add(link); feed.getAuthors().add(new Person("John Brown")); Entry entry = new Entry(); entry.setTitle("Hello World"); Content content = new Content(); content.setType(MediaType.TEXT_HTML_TYPE); content.setText("Nothing much"); entry.setContent(content); feed.getEntries().add(entry); return feed; } }
2.7.12.1. Atom プロバイダーでの JAXB の使用
org.jboss.resteasy.plugins.providers.atom.Content
クラスを使用すると、コンテンツのボディーであるアノテーション付きのオブジェクトのマーシャリングおよびマーシャリング解除できます。
例: カスタマーのエントリー
@XmlRootElement(namespace = "http://jboss.org/Customer") @XmlAccessorType(XmlAccessType.FIELD) public class Customer { @XmlElement private String name; public Customer() { } public Customer(String name) { this.name = name; } public String getName() { return name; } } @Path("atom") public static class AtomServer { @GET @Path("entry") @Produces("application/atom+xml") public Entry getEntry() { Entry entry = new Entry(); entry.setTitle("Hello World"); Content content = new Content(); content.setJAXBObject(new Customer("bill")); entry.setContent(content); return entry; } }
Content.setJAXBObject()
メソッドを使用すると、適切にマーシャリングするコンテンツオブジェクトを指定できます。XML とは異なるベース形式 (application/atom+json
) を使用している場合、添付の Mission オブジェクトは同じ形式でマーシャリングされます。Atom ドキュメントを入力として用意している場合は、Content.getJAXBObject(Class clazz)
メソッドを使用して Content
から正確な JAXB オブジェクトを抽出することもできます。
例: カスタマーオブジェクトを抽出する Atom ドキュメント
@Path("atom") public static class AtomServer { @PUT @Path("entry") @Produces("application/atom+xml") public void putCustomer(Entry entry) { Content content = entry.getContent(); Customer cust = content.getJAXBObject(Customer.class); } }
2.7.13. YAML プロバイダー
resteasy-yaml-provider
モジュールはサポートされません。RESTEasy のマーシャリング解除に使用される SnakeYAML
ライブラリーでセキュリティー上の問題があるため、この使用は推奨されません。
RESTEasy には、SnakeYAML
ライブラリーを使用した YAML のサポートが同梱されています。
JBoss EAP 7.1 より前のリリースでは、YAML プロバイダー設定はデフォルトで有効にされていました。そのため、YAML がアプリケーションでこれを使用するよう Maven 依存関係を設定することのみが必要でした。JBoss EAP 7.1 より、YAML プロバイダーはデフォルトで無効になり、アプリケーションで明示的に有効にする必要があります。
YAML プロバイダーの有効化
アプリケーションで YAML プロバイダーを有効にするには、以下の手順を実行します。
-
javax.ws.rs.ext.Providers
という名前のファイルを作成または更新します。 以下の内容をファイルに追加します。
org.jboss.resteasy.plugins.providers.YamlProvider
-
WAR または JAR ファイルの
META-INF/services/
フォルダーにファイルを配置します。
YAML プロバイダー Maven の依存関係
アプリケーションで YAML プロバイダーを使用するには、アプリケーションのプロジェクト POM ファイルに snakeyaml
JAR 依存関係を追加する必要があります。
例: YAML の Maven 依存関係
<dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-yaml-provider</artifactId> <version>${version.org.jboss.resteasy}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>${version.org.yaml.snakeyaml}</version> </dependency>
YAML プロバイダーコードの例
YAML プロバイダーは以下の mime タイプを認識します。
- text/x-yaml
- text/yaml
- application/x-yaml
以下は、リソースメソッドで YAML を使用する方法の例になります。
例: リソース生成 YAML
import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; @Path("/yaml") public class YamlResource { @GET @Produces("text/x-yaml") public MyObject getMyObject() { return createMyObject(); } ... }
2.8. JSON 処理での JSON API の使用 (JSON-P)
JSON 処理 (JSON-P) の JSON API は、JSR 353 で定義されている Java EE 7 仕様で導入されました。追加の仕様は、Java EE 8 の JSR 374 で定義されています。JSON Processing の Jakarta EE と同等のものは、Jakarta JSON Processing 1.1 仕様 に含まれています。
JSON-P は JSON を処理する API を定義します。JBoss EAP は、リクエストまたは応答エンティティーとして javax.json.JsonObject
、javax.json.JsonArray
、javax.json.JsonStructure
をサポートします。
JSON 処理 (JSON-P) の JSON API は、Padding (JSONP) では JSON とは異なります。
JSON-P が同じクラスパスにある場合は、Jackson と競合しません。
JsonObject
を作成するには、Json.createObjectBuilder()
を呼び出して JSON オブジェクトをビルドして JsonObjectBuilder
を使用します。
例: javax.json.JsonObject
の作成
JsonObject obj = Json.createObjectBuilder().add("name", "Bill").build();
例: javax.json.JsonObject
の対応 JSON
{ "name":"Bill" }
JsonArray
を作成するには、Json.createArrayBuilder()
を呼び出して SON アレイを構築し、JsonArrayBuilder
を使用します。
例: javax.json.JsonArray
の作成
JsonArray array = Json.createArrayBuilder() .add(Json.createObjectBuilder().add("name", "Bill").build()) .add(Json.createObjectBuilder().add("name", "Monica").build()).build();
例: javax.json.JsonArray
の対応 JSON
[ { "name":"Bill" }, { "name":"Monica" } ]
JsonStructure
は JsonObject
および JsonArray
の親クラスです。
例: javax.json.JsonStructure
の作成
JsonObject obj = Json.createObjectBuilder().add("name", "Bill").build(); JsonArray array = Json.createArrayBuilder() .add(Json.createObjectBuilder().add("name", "Bill").build()) .add(Json.createObjectBuilder().add("name", "Monica").build()).build(); JsonStructure sObj = (JsonStructure) obj; JsonStructure sArray = (JsonStructure) array;
JsonObject
、JsonArray
、JsonStructure
、を JAX-RS リソースで直接使用できます。
例: JSON-P を使用した JAX-RS リソース
@Path("object") @POST @Produces("application/json") @Consumes("application/json") public JsonObject object(JsonObject obj) { // do something return obj; } @Path("array") @POST @Produces("application/json") @Consumes("application/json") public JsonArray array(JsonArray array) { // do something return array; } @Path("structure") @POST @Produces("application/json") @Consumes("application/json") public JsonStructure structure(JsonStructure structure) { // do something return structure; }
クライアントから JSON-P を使用して JSON を送信することもできます。
例: JSON-P を使用するクライアント
WebTarget target = client.target(...); JsonObject obj = Json.createObjectBuilder().add("name", "Bill").build(); JsonObject newObj = target.request().post(Entity.json(obj), JsonObject.class);
2.9. RESTEasy/Jakarta Enterprise Beans の統合
RESTEasy を Jakarta Enterprise Beans と統合するには、Jakarta RESTful Web Services アノテーションを Jakarta RESTful Web Services エンドポイントとして公開する Jakarta Enterprise Beans クラスに追加します。また、Bean のビジネスインターフェイスにアノテーションを適用することもできます。Bean をエンドポイントとしてアクティブ化するには、以下の 2 つの方法があります。
-
web.xml
ファイルの使用。 -
javax.ws.rs.core.Application
の使用
Jakarta Enterprise Beans を Jakarta RESTful Web Services リソースとして機能させるには、ステートレスセッション Bean の @Remote
または @Local
インターフェイスに Jakarta RESTful Web Services アノテーションを付けます。
@Local @Path("/Library") public interface Library { @GET @Path("/books/{isbn}") public String getBook(@PathParam("isbn") String isbn); } @Stateless public class LibraryBean implements Library { ... }
Library
インターフェイスは完全修飾名で参照され、LibraryBean
は単純なクラス名でのみ参照されることに注意してください。
次に、RESTEasy web.xml
ファイルで resteasy.jndi.resources
コンテキストパラメーターを使用して、Jakarta Enterprise Beans を RESTEasy で手動で登録します。
<web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>resteasy.jndi.resources</param-name> <param-value>java:module/LibraryBean!org.app.Library</param-value> </context-param> <listener> <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
resteasy.jndi.resources
コンテキストパラメーターに対して、複数の Jakarta Enterprise Bean 名をコンマで区切り、複数の Java Naming and Directory Interface 名を指定することもできます。
Jakarta Enterprise Beans を RESTEasy エンドポイントとしてアクティベートする代替の Jakarta EE-standard 方法は、javax.ws.rs.core.Application
を使用することです。これには、Jakarta Enterprise Beans 実装クラスをアプリケーションの getClasses()
メソッドによって返されるセットに追加します。このアプローチでは、web.xml
ファイルで指定する必要はありません。
RESTEasy と Jakarta Enterprise Beans の統合を示す実例については、JBoss EAP に同梱される kitchensink
、helloworld-html5
、managed-executor-service
クイックスタートを参照してください。
2.10. Spring の統合
アプリケーションには既存の Jakarta XML Web Services サービスおよびクライアント設定が必要です。
RESTEasy は Spring 4.2.x と統合します。
Maven ユーザーは resteasy-spring
アーティファクトを使用する必要があります。または、JAR を JBoss EAP のモジュールとして利用できます。
RESTEasy には独自の Spring ContextLoaderListener
が含まれています。これは、BeanFactory
が Bean を作成する際に Jakarta RESTful Web Services アノテーションを処理する RESTEasy 固有の BeanPostProcessor
を登録します。そのため、RESTEasy は Bean クラスで @Provider
および Jakarta RESTful Web Services リソースアノテーションを自動的にスキャンし、それらを Jakarta RESTful Web Services リソースとして登録します。
以下を web.xml
ファイルに追加し、RESTEasy/Spring 統合機能を有効にします。
<web-app> <display-name>Archetype Created Web Application</display-name> <listener> <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class> </listener> <listener> <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
SpringContextLoaderListener
は、ResteasyBootstrap
の後に宣言する必要があります。これは、初期化される ServletContext
属性を使用するためです。
RESTEasy と Spring の統合を示す web アプリケーションの作業例は、JBoss EAP に同梱される spring-resteasy
クイックスタートを参照してください。
2.11. CDI の統合
RESTEasy と CDI の統合は、resteasy-cdi
モジュールにより提供されます。
JAX-RS および CDI 仕様の両方が、独自のコンポーネントモデルを導入します。一連の基本制約を満たす CDI アーカイブに含まれるすべてのクラスは、暗黙的に CDI Bean です。JAX-RS コンポーネントになるようにするには、@Path
or @Provider
を使用して Java クラスを明示的に宣言する必要があります。統合コードがない場合、JAX-RS アノテーションを持つ CDI bean に適したクラスにアノテーションを付けると、誤った結果が出され、JAX-RS コンポーネントは CDI によって管理されません。resteasy-cdi
モジュールは、RESTEasy が CDI コンテナーから取得したクラスインスタンスと連携できるようにするブリッジです。
Web サービスの呼び出し中に、resteasy-cdi
モジュールは JAX-RS コンポーネントの管理対象インスタンスに対して CDI コンテナーを要求します。次に、このインスタンスが RESTEasy に渡されます。Bean デプロイメントアーカイブではない JAR ファイルに置かれているクラスなど、何らかの理由で管理インスタンスが利用できない場合、RESTEasy はクラス自体をインスタンス化するためにフォールバックします。
これにより、インジェクション、ライフサイクル管理、イベント、デコレーション、インターセプターバインディングなどの CDI サービスを JAX-RS コンポーネントで使用できます。
2.11.1. デフォルトのスコープ。
スコープを明示的に定義しない CDI bean はデフォルトで @Dependent
スコープが設定されます。この擬似スコープは、Bean がインジェクトされる Bean のライフサイクルに適応することを意味します。リクエスト、セッション、アプリケーションなどの通常のスコープは、コンポーネントのライフサイクル境界を明示的に指定するため、JAX-RS コンポーネントにより適しています。そのため、resteasy-cdi
モジュールは以下の方法でデフォルトのスコーピングを変更します。
- JAX-RS root リソースがスコープを明示的に定義しない場合は、リクエストスコープにバインドされます。
-
JAX-RS プロバイダーまたは
javax.ws.rs.Application
サブクラスがスコープを明示的に定義しない場合は、アプリケーションスコープにバインドされます。
スコープを宣言しないすべての bean のスコープは resteasy-cdi
モジュールによって変更されるため、セッション bean にも影響を及ぼします。これにより、仕様によりこれらのコンポーネントが @RequestScoped
になることが禁止されるため、ステートレスセッション Bean またはシングルトンの範囲が自動的に変更されると競合が発生します。そのため、ステートレスセッション Bean またはシングルトンを使用する場合は、スコープを明示的に定義する必要があります。この要件は、今後のリリースで削除される可能性があります。
resteasy-cdi
モジュールは JBoss EAP にバンドルされています。したがって、モジュールを個別にダウンロードしたり、設定を追加したりする必要はありません。JAX-RS リソースで CDI bean を使用する作業例は、JBoss EAP に同梱される kitchensink
クイックスタートを参照してください。
2.12. RESTEasy フィルターおよびインターセプター
JAX-RS にはインターセプターにフィルターとインターセプターという 2 つの異なる概念があります。フィルターは主に、着信および発信要求ヘッダーまたは応答ヘッダーを変更または処理するために使用されます。リクエストおよび応答処理の前後に実行されます。
2.12.1. サーバー側フィルター
サーバー側では、ContainerRequestFilters
および ContainerResponseFilters
という 2 種類のフィルターを利用できます。ContainerRequestFilters
は JAX-RS リソースメソッドが呼び出される前に実行されます。ContainerResponseFilters
は JAX-RS リソースメソッドの呼び出し後に実行されます。
さらに、ContainerRequestFilters
には pre-matching および post-matching のタイプがあります。事前一致する ContainerRequestFilters
は @PreMatching
アノテーションで指定され、JAX-RS リソースメソッドが受信 HTTP リクエストに一致する前に実行されます。一致しない ContainerRequestFilters
は @PostMatching
アノテーションで指定され、JAX-RS リソースメソッドが受信 HTTP リクエストに一致した後に実行されます。
事前一致フィルターは多くの場合、要求属性を変更して、.xml
をストリップし、Accept
ヘッダーを追加するなど、特定のリソースメソッドへの一致方法を変更するために使用されます。ContainerRequestFilters
は ContainerRequestContext.abortWith (Response)
を呼び出して要求を中止できます。たとえば、フィルターはカスタム認証プロトコルを実装している場合に中止する必要があるかもしれません。
リソースクラスメソッドの実行後、JAX-RS はすべての ContainerResponseFilters
を実行します。これらのフィルターにより、発信応答がマーシャリングされ、クライアントに送信される前に送信応答を変更できます。
例: 要求フィルター
public class RoleBasedSecurityFilter implements ContainerRequestFilter { protected String[] rolesAllowed; protected boolean denyAll; protected boolean permitAll; public RoleBasedSecurityFilter(String[] rolesAllowed, boolean denyAll, boolean permitAll) { this.rolesAllowed = rolesAllowed; this.denyAll = denyAll; this.permitAll = permitAll; } @Override public void filter(ContainerRequestContext requestContext) throws IOException { if (denyAll) { requestContext.abortWith(Response.status(403).entity("Access forbidden: role not allowed").build()); return; } if (permitAll) return; if (rolesAllowed != null) { SecurityContext context = ResteasyProviderFactory.getContextData(SecurityContext.class); if (context != null) { for (String role : rolesAllowed) { if (context.isUserInRole(role)) return; } requestContext.abortWith(Response.status(403).entity("Access forbidden: role not allowed").build()); return; } } return; } }
例: 応答フィルター
public class CacheControlFilter implements ContainerResponseFilter { private int maxAge; public CacheControlFilter(int maxAge) { this.maxAge = maxAge; } public void filter(ContainerRequestContext req, ContainerResponseContext res) throws IOException { if (req.getMethod().equals("GET")) { CacheControl cc = new CacheControl(); cc.setMaxAge(this.maxAge); res.getHeaders().add("Cache-Control", cc); } } }
2.12.2. クライアント側のフィルター
クライアント側のフィルターの詳細は、本ガイドの JAX-RS Client API のセクションを参照してください。
2.12.3. RESTEasy インターセプター
2.12.3.1. JAX-RS 呼び出しのインターセプト
RESTEasy は JAX-RS 呼び出しをインターセプトし、インターセプターと呼ばれるリスナーのようなオブジェクトでそれらをルーティングできます。
フィルターはリクエストまたは応答ヘッダーを変更しますが、インターセプターはメッセージボディーを処理します。インターセプターは、対応するリーダーまたはライターと同じ呼び出しスタックで実行されます。ReaderInterceptors
は MessageBodyReaders
の実行をラップします。WriterInterceptors
は MessageBodyWriters
の実行をラップします。これは、特定のコンテンツエンコーディングを実装するために使用できます。これらは、デジタル署名の生成や、マーシャリング前後の Java オブジェクトモデルの投稿または事前処理に使用することができます。
ReaderInterceptors
および WriterInterceptors
は、サーバー側またはクライアント側のいずれかで使用することができます。これらは、@ServerInterceptor
または @ClientInterceptor
のいずれかや、@Provider
でアノテーションが付けられたため、RESTEasy はそれらをインターセプター一覧に追加するかどうかを認識できます。
これらのインターセプターは、MessageBodyReader.readFrom()
または MessageBodyWriter.writeTo()
の呼び出しをラップします。これらは、Output
や Input
ストリームをラップするために使用できます。
例: インターセプター
@Provider public class BookReaderInterceptor implements ReaderInterceptor { @Inject private Logger log; @Override @ReaderInterceptorBinding public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException { log.info("*** Intercepting call in BookReaderInterceptor.aroundReadFrom()"); VisitList.add(this); Object result = context.proceed(); log.info("*** Back from intercepting call in BookReaderInterceptor.aroundReadFrom()"); return result; } }
インターセプターと MessageBodyReader
または Writer
は、単一の大きな Java 呼び出しスタックで呼び出されます。ReaderInterceptorContext.proceed()
または WriterInterceptorContext.proceed()
は、次のインターセプターに移動するために呼び出されます。または、呼び出すインターセプターがない場合は、MessageBodyReader
や MessageBodyWriter
の readFrom()
や writeTo()
メソッドになります。このラッピングにより、オブジェクトは Reader
や Writer
に到達する前に変更でき、proceed()
が返された後にクリーンアップされます。
以下の例は、ヘッダー値を応答に追加するサーバー側のインターセプターです。
@Provider public class BookWriterInterceptor implements WriterInterceptor { @Inject private Logger log; @Override @WriterInterceptorBinding public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { log.info("*** Intercepting call in BookWriterInterceptor.aroundWriteTo()"); VisitList.add(this); context.proceed(); log.info("*** Back from intercepting call in BookWriterInterceptor.aroundWriteTo()"); } }
2.12.3.2. インターセプターの登録
アプリケーションで RESTEasy JAX-RS インターセプターを登録するには、context-param
要素の resteasy.providers
パラメーターの下で web.xml
ファイルにリストするか、これをクラスまたは Application.getClasses()
や Application.getSingletons()
メソッドのオブジェクトとして返します。
<context-param> <param-name>resteasy.providers</param-name> <param-value>my.app.CustomInterceptor</paramvalue> </context-param>
package org.jboss.resteasy.example; import javax.ws.rs.core.Application; import java.util.HashSet; import java.util.Set; public class MyApp extends Application { public java.util.Set<java.lang.Class<?>> getClasses() { Set<Class<?>> resources = new HashSet<Class<?>>(); resources.add(MyResource.class); resources.add(MyProvider.class); return resources; } }
package org.jboss.resteasy.example; import javax.ws.rs.core.Application; import java.util.HashSet; import java.util.Set; public class MyApp extends Application { protected Set<Object> singletons = new HashSet<Object>(); public MyApp() { singletons.add(new MyResource()); singletons.add(new MyProvider()); } @Override public Set<Object> getSingletons() { return singletons; } }
2.12.4. gzip 圧縮および展開
RESTEasy は GZIP 圧縮および展開に対応しています。GZIP 圧縮をサポートするため、クライアントフレームワークまたは JAX-RS サービスは gzip
の Content-Encoding
とメッセージボディーを自動的に展開し、このヘッダーを手作業で設定する必要がないように Accept-Encoding
ヘッダーを gzip, deflate
に自動的に設定できます。GZIP 圧縮をサポートするため、クライアントフレームワークがリクエストを送信している場合や、サーバーが gzip
に設定された Content-Encoding
ヘッダーを使用して応答を送信している場合に、RESTEasy は出力メッセージを圧縮します。@org.jboss.resteasy.annotation.GZIP
アノテーションを使用して Content-Encoding
ヘッダーを設定できます。
以下の例は、gzip 圧縮する、出力メッセージボディーの order
をタグ付けします。
例: GZIP 圧縮
@Path("/") public interface MyProxy { @Consumes("application/xml") @PUT public void put(@GZIP Order order); }
例: サーバー応答のタグ付け GZIP 圧縮
@Path("/") public class MyService { @GET @Produces("application/xml") @GZIP public String getData() {...} }
2.12.4.1. GZIP 圧縮および展開の設定
RESTEasy は、サイズが大きいものの、攻撃者が圧縮し、サーバーに送信しているエンティティーの展開を防ぐために、GZIP 圧縮および展開をデフォルトで無効にします。
GZIP 圧縮と展開には、以下の 3 つのインターセプターが関係します。
-
org.jboss.resteasy.plugins.interceptors.GZIPDecodingInterceptor
:Content-Encoding
ヘッダーが存在し、値がgzip
の場合、GZIPDecodingInterceptor
はメッセージボディーを展開するInputStream
をインストールします。 -
org.jboss.resteasy.plugins.interceptors.GZIPEncodingInterceptor
:Content-Encoding
ヘッダーが存在し、値がgzip
の場合、GZIPEncodingInterceptor
はメッセージボディーを圧縮するOutputStream
をインストールします。 org.jboss.resteasy.plugins.interceptors.AcceptEncodingGZIPFilter
:Accept-Encoding
ヘッダーが存在しない場合、AcceptEncodingGZIPFilter
はgzip, deflate
の値でAccept-Encoding
ヘッダーを追加します。Accept-Encoding
ヘッダーが存在するがgzip
が含まれていない場合は、AcceptEncodingGZIPFilter
インターセプターが値, gzip
を追加します。注記GZIP 圧縮または展開を有効にしても、
AcceptEncodingGZIPFilter
インターセプターの存在には依存しません。
GZIP デコンプレッシングを有効にすると、GZIPDecodingInterceptor
が圧縮メッセージボディーから抽出できるバイト数の上限が設定されます。デフォルトの制限は 10,000,000
です。
2.12.4.2. サーバー側の GZIP 設定
インターセプターを有効にするには、クラスパスの javax.ws.rs.ext.Providers
ファイルにクラス名を追加します。圧縮ファイルの上限は、web アプリケーションコンテキストパラメーター resteasy.gzip.max.input
を使用して設定します。サーバー側でこの制限を超えると、GZIPDecodingInterceptor
はステータス 413 - Request Entity Too Large
の応答と、上限を指定するメッセージを返します。
2.12.4.2.1. クライアント側の GZIP 設定
GZIP インターセプターを有効にするには、Client
や WebTarget
などでこれを登録します。例を以下に示します。
Client client = new ResteasyClientBuilder() // Activate gzip compression on client: .register(AcceptEncodingGZIPFilter.class) .register(GZIPDecodingInterceptor.class) .register(GZIPEncodingInterceptor.class) .build();
圧縮ファイルの上限を設定するには、特定の値で GZIPDecodingInterceptor
のインスタンスを作成します。
Client client = new ResteasyClientBuilder() // Activate gzip compression on client: .register(AcceptEncodingGZIPFilter.class) .register(new GZIPDecodingInterceptor(256)) .register(GZIPEncodingInterceptor.class) .build();
クライアント側で上限を超えると、GZIPDecodingInterceptor
は上限を指定するメッセージとともに ProcessingException
を出力します。
2.12.5. リソースごとのメソッドフィルターとインターセプター
フィルターまたはインターセプターを特定のリソースメソッドに対してのみ実行したい場合があります。これは、以下の 2 つのの方法で実行できます。
DynamicFeature
インターフェイスの実装
DynamicFeature
インターフェイスには、コールバックメソッド configure(ResourceInfo resourceInfo, FeatureContext context)
が含まれます。これは、デプロイされた各 JAX-RS メソッドに対して呼び出されます。ResourceInfo
パラメーターには、デプロイされている現在の JAX-RS メソッドについての情報が含まれます。FeatureContext
は、Configurable
インターフェイスの拡張機能です。このパラメーターの register()
メソッドを使用して、このメソッドに割り当てるフィルターとインターセプターをバインドできます。
例: DynamicFeature インターフェイスの使用
@Provider public class AnimalTypeFeature implements DynamicFeature { @Override public void configure(ResourceInfo info, FeatureContext context) { if (info.getResourceMethod().getAnnotation(GET.class) != null) AnimalFilter filter = new AnimalFilter(); context.register(filter); } } }
上記の例では、AnimalTypeFeature
を使用して登録するプロバイダーは、インターフェイスのいずれかを実装する必要があります。この例では、以下のインターフェイスのいずれかを実装する必要があるプロバイダー AnimalFilter
を登録します。これは、ContainerRequestFilter
、ContainerResponseFilter
、ReaderInterceptor
、WriterInterceptor
、または Feature
のいずれかを実装する必要があります。この場合、AnimalFilter
は GET アノテーションが付けられたすべてのリソースメソッドに適用されます。詳細は DynamicFeature Documentation を参照してください。
@NameBinding アノテーションを使用します。
@NameBinding
は CDI インターセプターのように機能します。@NameBinding
でカスタムアノテーションにアノテーションを付け、そのカスタムアノテーションをフィルターおよびリソースメソッドに適用します。
例: @NameBinding の使用
@NameBinding public @interface DoIt {} @DoIt public class MyFilter implements ContainerRequestFilter {...} @Path("/root") public class MyResource { @GET @DoIt public String get() {...} }
詳細は NameBinding Documentation のドキュメントを参照してください。
2.12.6. 順序付け
順序付けは、フィルターまたはインターセプタークラスの @Priority
アノテーションを使用して実行されます。
2.12.7. フィルターおよびインターセプターによる例外処理
フィルターまたはインターセプターに関連する例外は、クライアント側またはサーバー側で発生する可能性があります。クライアント側では、javax.ws.rs.client.ProcessingException
および javax.ws.rs.client.ResponseProcessingException
という例外を処理する必要があります。javax.ws.rs.client.ProcessingException
は、リクエストがサーバーに送信される前にエラーが発生していた場合、クライアント側で出力されます。サーバーからクライアントが受信した応答の処理にエラーが発生すると、javax.ws.rs.client.ResponseProcessingException
がクライアント側で出力されます。
サーバー側では、フィルターまたはインターセプターによって発生する例外は JAX-RS メソッドから発生する他の例外と同じ方法で処理されます。これは、出力される例外の ExceptionMapper
を検索します。JAX-RS メソッドにおける例外の処理方法の詳細は、Exception Handling セクションを参照してください。
2.13. RESTEasy プロバイダーおよびインターセプターのロギング
RESTEasy は、使用されたプロバイダーとインターセプターを DEBUG
レベルのロギングでログに記録します。以下の管理 CLI コマンドを使用すると、RESTEasy に関連するすべてのログレベルを有効にできます。
/subsystem=logging/console-handler=CONSOLE:write-attribute(name=level,value=ALL) /subsystem=logging/logger=org.jboss.resteasy:add(level=ALL) /subsystem=logging/logger=javax.xml.bind:add(level=ALL) /subsystem=logging/logger=com.fasterxml.jackson:add(level=ALL)
2.14. 例外処理
2.14.1. 例外マッパーの作成
例外マッパーは、発生する例外をキャッチし、特定の HTTP 応答を書き込むアプリケーションが提供するカスタムコンポーネントです。
例外マッパーを作成する場合は、@Provider
アノテーションが付けられたクラスを作成し、ExceptionMapper
インターフェイスを実装します。
以下は例外マッパーの例です。
@Provider public class EJBExceptionMapper implements ExceptionMapper<javax.ejb.EJBException> { public Response toResponse(EJBException exception) { return Response.status(500).build(); } }
例外マッパーを登録するには、web.xml
ファイルの resteasy.providers
context-param
一覧し、ResteasyProviderFactory
クラスでプログラムで登録します。
2.14.2. 内部で出力される例外の管理
例外 | HTTP コード | 説明 |
---|---|---|
BadRequestException | 400 | 正しくない要求。リクエストが適切にフォーマットされていなかったか、リクエスト入力の処理に問題がありました。 |
UnauthorizedException | 401 | 承認されていません。RESTEasy のアノテーションベースのロールベースのセキュリティーを使用している場合には、セキュリティー例外が発生します。 |
InternalServerErrorException | 500 | 内部サーバーエラー。 |
MethodNotAllowedException | 405 | リソースによって呼び出された HTTP 操作を処理するための Jakarta RESTful Web Services メソッドはありません。 |
NotAcceptableException | 406 | Accept ヘッダーにリストされているメディアタイプを生成できる Jakarta RESTful Web Services メソッドはありません。 |
NotFoundException | 404 | リクエストパス/リソースを提供する Jakarta RESTful Web Services メソッドはありません。 |
ReaderException | 400 | MessageBodyReaders から発生するすべての例外は、この例外内でラップされます。ラップされた例外に対する ExceptionMapper がない場合、または例外が WebApplicationException ではない場合、デフォルトでは RESTEasy は 400 コードを返します。 |
WriterException | 500 | MessageBodyWriters から発生するすべての例外は、この例外内でラップされます。ラップされた例外に対する ExceptionMapper がない場合、または例外が WebApplicationException ではない場合、デフォルトでは RESTEasy は 400 コードを返します。 |
JAXBUnmarshalException | 400 | JAXB プロバイダー (XML および Jackson) は、この例外を読み取りで出力します。これは、JAXBExceptions をラップする可能性があります。このクラスは ReaderException を拡張します。 |
JAXBMarshalException | 500 | JAXB プロバイダー (XML および Jackson) は書き込みでこの例外を出力します。これは、readsExceptions をラップする可能性があります。このクラスは WriterException を拡張します。 |
ApplicationException | 該当なし | アプリケーションコードから出力されるすべての例外をラップし、InvocationTargetException と同じように機能します。ラップされた例外に対する ExceptionMapper がある場合は、要求の処理に使用されます。 |
失敗 | 該当なし | 内部 RESTEasy エラー。ログに記録されない。 |
LoggableFailure | 該当なし | 内部 RESTEasy エラー。ログが記録されています。 |
DefaultOptionsMethodException | 該当なし |
ユーザーが |
UnrecognizedPropertyExceptionHandler | 400 | RESTEasy Jackson プロバイダーは、JSON データが無効であると判断するとこの例外が発生します。 |
2.15. JAX-RS Web サービスのセキュア化
RESTEasy は JAX-RS メソッドでの @RolesAllowed
、@PermitAll
、@DenyAll
アノテーションに対応しています。ただし、これらのアノテーションを認識させるには、ロールベースのセキュリティーを有効にする必要があります。
2.15.1. ロールベースのセキュリティーの有効化
以下の手順に従って、web.xml
ファイルを設定し、ロールベースのセキュリティーを有効にします。
アプリケーションが EJB を使用する場合は、ロールベースのセキュリティーをアクティベートしないでください。EJB コンテナーは RESTEasy ではなく機能を提供します。
RESTEasy JAX-RS Web サービスのロールベースのセキュリティーの有効化
-
テキストエディターでアプリケーションの
web.xml
ファイルを開きます。 以下の
<context-param>
をファイルに、<web-app>
内に追加します。<context-param> <param-name>resteasy.role.based.security</param-name> <param-value>true</param-value> </context-param>
<security-role>
タグを使用して、RESTEasy JAX-RS WAR ファイル内で使用されるすべてのロールを宣言します。<security-role> <role-name>ROLE_NAME</role-name> </security-role> <security-role> <role-name>ROLE_NAME</role-name> </security-role>
すべてのロールについて JAX-RS ランタイムによって処理されるすべての URL へのアクセスを承認します。
<security-constraint> <web-resource-collection> <web-resource-name>Resteasy</web-resource-name> <url-pattern>/PATH</url-pattern> </web-resource-collection> <auth-constraint> <role-name>ROLE_NAME</role-name> <role-name>ROLE_NAME</role-name> </auth-constraint> </security-constraint>
このアプリケーションの適切なログイン設定を定義します。
<login-config> <auth-method>BASIC</auth-method> <realm-name>jaxrs</realm-name> </login-config>
ロールベースのセキュリティーは、定義されたロールとともにアプリケーション内で有効にされています。
例: ロールベースのセキュリティー設定
<web-app> <context-param> <param-name>resteasy.role.based.security</param-name> <param-value>true</param-value> </context-param> <security-constraint> <web-resource-collection> <web-resource-name>Resteasy</web-resource-name> <url-pattern>/security</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>user</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>jaxrs</realm-name> </login-config> <security-role> <role-name>admin</role-name> </security-role> <security-role> <role-name>user</role-name> </security-role> </web-app>
2.15.2. アノテーションを使用した JAX-RS Web サービスのセキュア化
アノテーションを使用して JAX-RS Web サービスを保護するには、以下の手順を行います。
- ロールベースのセキュリティーを有効化します。
JAX-RS Web サービスにセキュリティーアノテーションを追加します。RESTEasy は以下のアノテーションをサポートします。
@RolesAllowed
-
メソッドにアクセスできるロールを定義します。すべてのロールは
web.xml ファイル
で定義する必要があります。 @PermitAll
-
web.xml
ファイルに定義されているすべてのロールがメソッドにアクセスできるようにします。 @DenyAll
- メソッドへのすべてのアクセスを拒否します。
以下の例は、@RolesAllowed
アノテーションを使用して admin
ロールが Web サービスにアクセスできることを指定します。
@RolesAllowed("admin") @Path("/test") public class TestService { ... }
2.15.3. プログラムによるセキュリティーの設定
JAX-RS には、セキュアな要求のセキュリティー情報を収集するためのプログラム API が含まれています。javax.ws.rs.core.SecurityContext
インターフェイスには、セキュアな HTTP 呼び出しを行うユーザーのアイデンティティーを判断するメソッドがあります。また、現在のユーザーが特定のロールに属するかどうかをチェックすることができるメソッドもあります。
public interface SecurityContext { public Principal getUserPrincipal(); public boolean isUserInRole(String role); public boolean isSecure(); public String getAuthenticationScheme(); }
SecurityContext
インスタンスにアクセスするには、@Context
アノテーションを使用して、フィールド、セッターメソッド、またはリソースメソッドパラメーターにインジェクトします。
@Path("test") public class SecurityContextResource { @Context SecurityContext securityContext; @GET @Produces("text/plain") public String get() { if (!securityContext.isUserInRole("admin")) { throw new WebApplicationException(Response.serverError().status(HttpResponseCodes.SC_UNAUTHORIZED) .entity("User " + securityContext.getUserPrincipal().getName() + " is not authorized").build()); } return "Good user " + securityContext.getUserPrincipal().getName(); } }
2.16. RESTEasy Asynchronous Job Service
RESTEasy Asynchronous Job Service は、HTTP プロトコルに非同期動作を追加するように設計されています。HTTP は同期プロトコルですが、非同期呼び出しを認識します。HTTP 1.1 202 accepted
は、サーバーが処理応答を受信し、承認したものの、処理が完了していないことを示します。Asynchronous Job Service はこれに基づいてビルドされます。
2.16.1. 非同期ジョブサービスの有効化
Asynchronous Job Service は web.xml
ファイルで有効化します。
<context-param> <param-name>resteasy.async.job.service.enabled</param-name> <param-value>true</param-value> </context-param>
2.16.2. 非同期ジョブの設定
このセクションでは、RESTEasy を使用した非同期ジョブのクエリーパラメーターの例について説明します。
ロールベースのセキュリティーは、ポータブルに実装できないため Asynchronous Job Service とは連携しません。Asynchronous Job Service を使用する場合は、代わりに web.xml
ファイルでの XML 宣言を使用してアプリケーションセキュリティーを確立する必要があります。
GET、DELETE、および PUT メソッドは非同期的に呼び出すことができますが、これらのメソッドの HTTP 1.1 コントラクトが破損します。これらの呼び出しによってリソースが複数回呼び出されると、その状態が変更されることはありませんが、各呼び出しで新しいジョブエントリーとしてサーバーの状態が変更されます。
asynch
クエリーパラメーターは、バックグラウンドで呼び出しを実行するために使用されます。202 Accepted
応答が返されます。また、バックグラウンドメソッドの応答がある場所を指す URL が含まれる場所のヘッダーも返されます。
POST http://example.com/myservice?asynch=true
上記の例では、202 Accepted
応答を返します 。また、バックグラウンドメソッドの応答がある場所を指す URL のある場所のヘッダーも返します。以下は場所ヘッダーの例です。
HTTP/1.1 202 Accepted Location: http://example.com/asynch/jobs/3332334
URI は以下の形式になります。
/asynch/jobs/{job-id}?wait={milliseconds}|nowait=true
この URL では、GET、POST、および DELETE 操作を実行できます。
-
ジョブが完了したら、GET は応答として呼び出される JAX-RS リソースメソッドを返します。ジョブが完了していない場合、GET は
202 Accepted
応答コードを返します。GET を呼び出してもジョブは削除されないため、複数回呼び出すことができます。 - POST はジョブの応答を読み取り、完了するとジョブを削除します。
- DELETE は、ジョブキューを手動でクリーンアップするために呼び出されます。
ジョブキューが満杯になると、DELETE を呼び出すことなく、メモリーから最も古いジョブを自動的に無効にします。
GET および POST 操作では、wait
および nowait
クエリーパラメーターを使用して、最大待機時間を定義できます。wait
パラメーターが指定されていない場合、操作はデフォルトで nowait=true
に設定され、ジョブが完了しない場合には待機しません。wait
パラメーターはミリ秒単位で定義されます。
POST http://example.com/asynch/jobs/122?wait=3000
RESTEasy は、一方向のクエリーパラメーターを使用して oneway
ジョブの発生と無効化をサポートします。
POST http://example.com/myservice?oneway=true
上記の例では 202 Accepted
応答が返されますが、ジョブは作成されません。
Asynchronous Job Service の設定パラメーターは、付録の RESTEasy Asynchronous Job Service Configuration Parameters のセクションを参照してください。
2.17. RESTEasy JavaScript API
2.17.1. RESTEasy JavaScript API について
RESTEasy は、AJAX 呼び出しを使用して JAX-RS 操作を呼び出す JavaScript API を生成できます。各 JAX-RS リソースクラスは、宣言するクラスまたはインターフェイスと同じ名前の JavaScript オブジェクトを生成します。JavaScript オブジェクトには、各 JAX-RS メソッドがプロパティーとして含まれています。
@Path("foo") public class Foo { @Path("{id}") @GET public String get(@QueryParam("order") String order, @HeaderParam("X-Foo") String header, @MatrixParam("colour") String colour, @CookieParam("Foo-Cookie") String cookie) { } @POST public void post(String text) { } }
以下の JavaScript コードは、前の例で生成した JAX-RS API を使用します。
var text = Foo.get({order: 'desc', 'X-Foo': 'hello', colour: 'blue', 'Foo-Cookie': 123987235444}); Foo.post({$entity: text});
各 JavaScript API メソッドは、任意のオブジェクトを単一のパラメーターとして取得します。ここでの各プロパティーは名前で識別される Cookie、ヘッダー、パス、クエリーまたはフォームパラメーター、または API パラメータープロパティーです。API パラメータープロパティーの詳細は、RESTEasy Javascript API Parameters の付録を参照してください。
2.17.1.1. RESTEasy JavaScript API サーブレットの有効化
RESTEasy JavaScript API はデフォルトで無効にされています。以下の手順に従って、web.xml
ファイルを更新して有効にします。
-
テキストエディターでアプリケーションの
web.xml
ファイルを開きます。 以下の設定を
web-app
タグ内のファイルに追加します。<servlet> <servlet-name>RESTEasy JSAPI</servlet-name> <servlet-class>org.jboss.resteasy.jsapi.JSAPIServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>RESTEasy JSAPI</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
2.17.1.2. ビルド AJAX クエリー
RESTEasy JavaScript API を使用すると、手動で要求を作成することができます。以下にこの動作の例をいくつか示します。
例: RESTEasy JavaScript API クライアントの動作をオーバーライドするために使用される REST オブジェクト
// Change the base URL used by the API: REST.apiURL = "http://api.service.com"; // log everything in a div element REST.log = function(text) { jQuery("#log-div").append(text); };
REST
オブジェクトには以下の読み書きプロパティーが含まれます。
-
apiURL
: デフォルトでは JAX-RS ルート URL に設定されます。要求の作成時にすべての JavaScript クライアント API 機能によって使用されます。 -
log
: RESTEasy クライアント API ログを受信するためにfunction(string)
に設定します。これは、クライアント API のデバッグ、およびそれらを確認できる場所のログの配置を行う場合に便利です。
例: REST.Request() メソッドを使用したカスタムリクエストの構築
var r = new REST.Request(); r.setURI("http://api.service.com/orders/23/json"); r.setMethod("PUT"); r.setContentType("application/json"); r.setEntity({id: "23"}); r.addMatrixParameter("JSESSIONID", "12309812378123"); r.execute(function(status, request, entity) { log("Response is " + status); });
2.18. リソースメタデータを変更する RESTEasy SPI
JBoss EAP は、ResourceBuilder
を使用して作成されるリソースクラスメタデータを編集するための RESTEasy サービスプロバイダーインターフェイス (SPI) を提供します。JAX-RS デプロイメントを処理する場合、RESTEasy は ResourceBuilder
を使用して各 JAX-RS リソースのメタデータを作成します。このようなメタデータは、特に ResourceClass
インターフェイスで org.jboss.resteasy.spi.metadata
パッケージのメタデータ SPI を使用して定義されます。
package org.jboss.resteasy.spi.metadata; public interface ResourceClass { String getPath(); Class<?> getClazz(); ResourceConstructor getConstructor(); FieldParameter[] getFields(); SetterParameter[] getSetters(); ResourceMethod[] getResourceMethods(); ResourceLocator[] getResourceLocators(); }
RESTasy では、ResourceClassProcessor
インターフェイスの実装によって、メタデータ生成のカスタマイズが可能になります。以下の例は、この SPI の使用例を示しています。
package org.jboss.resteasy.test.core.spi.resource; import org.jboss.logging.Logger; import org.jboss.resteasy.spi.metadata.ResourceClass; import org.jboss.resteasy.spi.metadata.ResourceClassProcessor; import javax.ws.rs.ext.Provider; @Provider public class ResourceClassProcessorImplementation implements ResourceClassProcessor { protected static final Logger logger = Logger.getLogger(ResourceClassProcessorImplementation.class.getName()); @Override public ResourceClass process(ResourceClass clazz) { logger.info(String.format("ResourceClassProcessorImplementation#process method called on class %s", clazz.getClazz().getSimpleName())); String clazzName = clazz.getClazz().getSimpleName(); if (clazzName.startsWith("ResourceClassProcessorEndPoint") || clazzName.equals("ResourceClassProcessorProxy") || clazzName.equals("ResourceClassProcessorProxyEndPoint")) { return new ResourceClassProcessorClass(clazz); } return clazz; }
ResteasyProviderFactory
クラスを使用して保存される新しいプロセッサーは、通常の JAX-RS アノテーション付きプロバイダーとして解決されます。これにより、リソースメタデータクラスを、以下のようなさまざまな高度なシナリオに使用できるカスタムバージョンでラップできます。
- 追加リソースメソッドまたはロケーターをリソースに追加。
- HTTP メソッドの変更
-
@Produces
または@Consumes
メディアタイプの変更。
2.19. MicroProfile REST クライアント
MicroProfile REST クライアントはテクノロジープレビューとしてのみ提供されます。テクノロジープレビューの機能は、Red Hat の本番環境のサービスレベルアグリーメント (SLA) ではサポートされず、機能的に完全ではないことがあるため、Red Hat は本番環境での使用は推奨しません。テクノロジープレビューの機能は、最新の製品機能をいち早く提供して、開発段階で機能のテストを行いフィードバックを提供していただくことを目的としています。
テクノロジープレビュー機能のサポート範囲については、Red Hat カスタマーポータルの テクノロジープレビュー機能のサポート範囲 を参照してください。
JBoss EAP 7.3 は、HTTP 上で RESTful サービスを呼び出すために型安全なアプローチを利用するため、JAX-RS 2.1 クライアント上に構築される MicroProfile REST クライアント 1.4.x をサポートするようになりました。MicroProfile Type Safe REST クライアントは Java インターフェイスとして定義されます。MicroProfile REST クライアントでは、実行可能コードでクライアントアプリケーションの書き込みを行うことができます。
MicroProfile REST クライアントは以下を有効にします。
- 直感的構文
- プロバイダーのプログラムによる登録
- プロバイダーの宣言的登録
- ヘッダーの宣言的仕様
- サーバー上のヘッダーの伝搬
-
ResponseExceptionMapper
- CDI 統合
2.19.1. 直感的構文
MicroProfile REST クライアントは、CORBA、Java Remote Method Invocation (RMI)、JBoss Remoting Project、および RESTEasy にも実装されている分散オブジェクト通信のバージョンを有効にします。たとえば、以下のリソースについて見てみましょう。
@Path("resource") public class TestResource { @Path("test") @GET String test() { return "test"; } }
TestResource
クラスにアクセスする JAX-RS のネイティブな方法は次のとおりです。
Client client = ClientBuilder.newClient(); String response = client.target("http://localhost:8081/test").request().get(String.class);
ただし、Microprofile REST クライアントは test()
メソッドを直接呼び出すことで、より直感的な構文をサポートします。
@Path("resource") public interface TestResourceIntf { @Path("test") @GET public String test(); } TestResourceIntf service = RestClientBuilder.newBuilder() .baseUrl(http://localhost:8081/)) .build(TestResourceIntf.class); String s = service.test();
上記の例では、TestResource
クラスへの呼び出しは、呼び出し service.test()
が示すように TestResourceIntf
クラスにより大幅に容易になります。
以下の例は、TestResourceIntf
クラスのより詳細なバージョンです。
@Path("resource") public interface TestResourceIntf2 { @Path("test/{path}")mes("text/plain") @Produces("text/html") @POST public String test(@PathParam("path") String path, @QueryParam("query") String query, String entity); }
service.test("p", "q", "e")
メソッドを呼び出すと、以下のような HTTP メッセージが生成されます。
POST /resource/test/p/?query=q HTTP/1.1 Accept: text/html Content-Type: text/plain Content-Length: 1 e
2.19.2. プロバイダーのプログラムによる登録
MicroProfile REST クライアントでは、プロバイダーを登録してクライアント環境を設定することもできます。例を以下に示します。
TestResourceIntf service = RestClientBuilder.newBuilder() .baseUrl(http://localhost:8081/)) .register(MyClientResponseFilter.class) .register(MyMessageBodyReader.class) .build(TestResourceIntf.class);
2.19.3. プロバイダーの宣言型登録
以下のように org.eclipse.microprofile.rest.client.annotation.RegisterProvider
アノテーションをターゲットインターフェイスに追加して、プロバイダーを宣言的に登録することもできます。
@Path("resource") @RegisterProvider(MyClientResponseFilter.class) @RegisterProvider(MyMessageBodyReader.class) public interface TestResourceIntf2 { @Path("test/{path}") @Consumes("text/plain") @Produces("text/html") @POST public String test(@PathParam("path") String path, @QueryParam("query") String query, String entity); }
MyClientResponseFilter
クラスと MyMessageBodyReader
クラスをアノテーションで宣言すると、RestClientBuilder.register()
メソッドを呼び出す必要がなくなります。
2.19.4. ヘッダーの宣言的仕様
HTTP リクエストのヘッダーは、以下の方法で指定できます。
- リソースメソッドパラメーターのいずれかにアノテーションを付けます。
-
org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam
アノテーションを宣言で使用。
以下の例では、@HeaderValue
アノテーションを持つリソースメソッドパラメーターのいずれかにアノテーションを付け、ヘッダーの設定を示しています。
@POST @Produces(MediaType.TEXT_PLAIN) @Consumes(MediaType.TEXT_PLAIN) String contentLang(@HeaderParam(HttpHeaders.CONTENT_LANGUAGE) String contentLanguage, String subject);
以下の例は、org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam
アノテーションを使用してヘッダーを設定する例になります。
@POST @Produces(MediaType.TEXT_PLAIN) @Consumes(MediaType.TEXT_PLAIN) @ClientHeaderParam(name=HttpHeaders.CONTENT_LANGUAGE, value="{getLanguage}") String contentLang(String subject); default String getLanguage() { return ...; }
2.19.5. サーバー上のヘッダーの伝搬
org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory
のインスタンスが有効であれば、受信ヘッダーの送信要求に一括転送できます。デフォルトのインスタンス org.eclipse.microprofile.rest.client.ext.DefaultClientHeadersFactoryImpl
は、コンマ区切りの設定プロパティー org.eclipse.microprofile.rest.client.propagateHeaders
に一覧表示される着信ヘッダーで設定されるマップを返します。ClientHeadersFactory
インターフェイスをインスタンス化するルールは次のとおりです。
-
JAX-RS リクエストのコンテキストで呼び出される
ClientHeadersFactory
インスタンス は、@Context
アノテーションが付けられたフィールドおよびメソッドの挿入をサポートする必要があります。 -
CDI によって管理される
ClientHeadersFactory
インスタンス は、適切な CDI 管理インスタンスを使用する必要があります。@Inject
インジェクションもサポートする必要があります。
org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory
インターフェイスは以下のように定義されます。
public interface ClientHeadersFactory { /** * Updates the HTTP headers to send to the remote service. Note that providers * on the outbound processing chain could further update the headers. * * @param incomingHeaders - the map of headers from the inbound JAX-RS request. This will * be an empty map if the associated client interface is not part of a JAX-RS request. * @param clientOutgoingHeaders - the read-only map of header parameters specified on the * client interface. * @return a map of HTTP headers to merge with the clientOutgoingHeaders to be sent to * the remote service. */ MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders, MultivaluedMap<String, String> clientOutgoingHeaders); }
ClientHeadersFactory
インターフェイスの詳細は、ClientHeadersFactory Javadoc を参照してください。
2.19.6. ResponseExceptionMapper
org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper
クラスは、JAX-RS で定義される javax.ws.rs.ext.ExceptionMapper
クラスのクライアント側のクラスです。つまり、ExceptionMapper.toResponse ()
メソッドは、サーバー側の処理中に出力された Exception
クラスを Response
に変えるため、ResponseExceptionMapper.toThrowable()
メソッドは HTTP エラーステータスとともにクライアント側で受信された Response クラスを Exception
クラスに変換します。
ResponseExceptionMapper
クラスは、プログラム上または宣言的に登録できます。登録済みの ResponseExceptionMapper
クラスがない場合、デフォルトの ResponseExceptionMapper
クラスは、ステータス >= 400
の応答を WebApplicationException
クラスにマップします。
2.19.7. CDI の統合
MicroProfile REST クライアントでは、CDI bean として管理されるインターフェイスに @RegisterRestClient
クラスを付ける必要があります。例を以下に示します。
@Path("resource") @RegisterProvider(MyClientResponseFilter.class) public static class TestResourceImpl { @Inject TestDataBase db; @Path("test/{path}") @Consumes("text/plain") @Produces("text/html") @POST public String test(@PathParam("path") String path, @QueryParam("query") String query, String entity) { return db.getByName(query); } } @Path("database") @RegisterRestClient public interface TestDataBase { @Path("") @POST public String getByName(String name); }
ここで、MicroProfile REST クライアント実装は TestDataBase
クラスサービスのクライアントを作成し、TestResourceImpl
クラスによるアクセスを容易にします。ただし、TestDataBase
クラス実装へのパスに関する情報は含まれません。この情報は、オプションの @Reg- isterProvider
パラメーター baseUri
で提供されます。
@Path("database") @RegisterRestClient(baseUri="https://localhost:8080/webapp") public interface TestDataBase { @Path("") @POST public String getByName(String name); }
これは、https://localhost:8080/webapp で TestDataBase
実装にアクセスできることを示しています。以下のシステム変数を外部に提供することもできます。
<fully qualified name of TestDataBase>/mp-rest/url=<URL>
たとえば、以下は、https://localhost:8080/webapp で com.bluemonkeydiamond.TestDatabase
クラスの実装にアクセスできることを示しています。
com.bluemonkeydiamond.TestDatabase/mp-rest/url=https://localhost:8080/webapp
2.20. CompletionStage タイプのサポート
JAX-RS 2.1 仕様は、@Suspended
アノテーションを使用する代わりに CompletionStage
を返すことで非同期リソースメソッドの宣言をサポートします。
リソースのメソッドがサブスクライブしている CompletionStage
を返すと常に、リクエストは一時停止されます。このリクエストは、 CompletionStage
が以下の場合にのみ再開されます。
- 値に解決され、メソッドの戻り値として処理される。
- エラーケースとして処理され、例外はリソースメソッドによって発生したかのように処理される。
以下は、CompletionStage
を使用した非同期処理の例です。
public class SimpleResource { @GET @Path("basic") @Produces("text/plain") public CompletionStage<Response> getBasic() throws Exception { final CompletableFuture<Response> response = new CompletableFuture<>(); Thread t = new Thread() { @Override public void run() { try { Response jaxrs = Response.ok("basic").type(MediaType.TEXT_PLAIN).build(); response.complete(jaxrs); } catch (Exception e) { response.completeExceptionally(e); } } }; t.start(); return response; } }
2.21. 非同期リクエスト処理および反応的戻り値の型の RESTEasy サポートの拡張
RESTEasy サポートの拡張はテクノロジープレビューとしてのみ提供されます。テクノロジープレビューの機能は、Red Hat の本番環境のサービスレベルアグリーメント (SLA) ではサポートされず、機能的に完全ではないことがあるため、Red Hat は本番環境での使用は推奨しません。テクノロジープレビューの機能は、最新の製品機能をいち早く提供して、開発段階で機能のテストを行いフィードバックを提供していただくことを目的としています。
テクノロジープレビュー機能のサポート範囲については、Red Hat カスタマーポータルの テクノロジープレビュー機能のサポート範囲 を参照してください。
2.21.1. プラグ可能なリアクティブタイプ
JAX-RS 2.1 は、さまざまなリアクティブライブラリーをサポートする拡張性があります。RESTEasy のオプションのモジュール resteasy-rxJava2
は、以下のリアクティブタイプをサポートします。
-
IO.reactivex.Single
: 最も大きな値を保持するためCompletionStage
に類似しています。 -
io.reactivex.Flowable
:io.reactivex.Publisher
を実装します。 -
io.reactivex.Observable
:Flowable
に似ていますが、バックプレッシャーに対応していないことを除き、ロードを制御するサブスクライバーの機能は、Subscription.request()
を呼び出すことでプロデューサーから受信します。
Resteasy-RxJava2
をインポートする場合は、サーバー側でリソースメソッドからこれらのリアクティブタイプを返し、クライアント側でそれを受け取ります。
Resteasy-RxJava2
モジュールは、クライアント側で Singles、Observables、および Flowables にそれぞれアクセスするための以下のクラスをサポートします。
-
org.jboss.resteasy.rxjava2.SingleRxInvoker
-
org.jboss.resteasy.rxjava2.FlowableRxInvoker
-
org.jboss.resteasy.rxjava2.ObservableRxInvoker
2.21.2. 追加のリアクティブクラスの拡張
RESTEasy は、追加のリアクティブクラスの拡張をサポートするフレームワークを実装します。サーバー側では、リソースメソッドが CompletionStage
タイプを返すと、RESTEasy は org.jboss.resteasy.core.AsyncResponseConsumer.CompletionStageResponseConsumer
クラスを使用してそれにサブスクライブします。CompletionStage
が完了すると、CompletionStageResponseConsumer.accept()
が呼び出され、結果がクライアントに返されます。
CompletionStage
のサポートは RESTEasy に組み込まれています。Single
などのクラスにこのサポートを拡張するには、Single
を CompletionStage
に変換するメカニズムを提供します。resteasy-rxjava2
モジュールでは、org.jboss.resteasy.spi.AsyncResponseProvider<Single<?>>
インターフェイスを実装する org.jboss.resteasy.rxjava2.SingleProvider
がこのメカニズムを提供します。
public interface AsyncResponseProvider<T> { public CompletionStage toCompletionStage(T asyncResponse); }
SingleProvider
では、RESTEasy は Single
を取得し CompletionStage
に変換して CompletionStageResponseConsumer
を使用し、Single
の最終的な値を処理できます。同様に、リソースメソッドが Flowable
などのストリーミングの反応的なクラスを返す場合、RESTEasy はそのクラスをサブスクライブし、データ要素のストリームを受け取り、クライアントに送信します。AsyncResponseConsumer
には複数のサポートクラスがあり、それぞれは異なるストリーミングモードを実装します。
たとえば、AsyncResponseConsumer.AsyncGeneralStreamingSseResponseConsumer
は一般的なストリーミングおよび SSE ストリーミングを処理します。サブスクリプションは、org.reactivestreams.Publisher.subscribe()
を呼び出すことで行われるため、たとえば Flowable
を Publisher
に変えるメカニズが必要です。つまり、org.jboss.resteasy.spi.AsyncStreamProvider<Flowable>
の実装が呼び出されます。これは、AsyncStreamProvider
を定義します。
public interface AsyncStreamProvider<T> { public Publisher toAsyncStream(T asyncResponse); }
resteasy-rxjava2
モジュールでは、org.jboss.resteasy.FlowableProvider
は、Flowable
のメカニズムを提供します。
つまり、サーバー側では、ストリームの AsyncStreamProvider
インターフェイスの @Provider
アノテーションを宣言するか、単一値の AsyncResponseProvider
インターフェイスを宣言して、他の反応的なタイプのサポートを追加できます。これらのインターフェイスには、ストリームの場合には、新しいリアクティブタイプを Publisher
に、単一の値の場合には CompletionStage
に変換する単一のメソッドがあります。
クライアント側では、JAX-RS 2.1 でリアクティブクラスのサポート要件が 2 つ課せられています。
-
javax.ws.rs.client.CompletionStageRxInvoker
のインターフェイスの実装としてのCompletionStage
のサポート。 - 実装するプロバイダーの登録をサポートしていることによる拡張性
public interface RxInvokerProvider<T extends RxInvoker> { public boolean isProviderFor(Class<T> clazz); public T getRxInvoker(SyncInvoker syncInvoker, ExecutorService executorService); }
RxInvokerProvider が登録されれば javax.ws.rs.client.Invocation.Builder
メソッドを呼び出して RxInvoker をリクエストできます。
public <T extends RxInvoker> T rx(Class<T> clazz);
RxInvoker を使用して、適切な反応的なクラスを返す呼び出しを作成できます。例を以下に示します。
FlowableRxInvoker invoker = client.target(generateURL("/get/string")).request().rx(FlowableRxInvoker.class); Flowable<String> flowable = (Flowable<String>) invoker.get();
RESTEasy は、RxInvokers
の実装に部分的なサポートを提供します。たとえば、上記の SingleProvider
は org.jboss.resteasy.spi.AsyncClientResponseProvider<Single<?>>
も実装します。ここでは AsyncClientResponseProvider
は以下のように定義されます。
public interface AsyncClientResponseProvider<T> { public T fromCompletionStage(CompletionStage<?> completionStage); }
2.21.3. Reactive Clients API
RESTEasy は RxInvoker
という新型のインボーカーと、CompletionStageRxInvoker
というこの型のデフォルト実装を定義します。CompletionStageRxInvoker
は、Java 8 のインターフェイス CompletionStage
を実装します。このインターフェイスは、非同期計算の管理専用のメソッドを多数宣言します。
2.21.4. 非同期フィルター
特定のリソースが利用可能になるまでフィルターの実行を一時停止する必要がある場合は、非同期フィルターに変換できます。リクエストを非同期にしても、リソースメソッド宣言や追加のフィルター宣言を変更する必要はありません。
フィルターの実行を非同期にするには、以下をキャストする必要があります。
-
事前およびポスト要求フィルターの
SuspendableContainerRequestContext
へのContainerRequestContext
。 -
応答フィルターの
SuspendableContainerResponseContext
へのContainerResponseContext
。
これらのコンテキストオブジェクトは、suspend()
メソッドを呼び出して、現在のフィルターの実行を非同期にすることができます。非同期の場合には、フィルターチェーンは中断され、以下のメソッドのいずれかがコンテキストオブジェクトで呼び出された後にのみ再開されます。
-
abortWith(Response)
: フィルターチェーンを終了し、指定の Response をクライアントに返します。これは ContainerRequestFilter にのみ適用されます。 -
resume()
: 次のフィルターを呼び出して、フィルターチェーンの実行を切り替えます。 -
resume(Throwable)
: 指定された例外を出力して、フィルターチェーンの実行を中止します。これは、フィルターが同期され、指定の例外が発生したかのように動作します。
2.21.5. プロキシー
プロキシーは、汎用 JAX-RS 呼び出しをアプリケーション固有のインターフェイス呼び出しに置き換える、直感的なプログラミングスタイルをサポートする RESTEasy 拡張です。プロキシーフレームワークは CompletionStage
と RxJava2 タイプの両方、Single
、Observable
、Flowable
を含めるように拡張されています。以下の例は、RESTEasy プロキシーがどのように機能するかを表しています。
例 1:
@Path("") public interface RxCompletionStageResource { @GET @Path("get/string") @Produces(MediaType.TEXT_PLAIN) public CompletionStage<String> getString(); } @Path("") public class RxCompletionStageResourceImpl { @GET @Path("get/string") @Produces(MediaType.TEXT_PLAIN) public CompletionStage<String> getString() { .... } } public class RxCompletionStageProxyTest { private static ResteasyClient client; private static RxCompletionStageResource proxy; static { client = new ResteasyClientBuilder().build(); proxy = client.target(generateURL("/")).proxy(RxCompletionStageResource.class); } @Test public void testGet() throws Exception { CompletionStage<String> completionStage = proxy.getString(); Assert.assertEquals("x", completionStage.toCompletableFuture().get()); } }
例 2:
public interface Rx2FlowableResource { @GET @Path("get/string") @Produces(MediaType.TEXT_PLAIN) @Stream public Flowable<String> getFlowable(); } @Path("") public class Rx2FlowableResourceImpl { @GET @Path("get/string") @Produces(MediaType.TEXT_PLAIN) @Stream public Flowable<String> getFlowable() { ... } } public class Rx2FlowableProxyTest { private static ResteasyClient client; private static Rx2FlowableResource proxy; static { client = new ResteasyClientBuilder().build(); proxy = client.target(generateURL("/")).proxy(Rx2FlowableResource.class); } @Test public void testGet() throws Exception { Flowable<String> flowable = proxy.getFlowable(); flowable.subscribe( (String o) -> stringList.add(o), (Throwable t) -> errors.incrementAndGet(), () -> latch.countDown()); boolean waitResult = latch.await(30, TimeUnit.SECONDS); Assert.assertTrue("Waiting for event to be delivered has timed out.", waitResult); Assert.assertEquals(0, errors.get()); Assert.assertEquals(xStringList, stringList); } }
第3章 JAX-WS Web サービスの開発
XML ベースの Web サービス (JAX-WS) 向けの Java API は、Web サービスにアクセスして公開するために使用されるクラスや、AIX と Java 間のマッピングを定義します。JBossWS は最新の JAX-WS specification を実装し、ユーザーはベンダーに依存しない Web サービス使用のニーズを参照できます。JAX-WS の Jakarta EE は Jakarta XML Web Services Specification 2.3 仕様 です。
3.1. JAX-WS ツールの使用
以下の JAX-WS コマンドラインツールは JBoss EAP ディストリビューションに含まれています。これらのツールは、サーバー および クライアント側 の開発にさまざまな方法で使用できます。
コマンド | 説明 |
---|---|
JAX-WS の移植可能なアーティファクトを生成し、抽象的なコントラクトを提供します。ボトムアップ開発に使用します。 | |
抽象的なコントラクト (WSDL や Schema ファイル) を使用し、サーバーとクライアントの両方に対してアーティファクトを生成します。トップダウンおよびクライアント開発に使用されます。 |
これらのツールの使用方法の詳細は、JAX-WS Tools を参照してください。
3.1.1. サーバー側開発ストラテジー
サーバー側で Web サービスエンドポイントを開発する場合、ボトムアップ開発 として知られる Java コードや、トップダウン開発というサービスを定義する WSDL から開始するオプションを利用できます。これが新しいサービスで、既存のコントラクトがない場合、ボトムアップアプローチが最速のルートになります。サービスを稼働させるためには、クラスにアノテーションをいくつか追加する必要があります。ただし、すでに定義されているコントラクトでサービスを開発する場合は、ツールによるアノテーション付きコードの生成を可能にするため、トップダウンアプローチを使用した方がはるかに簡単です。
ボトムアップのユースケース:
- 既存の EJB3 Bean を Web サービスとして公開
- 新規サービスの提供と、コントラクトが生成されるようにする。
トップダウンのユースケース:
- 既存の Web サービスの実装を置き換え、古いクライアントとの互換性を失うことはできません。
- サードパーティーが指定したコントラクトに準拠するサービスを公開している (たとえば、定義済みプロトコルを使用して返送するベンダー)。
- 事前に開発した XML Schema と WSDL に準拠するサービスを作成します。
wsprovide を使用したダウンアップグレードストラテジー
ボトムアップストラテジーでは、サービスの Java コードを開発し、JAX-WS アノテーションを使用してアノテーションを付ける必要があります。これらのアノテーションを使用して、サービスに生成されるコントラクトをカスタマイズできます。XML ベースの Web Services の Jakarta EE は Jakarta XML Web Services Specification 2.3 です。たとえば、操作名を任意のものにマッピングするように変更できます。ただし、すべてのアノテーションには適切なデフォルトがあるため、@WebService
アノテーションのみが必要になります。
これは、単一クラスを作成するのと同じように簡単に実行できます。
package echo; @javax.jws.WebService public class Echo { public String echo(String input) { return input; } }
デプロイメントはこのクラスを使用して構築でき、JBossWS にデプロイするために必要な唯一の Java コードです。ラッパークラスと呼ばれるその他の Java アーティファクトは、デプロイ時に生成されます。
wsprovide
ツールの主な目的は、移植可能な AX-WS アーティファクトを生成することです。さらに、サービスの WSDL ファイルを提供するのに使用できます。これは、-w
オプションを使用して wsprovide
を呼び出すことで取得できます。
$ javac -d . Echo.java $ EAP_HOME/bin/wsprovide.sh --classpath=. -w echo.Echo
アクセスメントを検査すると、EchoService
という名前のサービスが表示されます。
<wsdl:service name="EchoService"> <wsdl:port name="EchoPort" binding="tns:EchoServiceSoapBinding"> <soap:address location="http://localhost:9090/EchoPort"/> </wsdl:port> </wsdl:service>
予想通りに、このサービスは、echo
という操作を定義します。
<wsdl:portType name="Echo"> <wsdl:operation name="echo"> <wsdl:input name="echo" message="tns:echo"> </wsdl:input> <wsdl:output name="echoResponse" message="tns:echoResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType>
デプロイ時に、このツールを実行する必要はありません。これは、移植可能なアーティファクトまたはサービスの抽象的なコントラクトを生成する場合にのみ必要です。
デプロイメントの POJO エンドポイントは、簡単な web.xml
ファイルに作成できます。
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>Echo</servlet-name> <servlet-class>echo.Echo</servlet-class> </servlet> <servlet-mapping> <servlet-name>Echo</servlet-name> <url-pattern>/Echo</url-pattern> </servlet-mapping> </web-app>
web.xml
と単一の Java クラスを使用して WAR を作成できます。
$ mkdir -p WEB-INF/classes $ cp -rp echo WEB-INF/classes/ $ cp web.xml WEB-INF $ jar cvf echo.war WEB-INF added manifest adding: WEB-INF/(in = 0) (out= 0)(stored 0%) adding: WEB-INF/classes/(in = 0) (out= 0)(stored 0%) adding: WEB-INF/classes/echo/(in = 0) (out= 0)(stored 0%) adding: WEB-INF/classes/echo/Echo.class(in = 340) (out= 247)(deflated 27%) adding: WEB-INF/web.xml(in = 576) (out= 271)(deflated 52%)
WAR は JBoss EAP にデプロイできます。これにより、内部的に wsprovide
が呼び出され、その WSDL が生成されます。デプロイメントに成功し、デフォルト設定を使用している場合は、管理コンソールで利用できるようにする必要があります。
移植可能な JAX-WS デプロイメントでは、先に生成されたラッパークラスをデプロイメントに追加することができます。
wsconsume を使用したトップダウンストラテジー
トップダウンの開発ストラテジーは、サービスの抽象的なコントラクトで始まります。これには WSDL ファイルおよびゼロ以上のスキーマファイルが含まれます。wsconsume
ツールを使用してこのコントラクトを消費し、アノテーション付きの Java クラスと、それを定義するオプションのプションのソースを生成します。
wsconsume
には、Unix システムのシンボリックリンクに問題がある可能性があります。
ボトムアップの例から WSDL ファイルを使用して、このサービスに準拠する新しい Java 実装を生成することができます。-k
オプションは wsconsume
に渡され、Java クラスのみを提供する代わりに、生成された Java ソースファイルを保存します。
$ EAP_HOME/bin/wsconsume.sh -k EchoService.wsdl
以下の表には、生成される各ファイルの目的をまとめています。
ファイル | 目的 |
---|---|
Echo.java | サービスエンドポイントインターフェイス |
EchoResponse.java | 応答メッセージのラッパー Bean |
EchoService.java | JAX-WS クライアントによってのみ使用されます。 |
Echo_Type.java | 要求メッセージのラッパー Bean |
ObjectFactory.java | JAXB XML レジストリー |
package-info.java | JAXB パッケージのアノテーションのホルダー |
サービスエンドポイントインターフェイスを調べると、ボトムアップの例で手動で記述したクラスよりも明示的なアノテーションが表示されますが、これらは同じコントラクトに対して評価されます。
@WebService(targetNamespace = "http://echo/", name = "Echo") @XmlSeeAlso({ObjectFactory.class}) public interface Echo { @WebMethod @RequestWrapper(localName = "echo", targetNamespace = "http://echo/", className = "echo.Echo_Type") @ResponseWrapper(localName = "echoResponse", targetNamespace = "http://echo/", className = "echo.EchoResponse") @WebResult(name = "return", targetNamespace = "") public java.lang.String echo( @WebParam(name = "arg0", targetNamespace = "") java.lang.String arg0 ); }
パッケージ化以外の不明な部分は実装クラスで、上記のインターフェイスを使用して記述できるようになりました。
package echo; @javax.jws.WebService(endpointInterface="echo.Echo") public class EchoImpl implements Echo { public String echo(String arg0) { return arg0; } }
3.1.2. クライアント側の開発ストラテジー
クライアント側の詳細を確認する前に、Web サービスの中心となる切り離す概念を理解することが重要です。Web サービスは、この方法で使用することもできますが、内部 RPC には適していません。CORBA や RMI などのより優れた技術があります。Web サービスは、特に相互運用可能な粒度の細かい対応のために設計されました。Web サービスの対話に参加しているいかなるパーティも、特定のオペレーティングシステム上で実行したり、特定のプログラミング言語で記述されているといった保証はありません。そのため、クライアントおよびサーバー実装は明確に分離することが重要です。一般的なのは、抽象的なコントラクト定義のみです。何らかの理由でソフトウェアがこの原理に準拠しない場合は、Web サービスを使用しないことをお勧めします。上記の理由により、クライアント開発に推奨される方法は、クライアントが同じサーバーで実行されている場合でもトップダウンアプローチに従うことです。
wsconsume を使用したトップダウンストラテジー
このセクションは、サーバー側のトップダウンセクションのプロセスを繰り返しますが、デプロイ済みの WSDL を使用します。これは、デプロイ時に計算される soap:address
の正しい値を取得します (下記参照)。この値は、必要に応じて手動で編集できますが、正しいパスを指定する必要があります。
例: デプロイ済みコンポーネントの soap:address
<wsdl:service name="EchoService"> <wsdl:port name="EchoPort" binding="tns:EchoServiceSoapBinding"> <soap:address location="http://localhost.localdomain:8080/echo/Echo"/> </wsdl:port> </wsdl:service>
wsconsume
を使用して、デプロイされた WSDL の Java クラスを生成します。
$ EAP_HOME/bin/wsconsume.sh -k http://localhost:8080/echo/Echo?wsdl
EchoService.java
クラスが、WSDL が取得元の場所を保存する方法に注目してください。
@WebServiceClient(name = "EchoService", wsdlLocation = "http://localhost:8080/echo/Echo?wsdl", targetNamespace = "http://echo/") public class EchoService extends Service { public final static URL WSDL_LOCATION; public final static QName SERVICE = new QName("http://echo/", "EchoService"); public final static QName EchoPort = new QName("http://echo/", "EchoPort"); ... @WebEndpoint(name = "EchoPort") public Echo getEchoPort() { return super.getPort(EchoPort, Echo.class); } @WebEndpoint(name = "EchoPort") public Echo getEchoPort(WebServiceFeature... features) { return super.getPort(EchoPort, Echo.class, features); } }
ご覧のとおり、この生成されたクラスは JAX-WS、javax.xml.ws.Service
の主なクライアントエントリーポイントを拡張します。Service
を直接使用することはできますが、設定情報を提供するため、これははるかに簡単です。サービスエンドポイントインターフェイスのインスタンスを返す getEchoPort()
メソッドに注意してください。返されたインターフェイスでメソッドを呼び出すと、Web サービスの操作をすべて呼び出すことができます。
実稼働環境のアプリケーションで、リモート WSDL URL を参照しないでください。これにより、Service
オブジェクトをインスタンス化するたびにネットワーク I/O が発生します。代わりに、保存されたローカルコピーでツールを使用するか、コンストラクターの URL バージョンを使用して、新しい WSDL の場所を提供します。
クライアントの書き込みとコンパイル:
import echo.*; public class EchoClient { public static void main(String args[]) { if (args.length != 1) { System.err.println("usage: EchoClient <message>"); System.exit(1); } EchoService service = new EchoService(); Echo echo = service.getEchoPort(); System.out.println("Server said: " + echo.echo(args0)); } }
以下のように ENDPOINT_ADDRESS_PROPERTY
を設定すると、ランタイム時に操作のエンドポイントアドレスを変更できます。
EchoService service = new EchoService(); Echo echo = service.getEchoPort(); /* Set NEW Endpoint Location */ String endpointURL = "http://NEW_ENDPOINT_URL"; BindingProvider bp = (BindingProvider)echo; bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL); System.out.println("Server said: " + echo.echo(args0));
3.2. JAX-WS Web サービスエンドポイント
3.2.1. JAX-WS Web サービスエンドポイント
JAX-WS Web サービスエンドポイントは、Web サービスのサーバーコンポーネントです。クライアントおよび他の Web サービスは、Simple Object Access Protocol (SOAP) と呼ばれる XML 言語を使用して HTTP プロトコルを介して通信します。エンドポイント自体は JBoss EAP コンテナーにデプロイされます。
WSDL 記述子は、以下のいずれかの方法で作成できます。
- WSDL 記述子の手動書き込み
- WSDL 記述子を自動的に作成する JAX-WS アノテーションを使用します。これは、WSDL 記述子を作成するための最も一般的な方法です。
エンドポイント実装 Bean には JAX-WS アノテーションが付けられ、サーバーにデプロイされます。サーバーは、クライアント消費のために、抽象的なコントラクトを自動的に生成し、パブリッシュします。すべてのマーシャリングおよびアンマーシャリングは、Java Architecture for XML Binding (JAXB) サービスに委任されます。
エンドポイント自体は Plain Old Java Object (POJO) または Jakarta EE Web アプリケーションである場合があります。EJB3 ステートレスセッション Bean を使用してエンドポイントを公開することもできます。Web アーカイブ (WAR) ファイルにパッケージ化されます。Java Service Endpoint (JSE) と呼ばれるエンドポイントをパッケージ化するための仕様は JSR-181 で定義されています。JAX-WS API 仕様の Jakarta EE は Jakarta Web Services Metadata Specification 2.1 です。
例: POJO エンドポイント
@WebService @SOAPBinding(style = SOAPBinding.Style.RPC) public class JSEBean { @WebMethod public String echo(String input) { ... } }
例: Web サービスエンドポイント
<web-app ...> <servlet> <servlet-name>TestService</servlet-name> <servlet-class>org.jboss.quickstarts.ws.jaxws.samples.jsr181pojo.JSEBean01</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestService</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
以下の EJB3 ステートレスセッション Bean は、リモートインターフェイスとエンドポイント操作に同じメソッドを公開します。
@Stateless @Remote(EJB3RemoteInterface.class) @WebService @SOAPBinding(style = SOAPBinding.Style.RPC) public class EJB3Bean implements EJB3RemoteInterface { @WebMethod public String echo(String input) { ... } }
サービスエンドポイントインターフェイス
JAX-WS サービスは通常 Java サービスエンドポイントインターフェイス (SEI) を実装します。これは、WSDL ポートタイプから、直接またはアノテーションを使用してマッピングされる可能性があります。この SEI は、Java オブジェクトとその XML 表現の詳細を隠す高度な抽象化を提供します。
エンドポイントプロバイダーインターフェイス
JAX-WS サービスは、XML メッセージレベルで動作する必要があることがあります。エンドポイント Provider
インターフェイスは、この機能を実装する Web サービスにこの機能を提供します。
エンドポイントの使用およびアクセス
Web サービスをデプロイしたら、WSDL を消費してアプリケーションの基盤となるコンポーネントのスタブを作成できます。その後、アプリケーションはエンドポイントにアクセスしてその作業を実行できます。
3.2.2. JAX-WS Web サービスエンドポイントの開発およびデプロイ
JAX-WS サービスエンドポイントは、JAX-WS クライアントからのリクエストに応答し、それ自体の WSDL 定義を公開するサーバー側のコンポーネントです。
JAX-WS エンドポイントアプリケーションの開発方法の実例については、JBoss EAP に同梱される以下のクイックスタートを参照してください。
- jaxws-addressing
- jaxws-ejb
- jaxws-pojo
- jaxws-retail
- wsat-simple
- wsba-coordinator-completion-simple
- wsba-participant-completion-simple
開発要件
Web サービスは、JAX-WS API および JSR 181: Web Services Metadata for the Java Platform 仕様の要件を満たす必要があります。有効な実装は以下の要件を満たします。Web Services Metadata の Jakarta EE に相当するものは、Jakarta Web Services Metadata Specification 2.1 仕様 に含まれています。
-
これには
javax.jws.WebService
アノテーションが含まれます。 - すべてのメソッドパラメーターと戻り値のタイプは、JSR 222: JavaTM Architecture for XML Binding (JAXB) 2.0 仕様と互換性があります。XML Binding の Jakarta EE に相当するものは、Jakarta XML Binding Specification 2.3 仕様 を参照してください。
これらの要件を満たす Web サービス実装の例を以下に示します。
例: Web サービス実装
package org.jboss.quickstarts.ws.jaxws.samples.retail.profile; import javax.ejb.Stateless; import javax.jws.WebService; import javax.jws.WebMethod; import javax.jws.soap.SOAPBinding; @Stateless @WebService( name = "ProfileMgmt", targetNamespace = "http://org.jboss.ws/samples/retail/profile", serviceName = "ProfileMgmtService") @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) public class ProfileMgmtBean { @WebMethod public DiscountResponse getCustomerDiscount(DiscountRequest request) { DiscountResponse dResponse = new DiscountResponse(); dResponse.setCustomer(request.getCustomer()); dResponse.setDiscount(10.00); return dResponse; } }
以下は、ProfileMgmtBean
bean によって DiscountRequest
の例です。アノテーションは詳細度のために含まれています。通常、これは JAXB デフォルトは妥当な設定であり、指定する必要はありません。
例: DiscountRequest クラス
package org.jboss.test.ws.jaxws.samples.retail.profile; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; import org.jboss.test.ws.jaxws.samples.retail.Customer; @XmlAccessorType(XmlAccessType.FIELD) @XmlType( name = "discountRequest", namespace="http://org.jboss.ws/samples/retail/profile", propOrder = { "customer" } ) public class DiscountRequest { protected Customer customer; public DiscountRequest() { } public DiscountRequest(Customer customer) { this.customer = customer; } public Customer getCustomer() { return customer; } public void setCustomer(Customer value) { this.customer = value; } }
デプロイメントのパッケージ化
実装クラスは JAR デプロイメントでラップされます。デプロイメントに必要なメタデータは、実装クラスとサービスエンドポイントインターフェイス上のアノテーションから取得されます。管理 CLI または管理コンソールを使用して JAR をデプロイでき、HTTP エンドポイントが自動的に作成されます。
以下のリストは、EJB Web サービスの JAR デプロイメントの構造の例を示しています。
$ jar -tf jaxws-samples-retail.jar org/jboss/test/ws/jaxws/samples/retail/profile/DiscountRequest.class org/jboss/test/ws/jaxws/samples/retail/profile/DiscountResponse.class org/jboss/test/ws/jaxws/samples/retail/profile/ObjectFactory.class org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmt.class org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtBean.class org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtService.class org/jboss/test/ws/jaxws/samples/retail/profile/package-info.class
3.3. JAX-WS Web サービスクライアント
3.3.1. JAX-WS Web サービスの消費とアクセス
Web サービスエンドポイントの作成後、手動で、あるいは JAX-WS アノテーションを使用して、その WSDL にアクセスできます。XML ベースの Web Services の Jakarta EE は Jakarta Enterprise Web Services 1.4 仕様 です。これは、Web サービスと通信する基本的なクライアントアプリケーションを作成するのに使用できます。パブリッシュされた WSDL から Java コードを生成するプロセスは、Web サービスを使用すると呼ばれます。これは以下の場合に発生します。
クライアントアーティファクトの作成
クライアントアーティファクトを作成する前に、WSDL コントラクトを作成する必要があります。以下の WSDL コントラクトは、本セクションの残りの部分に記載の例に使用します。
以下の例は、ProfileMgmtService.wsdl
ファイルにこの WSDL コントラクトがあることを前提としています。
<definitions name='ProfileMgmtService' targetNamespace='http://org.jboss.ws/samples/retail/profile' xmlns='http://schemas.xmlsoap.org/wsdl/' xmlns:ns1='http://org.jboss.ws/samples/retail' xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/' xmlns:tns='http://org.jboss.ws/samples/retail/profile' xmlns:xsd='http://www.w3.org/2001/XMLSchema'> <types> <xs:schema targetNamespace='http://org.jboss.ws/samples/retail' version='1.0' xmlns:xs='http://www.w3.org/2001/XMLSchema'> <xs:complexType name='customer'> <xs:sequence> <xs:element minOccurs='0' name='creditCardDetails' type='xs:string'/> <xs:element minOccurs='0' name='firstName' type='xs:string'/> <xs:element minOccurs='0' name='lastName' type='xs:string'/> </xs:sequence> </xs:complexType> </xs:schema> <xs:schema targetNamespace='http://org.jboss.ws/samples/retail/profile' version='1.0' xmlns:ns1='http://org.jboss.ws/samples/retail' xmlns:tns='http://org.jboss.ws/samples/retail/profile' xmlns:xs='http://www.w3.org/2001/XMLSchema'> <xs:import namespace='http://org.jboss.ws/samples/retail'/> <xs:element name='getCustomerDiscount' nillable='true' type='tns:discountRequest'/> <xs:element name='getCustomerDiscountResponse' nillable='true' type='tns:discountResponse'/> <xs:complexType name='discountRequest'> <xs:sequence> <xs:element minOccurs='0' name='customer' type='ns1:customer'/> </xs:sequence> </xs:complexType> <xs:complexType name='discountResponse'> <xs:sequence> <xs:element minOccurs='0' name='customer' type='ns1:customer'/> <xs:element name='discount' type='xs:double'/> </xs:sequence> </xs:complexType> </xs:schema> </types> <message name='ProfileMgmt_getCustomerDiscount'> <part element='tns:getCustomerDiscount' name='getCustomerDiscount'/> </message> <message name='ProfileMgmt_getCustomerDiscountResponse'> <part element='tns:getCustomerDiscountResponse' name='getCustomerDiscountResponse'/> </message> <portType name='ProfileMgmt'> <operation name='getCustomerDiscount' parameterOrder='getCustomerDiscount'> <input message='tns:ProfileMgmt_getCustomerDiscount'/> <output message='tns:ProfileMgmt_getCustomerDiscountResponse'/> </operation> </portType> <binding name='ProfileMgmtBinding' type='tns:ProfileMgmt'> <soap:binding style='document' transport='http://schemas.xmlsoap.org/soap/http'/> <operation name='getCustomerDiscount'> <soap:operation soapAction=''/> <input> <soap:body use='literal'/> </input> <output> <soap:body use='literal'/> </output> </operation> </binding> <service name='ProfileMgmtService'> <port binding='tns:ProfileMgmtBinding' name='ProfileMgmtPort'> <!-- service address will be rewritten to actual one when WSDL is requested from running server --> <soap:address location='http://SERVER:PORT/jaxws-retail/ProfileMgmtBean'/> </port> </service> </definitions>
JAX-WS アノテーションを使用して Web サービスのエンドポイントを作成すると、WSDL コントラクトは自動的に生成されます。その URL のみが必要になります。この URL を見つけるには、Runtime に移動して、該当するサーバーを選択し、Webservicesを 選択してエンドポイントを選択します。
Wsconsume.sh
ツールまたは wsconsume.bat
ツールは、抽象的なコントラクト (WSDL) を消費し、アノテーション付きの Java クラスと、これを定義するオプションのソースを生成するために使用されます。このファイルは EAP_HOME/bin/
ディレクトリーにあります。
$ ./wsconsume.sh --help WSConsumeTask is a cmd line tool that generates portable JAX-WS artifacts from a WSDL file. usage: org.jboss.ws.tools.cmd.WSConsume [options] <wsdl-url> options: -h, --help Show this help message -b, --binding=<file> One or more JAX-WS or Java XML Binding files -k, --keep Keep/Generate Java source -c --catalog=<file> Oasis XML Catalog file for entity resolution -p --package=<name> The target package for generated source -w --wsdlLocation=<loc> Value to use for @WebService.wsdlLocation -o, --output=<directory> The directory to put generated artifacts -s, --source=<directory> The directory to put Java source -t, --target=<2.0|2.1|2.2> The JAX-WS target -q, --quiet Be somewhat more quiet -v, --verbose Show full exception stack traces -l, --load-consumer Load the consumer and exit (debug utility) -e, --extension Enable SOAP 1.2 binding extension -a, --additionalHeaders Enable processing of implicit SOAP headers -n, --nocompile Do not compile generated sources
以下のコマンドは、出力に一覧表示されている .java
ファイルを ProfileMgmtService.wsdl
ファイルから生成します。ソースは、-p
スイッチで指定されたパッケージのディレクトリー構造を使用します。
[user@host bin]$ wsconsume.sh -k -p org.jboss.test.ws.jaxws.samples.retail.profile ProfileMgmtService.wsdl output/org/jboss/test/ws/jaxws/samples/retail/profile/Customer.java output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountRequest.java output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountResponse.java output/org/jboss/test/ws/jaxws/samples/retail/profile/ObjectFactory.java output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmt.java output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtService.java output/org/jboss/test/ws/jaxws/samples/retail/profile/package-info.java output/org/jboss/test/ws/jaxws/samples/retail/profile/Customer.class output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountRequest.class output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountResponse.class output/org/jboss/test/ws/jaxws/samples/retail/profile/ObjectFactory.class output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmt.class output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtService.class output/org/jboss/test/ws/jaxws/samples/retail/profile/package-info.class
.java
ソースファイルとコンパイルされた .class
ファイルはどちらも、コマンドを実行するディレクトリー内の output/
ディレクトリーに生成されます。
ファイル | 説明 |
---|---|
| サービスエンドポイントインターフェイス。 |
| カスタムデータタイプ。 |
| カスタムデータタイプ。 |
| JAXB XML レジストリー。 |
| JAXB パッケージのアノテーション。 |
| サービスファクトリー。 |
wsconsume
コマンドは、すべてのカスタムデータタイプ (JAXB アノテーションが付けられたクラス)、サービスエンドポイントインターフェイス、およびサービスファクトリークラスを生成します。これらのアーティファクトは、Web サービスクライアントの実装をビルドするために使用されます。
サービススタブの設定
Web サービスクライアントはサービススタブを使用して、リモート Web サービス呼び出しの詳細を抽象化します。クライアントアプリケーションの場合、Web サービスの呼び出しは他のビジネスコンポーネントの呼び出しと同様になります。この場合、サービスエンドポイントインターフェイスはビジネスインターフェイスとして機能し、サービスファクトリークラスはサービススタブとしての構築に使用されません。
以下の例では、まずは WSDL の場所とサービス名を使用してサービスファクトリーを作成します。次に、 wsconsume
により作成されたサービスエンドポイントインターフェイスを使用してサービススタブを構築します。最後に、スタブは他のビジネスインターフェイスと同様に使用できます。
JBoss EAP 管理コンソールでは、エンドポイントの WSDL URL を確認できます。この URL を見つけるには、Runtime に移動して、該当するサーバーを選択し、Webservicesを 選択してエンドポイントを選択します。
import javax.xml.ws.Service; [...] Service service = Service.create( new URL("http://example.org/service?wsdl"), new QName("MyService") ); ProfileMgmt profileMgmt = service.getPort(ProfileMgmt.class); // Use the service stub in your application
3.3.2. JAX-WS クライアントアプリケーションの開発
クライアントは Java Enterprise Edition 7 コンテナーにデプロイされた JAX-WS エンドポイントと通信し、そこから要求を行います。以下のクラス、メソッド、およびその他の実装の詳細は、JBoss EAP に含まれる Javadocs バンドルの関連セクションを参照してください。
概要
Service
は、WSDL サービスを表す抽象化です。WSDL サービスは、一連の関連ポートです。それぞれには、特定のプロトコルとエンドポイントアドレスに結合したポートタイプが含まれます。
通常、サービスの生成は、残りのコンポーネントのスタブが、既存の WSDL コントラクトから生成されると行われます。WSDL コントラクトは、デプロイされたエンドポイントの WSDL URL 経由で利用できます。または、EAP_HOME/bin/
ディレクトリーの wsprovide
ツールを使用してエンドポイントソースから作成できます。
このタイプの使用は、静的なユースケースと呼ばれます。この場合、コンポーネントのスタブのいずれかとして作成された Service
クラスのインスタンスを作成します。
Service.create
メソッドを使用してサービスを手動で作成することもできます。これは、動的なユースケースと呼ばれます。
使用方法
静的ユースケース
JAX-WS クライアントの静的ユースケースでは、すでに WSDL コントラクトが存在することを前提としています。これは外部ツールで生成されるか、JAX-WS エンドポイントの作成時に適切な JAX-WS アノテーションを使用して生成される可能性があります。
コンポーネントのスタブを生成するには、EAP_HOME/bin
に含まれる wsconsume
ツールを使用します。このツールは、コンテナー URL またはファイルをパラメーターとして取り、ディレクトリーツリーで構造化された複数のファイルを生成します。Service
を表すソースおよびクラスファイルはそれぞれ _Service.java
と _Service.class
です。
生成された実装クラスには、引数のないパブリックコンストラクターと、2 つの引数を持つコンストラクターがあります。これら 2 つの引数はそれぞれ WSDL の場所 (java.net.URL
) とサービス名 (javax.xml.namespace.QName
) を表します。
引数なしのコンストラクターは最もよく使用されます。この場合、WSDL の場所とサービス名は WSDL にあるものです。これらは、生成されたクラスをデコレートする @WebServiceClient
アノテーションから暗黙的に設定されます。
@WebServiceClient(name="StockQuoteService", targetNamespace="http://example.com/stocks", wsdlLocation="http://example.com/stocks.wsdl") public class StockQuoteService extends javax.xml.ws.Service { public StockQuoteService() { super(new URL("http://example.com/stocks.wsdl"), new QName("http://example.com/stocks", "StockQuoteService")); } public StockQuoteService(String wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } ... }
サービスからポートを取得する方法と、ポートでの操作の呼び出し方法に関する詳細は、Dynamic Proxy を参照してください。XML ペイロードを直接使用するか、SOAP メッセージ全体の XML 表現を使用する方法は、Dispatch を参照してください。
動的ユースケース
動的の場合、スタブは自動的に生成されません。代わりに、Web サービスクライアントは Service.create
メソッドを使用して Service
インスタンスを作成します。以下のコードはこのプロセスを示しています。
URL wsdlLocation = new URL("http://example.org/my.wsdl"); QName serviceName = new QName("http://example.org/sample", "MyService"); Service service = Service.create(wsdlLocation, serviceName);
ハンドラー解決
JAX-WS は、ハンドラーと呼ばれるメッセージ処理モジュールに柔軟なプラグインフレームワークを提供します。これらのハンドラーは JAX-WS ランタイムシステムの機能を拡張します。Service
インスタンスは、getHandlerResolver
と setHandlerResolver
メソッドのペア経由で HandlerResolver
へのアクセスを提供し、サービス別、ポート別、またはプロトコル別のバインディングベースでハンドラーのセットを設定することができます。
Service
インスタンスでプロキシーまたは Dispatch
インスタンスを作成すると、現在サービスに登録されているハンドラーリゾルバーが必要なハンドラーチェーンを作成します。Service
インスタンスに設定されたハンドラーリゾルバーへの変更は、以前に作成されたプロキシーまたは Dispatch
インスタンスでのハンドラーには影響を与えません。
Executor
Service
インスタンスは、java.util.concurrent.Executor
を使用して設定できます。Executor
は、アプリケーションによってリクエストされた非同期コールバックを呼び出します。Service
の setExecutor
および getExecutor
メソッドは、サービスに設定された Executor
を変更および取得できます。
動的プロキシー
動的プロキシーは、Service
で提供される getPort
メソッドのいずれかを使用するクライアントプロキシーのインスタンスです。portName
は、サービスが使用する WSDL ポートの名前を指定します。serviceEndpointInterface
は、作成された動的プロキシーインスタンスでサポートされるサービスエンドポイントインターフェイスを指定します。
public <T> T getPort(QName portName, Class<T> serviceEndpointInterface) public <T> T getPort(Class<T> serviceEndpointInterface)
Service Endpoint Interface は通常 wsconsume
ツールを使用して生成されます。これは、WSDL を解析し、そこから Java クラスを作成します。
ポートを返す、タイプ指定されたメソッドも提供されます。これらのメソッドは、SEI を実装する動的プロキシーも返します。以下の例を参照してください。
@WebServiceClient(name = "TestEndpointService", targetNamespace = "http://org.jboss.ws/wsref", wsdlLocation = "http://localhost.localdomain:8080/jaxws-samples-webserviceref?wsdl") public class TestEndpointService extends Service { ... public TestEndpointService(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } @WebEndpoint(name = "TestEndpointPort") public TestEndpoint getTestEndpointPort() { return (TestEndpoint)super.getPort(TESTENDPOINTPORT, TestEndpoint.class); } }
@WebServiceRef
@WebServiceRef
アノテーションは Web サービスへの参照を宣言します。これは、JSR 250 で定義された javax.annotation.Resource
アノテーションによって示されるリソースパターンに従います。これらのアノテーションの Jakarta EE と同等の Jakarta EE は、Jakarta Annotations 1.3 仕様 に含まれています。
-
これを使用して、生成される
Service
クラスであるタイプのリファンレスを定義できます。この例では、type および value 要素はそれぞれ、生成されたService
クラスターイプを参照します。さらに、リファンレンスタイプがアノテーションが適用されるフィールドまたはメソッド宣言によって推定される場合、type および value 要素にはデフォルト値のObject.class
が設定されることがあります。これは必須ではありません。タイプを推定できない場合、type 要素がデフォルト以外の値を持つ必要があります。 これは、タイプが SEI のリファンレスを定義するのに使用できます。この場合、リファレンスのタイプが annotated フィールドまたは method 宣言から推測される場合、type 要素はデフォルト値で存在することがあります (ただし、必須ではありません)。ただし、value 要素は常に存在し、
javax.xml.ws.Service
ファイルのサブタイプである、生成されたサービスクラスターイプを参照する必要があります。wsdlLocation
要素が存在する場合は、参照される生成サービスクラスの@WebService
アノテーションで指定された WSDL の場所情報を上書きします。public class EJB3Client implements EJB3Remote { @WebServiceRef public TestEndpointService service4; @WebServiceRef public TestEndpoint port3; }
Dispatch
XML Web サービスは、Java EE コンテナーおよびすべてのクライアントにデプロイされる、エンドポイント間の通信に XML メッセージを使用します。XML メッセージは、Simple Object Access Protocol (SOAP) と呼ばれる XML 言語を使用します。JAX-WS API は、エンドポイントとクライアントのメカニズムを提供し、各エンドポイントが SOAP メッセージを送受信できるようにします。マーシャリングは、Java オブジェクトを SOAP XML メッセージに変換するプロセスです。マーシャリング解除とは、SOAP XML メッセージを Java オブジェクトに変換するプロセスのことです。
変換の結果ではなく、raw SOAP メッセージ自体にアクセスする必要がある場合があります。Dispatch
クラスはこの機能を提供します。Dispatch
は、以下の定数のいずれかによって識別される、2 つの使用モードのいずれかで動作します。
-
javax.xml.ws.Service.Mode.MESSAGE
: このモードは、クライアントアプリケーションがプロトコル固有のメッセージ構造で直接動作するようにします。SOAP プロトコルバインディングを使用すると、クライアントアプリケーションは SOAP メッセージで直接機能します。 -
javax.xml.ws.Service.Mode.PAYLOAD
: このモードでは、クライアントがペイロード自体と動作します。たとえば、SOAP プロトコルバインディングと使用すると、クライアントアプリケーションは SOAP メッセージ全体ではなく SOAP ボディーのコンテンツで動作します。
Dispatch
は低レベルの API で、クライアントはメッセージまたはペイロードを XML として設定する必要があります。これは、個別のプロトコルの標準と、メッセージまたはペイロード構造の詳細な知識とメッセージに忠実に従った状態を意味します。Dispatch
は、メッセージの入出力または、任意のタイプのメッセージペイロードに対応した汎用クラスです。
Service service = Service.create(wsdlURL, serviceName); Dispatch dispatch = service.createDispatch(portName, StreamSource.class, Mode.PAYLOAD); String payload = "<ns1:ping xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>"; dispatch.invokeOneWay(new StreamSource(new StringReader(payload))); payload = "<ns1:feedback xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>"; Source retObj = (Source)dispatch.invoke(new StreamSource(new StringReader(payload)));
非同期呼び出し
BindingProvider
インターフェイスは、クライアントが使用できるプロトコルバインディングを提供するコンポーネントを表します。これはプロキシーによって実装され、Dispatch
インターフェイスによって拡張されます。
BindingProvider
インスタンスは非同期操作機能を提供する可能性があります。非同期操作の呼び出しは、呼び出し時に BindingProvider
インスタンスから切り離されます。操作が完了しても、応答コンテキストは更新されません。代わりに、Response
インターフェイスを使用して個別の応答コンテキストを利用できます。
public void testInvokeAsync() throws Exception { URL wsdlURL = new URL("http://" + getServerHost() + ":8080/jaxws-samples-asynchronous?wsdl"); QName serviceName = new QName(targetNS, "TestEndpointService"); Service service = Service.create(wsdlURL, serviceName); TestEndpoint port = service.getPort(TestEndpoint.class); Response response = port.echoAsync("Async"); // access future String retStr = (String) response.get(); assertEquals("Async", retStr); }
@oneway 呼び出し
@oneway
アノテーションは、指定の web メソッドが入力メッセージを取得し、出力メッセージを返しないことを示します。通常、@Oneway
メソッドは、ビジネスメソッドの実行前に、制御のスレッドを呼び出しアプリケーションに戻します。
@WebService (name="PingEndpoint") @SOAPBinding(style = SOAPBinding.Style.RPC) public class PingEndpointImpl { private static String feedback; @WebMethod @Oneway public void ping() { log.info("ping"); feedback = "ok"; } @WebMethod public String feedback() { log.info("feedback"); return feedback; } }
タイムアウトの設定
2 種類のプロパティーが HTTP 接続のタイムアウトの動作と、メッセージの受信を待機しているクライアントのタイムアウトを制御します。最初のプロパティーは、javax.xml.ws.client.connectionTimeout
で、次のプロパティーは、javax.xml.ws.client.receiveTimeout
です。それぞれはミリ秒単位で示され、正しい構文を以下に示します。
public void testConfigureTimeout() throws Exception { //Set timeout until a connection is established ((BindingProvider)port).getRequestContext().put("javax.xml.ws.client.connectionTimeout", "6000"); //Set timeout until the response is received ((BindingProvider) port).getRequestContext().put("javax.xml.ws.client.receiveTimeout", "1000"); port.echo("testTimeout"); }
3.4. Web Services サブシステムの設定
JBossWS コンポーネントは Web サービスエンドポイントの処理を処理し、webservices
サブシステムを介して JBoss EAP に提供されます。サブシステムは、パブリッシュされたエンドポイントアドレスおよびエンドポイントハンドラーチェーンの設定をサポートします。
デフォルトの webservices
サブシステムはサーバーのドメインおよびスタンドアロン設定ファイルで提供されます。これには、事前定義されたエンドポイントおよびクライアント設定が複数含まれます。
<subsystem xmlns="urn:jboss:domain:webservices:2.0"> <wsdl-host>${jboss.bind.address:127.0.0.1}</wsdl-host> <endpoint-config name="Standard-Endpoint-Config"/> <endpoint-config name="Recording-Endpoint-Config"> <pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM"> <handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/> </pre-handler-chain> </endpoint-config> <client-config name="Standard-Client-Config"/> </subsystem>
3.4.1. エンドポイント設定
JBossWS では、追加の設定データを事前定義し、エンドポイント実装に関連付けることができます。事前定義されたエンドポイント設定は JAX-WS クライアントおよび JAX-WS エンドポイントの設定に使用できます。エンドポイント設定には JAX-WS ハンドラーとキー/値のプロパティー宣言を含めることができます。この機能は、ハンドラーを Web サービスのエンドポイントに追加し、JBossWS および Apache CXF 内部を制御する key/value プロパティーを設定する便利な方法を提供します。
webservices
サブシステムを使用すると、エンドポイント設定データの名前付きセットを定義できます。各エンドポイント設定にはサブシステム内で一意な名前を付ける必要があります。その後、org.jboss.ws.api.annotation.EndpointConfig
アノテーションを使用して、デプロイされたプリケーションの JAX-WS 実装にエンドポイント設定を割り当てることができます。エンドポイント設定の割り当ての詳細は、Assigning a Configuration を参照してください。
デフォルトの JBoss EAP 設定には、事前定義されたエンドポイント設定があります。
-
Standard-Endpoint-Config
は、明示的に割り当てられたエンドポイント設定のないエンドポイントに使用されるエンドポイント設定です。 -
Recording-Endpoint-Config
は、録画ハンドラーを含むカスタムエンドポイント設定の例になります。
エンドポイント設定の追加
管理 CLI を使用して新しいエンドポイント設定を追加できます。
/subsystem=webservices/endpoint-config=My-Endpoint-Config:add
エンドポイント設定の設定
管理 CLI を使用してエンドポイント設定の key/value プロパティー宣言を追加できます。
/subsystem=webservices/endpoint-config=Standard-Endpoint-Config/property=PROPERTY_NAME:add(value=PROPERTY_VALUE)
これらのエンドポイント設定の handler chains および handlers を設定できます。
エンドポイント設定の削除
管理 CLI を使用してエンドポイント設定を削除できます。
/subsystem=webservices/endpoint-config=My-Endpoint-Config:remove
3.4.2. ハンドラーチェーン
各エンドポイント設定は PRE
または POST
ハンドラーチェーンに関連付けることができます。各ハンドラーチェーンには JAX-WS 準拠のハンドラーが含まれ、メッセージに対して追加の処理を実行できます。アウトバウンドメッセージの場合、PRE
ハンドラーチェーンハンドラーは、@HandlerChain
アノテーションなどの標準の JAX-WS を使用してエンドポイントにアタッチされたハンドラーの前に実行されます。POST
ハンドラーチェーンハンドラーは、通常のエンドポイントハンドラーの後に実行されます。受信メッセージには、逆が適用されます。
サーバーアウトバウンドメッセージ
Endpoint --> PRE Handlers --> Endpoint Handlers --> POST Handlers --> ... --> Client
サーバーインバウンドメッセージ
Client --> ... --> POST Handlers --> Endpoint Handlers --> PRE Handlers --> Endpoint
ハンドラーチェーンの追加
以下の管理 CLI コマンドを使用すると、POST
ハンドラーチェーンをエンドポイント設定に追加できます。
/subsystem=webservices/endpoint-config=My-Endpoint-Config/post-handler-chain=my-post-handler-chain:add
以下の管理 CLI コマンドを使用すると、PRE
ハンドラーチェーンをエンドポイント設定に追加できます。
/subsystem=webservices/endpoint-config=My-Endpoint-Config/pre-handler-chain=my-pre-handler-chain:add
ハンドラーチェーンの設定
protocol-bindings
属性を使用して、ハンドラーチェーンの開始をトリガーするプロトコルを設定します。
/subsystem=webservices/endpoint-config=My-Endpoint-Config/post-handler-chain=my-post-handler-chain:write-attribute(name=protocol-bindings,value=##SOAP11_HTTP)
ハンドラーチェーンのハンドラーの設定に関する詳細は、ハンドラー のセクションを参照してください。
ハンドラーチェーンの削除
管理 CLI を使用してハンドラーチェーンを削除できます。
/subsystem=webservices/endpoint-config=My-Endpoint-Config/post-handler-chain=my-post-handler-chain:remove
3.4.3. ハンドラー
JAX-WS ハンドラーがハンドラーチェーンに追加され、ハンドラークラスの完全修飾名を指定します。エンドポイントがデプロイされると、参照デプロイメントごとにそのクラスのインスタンスが作成されます。org.jboss.as.webservices.server.integration
モジュールのデプロイメントクラスローダーまたはクラスローダーのいずれかがハンドラークラスをロードできる必要があります。
利用可能なハンドラーの一覧は、Handler Java ドキュメントを参照してください。
ハンドラーの追加
以下の管理 CLI コマンドを使用するとハンドラーをハンドラーチェーンに追加できます。ハンドラーのクラス名を指定する必要があります。
/subsystem=webservices/endpoint-config=My-Endpoint-Config/post-handler-chain=my-post-handler-chain/handler=my-handler:add(class="com.arjuna.webservices11.wsarj.handler.InstanceIdentifierInHandler")
ハンドラーの設定
管理 CLI を使用してハンドラーのクラスを更新できます。
/subsystem=webservices/endpoint-config=My-Endpoint-Config/post-handler-chain=my-post-handler-chain/handler=my-handler:add(class="org.jboss.ws.common.invocation.RecordingServerHandler")
ハンドラーの削除
管理 CLI を使用してハンドラーを削除できます。
/subsystem=webservices/endpoint-config=My-Endpoint-Config/post-handler-chain=my-post-handler-chain/handler=my-handler:
3.4.4. 公開されたエンドポイントアドレス
WSDL コントラクトで公開されるエンドポイントの <soap:address>
要素の書き換えがサポートされます。この機能は、各エンドポイントのクライアントに公開されるサーバーアドレスを制御するのに役立ちます。
以下の表は、この機能に設定できる属性を表しています。
名前 | 説明 |
---|---|
modify-wsdl-address | このブール値は、アドレスのリライト機能を有効化・無効化します。
|
wsdl-host |
|
wsdl-path-rewrite-rule |
この文字列は、サーバーから公開される各 |
wsdl-port | SOAP アドレスの書き換えに使用される HTTP ポートを明示的に定義するには、このプロパティーを設定します。それ以外の場合は、インストールされた HTTP コネクターのリストをクエリーして HTTP ポートを特定します。 |
wsdl-secure-port | SOAP アドレスの書き換えに使用される HTTPS ポートを明示的に定義するには、このプロパティーを設定します。そうでない場合は、インストールされた HTTPS コネクターのリストをクエリーして HTTPS ポートを特定します。 |
wsdl-uri-scheme |
このプロパティーは、 |
管理 CLI を使用してこれらの属性を更新できます。例を以下に示します。
/subsystem=webservices:write-attribute(name=wsdl-uri-scheme, value=https)
3.4.5. ランタイム情報の表示
各 Web サービスエンドポイントは、エンドポイント実装を提供するデプロイメントで公開されます。各エンドポイントはデプロイメントリソースとしてクエリーできます。各 Web サービスのエンドポイントは、Web コンテキストと WSDL URL を指定します。管理 CLI または管理コンソールを使用してこのランタイム情報にアクセスできます。
以下の管理 CLI コマンドは、jaxws-samples-handlerchain.war
デプロイメントからの TestService
エンドポイントの詳細を表示します。
/deployment="jaxws-samples-handlerchain.war"/subsystem=webservices/endpoint="jaxws-samples-handlerchain:TestService":read-resource(include-runtime=true) { "outcome" => "success", "result" => { "average-processing-time" => 23L, "class" => "org.jboss.test.ws.jaxws.samples.handlerchain.EndpointImpl", "context" => "jaxws-samples-handlerchain", "fault-count" => 0L, "max-processing-time" => 23L, "min-processing-time" => 23L, "name" => "TestService", "request-count" => 1L, "response-count" => 1L, "total-processing-time" => 23L, "type" => "JAXWS_JSE", "wsdl-url" => "http://localhost:8080/jaxws-samples-handlerchain?wsdl" } }
read-resource
操作で include-runtime=true
フラグを使用すると、ランタイム統計が返されます。ただし、Web サービスエンドポイントの統計収集はデフォルトで無効にされています。以下の管理 CLI コマンドを使用すると、Web サービスエンドポイントの統計を有効にできます。
/subsystem=webservices:write-attribute(name=statistics-enabled,value=true)
管理コンソールの Runtime タブから、Web サービスエンドポイントのランタイム情報を表示することもできます。そのためには、該当するサーバーを選択し、Webservices を選択してエンドポイントを選択します。
3.5. クライアントおよびエンドポイント設定の割り当て
クライアントおよびエンドポイント設定は以下の方法で割り当てることができます。
- クライアントに対するアノテーション、エンドポイント、または API プログラムによる使用による明示的な割り当て。
- デフォルト記述子からの設定の自動割り当て。
- コンテナーからの設定の自動割り当て。
3.5.1. 明示的な設定の割り当て
明示的な設定割り当ては、指定の設定に従ってセットアップされる必要のあるエンドポイントまたはクライアントを開発者が事前に認識するためのものです。この設定は、アプリケーションデプロイメントに含まれる記述子か、webservices
サブシステムに含まれます。
3.5.1.1. 設定デプロイメント記述子
JAX-WS クライアントおよびエンドポイント実装を含むことができる Java EE アーカイブには、事前定義のクライアントおよびエンドポイント設定宣言が含まれる可能性があります。すべてのエンドポイントまたは特定のアーカイブのクライアント設定定義は、単一のデプロイメント記述子ファイルに提供する必要があります。これは、EAP_HOME/docs/schema/jbossws-jaxws-config_4_0.xsd
にあるスキーマの実装である必要があります。デプロイメント記述子ファイルには、多くのエンドポイントまたはクライアント設定を定義できます。各設定には、アプリケーションがデプロイされるサーバー内で一意の名前が必要です。設定名は、アプリケーション外のエンドポイントまたはクライアント実装で参照することはできません。
例: 2 つのエンドポイント設定を持つ記述子
<?xml version="1.0" encoding="UTF-8"?> <jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd"> <endpoint-config> <config-name>org.jboss.test.ws.jaxws.jbws3282.Endpoint4Impl</config-name> <pre-handler-chains> <javaee:handler-chain> <javaee:handler> <javaee:handler-name>Log Handler</javaee:handler-name> <javaee:handler-class>org.jboss.test.ws.jaxws.jbws3282.LogHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </pre-handler-chains> <post-handler-chains> <javaee:handler-chain> <javaee:handler> <javaee:handler-name>Routing Handler</javaee:handler-name> <javaee:handler-class>org.jboss.test.ws.jaxws.jbws3282.RoutingHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </post-handler-chains> </endpoint-config> <endpoint-config> <config-name>EP6-config</config-name> <post-handler-chains> <javaee:handler-chain> <javaee:handler> <javaee:handler-name>Authorization Handler</javaee:handler-name> <javaee:handler-class>org.jboss.test.ws.jaxws.jbws3282.AuthorizationHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </post-handler-chains> </endpoint-config> </jaxws-config>
同様に、クライアント設定は記述子に指定できます。これは前述のスキーマを実装しています。
<?xml version="1.0" encoding="UTF-8"?> <jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd"> <client-config> <config-name>Custom Client Config</config-name> <pre-handler-chains> <javaee:handler-chain> <javaee:handler> <javaee:handler-name>Routing Handler</javaee:handler-name> <javaee:handler-class>org.jboss.test.ws.jaxws.clientConfig.RoutingHandler</javaee:handler-class> </javaee:handler> <javaee:handler> <javaee:handler-name>Custom Handler</javaee:handler-name> <javaee:handler-class>org.jboss.test.ws.jaxws.clientConfig.CustomHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </pre-handler-chains> </client-config> <client-config> <config-name>Another Client Config</config-name> <post-handler-chains> <javaee:handler-chain> <javaee:handler> <javaee:handler-name>Routing Handler</javaee:handler-name> <javaee:handler-class>org.jboss.test.ws.jaxws.clientConfig.RoutingHandler</javaee:handler-class> </javaee:handler> </javaee:handler-chain> </post-handler-chains> </client-config> </jaxws-config>
3.5.1.2. アプリケーションサーバー設定
JBoss EAP では、webservices
サブシステムで JBossWS クライアントおよびサーバーの事前定義設定を宣言できます。そのため、サーバー全体のハンドラーを宣言して、特定の設定に割り当てられた各エンドポイントまたはクライアントのチェーンに追加することができます。
標準設定
同じ JBoss EAP インスタンスおよびエンドポイントで実行されているクライアントには、デフォルトで標準設定が割り当てられます。異なる設定が設定されていない限り、デフォルトが使用されます。これにより、管理者はクライアントおよびエンドポイント設定のデフォルトのハンドラーチェーンを調整することができます。webservices
サブシステムで使用されるデフォルトのクライアントおよびエンドポイント設定の名前は、Standard-Client-Config
および Standard-Endpoint-Config
です。
ハンドラーのクラスロード
サーバー全体のハンドラーを設定する場合、ハンドラークラスは各 ws デプロイメントクラスローダーで利用可能である必要があります。そのため、特定の事前に定義された設定を使用するデプロイメントで適切なモジュール依存関係を指定する必要がある場合があります。適切なモジュール依存関係がデプロイメントで指定されるようにする方法として、org.jboss.ws.spi
などのデプロイメントに対する依存関係として自動的に設定されるいずれかのモジュールのハンドラークラスを含むモジュールに依存関係を追加します。
設定例:
例: デフォルトのサブシステム設定
<subsystem xmlns="urn:jboss:domain:webservices:2.0"> <!-- ... --> <endpoint-config name="Standard-Endpoint-Config"/> <endpoint-config name="Recording-Endpoint-Config"> <pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM"> <handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/> </pre-handler-chain> </endpoint-config> <client-config name="Standard-Client-Config"/> </subsystem>
デプロイメント固有の ws-security エンドポイントセットアップの設定ファイル:
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd"> <endpoint-config> <config-name>Custom WS-Security Endpoint</config-name> <property> <property-name>ws-security.signature.properties</property-name> <property-value>bob.properties</property-value> </property> <property> <property-name>ws-security.encryption.properties</property-name> <property-value>bob.properties</property-value> </property> <property> <property-name>ws-security.signature.username</property-name> <property-value>bob</property-value> </property> <property> <property-name>ws-security.encryption.username</property-name> <property-value>alice</property-value> </property> <property> <property-name>ws-security.callback-handler</property-name> <property-value>org.jboss.test.ws.jaxws.samples.wsse.policy.basic.KeystorePasswordCallback</property-value> </property> </endpoint-config> </jaxws-config>
JBoss EAP のデフォルト設定は以下で SOAP メッセージの schema-validation にデフォルト設定変更されました。
<subsystem xmlns="urn:jboss:domain:webservices:2.0"> <!-- ... --> <endpoint-config name="Standard-Endpoint-Config"> <property name="schema-validation-enabled" value="true"/> </endpoint-config> <!-- ... --> <client-config name="Standard-Client-Config"> <property name="schema-validation-enabled" value="true"/> </client-config> </subsystem>
3.5.1.3. EndpointConfig アノテーション
任意のアプリケーションで設定が利用可能になると org.jboss.ws.api.annotation.EndpointConfig
アノテーションを使用してエンドポイント設定を JAX-WS エンドポイント実装に割り当てます。webservices
サブシステムに定義された設定を割り当てる場合は、設定名のみを指定する必要があります。アプリケーションに定義されている設定を割り当てる場合は、デプロイメント記述子への相対パスと設定名を指定する必要があります。
例: EndpointConfig アノテーション
@EndpointConfig(configFile = "WEB-INF/my-endpoint-config.xml", configName = "Custom WS-Security Endpoint") public class ServiceImpl implements ServiceIface { public String sayHello() { return "Secure Hello World!"; } }
3.5.1.4. JAX-WS 機能
org.jboss.ws.api.configuration.ClientConfigFeature
を使用することで JBossWS が提供する JAX-WS Feature 拡張の設定を行うこともできます。
import org.jboss.ws.api.configuration.ClientConfigFeature; Service service = Service.create(wsdlURL, serviceName); Endpoint port = service.getPort(Endpoint.class, new ClientConfigFeature("META-INF/my-client-config.xml", "Custom Client Config")); port.echo("Kermit");
また、true
を ClientConfigFeature
コンストラクターに渡すことで、指定された設定からプロパティーを設定することもできます。
Endpoint port = service.getPort(Endpoint.class, new ClientConfigFeature("META-INF/my-client-config.xml", "Custom Client Config"), true);
JBossWS は、現在のスレッドコンテキストクラスローダーを使用してリソースとして解決した後、指定された設定ファイルを解析します。EAP_HOME/docs/schema/jbossws-jaxws-config_4_0.xsd
スキーマは記述子の内容を定義し、jbossws-spi
アーティファクトに含まれます。
設定ファイルの null
を渡すと、利用可能な場合に、現在のコンテナー設定から設定が読み込まれます。
Endpoint port = service.getPort(Endpoint.class, new ClientConfigFeature(null, "Container Custom Client Config"));
3.5.1.5. API での明示的なセットアップ
または、JBossWS API にはクライアントのビルド時に設定の割り当てに使用できるファシリティークラスが含まれています。
ハンドラー
JAX-WS ハンドラーは、以下のようにクライアント設定から読み込まれます。
import org.jboss.ws.api.configuration.ClientConfigUtil; import org.jboss.ws.api.configuration.ClientConfigurer; Service service = Service.create(wsdlURL, serviceName); Endpoint port = service.getPort(Endpoint.class); BindingProvider bp = (BindingProvider)port; ClientConfigurer configurer = ClientConfigUtil.resolveClientConfigurer(); configurer.setConfigHandlers(bp, "META-INF/my-client-config.xml", "Custom Client Config"); port.echo("Kermit");
ClientConfigUtil
ユーティリティークラスを使用してハンドラーを設定することもできます。
ClientConfigUtil.setConfigHandlers(bp, "META-INF/my-client-config.xml", "Custom Client Config");
デフォルトの ClientConfigurer
実装は、現在のスレッドコンテキストクラスローダーを使用してリソースとして解決した後、指定された設定ファイルを解析します。EAP_HOME/docs/schema/jbossws-jaxws-config_4_0.xsd
スキーマは記述子の内容を定義し、jbossws-spi
アーティファクトに含まれます。
設定ファイルの null
を渡すと、利用可能な場合に、現在のコンテナー設定から設定が読み込まれます。
ClientConfigurer configurer = ClientConfigUtil.resolveClientConfigurer(); configurer.setConfigHandlers(bp, null, "Container Custom Client Config");
プロパティー
同様に、プロパティーは以下のようにクライアント設定から読み込まれます。
import org.jboss.ws.api.configuration.ClientConfigUtil; import org.jboss.ws.api.configuration.ClientConfigurer; Service service = Service.create(wsdlURL, serviceName); Endpoint port = service.getPort(Endpoint.class); ClientConfigUtil.setConfigProperties(port, "META-INF/my-client-config.xml", "Custom Client Config"); port.echo("Kermit");
ClientConfigUtil
ユーティリティークラスを使用してプロパティーを設定することもできます。
ClientConfigurer configurer = ClientConfigUtil.resolveClientConfigurer(); configurer.setConfigProperties(port, "META-INF/my-client-config.xml", "Custom Client Config");
デフォルトの ClientConfigurer
実装は、現在のスレッドコンテキストクラスローダーを使用してリソースとして解決した後、指定された設定ファイルを解析します。EAP_HOME/docs/schema/jbossws-jaxws-config_4_0.xsd
スキーマは記述子の内容を定義し、jbossws-spi
アーティファクトに含まれます。
設定ファイルの null
を渡すと、利用可能な場合に、現在のコンテナー設定から設定が読み込まれます。
ClientConfigurer configurer = ClientConfigUtil.resolveClientConfigurer(); configurer.setConfigProperties(port, null, "Container Custom Client Config");
3.5.2. デフォルト記述子からの自動設定
アプリケーション開発者は、クライアントおよびエンドポイントの実装に使用する必要がある設定を認識していない場合があります。その他の場合は、コンパイルタイム依存関係であるため、JBossWS API の明示的な使用が許可されない可能性があります。このようなシナリオに対応するため、JBossWS ではデフォルトのクライアント、jaxws-client-config.xml
、およびエンドポイント、jaxws-endpoint-config.xml
、記述子をルートディレクトリー内のアプリケーションに含めることができます。これらは、設定ファイル名が指定されていない場合に設定を取得するために解析されます。
<config-file>WEB-INF/jaxws-endpoint-config.xml</config-file>
設定名が指定されていない場合、JBossWS は以下のようにという名前の設定を自動的に検索します。
- JAX-WS エンドポイントの場合、エンドポイント実装クラスの完全修飾名 (FQN)。
- JAX-WS クライアントの場合、サービスエンドポイントインターフェイスの FQN。
Dispatch
クライアントの自動設定名は選択されません。
たとえば、事前設定されていないエンドポイント実装クラス org.foo.bar.EndpointImpl
により、JBossWS はアプリケーションデプロイメントのルートで jaxws-endpoint-config.xml
記述子内で org.foo.bar.EndpointImpl
という名前の設定を検索します。同様に、クライアント側では、org.foo.bar.Endpoint
インターフェイスを実装するクライアントプロキシーには、jaxws-client-config.xml
記述子の org.foo.bar.Endpoint
という名前の設定からセットアップ読み取りが行われます。
3.5.3. コンテナーからの自動設定割り当て
JBossWS は、明示的な設定が指定されておらず、デフォルトの記述子が利用できないか、関連する設定を含まない場合に、コンテナーから事前定義された設定を取得するようフォールバックします。この動作により、コンテナーはデプロイされたアプリケーションから独立して管理できるため、JAX-WS クライアントおよびエンドポイントの設定に対する追加の制御が管理者に付与されます。
JBossWS は明示的に名前が付けられた設定の webservices
サブシステムにアクセスします。使用されるデフォルト設定名は以下のようになります。
- JAX-WS エンドポイントの場合、エンドポイント実装クラスの完全修飾名。
- JAX-WS クライアントの場合は、サービスエンドポイントインターフェイスの完全修飾名。
Dispatch
クライアントは自動的には設定されません。上記で計算した名前を使用して設定が見つからない場合、Standard-Client-Config
および Standard-Endpoint-Config
設定がクライアントとエンドポイントにそれぞれ使用されます。
3.6. Web サービスアプリケーションのモジュール依存関係の設定
JBoss EAP Web サービスは、org.jboss.as.webservices.*
と org.jboss.ws.*
モジュールを含むモジュールおよびライブラリーのセットとして提供されます。これらのモジュールを変更する必要はありません。
JBoss EAP では、対応するモジュールに依存関係を明示的に設定しない限り、直接 JBossWS 実装クラスを使用することはできません。デプロイメントに追加するモジュール依存関係を宣言します。
JBossWS API は、webservices
サブシステムが利用可能になるたびにデフォルトで利用可能です。これらのモジュールの明示的な依存関係宣言を作成せずに使用できます。
3.6.1. MANIFEST.MF の使用
デプロイメントの依存関係を設定するには、MANIFEST.MF
ファイルに追加します。例を以下に示します。
Manifest-Version: 1.0 Dependencies: org.jboss.ws.cxf.jbossws-cxf-client services export,foo.bar
この MANIFEST.MF
ファイルは、org.jboss.ws.cxf.jbossws-cxf-client
および foo.bar
モジュールの依存関係を宣言します。export
および services
オプションを含む MANIFEST.MF
ファイルで依存関係を宣言する方法は、JBoss EAP Development Guide の Add a Dependency Configuration to MANIFEST.MF を参照してください。
Apache CXF エンドポイントやハンドラーなどのエンドポイントおよびハンドラーでアノテーションを使用する場合は、マニフェストファイルに適切なモジュール依存関係を追加します。この手順を省略すると、アノテーションは選択されず、完全に無視されます。
3.6.1.1. JAXB の使用
インコンテナーを実行しているクライアントまたはエンドポイントで JAXB コンテキストを正常に直接使用するには、JAXB 実装を設定します。たとえば、以下の依存関係を設定します。
Dependencies: com.sun.xml.bind services export
3.6.1.2. Apache CXF の使用
Apache CXF API および実装クラスを使用するには、org.apache.cxf
(API) モジュールまたは org.apache.cxf.impl
(実装) モジュールに依存関係を追加します。例を以下に示します。
Dependencies: org.apache.cxf services
依存関係は、JBossWS のカスタマイズや追加の拡張がない純粋な Apache CXF です。そのため、クライアント側の集約モジュールは、必要なすべての Web サービスの依存関係で利用できます。
3.6.1.3. クライアント側の Web サービスアグリゲーションモジュール
Web サービスの機能すべてを使用する場合は、便利なクライアントモジュールへの依存関係を設定できます。例を以下に示します。
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client services
JBossWS 固有のクラスをロードしてすべての JBossWS 機能を有効にするには、services
オプションが必要です。org.jboss.ws.cxf.jbossws-cxf-client
および org.apache.cxf
モジュールの依存関係を宣言する場合は、services
オプションがほぼ常に必要になります。このオプションは、多くの JBossWS コンポーネントおよび Apache CXF Bus 拡張の接続に使用される Service
API でのクラスのロードに影響します。
3.6.1.4. アノテーションのスキャン
アプリケーションサーバーは、ユーザーデプロイメントで JAX-WS エンドポイントを検出するためにアノテーションインデックスを使用します。web.xml
記述子で参照しているなど、異なるモジュールに属するクラスの Web サービスエンドポイントを宣言する場合は、annotations
タイプ依存関係を使用します。この依存関係がないと、エンドポイントは webservices
サブシステムのアノテーション付きクラスとして表示されないため、無視されます。
Dependencies: my.org annotations
3.6.2. jboss-deployment-structure.xml の使用
状況によっては、MANIFEST.MF
ファイルでモジュールの依存関係を設定する便利なアプローチは機能しない場合があります。たとえば、MANIFEST.MF
ファイルに依存関係を設定しても、指定のモジュール依存関係から特定のリソースをインポートおよびエクスポートしても機能しません。これらのシナリオでは、jboss-deployment-structure.xml
記述子ファイルをデプロイメントに追加し、そのデプロイメントにモジュール依存関係を設定します。
jboss-deployment-structure.xml
の使用に関する詳細は、JBoss EAP Development Guide の jboss-deployment-structure.xml への依存関係設定の追加 を参照してください。
3.7. HTTP タイムアウトの設定
HTTP セッションタイムアウトは、指定した期間内にアクティビティーがないため、HTTP セッションが無効とみなされる期間を定義します。
HTTP セッションのタイムアウトは、以下の場所で優先順に設定できます。
アプリケーション
以下の設定をファイルに追加すると、アプリケーションの
web.xml
設定ファイルで HTTP セッションタイムアウトを定義できます。この値は分単位です。<session-config> <session-timeout>30</session-timeout> </session-config>
WAR ファイルを変更した場合は、アプリケーションを再デプロイします。WAR ファイルを展開する場合、JBoss EAP は自動的にアプリケーションをアンデプロイおよび再デプロイするため、追加のアクションは必要ありません。
サーバー
以下の管理 CLI コマンドを使用すると、
undertow
サブシステムでデフォルトの HTTP セッションタイムアウトを設定できます。この値は分単位です。/subsystem=undertow/servlet-container=default:write-attribute(name=default-session-timeout,value=30)
デフォルト
デフォルトの HTTP セッションタイムアウトは 30 分です。
3.8. JAX-WS Web サービスのセキュア化
WS-Security は、HTTPS などのトランスポートレベルプロトコルを超えたサービスを保護する手段を提供します。WS-Security 標準で定義されたヘッダーなど、多くの標準を使用して以下を実行できます。
- サービス間で認証トークンを渡します。
- メッセージまたはメッセージの一部を暗号化する。
- メッセージに署名します。
- メッセージにタイムスタンプを付けます。
WS-Security は、公開鍵暗号および秘密鍵暗号を使用します。公開鍵暗号により、ユーザーは公開鍵と秘密鍵のペアを持ちます。これらは、高い素数とキー関数を使用して生成されます。
キーは関連するものですが、相互に派生させることはできません。これらの鍵を使用すると、メッセージを暗号化できます。たとえば、Scott が Adam にメッセージを送信する場合は、公開鍵を使用してメッセージを暗号化できます。次に、Adam は秘密鍵を使用してこのメッセージを復号化できます。秘密鍵のある唯一のメッセージであるため、このメッセージは Adam のみが復号化できます。
メッセージは署名することもできます。これにより、メッセージの信頼性を確保できます。Adam が Scott にメッセージを送信し、Scott が Adam からのメッセージであることを確認する必要がある場合、Adam は秘密鍵を使用してそのメッセージを署名できます。Scott は、公開鍵を使用して、メッセージが Adam からのものであることを検証できます。
3.8.1. Web Services Security (WS-Security) の適用
Web サービスは、WS-Security 機能が必要な多くの実際のシナリオをサポートします。これらのシナリオには、X509 証明書による署名および暗号化サポート、ユーザー名トークンによる認証および承認、および WS-SecurityPolicy 仕様でカバーされるすべての WS-Security 設定が含まれます。
その他の WS-* 機能については、WS-Security 機能のコアは Apache CXF エンジンを介して提供されます。さらに、JBossWS 統合では、WS-Security が有効なエンドポイントの設定を簡素化するいくつかの設定拡張が追加されました。
3.8.1.1. Apache CXF WS-Security 実装
Apache CXF は、複数の設定をサポートし、簡単に拡張できる WS-Security モジュールを特長としています。
システムは、低レベルのセキュリティー操作に対して Apache WSS4J に委譲するインターセプターに基づいています。インターセプターは、Spring 設定ファイルを使用するか、Apache CXF クライアント API を使用して直接設定できます。
Apache CXF の最近のバージョンでは、WS-SecurityPolicy のサポートが導入されました。WS-SecurityPolicy は、多くのセキュリティー設定をポリシーを介してサービスコントラクトに移動することを目的としています。これにより、クライアントがほぼ自動的に設定できるようになりました。これにより、ユーザーは必要なインターセプターの設定とインストールを手動で行う必要がなくなります。代わりに、Apache CXF WS-Policy エンジンがこれを行います。
3.8.1.2. WS-Security Policy のサポート
WS-SecurityPolicy は、特定の WSDL コントラクトで公開されるサービスとセキュアに通信するために必要なアクションを説明します。WSDL バインディングと操作は、WS-Policy フラグメントと、サービスと対話するためのセキュリティー要件を参照します。WS-SecurityPolicy 仕様は、暗号化にトランスポート (HTTPS) を使用して、暗号化に非対称および対称鍵などのものを指定できます。これは、暗号化または署名する部分またはヘッダー、その後に暗号化または署名するかどうか、タイムスタンプを含めるかどうか、派生キーを使用するかどうか、その他のキーを使用するかどうかなどを指定します。
ただし、一部の必須設定要素は公開されたエンドポイントコントラクトの一部やパブリックではなく、含まれるため、WS-SecurityPolicy では扱われません。これには、キーストアの場所、ユーザー名とパスワードなどが含まれます。Apache CXF では、Spring XML 記述子を使用するか、クライアント API またはアノテーションのいずれかを使用してこれらの要素を設定できます。
設定プロパティー | 説明 |
---|---|
ws-security.username |
|
ws-security.password |
|
ws-security.callback-handler |
キーストアおよび |
ws-security.signature.properties | 署名キーストアと crypto オブジェクトを設定するための WSS4J プロパティーが含まれるプロパティーファイル/オブジェクト。 |
ws-security.encryption.properties | 暗号キーストアおよび暗号化オブジェクトを設定するための WSS4J プロパティーが含まれるプロパティーファイル/オブジェクト。 |
ws-security.signature.username | 使用される署名キーストアのキーのユーザー名またはエイリアス。指定されていない場合は、プロパティーファイルに設定されたデフォルトのエイリアスが使用されます。これが設定されておらず、キーストアに単一のキーのみが含まれる場合、そのキーが使用されます。 |
ws-security.encryption.username |
使用される暗号キーストアのキーのユーザー名またはエイリアス。指定されていない場合は、プロパティーファイルに設定されたデフォルトのエイリアスが使用されます。これが設定されておらず、キーストアに単一のキーのみが含まれる場合、そのキーが使用されます。Web サービスプロバイダーの場合は、 |
ws-security.signature.crypto |
これは、signature プロパティーを指定する代わりに、完全な WSS4J |
ws-security.encryption.crypto |
これは、暗号化プロパティーを指定する代わりに、完全な WSS4J |
ws-security.enable.streaming | WS-Security メッセージのストリーミング (StAX ベースの) 処理を有効にします。 |
3.8.2. WS-Trust
WS-Trust は、WS-Security の拡張機能を定義する Web サービス仕様です。これは、分散システムにセキュリティーを実装するための一般的なフレームワークです。標準は、クライアントを認証し、さまざまなタイプの認証および承認データを含むトークンを発行できる集中型セキュリティートークンサービス (STS) に基づいています。この仕様は、セキュリティートークンの発行、交換、および検証に使用されるプロトコルを記述します。WS-Trust アーキテクチャーでは、以下の仕様が重要なロールを果たします。
- WS-SecurityPolicy 1.2
- SAML 2.0
- ユーザー名トークンプロファイル
- X.509 トークンプロファイル
- SAML トークンプロファイル
- Kerberos トークンプロファイル
WS-Trust 拡張機能は、複数のドメインにまたがり、セキュリティー鍵の共有を必要とするアプリケーションのニーズに対応します。これは、Web サービスプロバイダーと Web サービスプロバイダー間の信頼関係をブローカーするために、標準ベースの信頼できるサードパーティーの Web サービス (STS) を提供することで行います。このアーキテクチャーは、この情報の一般的な場所を提供することで、認証情報の変更を必要とするサービス更新の一時停止も軽減します。STS は、要求側とプロバイダーの両方がセキュリティートークンを取得し、検証する一般的なアクセスポイントです。
WS-Trust 仕様には、主に以下のコンポーネントがあります。
- セキュリティートークンを発行、更新、および検証するためのセキュリティートークンサービス (STS)。
- セキュリティートークン要求および応答のメッセージ形式。
- 鍵交換のメカニズム。
次のセクションでは、基本的な WS-Trust シナリオについて説明します。高度なシナリオは、Advanced WS-Trust Scenarios を参照してください。
3.8.2.1. シナリオ: 基本的な WS-Trust
ここでは、基本的な WS-Trust シナリオの例を示します。これは、Web サービス要求側 (ws-requester
)、Web サービスプロバイダー (ws-provider
)、およびセキュリティートークンサービス (STS) で設定されます。
ws-provider
では、非対称バインディングを使用して ws-requester
によって提示される SAML 2.0 トークンが必要です。これらの通信要件は、ws-provider
の WSDL で宣言されます。STS では、対称バインディングを使用して WSS UsernameToken 形式の要求に ws-requester
認証情報を提供する必要があります。STS からの応答には SAML 2.0 トークンが含まれています。これらの通信要件は、STS の WSDL で宣言されます。
-
ws-requester
はws-provider
に接続し、その WSDL を消費します。セキュリティートークンの発行者要件を見つけると、ws-requester
は、有効なリクエストを生成するために必要な情報を使用してSTSClient
を作成し、設定します。 -
STSClient
は STS に接続し、その WSDL を使用します。セキュリティーポリシーが検出されます。STSClient
は、適切な認証情報を使用して認証リクエストを作成し、送信します。 - STS はクレデンシャルを検証します。
-
応答として、STS は
ws-requester
が STS で認証したことを証明するセキュリティートークンを発行します。 -
STSClient
は、セキュリティートークンを含むメッセージをws-provider
に提示します。 -
ws-provider
は、トークンが STS によって発行されたことを確認し、ws-requester
が STS で正常に認証されたことを証明します。 -
ws-provider
は要求されたサービスを実行し、結果をws-requester
に返します。
3.8.2.2. Apache CXF サポート
Apache CXF はオープンソースの完全な Web サービスフレームワークです。JBossWS オープンソースプロジェクトは JBoss Web Services (JBossWS) スタックを Apache CXF プロジェクトモジュールと統合し、WS-Trust およびその他の JAX-WS 機能を提供します。この統合により、Apache CXF STS 実装のデプロイメントが容易になります。Apache CXF API は、Web サービスリクエスターとその STS との通信を容易にする STSClient
ユーティリティーも提供します。
3.8.3. セキュリティートークンサービス (STS)
Security Token Service (STS) は WS-Trust 仕様の中核となります。認証および承認の標準ベースのメカニズムです。STS は、トークンの形式、名前空間、または信頼の境界に基づいて、セキュリティートークンの発行、変更、および検証を行うための WS-Trust 仕様のプロトコルの実装です。STS は、Web サービス要求側と Web サービスプロバイダーとの間の信頼関係をブローカー化するために信頼できるサードパーティーとして機能する Web サービスです。これは、要求側とプロバイダーの両方が信頼する共通のアクセスポイントで、相互運用可能なセキュリティートークンを提供します。これにより、要求元とプロバイダー間の直接的な関係が不要になります。STS は認証用の標準ベースのメカニズムであるため、レルム間および異なるプラットフォーム間での相互運用性を確保します。
STS コントラクトは、他のアプリケーションおよびプロセスが対話する方法を定義します。特に、WSDL は WS-Trust ポリシーと WS-Security ポリシーを定義します。このポリシーでは、リクエストャーが STS のエンドポイントと正常に通信するために満たさなければならなりません。Web サービスリクエスターは STS のパッケージを使用し、STSClient
ユーティリティーを使用して、指定されたセキュリティーポリシーに準拠するメッセージリクエストを生成し、STS エンドポイントに送信します。STS は要求を検証し、適切な応答を返します。
3.8.3.1. PicketLink WS-Trust Security Token Service (STS) の設定
Picketlink STS は、Apache CXF Security Token Service 実装の代替を構築するためのオプションを提供します。PicketLink を使用して、Web アプリケーションの SAML SSO を設定することもできます。PicketLink を使用して SAML SSO を設定する方法は、How To Set Up SSO with SAML v2 を参照してください。
PicketLink WS-Trust STS として動作するようにアプリケーションを設定するには、以下の手順を実行する必要があります。
- WS-Trust STS アプリケーションのセキュリティードメインを作成します。
-
WS-Trust STS アプリケーションの
web.xml
ファイルを設定します。 - WS-Trust STS アプリケーションの認証システムを設定します。
- WS-Trust STS アプリケーションに必要な依存関係を宣言します。
- WS-Trust STS アプリケーションの Web サービスの部分を設定します。
-
WS-Trust STS アプリケーションの
picketlink.xml
ファイルを作成および設定します。
アプリケーションを作成およびデプロイする前に、セキュリティードメインを作成および設定する必要があります。
3.8.3.1.1. STS のセキュリティードメインの作成
STS は、提供された認証情報に基づいてプリンシパルの認証を処理し、その結果に基づいて適切なセキュリティートークンを発行します。これには、セキュリティードメインでアイデンティティーストアを設定する必要があります。このセキュリティードメインおよびアイデンティティーストアを作成する唯一の要件は、認証および承認メカニズムが適切に定義されていることです。これは、プロパティーファイル、データベース、LDAP など、さまざまなアイデンティティーストアを使用して STS アプリケーションをサポートすることができることを意味します。セキュリティードメインの詳細は、JBoss EAP Security Architecture ドキュメントの Security Domains を参照してください。
以下の例では、アイデンティティーストアのプロパティーファイルを使用する簡単な UsersRoles
ログインモジュールが使用されます。
セキュリティードメインを作成する CLI コマンド
/subsystem=security/security-domain=sts:add(cache-type=default)
/subsystem=security/security-domain=sts/authentication=classic:add
/subsystem=security/security-domain=sts/authentication=classic/login-module=UsersRoles:add(code=UsersRoles,flag=required,module-options=[usersProperties=${jboss.server.config.dir}/sts-users.properties,rolesProperties=${jboss.server.config.dir}/sts-roles.properties])
reload
結果の XML
<security-domain name="sts" cache-type="default"> <authentication> <login-module code="UsersRoles" flag="required"> <module-option name="usersProperties" value="${jboss.server.config.dir}/sts-users.properties"/> <module-option name="rolesProperties" value="${jboss.server.config.dir}/sts-roles.properties"/> </login-module> </authentication> </security-domain>
ここで使用する管理 CLI コマンドは、JBoss EAP スタンドアロンサーバーを実行していることを仮定しています。JBoss EAP 管理対象ドメインの管理 CLI を使用する場合の詳細は JBoss EAP Management CLI Guide を参照してください。
プロパティーファイル
UsersRoles
ログインモジュールは、プロパティーファイルを使用して、ユーザー/パスワードとユーザー/ロール情報を保存します。UsersRoles
モジュールの詳細は、JBoss EAP Login Module Reference を参照してください。この例では、プロパティーファイルには以下が含まれます。
例: sts-users.properties
ファイル
Eric=samplePass Alan=samplePass
例: sts-roles.properties
ファイル
Eric=All Alan=
また、セキュリティートークンの署名および暗号化のためにキーストアを作成する必要があります。このキーストアは、picketlink.xml
ファイルを設定する際に使用されます。
3.8.3.1.2. STS の web.xml ファイルの設定
STS の web.xml
ファイルには以下が含まれている必要があります。
-
STS 機能を有効化するための
<servlet>
と、URL をマップするための<servlet-mapping>
。 -
セキュアな領域の URL パターンにマップする
<url-pattern>
が含まれる<web-resource-collection>
を持つ<security-constraint>
。任意で、<security-constraint>
に許可されるロールを明記する<auth-constraint>
を含めることもできます。 -
BASIC 認証に設定された
<login-config>
-
<auth-constraint>
にロールが指定されている場合、これらのロールを<security-role>
で定義する必要があります。
例: web.xml
ファイル
<web-app> <!-- Define STS servlet --> <servlet> <servlet-name>STS-servlet</servlet-name> <servlet-class>com.example.sts.PicketLinkSTService</servlet-class> </servlet> <servlet-mapping> <servlet-name>STS-servlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <!-- Define a security constraint that requires the All role to access resources --> <security-constraint> <web-resource-collection> <web-resource-name>STS</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>All</role-name> </auth-constraint> </security-constraint> <!-- Define the Login Configuration for this Application --> <login-config> <auth-method>BASIC</auth-method> <realm-name>STS Realm</realm-name> </login-config> <!-- Security roles referenced by this web application --> <security-role> <description>The role that is required to log in to the IDP Application</description> <role-name>All</role-name> </security-role> </web-app>
3.8.3.1.3. STS の認証システムの設定
オーセンティケーターは、セキュリティートークンを発行および検証するユーザーの認証を行います。オーセンティケーターは、プリンシパルの認証および承認に使用されるセキュリティードメインを定義することで設定されます。
jboss-web.xml
ファイルには以下が必要です。
-
認証または承認に使用されるセキュリティードメインを指定する
<security-domain>
。
例: jboss-web.xml
ファイル
<jboss-web> <security-domain>sts</security-domain> <context-root>SecureTokenService</context-root> </jboss-web>
3.8.3.1.4. STS の必須依存関係を宣言します。
STS として機能する web アプリケーションは、org.picketlink
クラスを見つけることができるように依存関係を jboss-deployment-structure.xm
l ファイルに定義する必要があります。JBoss EAP は必要なすべての org.picketlink
と関連クラスを提供するため、アプリケーションはこれらの依存関係を宣言して使用することのみが必要となります。
例: jboss-deployment-structure.xml
を使用した依存関係の宣言
<jboss-deployment-structure> <deployment> <dependencies> <module name="org.picketlink"/> </dependencies> </deployment> </jboss-deployment-structure>
3.8.3.1.5. STS の Web-Service ポートの設定
STS として機能する Web アプリケーションでは、クライアントがセキュリティートークンを取得するために呼び出すために web サービスを定義する必要があります。これには、PicketLinkSTS
というサービス名と、PicketLinkSTSPort
というポートを定義する必要があります。ただし、対象のデプロイメント環境をより効果的に反映するように SOAP アドレスを変更することができます。
例: PicketLinkSTS.wsdl
ファイル
<?xml version="1.0"?> <wsdl:definitions name="PicketLinkSTS" targetNamespace="urn:picketlink:identity-federation:sts" xmlns:tns="urn:picketlink:identity-federation:sts" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsap10="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"> <wsdl:types> <xs:schema targetNamespace="urn:picketlink:identity-federation:sts" xmlns:tns="urn:picketlink:identity-federation:sts" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" elementFormDefault="qualified"> <xs:element name="MessageBody"> <xs:complexType> <xs:sequence> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##any"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> </wsdl:types> <wsdl:message name="RequestSecurityToken"> <wsdl:part name="rstMessage" element="tns:MessageBody"/> </wsdl:message> <wsdl:message name="RequestSecurityTokenResponse"> <wsdl:part name="rstrMessage" element="tns:MessageBody"/> </wsdl:message> <wsdl:portType name="SecureTokenService"> <wsdl:operation name="IssueToken"> <wsdl:input wsap10:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue" message="tns:RequestSecurityToken"/> <wsdl:output wsap10:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/Issue" message="tns:RequestSecurityTokenResponse"/> </wsdl:operation> </wsdl:portType> <wsdl:binding name="STSBinding" type="tns:SecureTokenService"> <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="IssueToken"> <soap12:operation soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue" style="document"/> <wsdl:input> <soap12:body use="literal"/> </wsdl:input> <wsdl:output> <soap12:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="PicketLinkSTS"> <wsdl:port name="PicketLinkSTSPort" binding="tns:STSBinding"> <soap12:address location="http://localhost:8080/SecureTokenService/PicketLinkSTS"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
さらに、web サービスが WSDL を使用するクラスが必要になります。
例: PicketLinkSTS
クラス
@WebServiceProvider(serviceName = "PicketLinkSTS", portName = "PicketLinkSTSPort", targetNamespace = "urn:picketlink:identity-federation:sts", wsdlLocation = "WEB-INF/wsdl/PicketLinkSTS.wsdl") @ServiceMode(value = Service.Mode.MESSAGE) public class PicketLinkSTService extends PicketLinkSTS { private static Logger log = Logger.getLogger(PicketLinkSTService.class.getName()); @Resource public void setWSC(WebServiceContext wctx) { log.debug("Setting WebServiceContext = " + wctx); this.context = wctx; } }
3.8.3.1.6. STS の picketlink.xml ファイルの作成および設定
picketlink.xml
ファイルは authenticator の動作を管理し、アプリケーションの起動時にロードされます。
JBoss EAP Security Token Service は、拡張ポイントを提供する複数のインターフェイスを定義します。実装はプラグイン処理でき、設定を使用してデフォルト値を一部のプロパティーに指定できます。How To Set Up SSO with SAML v2 の IDP および SP 設定と同様に、すべての STS 設定は、暗号化されたアプリケーションの picketlink.xml
で指定されています。以下は、picketlink.xml
ファイルで設定できる要素です。
以下のテキストでは、サービスプロバイダーは、クライアントによるセキュリティートークンの提示を必要とする Web サービスを参照します。
<PicketLinkSTS>
: これはルート要素です。STS 管理者が以下のプロパティーを設定できるようにするプロパティーを定義します。-
STSName
: セキュリティートークンサービスの名前を表す文字列。指定されていない場合は、デフォルトのPicketLinkSTS
値が使用されます。 -
TokenTimeout
: トークンのライフタイム値 (秒単位)。指定されない場合は、デフォルト値の3600
(時間) が使用されます。 -
EncryptToken
: 発行されたトークンを暗号化するかどうかを指定するブール値。デフォルト値はfalse
です。
-
-
<KeyProvider>
: この要素と、そのサブ要素すべては、PicketLink STS がトークンに署名および暗号化するために使用するキーストアの設定に使用されます。このセクションでは、キーストアの場所、パスワード、署名 (秘密鍵) のエイリアス、パスワードなどのプロパティーをすべて設定します。 -
<TokenProviders>
: このセクションは、各タイプのセキュリティートークンの処理に使用する必要があるTokenProvider
実装を指定します。この例では、SAMLV1.1
タイプのトークンを処理するプロバイダーと、SAMLV2.0
タイプのトークンを処理するプロバイダーがあります。WSTrustRequestHandler
はSTSConfiguration
のgetProviderForTokenType(String type)
メソッドを呼び出し、適切なTokenProvider
への参照を取得します。 -
<ServiceProviders>
: 本セクションでは、セキュリティートークンを必要とする web サービスである各サービスプロバイダーに使用する必要があるトークンタイプを指定します。WS-Trust リクエストにトークンタイプが含まれていない場合、WSTrustRequestHandler
はサービスプロバイダーのエンドポイントを使用して、発行する必要のあるトークンのタイプを特定する必要があります。
PicketLink を設定する場合は、セキュリティーが強化され、URL パラメーター内で応答が渡されないため、POST バインディングが推奨されます。
例: picketlink.xml
設定ファイル
<!DOCTYPE PicketLinkSTS> <PicketLinkSTS xmlns="urn:picketlink:federation:config:2.1" STSName="PicketLinkSTS" TokenTimeout="7200" EncryptToken="false"> <KeyProvider ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager"> <Auth Key="KeyStoreURL" Value="sts_keystore.jks"/> <Auth Key="KeyStorePass" Value="testpass"/> <Auth Key="SigningKeyAlias" Value="sts"/> <Auth Key="SigningKeyPass" Value="keypass"/> <ValidatingAlias Key="http://services.testcorp.org/provider1" Value="service1"/> </KeyProvider> <TokenProviders> <TokenProvider ProviderClass="org.picketlink.identity.federation.core.wstrust.plugins.saml.SAML11TokenProvider" TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" TokenElement="Assertion" TokenElementNS="urn:oasis:names:tc:SAML:1.0:assertion"/> <TokenProvider ProviderClass="org.picketlink.identity.federation.core.wstrust.plugins.saml.SAML20TokenProvider" TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" TokenElement="Assertion" TokenElementNS="urn:oasis:names:tc:SAML:2.0:assertion"/> </TokenProviders> <ServiceProviders> <ServiceProvider Endpoint="http://services.testcorp.org/provider1" TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" TruststoreAlias="service1"/> </ServiceProviders> </PicketLinkSTS>
デフォルトでは、picketlink.xml
ファイルは STS Web アプリケーションの WEB-INF/classes
ディレクトリーにあります。PicketLink 設定ファイルはファイルシステムから読み込むこともできます。PicketLink 設定ファイルをファイルシステムから読み込むには、picketlink-sts.xml
という名前を付け、${user.home}/picketlink-store/sts/
ディレクトリーに配置する必要があります。
3.8.3.2. クライアントでの WS-Trust Security Token Service (STS) の使用
STS からセキュリティートークンを取得するようにクライアントを設定するには、org.picketlink.identity.federation.api.wstrust.WSTrustClient
クラスを利用して STS に接続し、トークンを発行するように要求する必要があります。
最初にクライアントをインスタンス化する必要があります。
例: WSTrustClient の作成
WSTrustClient client = new WSTrustClient("PicketLinkSTS", "PicketLinkSTSPort", "http://localhost:8080/SecureTokenService/PicketLinkSTS", new SecurityInfo(username, password));
次に、WSTrustClient
を使用して SAML アサーションなどのトークンが発行されるように要求する必要があります。
例: アサーションの取得
org.w3c.dom.Element assertion = null; try { assertion = client.issueToken(SAMLUtil.SAML2_TOKEN_TYPE); } catch (WSTrustException wse) { System.out.println("Unable to issue assertion: " + wse.getMessage()); wse.printStackTrace(); }
アサーションを取得したら、そのアサーションを SOAP メッセージに追加および送信する方法があります。
クライアントは、
org.picketlink.trust.saml.assertion
キー下で SOAPMessageContext
に SAML2Assertion
をプッシュできます。例を以下に示します。bindingProvider.getRequestContext().put(SAML2Constants.SAML2_ASSERTION_PROPERTY, assertion);
-
SAML2
Assertion
は、セキュリティーコンテキストで JAAS サブジェクトの一部として利用できます。これは、PicketLink STS ログインモジュールと JAAS の対話がある場合に生じる可能性があります。
3.8.3.3. STS クライアントプール
JBoss EAP では、STS クライアントプール機能はサポートされていません。
STS クライアントプールは、サーバーに STS クライアントのプールを設定できるようにする機能であり、STS クライアント作成のボトルネックをなくすことができます。クライアントプールは、STS クライアントが SAML チケットを取得する必要があるログインモジュールに使用できます。これらの型には次のようなものがあります。
-
org.picketlink.identity.federation.core.wstrust.auth.STSIssuingLoginModule
-
org.picketlink.identity.federation.core.wstrust.auth.STSValidatingLoginModule
-
org.picketlink.trust.jbossws.jaas.JBWSTokenIssuingLoginModule
各ログインモジュールのプール内のクライアントのデフォルト数は、initialNumberOfClients
ログインモジュールオプションを使用して設定されます。
org.picketlink.identity.federation.bindings.stspool.STSClientPoolFactory
クラスはクライアントプール機能をアプリケーションに提供します。
STSClientPoolFactory の使用
STS クライアントは、STSClientConfig 設定をキーとして使用し 、サブプールに挿入されます。STS クライアントをサブプールに挿入するには、STSClientPool
インスタンスを取得して、設定に基づいてサブプールを初期化する必要があります。オプションで、プールを初期化する際に STS クライアントの最初の数を指定することができます。または、デフォルトの数を使用することもできます。
例: STS クライアントのサブプールへの挿入
final STSClientPool pool = STSClientPoolFactory.getPoolInstance(); pool.createPool(20, stsClientConfig); final STSClient client = pool.getClient(stsClientConfig);
クライアントでの作業が完了したら returnClient()
メソッドを呼び出し、クライアントをプールに返すことができます。
例: STS クライアントのサブプールへの返信
pool.returnClient();
例: 任意の設定でサブプールが存在するかどうかの確認
if (! pool.configExists(stsClientConfig) { pool.createPool(stsClientConfig); }
picketlink-federation
サブシステムを有効にすると、デプロイメント用に作成されたすべてのクライアントプールがアンデプロイプロセス中に自動的に破棄されます。プールを手動で破棄するには、以下を実行します。
例: サブプールの手動破棄
pool.destroyPool(stsClientConfig);
3.8.4. 認証済みアイデンティティー の EJB サブシステムへの伝搬
webservices
サブシステムには、アダプターが含まれます。これは、アノテーションまたはデプロイメント記述子のいずれかを使用して、Web サービスエンドポイントをセキュアにするために Elytron セキュリティードメインの設定することができます。
Elytron セキュリティーを有効にすると、JAAS サブジェクトまたはプリンシパルを Apache CXF エンドポイントの SecurityContext
にプッシュし、認証されたアイデンティティーを EJB コンテナーに伝播できます。
以下は、Apache CXF インターセプターを使用して、認証された情報を EJB コンテナーに伝播する方法の例になります。
public class PropagateSecurityInterceptor extends WSS4JInInterceptor { public PropagateSecurityInterceptor() { super(); getAfter().add(PolicyBasedWSS4JInInterceptor.class.getName()); } @Override public void handleMessage(SoapMessage message) throws Fault { ... final Endpoint endpoint = message.getExchange().get(Endpoint.class); final SecurityDomainContext securityDomainContext = endpoint.getSecurityDomainContext(); //push subject principal retrieved from CXF to ElytronSecurityDomainContext securityDomainContext.pushSubjectContext(subject, principal, null) } }
3.9. JAX-WS ロギング
JAX-WS ハンドラー または Apache CXF ロギングインターセプター を使用して、インバウンドおよびアウトバウンドメッセージのロギングを処理できます。
3.9.1. JAX-WS ハンドラーの使用
JAX-WS ハンドラーは、渡されるメッセージをログに記録するように設定できます。@HandlerChain
JAX-WS アノテーションを使用して、ハンドラーが希望のクライアントやエンドポイントに追加できるため、このアプローチは移植性があると言えます。
事前定義されたクライアントおよびエンドポイント設定メカニズムにより、ロギングハンドラーを任意のクライアントおよびエンドポイントの組み合わせに追加したり、一部のクライアントおよびエンドポイントのみに追加したりできます。ロギングハンドラーを一部のクライアントまたはエンドポイントのみに追加するには、@EndpointConfig
アノテーションと JBossWS API を使用します。
org.jboss.ws.api.annotation.EndpointConfig
アノテーションは、エンドポイント設定を JAX-WS エンドポイント実装に割り当てるために使用されます。webservices
サブシステムに定義された設定を割り当てる場合、設定名のみが指定されます。アプリケーションに定義されている設定を割り当てる場合は、デプロイメント記述子への相対パスと設定名を指定する必要があります。
3.9.2. Apache CXF ロギングインターセプターの使用
Apache CXF には、コンソール、クライアントのログファイル、またはサーバーのログファイルにメッセージをログに記録するために使用できるロギングインターセプターが同梱されています。これらのインターセプターは、以下を含む複数の方法でクライアント、エンドポイント、およびバスに追加できます。
システムプロパティー
org.apache.cxf.logging.enabled
システムプロパティーをtrue
に設定すると、ロギングインターセプターが、JVM で作成されるバスインスタンスに追加されます。システムプロパティーをpretty
に設定して、きれいな形式の XML 出力を出力することもできます。以下の管理 CLI コマンドを使用すると、このシステムプロパティーを設定できます。/system-property=org.apache.cxf.logging.enabled:add(value=true)
手動インターセプターの追加
ロギングインターセプターは、Apache CXF アノテーション
@org.apache.cxf.interceptor.InInterceptors
および@org.apache.cxf.interceptor.OutInterceptors
を使用してエンドポイントに選択的に追加できます。プログラムでロギングインターセプターの新しいインスンタスをクライアントまたはバスに追加することにより、クライアント側の同じ結果が得られます。
3.10. Web Services Addressing (WS-Addressing) の有効化
Web サービスのアドレス指定 (WS-Addressing) は、Web サービスと関連メッセージを処理するトランスポートに中立的なメカニズムを提供します。WS-Addressing を有効にするには、@Addressing
アノテーションを Web サービスエンドポイントに追加し、アクセスするクライアントを設定する必要があります。
以下の例は、アプリケーションに既存の JAX-WS サービスおよびクライアント設定があることを前提としています。使用できる完全な例は、JBoss EAP に同梱される jaxws-addressing
クイックスタートを参照してください。
@Addressing
アノテーションをアプリケーションの JAX-WS エンドポイントコードに追加します。例: @Addressing アノテーションのある JAX-WS エンドポイント
package org.jboss.quickstarts.ws.jaxws.samples.wsa; import org.jboss.quickstarts.ws.jaxws.samples.wsa.ServiceIface; import javax.jws.WebService; import javax.xml.ws.soap.Addressing; @WebService( portName = "AddressingServicePort", serviceName = "AddressingService", wsdlLocation = "WEB-INF/wsdl/AddressingService.wsdl", targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wsaddressing", endpointInterface = "org.jboss.quickstarts.ws.jaxws.samples.wsa.ServiceIface") @Addressing(enabled = true, required = true) public class ServiceImpl implements ServiceIface { public String sayHello() { return "Hello World!"; } }
JAX-WS クライアントコードを更新して WS-Addressing を設定します。
例: WS-Addressing 用に設定された JAX-WS クライアント
package org.jboss.quickstarts.ws.jaxws.samples.wsa; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import javax.xml.ws.soap.AddressingFeature; public final class AddressingClient { private static final String serviceURL = "http://localhost:8080/jaxws-addressing/AddressingService"; public static void main(String[] args) throws Exception { // construct proxy QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wsaddressing", "AddressingService"); URL wsdlURL = new URL(serviceURL + "?wsdl"); Service service = Service.create(wsdlURL, serviceName); org.jboss.quickstarts.ws.jaxws.samples.wsa.ServiceIface proxy = (org.jboss.quickstarts.ws.jaxws.samples.wsa.ServiceIface) service.getPort(org.jboss.quickstarts.ws.jaxws.samples.wsa.ServiceIface.class, new AddressingFeature()); // invoke method System.out.println(proxy.sayHello()); } }
クライアントおよびエンドポイントは WS-Addressing を使用して通信するようになりました。
3.11. Web サービス信頼できるメッセージングの有効化
Web Services Reliable Messaging (WS-Reliable Messaging) は、Apache CXF に内部で実装されます。インターセプターのセットは、信頼できるメッセージングプロトコルの低レベル要件と対話します。
WS-Reliable Messaging を有効にするには、以下のいずれかの手順を実行します。
- 適切な WS-Reliable Messaging ポリシー、アサーション、またはそれらの両方を指定する WSDL コントラクトを消費します。
- 信頼できるメッセージングインターセプターを手動で追加し、設定します。
- オプションの CXF Spring XML 記述子で信頼できるメッセージングポリシーを指定します。
- オプションの CXF Spring XML 記述子に Apache CXF 信頼性メッセージング機能を指定します。
最初のアプローチは移植可能な唯一の方法であり、Apache CXF WS-Policy エンジンに依存します。プロプライエタリーの他のアプローチにより、WS-Reliable Messaging Policy で対応していないプロトコル側面の詳細な設定が可能になります。
3.12. Web サービスポリシーの指定
Web Services Policies (WS-Policy) は Apache CXF WS-Policy フレームワークに依存します。このフレームワークは、以下の仕様に準拠しています。
ポリシーは、以下を含む複数の方法で使用することができます。
- ポリシーアサーションを WSDL コントラクトに追加し、ランタイムにアサーションを消費させ、適切に動作させます。
- CXF アノテーションまたは機能のいずれかを使用してエンドポイントポリシー添付を指定します。
- Apache CXF ポリシーフレームワークを使用してカスタムアサーションを定義し、他のタスクを完了します。
3.13. Apache CXF の統合
JBoss EAP 上の JBossWS によって提供されるすべての JAX-WS 機能は、ほとんどの Apache CXF プロジェクトモジュールを含む JBossWS スタックの適切な統合によって提供されます。
Apache CXF はオープンソースサービスフレームワークです。JAX-WS を含むフロントエンドプログラミング API を使用してサービスを構築および開発でき、サービスは HTTP や JMS などのさまざまなトランスポート上で SOAP や XML/HTTP などのさまざまなプロトコルを通信します。
JBossWS と Apache CXF 間の統合レイヤーは主に以下の目的で使用されます。
- JAX-WS などの標準の Web サービス API を JBoss EAP で使用可能。これは、ユーザーがこれに対応する必要なく、Apache CXF を内部的に活用します。
- JBoss EAP 上で WS-* を含む Apache CXF の高度な機能を使用できるようにし、これらのコンテナーでの実行に必要な統合手順をユーザーが処理、設定、考慮したりする必要性を省きます。
これらの目的をサポートするため、Apache CXF を使用した JBossWS 統合は JBossWS エンドポイントデプロイメントメカニズムをサポートし、Apache CXF 上で多数の内部カスタマイズが提供されます。
Apache CXF アーキテクチャーの詳細は、Apache CXF 公式ドキュメント を参照してください。
3.13.1. サーバー側統合のカスタマイズ
JBossWS サーバー側の Apache CXF との統合では、提供された Web サービスデプロイメントに適切な Apache CXF 構造を内部で作成します。デプロイメントに複数のエンドポイントが含まれる場合、それらはすべて同じ Apache CXF バス内に存在し、他のデプロイメントのバスインスタンスとは分離されます。
JBossWS はサーバー側で Apache CXF 設定オプションのほとんどに対して適切なデフォルト値を設定しますが、ユーザーはデプロイメント用に作成される Bus インスタンスを微調整する必要があるかもしれません。jboss-webservices.xml
記述子はデプロイメントレベルのカスタマイズに使用できます。
3.13.1.1. デプロイメント記述子プロパティー
jboss-webservices.xml
記述子は、プロパティー値を提供するために使用できます。
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" version="1.2"> ... <property> <name>...</name> <value>...</value> </property> ... </webservices>
Apache CXF との JBossWS 統合には、Apache CXF 内部を制御するために許可されるプロパティー名のセットが含まれています。
3.13.1.2. WorkQueue の設定
Apache CXF は、@Oneway
要求処理などの一部の操作を処理する WorkQueue
インスタンスを使用します。WorkQueueManager
は拡張として Bus にインストールされ、キューを追加または削除したり、既存のキューを制御したりできます。
サーバー側では、jboss-webservices.xml
で cxf.queue.<queue-name>.*
プロパティーを使用してキューを指定できます。たとえば、cxf.queue.default.maxQueueSize
プロパティーを使用して、デフォルトの WorkQueue
の最大キューサイズを設定できます。デプロイメント時に、JBossWS 統合は AutomaticWorkQueueImpl
の新しいインスタンスを現在設定されている WorkQueueManager
に追加できます。以下のプロパティーは、AutomaticWorkQueueImpl
constructor パラメーターを埋めるために使用されます。
プロパティー | デフォルト値 |
---|---|
cxf.queue.<queue-name>.maxQueueSize | 256 |
cxf.queue.<queue-name>.initialThreads | 0 |
cxf.queue.<queue-name>.highWaterMark | 25 |
cxf.queue.<queue-name>.lowWaterMark | 5 |
cxf.queue.<queue-name>.dequeueTimeout | 120000 |
3.13.1.3. ポリシー代替セレクター
Apache CXF ポリシーエンジンは、ポリシーの代替に対応するさまざまなストラテジーをサポートします。JBossWS 統合のデフォルトは MaximalAlternativeSelector
ですが、jboss-webservices.xml
ファイルで cxf.policy.alternativeSelector
プロパティーを使用して異なるセレクター実装を設定することもできます。
3.13.1.4. MBean 管理
Apache CXF を使用すると、JBoss EAP MBean サーバーにインストールされた MBean オブジェクトを管理できます。この機能は、jboss-webservices.xml
ファイルの cxf.management.enabled
プロパティーを使用してデプロイメントベースで有効にできます。cxf.management.installResponseTimeInterceptors
プロパティーを使用して、CXF 応答時間インターセプターのインストールを制御することもできます。これらのインターセプターは、MBean 管理を有効にするとデフォルトで追加されますが、場合によっては必要ない可能性があります。
例: jboss-webservices.xml
ファイルの MBean 管理
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" version="1.2"> <property> <name>cxf.management.enabled</name> <value>true</value> </property> <property> <name>cxf.management.installResponseTimeInterceptors</name> <value>false</value> </property> </webservices>
3.13.1.5. スキーマの検証
Apache CXF には、クライアントとサーバーの両方での SOAP メッセージの受信および送信を検証する機能が含まれます。検証は、サービスプロキシー (クライアント側) のビルドに使用されるエンドポイントの WSDL コントラクト (サーバー側) または WSDL コントラクトの関連するスキーマに対して実行されます。
スキーマの検証は、以下のいずれかの方法で有効にできます。
JBoss EAP サーバー設定の最適化
たとえば、以下の管理 CLI コマンドは、デフォルトの
Standard-Endpoint-Config
エンドポイント設定のスキーマ検証を有効にします。/subsystem=webservices/endpoint-config=Standard-Endpoint-Config/property=schema-validation-enabled:add(value=true)
事前定義されたクライアントまたはエンドポイント設定ファイルです。
参照された設定ファイルで
schema-validation-enabled
プロパティーをtrue
に設定すると、事前に設定されている設定 に、コンテナー内で実行しているエンドポイントまたはクライアントを関連付けることができます。クライアント側でプログラムで
クライアント側では、スキーマ検証をプログラムで有効にできます。例を以下に示します。
((BindingProvider)proxy).getRequestContext().put("schema-validation-enabled", true);
サーバー側で
@org.apache.cxf.annotations.SchemaValidation
アノテーションを使用します。サーバー側では、
@org.apache.cxf.annotations.SchemaValidation
アノテーションを使用できます。例を以下に示します。import javax.jws.WebService; import org.apache.cxf.annotations.SchemaValidation; @WebService(...) @SchemaValidation public class ValidatingHelloImpl implements Hello { ... }
3.13.1.6. Apache CXF インターセプター
jboss-webservices.xml
記述子 では、cxf.interceptors.in
および cxf.interceptors.out
プロパティーを指定できます。これらのプロパティーにより、宣言しているインターセプターを、デプロイメントを提供するために作成された Bus
インスタンスに割り当てることができます。
例: jboss-webservices.xml
ファイル
<?xml version="1.1" encoding="UTF-8"?> <webservices xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee"> <property> <name>cxf.interceptors.in</name> <value>org.jboss.test.ws.jaxws.cxf.interceptors.BusInterceptor</value> </property> <property> <name>cxf.interceptors.out</name> <value>org.jboss.test.ws.jaxws.cxf.interceptors.BusCounterInterceptor</value> </property> </webservices>
以下の方法のいずれかを使用してインターセプターを宣言できます。
-
エンドポイントクラスのアノテーションの使用 (
@org.apache.cxf.interceptor.InInterceptor
または@org.apache.cxf.interceptor.OutInterceptor
)。 -
org.apache.cxf.interceptor.InterceptorProvider
インターフェイスを使用したクライアント側での直接 API 使用。 - JBossWS 記述子の使用方法。
JBoss EAP では Spring 統合がサポートされなくなったため、JBossWS 統合では、際のクライアントまたはエンドポイントコードの変更を不要にするために jaxws-endpoint-config.xml
記述子ファイルを使用します。cxf.interceptors.in
および cxf.interceptors.out
プロパティーのインターセプタークラス名のリストを指定して、事前定義されたクライアントおよびエンドポイント設定内でインターセプターを宣言します。
例: jaxws-endpoint-config.xml
ファイル
<?xml version="1.0" encoding="UTF-8"?> <jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd"> <endpoint-config> <config-name>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointImpl</config-name> <property> <property-name>cxf.interceptors.in</property-name> <property-value>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointInterceptor,org.jboss.test.ws.jaxws.cxf.interceptors.FooInterceptor</property-value> </property> <property> <property-name>cxf.interceptors.out</property-name> <property-value>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointCounterInterceptor</property-value> </property> </endpoint-config> </jaxws-config>
指定された各インターセプタークラスの新規インスタンスは、設定が割り当てられるクライアントまたはエンドポイントに追加されます。インターセプタークラスには no-argument コンストラクターが必要です。
3.13.1.7. Apache CXF の機能
jboss-webservices.xml
記述子を使用すると、cxf.features
プロパティーを指定できます。このプロパティーを使用すると、デプロイメントを提供するために作成された Bus
インスタンスに属するエンドポイントに機能を宣言できます。
例: jboss-webservices.xml
ファイル
<?xml version="1.1" encoding="UTF-8"?> <webservices xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee"> <property> <name>cxf.features</name> <value>org.apache.cxf.feature.FastInfosetFeature</value> </property> </webservices>
以下の方法のいずれかを使用して機能を宣言できます。
-
エンドポイントクラスでのアノテーション使用 (例:
@org.apache.cxf.feature.Features
) -
org.apache.cxf.feature.AbstractFeature
クラスの拡張により、クライアント側で API の直接使用。 - JBossWS 記述子の使用方法。
JBoss EAP では Spring 統合がサポートされなくなったため、JBossWS 統合は追加の記述子 (jaxws-endpoint-config.xml
ファイルベースのアプローチ) を追加して、実際のクライアントまたはエンドポイントコードへの変更を回避します。cxf.features
プロパティーの機能クラス名のリストを指定して、事前定義のクライアントおよびエンドポイント設定内で機能を宣言できます。
例: jaxws-endpoint-config.xml
ファイル
<?xml version="1.0" encoding="UTF-8"?> <jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd"> <endpoint-config> <config-name>Custom FI Config</config-name> <property> <property-name>cxf.features</property-name> <property-value>org.apache.cxf.feature.FastInfosetFeature</property-value> </property> </endpoint-config> </jaxws-config>
指定された各機能クラスの新規インスタンスは、設定が割り当てられるクライアントまたはエンドポイントに追加されます。機能クラスには no-argument コンストラクターが必要です。
3.13.1.8. プロパティー駆動 Bean の作成
Apache CXF Interceptors および Apache CXF Features セクションでは、クライアントまたはエンドポイントの事前定義設定または jboss-webservices.xml
記述子でプロパティーを使用して CXF インターセプターおよび機能を宣言する方法を説明します。指定された feature または interceptor クラス名のみを取得することにより、コンテナーはクラスデフォルトコンストラクターを使用して bean インスタンスを作成しようとします。これは、vanilla CXF クラスのカスタム拡張機能が提供されない限り、機能またはインターセプター設定の制限を設定します。ただし、デフォルトのコンストラクター設定プロパティーは、最終的にスーパーコンストラクターを使用する前に設定されます。
この問題に対処するために、JBossWS 統合には、プロパティーからビルドする際に単純な bean 階層を設定するメカニズムが含まれています。プロパティーには bean 参照値を持たせることができます。これは、##
で始まる文字列です。プロパティー参照キーは、bean クラス名と各属性の値を指定するために使用されます。
たとえば、以下のプロパティーを使用すると、スタックは機能インスタンスをインストールします。
キー | 値 |
---|---|
cxf.features | ##foo, ##bar |
##foo | org.jboss.Foo |
##foo.par | 34 |
##bar | org.jboss.Bar |
##bar.color | blue |
以下のコードでは、同じ結果を作成できます。
import org.Bar; import org.Foo; ... Foo foo = new Foo(); foo.setPar(34); Bar bar = new Bar(); bar.setColor("blue");
このメカニズムは、クラスが適切な getter()
および setter()
メソッドを持つ有効な Bean であることを前提としています。値オブジェクトは、クラス定義を検査して正しいプリミティブ型にキャストされます。ネストされた Bean を設定することもできます。
付録A リファレンス資料
A.1. JAX-RS/RESTEasy アノテーション
アノテーション | 使用方法 |
---|---|
Cache |
応答の |
ClientInterceptor | インターセプターをクライアント側のインターセプターとして特定します。 |
ContentEncoding |
アノテーション付きのアノテーションで適用する |
Context |
|
@CookieParam | HTTP リクエスト cookie のクッキーまたはオブジェクト表現の値をメソッド呼び出しに指定できます。 |
DecorateTypes |
サポートされているタイプを指定するには、 |
デコレーター | デコレーションをトリガーする別のアノテーションに配置されるメタアノテーション。 |
DefaultValue |
HTTP リクエスト項目が存在しない場合にデフォルト値を定義するために、その他の |
DELETE |
メソッドが HTTP |
DoNotUseJAXBProvider |
このクラスまたはパラメーターは、JAXB |
Encoded |
クラス、メソッド、またはパラメーターで使用できます。デフォルトでは、 |
Form | これは、リクエストと要求の発信および受信を行うためにオブジェクトとして使用できます。 |
Formatted | インデントと改行で XML 出力をフォーマットします。これは、JAXB Decorator です。 |
GET |
メソッドが HTTP |
IgnoreMediaTypes | タイプ、メソッド、パラメーター、またはフィールドにフィールドを置くと、特定のメディアタイプに対して JAX-RS プロバイダーを使用しないように指示します。 |
ImageWriterParams |
|
Mapped |
|
MultipartForm | これは、multipart/form-data MIME タイプの受信/発進のオブジェクトとして使用できます。 |
NoCache |
|
NoJackson | Jackson プロバイダーをトリガーしない場合にクラス、パラメーター、フィールド、またはメソッドに配置します。 |
PartType |
List または Map を |
Path | これは、クラスまたはリソースメソッドに存在する必要があります。両方に存在する場合、リソースメソッドへの相対パスはクラスとメソッドの連結になります。 |
PathParam | 変数 URI パスのフラグメントをメソッド呼び出しにマッピングできるようにします。 |
POST |
メソッドが HTTP |
Priority | クラスの使用順序を示すアノテーション。値が小さい整数パラメーターを使用すると優先度が高くなります。 |
Provider | プロバイダースキャンフェーズで JAX-RS ランタイムによって プロバイダー として検出されるようにクラスをマークします。 |
PUT |
メソッドが HTTP |
QueryParam | URI クエリー文字列パラメーターまたは URL 形式のエンコードされたパラメーターをメソッド呼び出しにマップできるようにします。 |
ServerInterceptor | インターセプターをサーバー側のインターセプターとして特定します。 |
StringParameterUnmarshallerBinder |
文字列ベースのアノテーションインジェクターに適用されるように |
Stylesheet | XML スタイルシートヘッダーを指定します。 |
Wrapped | これをメソッドまたはパラメーターに配置すると、指定のオブジェクトのコレクションまたは配列をマーシャリングまたはマーシャリング解除できます。 |
WrappedMap | これは、JAXB オブジェクトのマップのマーシャリングまたはマーシャリング解除を行う場合は、メソッドまたはパラメーターに配置します。 |
XmlHeader | 返されたドキュメントの XML ヘッダーを設定します。 |
XmlNsMap |
|
XopWithMultipartRelated | このアノテーションは、アノテーション付きのオブジェクト間の XOP メッセージの受信/送信 (multipart / 関連としてパッケージ化) の処理と生成に使用できます。 |
A.2. RESTEasy 設定パラメーター
オプション名 | デフォルト値 | 説明 |
---|---|---|
resteasy.servlet.mapping.prefix | デフォルトなし |
Resteasy servlet-mapping の URL パターンが |
resteasy.scan | false |
|
resteasy.scan.providers | false |
|
resteasy.scan.resources | false | JAX-RS リソースクラスのスキャン。 |
resteasy.providers | デフォルトなし |
登録する完全修飾 |
resteasy.use.builtin.providers | true |
デフォルトのビルトイン |
resteasy.resources | デフォルトなし | 登録する完全修飾 JAX-RS リソースクラス名のコンマ区切りリスト。 |
resteasy.jndi.resources | デフォルトなし | JAX-RS リソースとして登録するオブジェクトを参照する JNDI 名のコンマ区切りリスト。 |
javax.ws.rs.Application | デフォルトなし |
仕様移植可能な方法でブートストラップする |
resteasy.media.type.mappings | デフォルトなし |
ファイル名の拡張子 (例: |
resteasy.language.mappings | デフォルトなし |
ファイル名の拡張子 ( |
resteasy.document.expand.entity.references | false |
外部エンティティーを展開するか、空の文字列に置き換えるか。JBoss EAP では、このパラメーターのデフォルトは |
resteasy.document.secure.processing.feature | true |
|
resteasy.document.secure.disableDTDs | true |
|
resteasy.wider.request.matching | true | JAX-RS 仕様で定義されているようにクラスレベルの式フィルタリングをオフにし、代わりに各 JAX-RS メソッドの完全な式に基づいて一致します。 |
resteasy.use.container.form.params | true |
|
resteasy.add.charset | true |
リソースメソッドが明示的な文字セットなしで |
これらのパラメーターは WEB-INF/web.xml
ファイルで設定されます。
サーブレット 3.0 コンテナーでは、resteasy.scan.*
ファイルの web.xml
設定は無視され、すべての JAX-RS アノテーションが付けられたコンポーネントが自動的にスキャンされます。
たとえば、javax.ws.rs.Application
パラメーターはサーブレット設定の init-param
内で設定されています。
<servlet> <servlet-name>Resteasy</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>org.jboss.resteasy.utils.TestApplication</param-value> </init-param> </servlet>
たとえば、resteasy.document.expand.entity.references
は context-param
内で設定されます。
<context-param> <param-name>resteasy.document.expand.entity.references</param-name> <param-value>true</param-value> </context-param>
以下の RESTEasy パラメーターのデフォルト値を変更すると、RESTEasy アプリケーションが XXE 攻撃に対して脆弱になる可能性があります。
- resteasy.document.expand.entity.references
- resteasy.document.secure.processing.feature
- resteasy.document.secure.disableDTDs
A.3. RESTEasy JavaScript API パラメーター
プロパティー | デフォルト値 | 説明 |
---|---|---|
$entity |
| |
$contentType |
| |
$accepts | */* |
|
$callback |
非同期呼び出しの関数 ( | |
$apiURL | 最後のスラッシュを含まない JAX-RS エンドポイントのベース URI に設定します。 | |
$username | ユーザー名およびパスワードが設定されている場合は、要求のクレデンシャルに使用されます。 | |
$password | ユーザー名およびパスワードが設定されている場合は、要求のクレデンシャルに使用されます。 |
A.4. rest.Request クラスメンバー
メンバー | 説明 |
---|---|
execute(callback) | 現在のオブジェクトに設定されたすべての情報でリクエストを実行します。値はオプションの引数コールバックに渡され、返されません。 |
setAccepts(acceptHeader) |
|
setCredentials(username, password) | 要求の認証情報を設定します。 |
setEntity(entity) | リクエストエンティティーを設定します。 |
setContentType(contentTypeHeader) |
|
setURI(uri) | 要求 URI を設定します。絶対 URI である必要があります。 |
setMethod(method) |
要求メソッドを設定します。デフォルトは |
setAsync(async) |
リクエストを非同期にするかどうかを制御します。デフォルト値は |
addCookie(name, value) | 要求の実行時に現在のドキュメントに指定の Cookie を設定します。これは、ブラウザーで永続化されます。 |
addQueryParameter(name, value) | クエリーパラメーターを URI クエリー部分に追加します。 |
addMatrixParameter(name, value) | リクエスト URI の最後のパスセグメントにマトリクスパラメーター (path パラメーター) を追加します。 |
addHeader(name, value) | 要求ヘッダーを追加します。 |
addForm(name, value) | フォームを追加します。 |
addFormParameter(name, value) | フォームパラメーターを追加します。 |
A.5. RESTEasy 非同期ジョブサービスの設定パラメーター
以下の表は、非同期ジョブサービスの設定可能な context-params
の詳細を示しています。これらのパラメーターは web.xml
ファイルで設定できます。
Parameter | 説明 |
---|---|
resteasy.async.job.service.max.job.results |
メモリーに一度に保持できるジョブ結果の数。デフォルト値は |
resteasy.async.job.service.max.wait |
クライアントがそのジョブをクエリーする際のジョブの最大待機時間。デフォルト値は、 |
resteasy.async.job.service.thread.pool.size |
ジョブを実行するバックグラウンドスレッドのスレッドプールサイズ。デフォルト値は |
resteasy.async.job.service.base.path |
ジョブ URI のベースパスを設定します。デフォルト値は |
<web-app> <context-param> <param-name>resteasy.async.job.service.enabled</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>resteasy.async.job.service.max.job.results</param-name> <param-value>100</param-value> </context-param> <context-param> <param-name>resteasy.async.job.service.max.wait</param-name> <param-value>300000</param-value> </context-param> <context-param> <param-name>resteasy.async.job.service.thread.pool.size</param-name> <param-value>100</param-value> </context-param> <context-param> <param-name>resteasy.async.job.service.base.path</param-name> <param-value>/asynch/jobs</param-value> </context-param> <listener> <listener-class> org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap </listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class> org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher </servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
A.6. JAX-WS ツール
wsconsume
wsconsume
は、JBoss EAP で提供されるコマンドラインツールです。これを使用して、JAX-WS サービスおよび移植可能な JAX-WS サービスおよびクライアントアーティファクトを生成します。
使用方法
wsconsume
ツールは EAP_HOME/bin
ディレクトリーにあり、以下の構文を使用します。
EAP_HOME/bin/wsconsume.sh [options] <wsdl-url>
Windows には、wsconsume.bat
スクリプトを使用してください。
使用例:
Example.wsdl
WSDL ファイルから Java クラスファイルを生成EAP_HOME/bin/wsconsume.sh Example.wsdl
Example.wsdl
WSDL ファイルから Java ソースおよびクラスファイルを生成EAP_HOME/bin/wsconsume.sh -k Example.wsdl
Example.wsdl
WSDL ファイルからmy.org
パッケージに Java ソースとクラスファイルを生成EAP_HOME/bin/wsconsume.sh -k -p my.org Example.wsdl
複数のバインディングファイルを使用した Java ソースおよびクラスファイルの生成
EAP_HOME/bin/wsconsume.sh -k -b schema-binding1.xsd -b schema-binding2.xsd Example.wsdl
利用可能な wsconsume
オプションの全一覧は、--help
引数を使用するか、以下の表を参照してください。
オプション | 説明 |
---|---|
-a、--additionalHeaders | 暗黙的な SOAP ヘッダーの処理を有効にします。 |
-b, --binding=<file> | JAX-WS バインディングまたはデプロイバインディングファイル。 |
-c --catalog=<file> | エンティティー解決用の Casis XML Catalog ファイル。 |
-d --encoding=<charset> | 生成されるソースに使用する文字セットエンコーディング。 |
-e、--extension | SOAP 1.2 バインディング拡張を有効にします。 |
-h、--help | このヘルプメッセージを表示します。 |
-j --clientjar=<name> | Web サービスを呼び出すために生成されたアーティファクトの JAR ファイルを作成します。 |
-k, --keep | Java ソースを維持/生成します。 |
-l, --load-consumer | コンシューマーを読み込み、終了します (debug ユーティリティー)。 |
-n, --nocompile | 生成されたソースをコンパイルしません。 |
-o, --output=<directory> | 生成されたアーティファクトを配置するディレクトリー。 |
-p --package=<name> | 生成されるソースのターゲットパッケージ。 |
-q, --quiet | より静かに |
-s, --source=<directory> | Java ソースを配置するディレクトリー。 |
-t, --target=<2.1 2.2> | JAX-WS 仕様のターゲット。 |
-v, --verbose | 完全例外スタックトレースを表示します。 |
-w --wsdlLocation=<loc> |
|
wsprovide
wsprovide
は、サービスエンドポイント実装の移植可能な JAX-WS アーティファクトを生成する JBoss EAP で提供されるコマンドラインツールです。また、AIA ファイルを生成するオプションもあります。
使用方法
wsprovide
ツールは EAP_HOME/bin
ディレクトリーにあり、以下の構文を使用します。
EAP_HOME/bin/wsprovide.sh [options] <endpoint class name>
Windows には wsprovide.bat
スクリプトを使用してください。
使用例:
output
ディレクトリーに移植可能なアーティファクト用のラッパークラスを生成します。EAP_HOME/bin/wsprovide.sh -o output my.package.MyEndpoint
出力ディレクトリーにラッパークラスと
output
を生成します。EAP_HOME/bin/wsprovide.sh -o output -w my.package.MyEndpoint
他の JAR を参照するエンドポイントの
output
ディレクトリーでラッパークラスを生成します。EAP_HOME/bin/wsprovide.sh -o output -c myapplication1.jar:myapplication2.jar my.org.MyEndpoint
利用可能な wsprovide
オプションの全一覧は、--help
引数を使用するか、以下の表を参照してください。
オプション | 説明 |
---|---|
-a, --address=<address> | WSDL で生成されたポート soap:address。 |
-c, --classpath=<path> | エンドポイントが含まれるクラスパス。 |
-e、--extension | SOAP 1.2 バインディング拡張を有効にします。 |
-h、--help | このヘルプメッセージを表示します。 |
-k, --keep | Java ソースを維持/生成します。 |
-l, --load-provider | プロバイダーを読み込み、終了します (debug ユーティリティー)。 |
-o, --output=<directory> | 生成されたアーティファクトを配置するディレクトリー。 |
-q, --quiet | より静かに |
-r, --resource=<directory> | リソースのアーティファクトを配置するディレクトリー。 |
-s, --source=<directory> | Java ソースを配置するディレクトリー。 |
-t, --show-traces | 完全例外スタックトレースを表示します。 |
-w, --wsdl | WSDL ファイル生成を有効にします。 |
A.7. JAX-WS Common API Reference
JAX-WS の開発概念は、Web サービスのエンドポイントとクライアント間で共有されています。これには、ハンドラーフレームワーク、メッセージコンテキスト、および障害処理が含まれます。
ハンドラーフレームワーク
ハンドラーフレームワークは、クライアントおよびエンドポイント (サーバーコンポーネント) の JAX-WS プロトコルバインディングによって実装されます。バインディングプロバイダーとして知られるプロキシーおよび Dispatch
インスタンスは、それぞれプロトコルバインディングを使用して抽象機能を特定のプロトコルにバインドします。
クライアントおよびサーバー側のハンドラーは、ハンドラーチェーンと呼ばれる順序付けされたリストに編成されます。ハンドラーチェーン内のハンドラーは、メッセージが送受信されるたびに呼び出されます。受信メッセージは、バインディングプロバイダーが処理する前にハンドラーによって処理されます。アウトバウンドメッセージは、バインディングプロバイダーが処理した後にハンドラーによって処理されます。
ハンドラーは、受信メッセージおよびアウトバウンドメッセージにアクセスして変更し、プロパティーのセットを管理するメソッドを提供するメッセージコンテキストで呼び出されます。メッセージコンテキストプロパティーは、ハンドラーとクライアントおよびサービスの実装間の通信だけでなく、個別のハンドラー間の通信を容易にします。異なるタイプのハンドラーは異なるタイプのメッセージコンテキストで呼び出されます。
- 論理ハンドラー
-
論理ハンドラーは、メッセージコンテキストプロパティーおよびメッセージペイロードでのみ動作します。論理ハンドラーはプロトコルに依存しないため、メッセージのプロトコル固有の部分には影響がありません。論理ハンドラーは、インターフェイス
javax.xml.ws.handler.LogicalHandler
を実装します。 - プロトコルハンドラー
-
プロトコルハンドラーは、メッセージコンテキストプロパティーおよびプロトコル固有のメッセージで動作します。プロトコルハンドラーは特定のプロトコルに固有のもので、メッセージのプロトコル固有の側面にアクセスし、変更する可能性があります。プロトコルハンドラーは、
javax.xml.ws.handler.LogicalHandler
を除き、javax.xml.ws.handler.Handler
から派生するインターフェイスを実装します。 - サービスエンドポイントハンドラー
サービスエンドポイントでは、ハンドラーは
@HandlerChain
アノテーションを使用して定義されます。ハンドラーチェーンファイルの場所は、externalForm
の絶対java.net.URL
か ソースファイルまたはクラスファイルからの相対パスのいずれかになります。@WebService @HandlerChain(file = "jaxws-server-source-handlers.xml") public class SOAPEndpointSourceImpl { ... }
- サービスクライアントハンドラー
JAX-WS クライアントでは、ハンドラーはサービスエンドポイントの場合と同様に
@HandlerChain
アノテーションを使用するか、JAX-WS API を使用して動的に定義されます。Service service = Service.create(wsdlURL, serviceName); Endpoint port = (Endpoint)service.getPort(Endpoint.class); BindingProvider bindingProvider = (BindingProvider)port; List<Handler> handlerChain = new ArrayList<Handler>(); handlerChain.add(new LogHandler()); handlerChain.add(new AuthorizationHandler()); handlerChain.add(new RoutingHandler()); bindingProvider.getBinding().setHandlerChain(handlerChain);
setHandlerChain
メソッドの呼び出しが必要です。
メッセージコンテキスト
MessageContext
インターフェイスは、すべての JAX-WS メッセージコンテキストのスーパーインターフェイスです。これは、追加のメソッドと定数を使用して Map<String,Object>
を拡張し、ハンドラーチェーンのハンドラーが処理関連の状態を共有できるようにするプロパティーセットを管理します。たとえば、ハンドラーは put
メソッドを使用してプロパティーをメッセージコンテキストに挿入します。その後、ハンドラーチェーンの他のハンドラーは get
メソッドを使用してメッセージを取得する可能性があります。
プロパティーは APPLICATION
または HANDLER
としてスコープ指定されます。すべてのプロパティーは、特定のエンドポイントのメッセージ交換パターン (MEP) のインスタンスに対するすべてのハンドラーで利用できます。たとえば、論理ハンドラーがプロパティーをメッセージコンテキストに置いた場合、そのプロパティーは MEP インスタンスの実行中にチェーンの任意のプロトコルハンドラーでも利用できます。
非同期メッセージ交換パターン (MEP) を使用すると、HTTP 接続レベルでメッセージを非同期に送受信できます。これを有効にするには、要求コンテキストに追加のプロパティーを設定します。
APPLICATION
レベルでスコープ設定されているプロパティーは、クライアントアプリケーションおよびサービスエンドポイント実装でも利用可能になります。プロパティーの defaultscope
のスコープは HANDLER
です。
論理メッセージと SOAP メッセージは、異なるコンテキストを使用します。
- 論理メッセージコンテキスト
-
論理ハンドラーが呼び出されると、
LogicalMessageContext
タイプのメッセージコンテキストを受け取ります。LogicalMessageContext
はMessageContext
を、メッセージペイロードを取得および変更するメソッドで拡張します。メッセージのプロトコル固有の側面へのアクセスは提供しません。プロトコルバインディングは、論理メッセージコンテキストを介して使用できるメッセージのコンポーネントを定義します。SOAP バインディングにデプロイされた論理ハンドラーは SOAP ボディーのコンテンツにアクセスできますが、SOAP ヘッダーにはアクセスできません。一方、XML/HTTP バインディングは論理ハンドラーがメッセージの XML ペイロード全体にアクセスできることを定義します。 - SOAP メッセージコンテキスト
-
SOAP ハンドラーが呼び出されると、
SOAPMessageContext
を受け取ります。SOAPMessageContext
はMessageContext
を SOAP メッセージペイロードを取得および変更するメソッドで拡張します。
障害処理
アプリケーションが SOAPFaultException
またはアプリケーション固有のユーザー例外を出力する可能性があります。後者の場合、必要な障害ラッパー Bean がデプロイメントに含まれていない場合は、ランタイム時に生成されます。
public void throwSoapFaultException() { SOAPFactory factory = SOAPFactory.newInstance(); SOAPFault fault = factory.createFault("this is a fault string!", new QName("http://foo", "FooCode")); fault.setFaultActor("mr.actor"); fault.addDetail().addChildElement("test"); throw new SOAPFaultException(fault); }
public void throwApplicationException() throws UserException { throw new UserException("validation", 123, "Some validation error"); }
JAX-WS アノテーション
JAX-WS API で使用できるアノテーションは JSR-224 で定義されます。これらのアノテーションは javax.xml.ws
パッケージにあります。XML Web Services の Jakarta EE と同等の Jakarta EE は、Jakarta XML Web Services Specification 2.3 仕様 です。
JWS API で使用できるアノテーションは JSR-181 で定義されます。これらのアノテーションは javax.jws
パッケージにあります。Web Services Metadata の Jakarta EE と同等のものは、Jakarta Web Services Metadata Specification 2.1 仕様 に含まれています。
A.8. 高度な WS-Trust シナリオ
A.8.1. シナリオ: SAML Holder-Of-Key Assertion シナリオ
WS-Trust は、ソフトウェアセキュリティートークンの管理に役立ちます。SAML アサーションは、セキュリティートークンのタイプです。Holder-Of-Key メソッドでは、STS はクライアントの公開鍵を含む SAML トークンを作成し、その秘密鍵で SAML トークンに署名します。クライアントには SAML トークンが含まれ、その秘密鍵で Web サービスへ発進 soap エンベロープを署名します。Web サービスは SOAP メッセージと SAML トークンを検証します。
このシナリオの実装には、以下が必要になります。
-
キーサブジェクトの確認メソッド Holder-Of-Key を持つ SAML トークンは、トークンをスヌーピングできないように保護する必要があります。多くの場合、トークンの所有を防ぐには、HTTPS と組み合わせたホルダーのキートークンで十分です。これは、セキュリティーポリシーが
sp:TransportBinding
およびsp:HttpsToken
を使用することを意味します。 -
Holder-Of-Key トークンには暗号化または署名キーがないため、
SymmetricKey
またはPublicKey
のsp:IssuedToken
をsp:SignedEndorsingSupportingTokens
とともに使用する必要があります。
A.8.1.1. Web サービスプロバイダー
本セクションでは、SAML Holder-Of-Key シナリオの web サービス要素の一覧を紹介します。コンポーネントには以下が含まれます。
A.8.1.1.1. Web Service Provider WSDL
Web Service Provider はコントラクトファーストエンドポイントです。その WS-trust とセキュリティーポリシーはすべて HolderOfKeyService.wsdl
WSDL で宣言されます。このシナリオでは、指定された STS から発行される SymmetricKey
の SAML 2.0 トークンを提供するには、ws-requester
が必要です。STS アドレスは、WSDL に提供されます。トランスポートバインディングポリシーが使用されます。トークンは署名と、sp:SignedEndorsingSupportingTokens
に宣言されます。
セキュリティー設定の詳細は、以下の一覧のコメントに記載されています。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy" name="HolderOfKeyService" xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsaws="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <types> <xsd:schema> <xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy" schemaLocation="HolderOfKeyService_schema1.xsd"/> </xsd:schema> </types> <message name="sayHello"> <part name="parameters" element="tns:sayHello"/> </message> <message name="sayHelloResponse"> <part name="parameters" element="tns:sayHelloResponse"/> </message> <portType name="HolderOfKeyIface"> <operation name="sayHello"> <input message="tns:sayHello"/> <output message="tns:sayHelloResponse"/> </operation> </portType> <!-- The wsp:PolicyReference binds the security requirements on all the endpoints. The wsp:Policy wsu:Id="#TransportSAML2HolderOfKeyPolicy" element is defined later in this file. --> <binding name="HolderOfKeyServicePortBinding" type="tns:HolderOfKeyIface"> <wsp:PolicyReference URI="#TransportSAML2HolderOfKeyPolicy" /> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="sayHello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <!-- The soap:address has been defined to use JBoss's https port, 8443. This is set in conjunction with the sp:TransportBinding policy for https. --> <service name="HolderOfKeyService"> <port name="HolderOfKeyServicePort" binding="tns:HolderOfKeyServicePortBinding"> <soap:address location="https://@jboss.bind.address@:8443/jaxws-samples-wsse-policy-trust-holderofkey/HolderOfKeyService"/> </port> </service> <wsp:Policy wsu:Id="TransportSAML2HolderOfKeyPolicy"> <wsp:ExactlyOne> <wsp:All> <!-- The wsam:Addressing element, indicates that the endpoints of this web service MUST conform to the WS-Addressing specification. The attribute wsp:Optional="false" enforces this assertion. --> <wsam:Addressing wsp:Optional="false"> <wsp:Policy /> </wsam:Addressing> <!-- The sp:TransportBinding element indicates that security is provided by the message exchange transport medium, https. WS-Security policy specification defines the sp:HttpsToken for use in exchanging messages transmitted over HTTPS. --> <sp:TransportBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <sp:TransportToken> <wsp:Policy> <sp:HttpsToken> <wsp:Policy/> </sp:HttpsToken> </wsp:Policy> </sp:TransportToken> <!-- The sp:AlgorithmSuite element, requires the TripleDes algorithm suite be used in performing cryptographic operations. --> <sp:AlgorithmSuite> <wsp:Policy> <sp:TripleDes /> </wsp:Policy> </sp:AlgorithmSuite> <!-- The sp:Layout element, indicates the layout rules to apply when adding items to the security header. The sp:Lax sub-element indicates items are added to the security header in any order that conforms to WSS: SOAP Message Security. --> <sp:Layout> <wsp:Policy> <sp:Lax /> </wsp:Policy> </sp:Layout> <sp:IncludeTimestamp /> </wsp:Policy> </sp:TransportBinding> <!-- The sp:SignedEndorsingSupportingTokens, when transport level security level is used there will be no message signature and the signature generated by the supporting token will sign the Timestamp. --> <sp:SignedEndorsingSupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <!-- The sp:IssuedToken element asserts that a SAML 2.0 security token of type Bearer is expected from the STS. The sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> attribute instructs the runtime to include the initiator's public key with every message sent to the recipient. The sp:RequestSecurityTokenTemplate element directs that all of the children of this element will be copied directly into the body of the RequestSecurityToken (RST) message that is sent to the STS when the initiator asks the STS to issue a token. --> <sp:IssuedToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> <sp:RequestSecurityTokenTemplate> <t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</t:TokenType> <!-- KeyType of "SymmetricKey", the client must prove to the WS service that it possesses a particular symmetric session key. --> <t:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</t:KeyType> </sp:RequestSecurityTokenTemplate> <wsp:Policy> <sp:RequireInternalReference /> </wsp:Policy> <!-- The sp:Issuer element defines the STS's address and endpoint information This information is used by the STSClient. --> <sp:Issuer> <wsaws:Address>http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts-holderofkey/SecurityTokenService</wsaws:Address> <wsaws:Metadata xmlns:wsdli="http://www.w3.org/2006/01/wsdl-instance" wsdli:wsdlLocation="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts-holderofkey/SecurityTokenService?wsdl"> <wsaw:ServiceName xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:stsns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" EndpointName="UT_Port">stsns:SecurityTokenService</wsaw:ServiceName> </wsaws:Metadata> </sp:Issuer> </sp:IssuedToken> </wsp:Policy> </sp:SignedEndorsingSupportingTokens> <!-- The sp:Wss11 element declares WSS: SOAP Message Security 1.1 options to be supported by the STS. These particular elements generally refer to how keys are referenced within the SOAP envelope. These are normally handled by Apache CXF. --> <sp:Wss11> <wsp:Policy> <sp:MustSupportRefIssuerSerial /> <sp:MustSupportRefThumbprint /> <sp:MustSupportRefEncryptedKey /> </wsp:Policy> </sp:Wss11> <!-- The sp:Trust13 element declares controls for WS-Trust 1.3 options. They are policy assertions related to exchanges specifically with client and server challenges and entropy behaviors. Again these are normally handled by Apache CXF. --> <sp:Trust13> <wsp:Policy> <sp:MustSupportIssuedTokens /> <sp:RequireClientEntropy /> <sp:RequireServerEntropy /> </wsp:Policy> </sp:Trust13> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> </definitions>
A.8.1.1.2. SSL 設定
この Web サービスは HTTPS を使用するため、undertow
サブシステムで SSL/TLS サポートを提供するよう JBoss EAP サーバーを設定する必要があります。
Web アプリケーションの HTTPS 設定に関する詳細は、How to Configure Server Security の Configure One-way and Two-way SSL/TLS for Applications を参照してください。
A.8.1.1.3. Web Service Provider インターフェイス
Web サービスプロバイダーのインターフェイス HolderOfKeyIface
クラスは、簡単な Web サービス定義です。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.holderofkey; import javax.jws.WebMethod; import javax.jws.WebService; @WebService ( targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy" ) public interface HolderOfKeyIface { @WebMethod String sayHello(); }
A.8.1.1.4. Web Service Provider の実装
Web サービス実装の HolderOfKeyImpl
クラスは単純な POJO です。標準の WebService
アノテーションを使用してサービスのエンドポイントを定義します。さらに、Apache CXF ランタイムのエンドポイントを設定するために使用される EndpointProperties
および EndpointProperty
には、Apache CXF アノテーションがあります。これらのアノテーションは、Web サービスの主な WS-Security 標準を Java で実装する Apache WSS4J プロジェクトから提供されます。これらのアノテーションは、プログラムでプロパティーをエンドポイントに追加します。プレーン Apache CXF では、これらのプロパティーは、Spring 設定の <jaxws:endpoint>
要素で <jaxws:properties>
要素を使用して設定されることがよくあります。これらのアノテーションにより、プロパティーをコードに設定できます。
WSS4J は、Crypto インターフェイスを使用して、署名の作成/検証に使用するキーと証明書を取得します。これは、このサービスの WSDL によってアサートされます。HolderOfKeyImpl
が提供する WSS4J 設定情報は Crypto の Merlin 実装用です。
最初の EndpointProperty
ステートメントは、Basic Security Profile 1.1 への準拠を無効にします。次の EndpointProperty
ステートメントは、(Merlin) Crypto 設定情報を含む Java プロパティーファイルを宣言します。最後の EndpointProperty
ステートメントは STSHolderOfKeyCallbackHandler
実装クラスを宣言します。これは、キーストアファイルの証明書のユーザーパスワードを取得するために使用されます。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.holderofkey; import org.apache.cxf.annotations.EndpointProperties; import org.apache.cxf.annotations.EndpointProperty; import javax.jws.WebService; @WebService ( portName = "HolderOfKeyServicePort", serviceName = "HolderOfKeyService", wsdlLocation = "WEB-INF/wsdl/HolderOfKeyService.wsdl", targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy", endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.holderofkey.HolderOfKeyIface" ) @EndpointProperties(value = { @EndpointProperty(key = "ws-security.is-bsp-compliant", value = "false"), @EndpointProperty(key = "ws-security.signature.properties", value = "serviceKeystore.properties"), @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.holderofkey.HolderOfKeyCallbackHandler") }) public class HolderOfKeyImpl implements HolderOfKeyIface { public String sayHello() { return "Holder-Of-Key WS-Trust Hello World!"; } }
A.8.1.1.5. 暗号化プロパティーおよびキーストアファイル
WSS4J の Crypto 実装は、Crypto 設定データを含む Java プロパティーファイルを使用してロードおよび設定されます。このファイルには、キーストアの場所、パスワード、デフォルトエイリアスなどの実装固有のプロパティーが含まれます。このアプリケーションは Merlin 実装を使用します。ServiceKeystore.properties
ファイルにはこの情報が含まれます。
servicestore.jks
ファイルは Java KeyStore (JKS) リポジトリーです。これには、myservicekey
および mystskey
の自己署名証明書が含まれています。
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=sspass org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey org.apache.ws.security.crypto.merlin.keystore.file=servicestore.jks
A.8.1.1.6. デフォルトの MANIFEST.MF
このアプリケーションには、org.jboss.ws.cxf.jbossws-cxf-client
モジュールで提供される JBossWS および Apache CXF API へのアクセスが必要です。依存関係ステートメントは、デプロイメント時に提供するようサーバーに指示します。
Manifest-Version: 1.0 Dependencies: org.jboss.ws.cxf.jbossws-cxf-client
A.8.2. シナリオ: SAML Bearer Assertion
WS-Trust は、ソフトウェアセキュリティートークンを管理します。SAML アサーションは、セキュリティートークンのタイプです。SAML Bearer シナリオでは、サービスプロバイダーは、サービスがトークンの署名を検証した後に SAML トークンで定義されたサブジェクトから受信 SOAP リクエストが送信されたことを自動的に信頼します。
このシナリオの実装には、以下の要件があります。
-
Bearer
サブジェクト確認メソッドを持つ SAML トークンは、トークンをスヌーピングしないように保護する必要があります。多くの場合、HTTPS と組み合わせてベアラートークンを使用することで、a man in the middle(中間者) によるトークンの所有を防ぐだけで十分です。これは、sp:TransportBinding
およびsp:HttpsToken
を使用するセキュリティーポリシーを意味します。 -
ベアラートークンには暗号化または署名キーがないため、
bearer
keyType のsp:IssuedToken
はsp:SupportingToken
またはsp:SignedSupportingTokens
とともに使用する必要があります。
A.8.2.1. Web サービスプロバイダー
本セクションでは、SAML Bearer シナリオの web サービス要素を検証します。コンポーネントには以下が含まれます。
A.8.2.1.1. Bearer Web Service Provider WSDL
Web サービスプロバイダーは、コントラクトファーストエンドポイントです。このファイルの WS-trust とセキュリティーポリシーはすべて BearerService.wsdl
WSDL で宣言されます。このシナリオでは、指定された STS から発行される SAML 2.0 Bearer トークンを提供するには、ws-requester
が必要です。STS のアドレスが WSDL に提供されます。HTTPS、TransportBinding
、および HttpsToken
ポリシーは HttpsToken ポリシーを使用して、ws-requester
および ws-provider
の間で送信されるメッセージの SOAP ボディーを保護します。セキュリティー設定の詳細は、以下の一覧にコメントとして提供されます。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy" name="BearerService" xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsaws="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <types> <xsd:schema> <xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy" schemaLocation="BearerService_schema1.xsd"/> </xsd:schema> </types> <message name="sayHello"> <part name="parameters" element="tns:sayHello"/> </message> <message name="sayHelloResponse"> <part name="parameters" element="tns:sayHelloResponse"/> </message> <portType name="BearerIface"> <operation name="sayHello"> <input message="tns:sayHello"/> <output message="tns:sayHelloResponse"/> </operation> </portType> <!-- The wsp:PolicyReference binds the security requirments on all the endpoints. The wsp:Policy wsu:Id="#TransportSAML2BearerPolicy" element is defined later in this file. --> <binding name="BearerServicePortBinding" type="tns:BearerIface"> <wsp:PolicyReference URI="#TransportSAML2BearerPolicy" /> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="sayHello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <!-- The soap:address has been defined to use JBoss's https port, 8443. This is set in conjunction with the sp:TransportBinding policy for https. --> <service name="BearerService"> <port name="BearerServicePort" binding="tns:BearerServicePortBinding"> <soap:address location="https://@jboss.bind.address@:8443/jaxws-samples-wsse-policy-trust-bearer/BearerService"/> </port> </service> <wsp:Policy wsu:Id="TransportSAML2BearerPolicy"> <wsp:ExactlyOne> <wsp:All> <!-- The wsam:Addressing element, indicates that the endpoints of this web service MUST conform to the WS-Addressing specification. The attribute wsp:Optional="false" enforces this assertion. --> <wsam:Addressing wsp:Optional="false"> <wsp:Policy /> </wsam:Addressing> <!-- The sp:TransportBinding element indicates that security is provided by the message exchange transport medium, https. WS-Security policy specification defines the sp:HttpsToken for use in exchanging messages transmitted over HTTPS. --> <sp:TransportBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <sp:TransportToken> <wsp:Policy> <sp:HttpsToken> <wsp:Policy/> </sp:HttpsToken> </wsp:Policy> </sp:TransportToken> <!-- The sp:AlgorithmSuite element, requires the TripleDes algorithm suite be used in performing cryptographic operations. --> <sp:AlgorithmSuite> <wsp:Policy> <sp:TripleDes /> </wsp:Policy> </sp:AlgorithmSuite> <!-- The sp:Layout element, indicates the layout rules to apply when adding items to the security header. The sp:Lax sub-element indicates items are added to the security header in any order that conforms to WSS: SOAP Message Security. --> <sp:Layout> <wsp:Policy> <sp:Lax /> </wsp:Policy> </sp:Layout> <sp:IncludeTimestamp /> </wsp:Policy> </sp:TransportBinding> <!-- The sp:SignedSupportingTokens element causes the supporting tokens to be signed using the primary token that is used to sign the message. --> <sp:SignedSupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <!-- The sp:IssuedToken element asserts that a SAML 2.0 security token of type Bearer is expected from the STS. The sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> attribute instructs the runtime to include the initiator's public key with every message sent to the recipient. The sp:RequestSecurityTokenTemplate element directs that all of the children of this element will be copied directly into the body of the RequestSecurityToken (RST) message that is sent to the STS when the initiator asks the STS to issue a token. --> <sp:IssuedToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> <sp:RequestSecurityTokenTemplate> <t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</t:TokenType> <t:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</t:KeyType> </sp:RequestSecurityTokenTemplate> <wsp:Policy> <sp:RequireInternalReference /> </wsp:Policy> <!-- The sp:Issuer element defines the STS's address and endpoint information This information is used by the STSClient. --> <sp:Issuer> <wsaws:Address>http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts-bearer/SecurityTokenService</wsaws:Address> <wsaws:Metadata xmlns:wsdli="http://www.w3.org/2006/01/wsdl-instance" wsdli:wsdlLocation="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts-bearer/SecurityTokenService?wsdl"> <wsaw:ServiceName xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:stsns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" EndpointName="UT_Port">stsns:SecurityTokenService</wsaw:ServiceName> </wsaws:Metadata> </sp:Issuer> </sp:IssuedToken> </wsp:Policy> </sp:SignedSupportingTokens> <!-- The sp:Wss11 element declares WSS: SOAP Message Security 1.1 options to be supported by the STS. These particular elements generally refer to how keys are referenced within the SOAP envelope. These are normally handled by Apache CXF. --> <sp:Wss11> <wsp:Policy> <sp:MustSupportRefIssuerSerial /> <sp:MustSupportRefThumbprint /> <sp:MustSupportRefEncryptedKey /> </wsp:Policy> </sp:Wss11> <!-- The sp:Trust13 element declares controls for WS-Trust 1.3 options. They are policy assertions related to exchanges specifically with client and server challenges and entropy behaviors. Again these are normally handled by Apache CXF. --> <sp:Trust13> <wsp:Policy> <sp:MustSupportIssuedTokens /> <sp:RequireClientEntropy /> <sp:RequireServerEntropy /> </wsp:Policy> </sp:Trust13> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> </definitions>
A.8.2.1.2. SSL 設定
この Web サービスは HTTPS を使用します。そのため、undertow
サブシステムで SSL サポートを提供するよう JBoss EAP サーバーを設定する必要があります。
Web アプリケーションの HTTPS 設定に関する詳細は、How to Configure Server Security の Configure One-way and Two-way SSL/TLS for Applications を参照してください。
A.8.2.1.3. Bearer Web Service Providers Interface
BearerIface
Bearer Web Service Provider Interface クラスは簡単な Web サービス定義です。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.bearer; import javax.jws.WebMethod; import javax.jws.WebService; @WebService ( targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy" ) public interface BearerIface { @WebMethod String sayHello(); }
A.8.2.1.4. Bearer Web Service Providers 実装
BearerImpl
Web Service Provider Implementation クラスは単純な POJO です。標準の WebService
アノテーションを使用してサービスのエンドポイントを定義します。さらに、Apache CXF ランタイムのエンドポイントを設定するために使用される EndpointProperties
および EndpointProperty
には、Apache CXF アノテーションがあります。これらのアノテーションは、Web サービスの主な WS-Security 標準を Java で実装する Apache WSS4J プロジェクトから提供されます。これらのアノテーションは、プログラムでプロパティーをエンドポイントに追加します。プレーン Apache CXF では、これらのプロパティーは、Spring 設定の <jaxws:endpoint>
要素で <jaxws:properties>
要素を使用して設定されることがよくあります。これらのアノテーションにより、プロパティーをコードに設定できます。
WSS4J は、Crypto インターフェイスを使用して、署名の作成/検証に使用するキーと証明書を取得します。これは、このサービスの WSDL によってアサートされます。BearerImpl
が提供する WSS4J 設定情報は、Crypto の Merlin 実装用です。
Web サービスプロバイダーは SAML トークンで定義されたサブジェクトから受信した受信 SOAP リクエストを自動的に信頼します。そのため、以前の例とは異なり、Crypto CallbackHandler
クラスや署名ユーザー名では必要ありません。ただし、メッセージ署名を検証するには、(Merlin) 暗号化設定情報を含む Java プロパティーファイルが必要になります。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.bearer; import org.apache.cxf.annotations.EndpointProperties; import org.apache.cxf.annotations.EndpointProperty; import javax.jws.WebService; @WebService ( portName = "BearerServicePort", serviceName = "BearerService", wsdlLocation = "WEB-INF/wsdl/BearerService.wsdl", targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy", endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.bearer.BearerIface" ) @EndpointProperties(value = { @EndpointProperty(key = "ws-security.signature.properties", value = "serviceKeystore.properties") }) public class BearerImpl implements BearerIface { public String sayHello() { return "Bearer WS-Trust Hello World!"; } }
A.8.2.1.5. 暗号化プロパティーおよびキーストアファイル
WSS4J の Crypto 実装は、Crypto 設定データを含む Java プロパティーファイルを使用してロードおよび設定されます。このファイルには、キーストアの場所、パスワード、デフォルトエイリアスなどの実装固有のプロパティーが含まれます。このアプリケーションは、Merlin 実装を使用します。ServiceKeystore.properties
ファイルにはこの情報が含まれます。
servicestore.jks
ファイルは Java KeyStore (JKS) リポジトリーです。これには、myservicekey
および mystskey
の自己署名証明書が含まれています。
自己署名証明書は、実稼働環境での使用には適していません。
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=sspass org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey org.apache.ws.security.crypto.merlin.keystore.file=servicestore.jks
A.8.2.1.6. デフォルトの MANIFEST.MF
このアプリケーションがデプロイされると、org.jboss.ws.cxf.jbossws-cxf-client
モジュールで提供される JBossWS および Apache CXF API へのアクセスが必要になります。依存関係ステートメントは、デプロイメント時に提供するようサーバーに指示します。
Manifest-Version: 1.0 Dependencies: org.jboss.ws.cxf.jbossws-cxf-client
A.8.2.2. Bearer Security トークンサービス
本セクションでは、SAML Bearer トークンを提供する Security Token Service 機能を提供するために重要な要素を説明します。コンポーネントには以下が含まれます。
A.8.2.2.1. セキュリティードメイン
STS では JBoss セキュリティードメインを設定する必要があります。Jboss-web.xml
記述子は JBossWS-trust-sts
という名前のセキュリティードメインを宣言し、認証にこのサービスによって使用されます。このセキュリティードメインには、プロパティーファイルと、JBoss EAP サーバー設定ファイルにセキュリティードメイン宣言を追加する必要があります。
このシナリオでは、ドメインにユーザー alice
、パスワード clarinet
、ロール friend
を含める必要があります。jbossws-users.properties
および jbossws-roles.properties
の以下のリストを参照してください。さらに、以下の XML をサーバー設定ファイルの JBoss security
サブシステムに追加する必要があります。
SOME_PATH を適切な情報に置き換えてください。
<security-domain name="JBossWS-trust-sts"> <authentication> <login-module code="UsersRoles" flag="required"> <module-option name="usersProperties" value="/SOME_PATH/jbossws-users.properties"/> <module-option name="unauthenticatedIdentity" value="anonymous"/> <module-option name="rolesProperties" value="/SOME_PATH/jbossws-roles.properties"/> </login-module> </authentication> </security-domain>
例: jboss-web.xml
ファイル
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.4//EN" "> <jboss-web> <security-domain>java:/jaas/JBossWS-trust-sts</security-domain> </jboss-web>
例: .jbossws-users.properties
ファイル
# A sample users.properties file for use with the UsersRolesLoginModule alice=clarinet
例: jbossws-roles.properties
ファイル
# A sample roles.properties file for use with the UsersRolesLoginModule alice=friend
A.8.2.2.2. STS WSDL
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" xmlns:tns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" xmlns:wstrust="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsap10="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"> <wsdl:types> <xs:schema elementFormDefault="qualified" targetNamespace='http://docs.oasis-open.org/ws-sx/ws-trust/200512'> <xs:element name='RequestSecurityToken' type='wst:AbstractRequestSecurityTokenType'/> <xs:element name='RequestSecurityTokenResponse' type='wst:AbstractRequestSecurityTokenType'/> <xs:complexType name='AbstractRequestSecurityTokenType'> <xs:sequence> <xs:any namespace='##any' processContents='lax' minOccurs='0' maxOccurs='unbounded'/> </xs:sequence> <xs:attribute name='Context' type='xs:anyURI' use='optional'/> <xs:anyAttribute namespace='##other' processContents='lax'/> </xs:complexType> <xs:element name='RequestSecurityTokenCollection' type='wst:RequestSecurityTokenCollectionType'/> <xs:complexType name='RequestSecurityTokenCollectionType'> <xs:sequence> <xs:element name='RequestSecurityToken' type='wst:AbstractRequestSecurityTokenType' minOccurs='2' maxOccurs='unbounded'/> </xs:sequence> </xs:complexType> <xs:element name='RequestSecurityTokenResponseCollection' type='wst:RequestSecurityTokenResponseCollectionType'/> <xs:complexType name='RequestSecurityTokenResponseCollectionType'> <xs:sequence> <xs:element ref='wst:RequestSecurityTokenResponse' minOccurs='1' maxOccurs='unbounded'/> </xs:sequence> <xs:anyAttribute namespace='##other' processContents='lax'/> </xs:complexType> </xs:schema> </wsdl:types> <!-- WS-Trust defines the following GEDs --> <wsdl:message name="RequestSecurityTokenMsg"> <wsdl:part name="request" element="wst:RequestSecurityToken"/> </wsdl:message> <wsdl:message name="RequestSecurityTokenResponseMsg"> <wsdl:part name="response" element="wst:RequestSecurityTokenResponse"/> </wsdl:message> <wsdl:message name="RequestSecurityTokenCollectionMsg"> <wsdl:part name="requestCollection" element="wst:RequestSecurityTokenCollection"/> </wsdl:message> <wsdl:message name="RequestSecurityTokenResponseCollectionMsg"> <wsdl:part name="responseCollection" element="wst:RequestSecurityTokenResponseCollection"/> </wsdl:message> <!-- This portType an example of a Requestor (or other) endpoint that Accepts SOAP-based challenges from a Security Token Service --> <wsdl:portType name="WSSecurityRequestor"> <wsdl:operation name="Challenge"> <wsdl:input message="tns:RequestSecurityTokenResponseMsg"/> <wsdl:output message="tns:RequestSecurityTokenResponseMsg"/> </wsdl:operation> </wsdl:portType> <!-- This portType is an example of an STS supporting full protocol --> <!-- The wsdl:portType and data types are XML elements defined by the WS_Trust specification. The wsdl:portType defines the endpoints supported in the STS implementation. This WSDL defines all operations that an STS implementation can support. --> <wsdl:portType name="STS"> <wsdl:operation name="Cancel"> <wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel" message="tns:RequestSecurityTokenMsg"/> <wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/CancelFinal" message="tns:RequestSecurityTokenResponseMsg"/> </wsdl:operation> <wsdl:operation name="Issue"> <wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue" message="tns:RequestSecurityTokenMsg"/> <wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal" message="tns:RequestSecurityTokenResponseCollectionMsg"/> </wsdl:operation> <wsdl:operation name="Renew"> <wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew" message="tns:RequestSecurityTokenMsg"/> <wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/RenewFinal" message="tns:RequestSecurityTokenResponseMsg"/> </wsdl:operation> <wsdl:operation name="Validate"> <wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Validate" message="tns:RequestSecurityTokenMsg"/> <wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/ValidateFinal" message="tns:RequestSecurityTokenResponseMsg"/> </wsdl:operation> <wsdl:operation name="KeyExchangeToken"> <wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/KET" message="tns:RequestSecurityTokenMsg"/> <wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/KETFinal" message="tns:RequestSecurityTokenResponseMsg"/> </wsdl:operation> <wsdl:operation name="RequestCollection"> <wsdl:input message="tns:RequestSecurityTokenCollectionMsg"/> <wsdl:output message="tns:RequestSecurityTokenResponseCollectionMsg"/> </wsdl:operation> </wsdl:portType> <!-- This portType is an example of an endpoint that accepts Unsolicited RequestSecurityTokenResponse messages --> <wsdl:portType name="SecurityTokenResponseService"> <wsdl:operation name="RequestSecurityTokenResponse"> <wsdl:input message="tns:RequestSecurityTokenResponseMsg"/> </wsdl:operation> </wsdl:portType> <!-- The wsp:PolicyReference binds the security requirments on all the STS endpoints. The wsp:Policy wsu:Id="UT_policy" element is later in this file. --> <wsdl:binding name="UT_Binding" type="wstrust:STS"> <wsp:PolicyReference URI="#UT_policy"/> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="Issue"> <soap:operation soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"/> <wsdl:input> <wsp:PolicyReference URI="#Input_policy"/> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <wsp:PolicyReference URI="#Output_policy"/> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="Validate"> <soap:operation soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Validate"/> <wsdl:input> <wsp:PolicyReference URI="#Input_policy"/> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <wsp:PolicyReference URI="#Output_policy"/> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="Cancel"> <soap:operation soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel"/> <wsdl:input> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="Renew"> <soap:operation soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew"/> <wsdl:input> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="KeyExchangeToken"> <soap:operation soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/KeyExchangeToken"/> <wsdl:input> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> <wsdl:operation name="RequestCollection"> <soap:operation soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/RequestCollection"/> <wsdl:input> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="SecurityTokenService"> <wsdl:port name="UT_Port" binding="tns:UT_Binding"> <soap:address location="http://localhost:8080/SecurityTokenService/UT"/> </wsdl:port> </wsdl:service> <wsp:Policy wsu:Id="UT_policy"> <wsp:ExactlyOne> <wsp:All> <!-- The sp:UsingAddressing element, indicates that the endpoints of this web service conforms to the WS-Addressing specification. More detail can be found here: [http://www.w3.org/TR/2006/CR-ws-addr-wsdl-20060529] --> <wsap10:UsingAddressing/> <!-- The sp:SymmetricBinding element indicates that security is provided at the SOAP layer and any initiator must authenticate itself by providing WSS UsernameToken credentials. --> <sp:SymmetricBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <!-- In a symmetric binding, the keys used for encrypting and signing in both directions are derived from a single key, the one specified by the sp:ProtectionToken element. The sp:X509Token sub-element declares this key to be a X.509 certificate and the IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never" attribute adds the requirement that the token MUST NOT be included in any messages sent between the initiator and the recipient; rather, an external reference to the token should be used. Lastly the WssX509V3Token10 sub-element declares that the Username token presented by the initiator should be compliant with Web Services Security UsernameToken Profile 1.0 specification. [ http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf ] --> <sp:ProtectionToken> <wsp:Policy> <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never"> <wsp:Policy> <sp:RequireDerivedKeys/> <sp:RequireThumbprintReference/> <sp:WssX509V3Token10/> </wsp:Policy> </sp:X509Token> </wsp:Policy> </sp:ProtectionToken> <!-- The sp:AlgorithmSuite element, requires the Basic256 algorithm suite be used in performing cryptographic operations. --> <sp:AlgorithmSuite> <wsp:Policy> <sp:Basic256/> </wsp:Policy> </sp:AlgorithmSuite> <!-- The sp:Layout element, indicates the layout rules to apply when adding items to the security header. The sp:Lax sub-element indicates items are added to the security header in any order that conforms to WSS: SOAP Message Security. --> <sp:Layout> <wsp:Policy> <sp:Lax/> </wsp:Policy> </sp:Layout> <sp:IncludeTimestamp/> <sp:EncryptSignature/> <sp:OnlySignEntireHeadersAndBody/> </wsp:Policy> </sp:SymmetricBinding> <!-- The sp:SignedSupportingTokens element declares that the security header of messages must contain a sp:UsernameToken and the token must be signed. The attribute IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient" on sp:UsernameToken indicates that the token MUST be included in all messages sent from initiator to the recipient and that the token MUST NOT be included in messages sent from the recipient to the initiator. And finally the element sp:WssUsernameToken10 is a policy assertion indicating the Username token should be as defined in Web Services Security UsernameToken Profile 1.0 --> <sp:SignedSupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> <wsp:Policy> <sp:WssUsernameToken10/> </wsp:Policy> </sp:UsernameToken> </wsp:Policy> </sp:SignedSupportingTokens> <!-- The sp:Wss11 element declares WSS: SOAP Message Security 1.1 options to be supported by the STS. These particular elements generally refer to how keys are referenced within the SOAP envelope. These are normally handled by Apache CXF. --> <sp:Wss11 xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <sp:MustSupportRefKeyIdentifier/> <sp:MustSupportRefIssuerSerial/> <sp:MustSupportRefThumbprint/> <sp:MustSupportRefEncryptedKey/> </wsp:Policy> </sp:Wss11> <!-- The sp:Trust13 element declares controls for WS-Trust 1.3 options. They are policy assertions related to exchanges specifically with client and server challenges and entropy behaviors. Again these are normally handled by Apache CXF. --> <sp:Trust13 xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <sp:MustSupportIssuedTokens/> <sp:RequireClientEntropy/> <sp:RequireServerEntropy/> </wsp:Policy> </sp:Trust13> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> <wsp:Policy wsu:Id="Input_policy"> <wsp:ExactlyOne> <wsp:All> <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <sp:Body/> <sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/> </sp:SignedParts> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> <wsp:Policy wsu:Id="Output_policy"> <wsp:ExactlyOne> <wsp:All> <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <sp:Body/> <sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/> <sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/> </sp:SignedParts> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> </wsdl:definitions>
A.8.2.2.3. STS 実装クラス
Apache CXF の SecurityTokenServiceProvider
は、WS-Trust 仕様で定義されているプロトコルおよび機能に準拠する Web サービスプロバイダーです。モジュールアーキテクチャーがあり、そのコンポーネントは設定可能または置き換え可能です。プラグインは実装および設定することで有効にされるオプション機能があります。SecurityTokenServiceProvider
から拡張し、デフォルト設定を上書きすることで、独自の STS をカスタマイズできます。
SampleSTSBearer
STS 実装クラスは、SecurityTokenServiceProvider
から拡張される属性です。
SampleSTSBearer
クラスは WebService
アノテーションではなく、WebServiceProvider
アノテーションで定義されます。このアノテーションは、Provider
サービスをプロバイダーベースのエンドポイントとして定義し、Web サービスへのメッセージング指向アプローチをサポートします。特に、交換したメッセージが XML ドキュメントであることを示唆します。SecurityTokenServiceProvider
は javax.xml.ws.Provider
インターフェイスの実装です。一方、WebService
アノテーションは SOAP レンダリングを使用したメッセージ交換をサポートするサービスエンドポイントのインターフェイスベースのエンドポイントを定義します。
BearerImpl
クラスで行ったように WSS4J アノテーションの EndpointProperties
および EndpointProperty
は Apache CXF ランタイムのエンドポイント設定を提供します。最初の EndpointProperty
ステートメントは、メッセージ署名に使用するユーザーの名前を宣言します。これは、キーストアのエイリアス名として使用され、署名用にユーザーの証明書および秘密鍵を取得します。次の EndpointProperty
ステートメントは、(Merlin) Crypto 設定情報を含む Java プロパティーを宣言します。この場合、メッセージの署名および暗号化の両方を行います。WSS4J はこのファイルと、メッセージ処理に必要な情報を読み取ります。最後の EndpointProperty
ステートメントは STSBearerCallbackHandler
実装クラスを宣言します。これは、キーストアファイルの証明書のユーザーパスワードを取得するために使用されます。
この実装では、トークンの発行、トークンの検証、および静的なプロパティーの操作をカスタマイズします。
StaticSTSProperties
は、STS でリソースを設定するための選択プロパティーを設定するために使用されます。これは、WSS4J アノテーションの設定の重複のように表示されます。値は同じですが、設定される基礎の構造は異なるため、この情報は両方の場所で宣言する必要があります。
setIssuer
設定は、発行している STS を一意に識別するため、重要です。発行者文字列は発行されたトークンに組み込まれ、トークンを検証すると、STS は発行者の文字列値を確認します。したがって、STS が発行されたトークンを認識できるように、一貫性のある方法で発行者の文字列を使用することが重要になります。
setEndpoints
呼び出しは、アドレス別に許可されるトークン受信側のセットの宣言を許可します。アドレスは reg-ex パターンとして指定されます。
tokenIssue
にはモジュラー構造があります。これにより、カスタム動作をメッセージの処理にインジェクトできます。この場合、SecurityTokenServiceProvider
のデフォルト動作を上書きし、SAML トークン処理を実行します。Apache CXF は、SAMLTokenProvider
の実装を提供します。これは、SAMLTokenProvider を作成する代わりに使用できます。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsbearer; import org.apache.cxf.annotations.EndpointProperties; import org.apache.cxf.annotations.EndpointProperty; import org.apache.cxf.sts.StaticSTSProperties; import org.apache.cxf.sts.operation.TokenIssueOperation; import org.apache.cxf.sts.service.ServiceMBean; import org.apache.cxf.sts.service.StaticService; import org.apache.cxf.sts.token.provider.SAMLTokenProvider; import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider; import javax.xml.ws.WebServiceProvider; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @WebServiceProvider(serviceName = "SecurityTokenService", portName = "UT_Port", targetNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/", wsdlLocation = "WEB-INF/wsdl/bearer-ws-trust-1.4-service.wsdl") //dependency on org.apache.cxf module or on module that exports org.apache.cxf (e.g. org.jboss.ws.cxf.jbossws-cxf-client) is needed, otherwise Apache CXF annotations are ignored @EndpointProperties(value = { @EndpointProperty(key = "ws-security.signature.username", value = "mystskey"), @EndpointProperty(key = "ws-security.signature.properties", value = "stsKeystore.properties"), @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsbearer.STSBearerCallbackHandler") }) public class SampleSTSBearer extends SecurityTokenServiceProvider { public SampleSTSBearer() throws Exception { super(); StaticSTSProperties props = new StaticSTSProperties(); props.setSignatureCryptoProperties("stsKeystore.properties"); props.setSignatureUsername("mystskey"); props.setCallbackHandlerClass(STSBearerCallbackHandler.class.getName()); props.setEncryptionCryptoProperties("stsKeystore.properties"); props.setEncryptionUsername("myservicekey"); props.setIssuer("DoubleItSTSIssuer"); List<ServiceMBean> services = new LinkedList<ServiceMBean>(); StaticService service = new StaticService(); service.setEndpoints(Arrays.asList( "https://localhost:(\\d)*/jaxws-samples-wsse-policy-trust-bearer/BearerService", "https://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-bearer/BearerService", "https://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-bearer/BearerService" )); services.add(service); TokenIssueOperation issueOperation = new TokenIssueOperation(); issueOperation.getTokenProviders().add(new SAMLTokenProvider()); issueOperation.setServices(services); issueOperation.setStsProperties(props); this.setIssueOperation(issueOperation); } }
A.8.2.2.4. STSBearerCallbackHandler クラス
STSBearerCallbackHandler
は、WSS4J Crypto API のコールバックハンドラーです。これは、キーストアの秘密鍵のパスワードを取得するために使用されます。このクラスにより、Apache CXF はメッセージ署名に使用するユーザー名のパスワードを取得できます。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsbearer; import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler; import java.util.HashMap; import java.util.Map; public class STSBearerCallbackHandler extends PasswordCallbackHandler { public STSBearerCallbackHandler() { super(getInitMap()); } private static Map<String, String> getInitMap() { Map<String, String> passwords = new HashMap<String, String>(); passwords.put("mystskey", "stskpass"); passwords.put("alice", "clarinet"); return passwords; } }
A.8.2.2.5. 暗号化プロパティーおよびキーストアファイル
WSS4J の Crypto 実装は、Crypto 設定データを含む Java プロパティーファイルを使用してロードおよび設定されます。このファイルには、キーストアの場所、パスワード、デフォルトエイリアスなどの実装固有のプロパティーが含まれます。このアプリケーションは、Merlin 実装を使用します。stsKeystore.properties
ファイルにはこの情報が含まれます。
servicestore.jks
ファイルは Java KeyStore (JKS) リポジトリーです。これには、myservicekey
および mystskey
の自己署名証明書が含まれています。
自己署名証明書は、実稼働環境での使用には適していません。
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=stsspass org.apache.ws.security.crypto.merlin.keystore.file=stsstore.jks
A.8.2.2.6. デフォルトの MANIFEST.MF
このアプリケーションには、org.jboss.ws.cxf.jbossws-cxf-client
モジュールで提供される JBossWS および Apache CXF API へのアクセスが必要です。また、org.jboss.ws.cxf.sts
モジュールも SampleSTS
コンストラクターで STS 設定を構築するために必要です。依存関係ステートメントは、デプロイメント時に提供するようサーバーに指示します。
Manifest-Version: 1.0 Dependencies: org.jboss.ws.cxf.jbossws-cxf-client,org.jboss.ws.cxf.sts
A.8.2.3. Web Service Requester
このセクションでは、SAML Bearer シナリオで説明したように、エンドポイントセキュリティーを実装する Web サービスを呼び出す際に重要な要素について詳しく説明します。ここで説明するコンポーネントには、以下が含まれます。
A.8.2.3.1. Web Service Requester 実装
ws-requester
(クライアント) は、Web サービスへの参照を作成する標準手順を使用します。エンドポイントのセキュリティー要件に対応するため、Web サービスの Request Context にはメッセージ生成に必要な情報が設定されます。さらに、STS と通信する STSClient
は、同じ値で設定されます。
接尾辞が .it
のキー文字列は、これらの設定を STSClient
に属するものとしてフラグします。内部 Apache CXF コードにより、のサービス呼び出し用に自動生成される STSClient
にこの情報が割り当てられます。
STSCLient
のセットアップには別の方法があります。ユーザーは STSClient
の独自のインスタンスを提供できます。Apache CXF コードはこのオブジェクトを使用し、自動生成は行いません。この方法で STSClient
を提供する場合は、ユーザーには org.apache.cxf.Bus
を指定する必要があります。また、設定キーには .it
接尾辞を付けないでください。これは、ActAs および OnBehalfOf の例で使用されます。
String serviceURL = "https://" + getServerHost() + ":8443/jaxws-samples-wsse-policy-trust-bearer/BearerService"; final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy", "BearerService"); Service service = Service.create(new URL(serviceURL + "?wsdl"), serviceName); BearerIface proxy = (BearerIface) service.getPort(BearerIface.class); Map<String, Object> ctx = ((BindingProvider)proxy).getRequestContext(); // set the security related configuration information for the service "request" ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler()); ctx.put(SecurityConstants.SIGNATURE_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); ctx.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey"); ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey"); //-- Configuration settings that will be transfered to the STSClient // "alice" is the name provided for the WSS Username. Her password will // be retreived from the ClientCallbackHander by the STSClient. ctx.put(SecurityConstants.USERNAME + ".it", "alice"); ctx.put(SecurityConstants.CALLBACK_HANDLER + ".it", new ClientCallbackHandler()); ctx.put(SecurityConstants.ENCRYPT_PROPERTIES + ".it", Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); ctx.put(SecurityConstants.ENCRYPT_USERNAME + ".it", "mystskey"); ctx.put(SecurityConstants.STS_TOKEN_USERNAME + ".it", "myclientkey"); ctx.put(SecurityConstants.STS_TOKEN_PROPERTIES + ".it", Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); ctx.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO + ".it", "true"); proxy.sayHello();
A.8.2.3.2. ClientCallbackHandler
ClientCallbackHandler
は WSS4J Crypto API のコールバックハンドラーです。これは、キーストアの秘密鍵のパスワードを取得するために使用されます。このクラスにより、Apache CXF はメッセージ署名に使用するユーザー名のパスワードを取得できます。
ここでは、ユーザー alice
とパスワードが提供されています。この情報は (JKS) キーストアではなく、セキュリティードメインで提供されています。これは jbossws-users.properties
ファイルで宣言されます。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class ClientCallbackHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof WSPasswordCallback) { WSPasswordCallback pc = (WSPasswordCallback) callbacks[i]; if ("myclientkey".equals(pc.getIdentifier())) { pc.setPassword("ckpass"); break; } else if ("alice".equals(pc.getIdentifier())) { pc.setPassword("clarinet"); break; } else if ("bob".equals(pc.getIdentifier())) { pc.setPassword("trombone"); break; } else if ("myservicekey".equals(pc.getIdentifier())) { // rls test added for bearer test pc.setPassword("skpass"); break; } } } } }
A.8.2.3.3. 暗号化プロパティーおよびキーストアファイル
WSS4J の Crypto 実装は、Crypto 設定データを含む Java プロパティーファイルを使用してロードおよび設定されます。このファイルには、キーストアの場所、パスワード、デフォルトエイリアスなどの実装固有のプロパティーが含まれます。このアプリケーションは、Merlin 実装を使用します。clientKeystore.properties
ファイルにはこの情報が含まれます。
clientstore.jks
ファイルは Java KeyStore (JKS) リポジトリーです。これには、myservicekey
および mystskey
の自己署名証明書が含まれています。
自己署名証明書は、実稼働環境での使用には適していません。
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=cspass org.apache.ws.security.crypto.merlin.keystore.alias=myclientkey org.apache.ws.security.crypto.merlin.keystore.file=META-INF/clientstore.jks
A.8.3. シナリオ: onBehalfOf WS-Trust
OnBehalfOf
機能は、プロキシーパターンを使用するシナリオで使用されます。このようなシナリオでは、クライアントは直接 STS にアクセスできず、プロキシーゲートウェイを介して通信します。プロキシーゲートウェイは呼び出し元を認証し、処理するために実際の STS に送信された RequestSecurityToken
(RST) の OnBehalfOf
要素に呼び出し元に関する情報を配置します。生成されるトークンにはプロキシーのクライアントに関連する要求のみが含まれるため、プロキシーは発行されたトークンの受信側に対して完全に透過的になります。
onBehalfOf
は RST の新しいサブ要素にすぎません。これは、トークンが STS とネゴシエートされる場合の元の呼び出し元に関する追加情報を提供します。OnBehalfOf
要素は通常、クライアントがサービスにアクセスするために、アイデンティティー要求 (名前、ロール、承認コードなど) を持つトークンの形式を取ります。
OnBehalfOf
シナリオは、基本的な WS-Trust シナリオの拡張機能です。この例では、onBehalfOf
サービスはユーザーの代わりに ws-service
を呼び出します。基本的なシナリオのコードには少数のみ追加されています。OnBehalfOf
Web サービスプロバイダーとコールバックハンドラーが追加されました。OnBehalfOf
Web サービス の WSDL は、ws-provider
と同じセキュリティーポリシーが適用されます。UsernameTokenCallbackHandler
は、ActAs
と共有するユーティリティーです。OnBehalfOf
要素のコンテンツを生成します。最後に、OnBehalfOf
と ActAs
の両方に共通なコードの追加があります。
A.8.3.1. Web サービスプロバイダー
ここでは、OnBehalfOf
の例の要件に対応するために更新された、基本的な WS-Trust シナリオからの web サービス要素について説明します。コンポーネントには以下が含まれます。
A.8.3.1.1. Web Service Provider WSDL
OnBehalfOf
web サービスプロバイダーの WSDL は、ws-provider
のクローンです。wsp:policy
セクションは同じです。サービスエンドポイント、targetNamespace
、portType
、binding
名、service
の更新があります。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy" name="OnBehalfOfService" xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsaws="http://www.w3.org/2005/08/addressing" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <types> <xsd:schema> <xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy" schemaLocation="OnBehalfOfService_schema1.xsd"/> </xsd:schema> </types> <message name="sayHello"> <part name="parameters" element="tns:sayHello"/> </message> <message name="sayHelloResponse"> <part name="parameters" element="tns:sayHelloResponse"/> </message> <portType name="OnBehalfOfServiceIface"> <operation name="sayHello"> <input message="tns:sayHello"/> <output message="tns:sayHelloResponse"/> </operation> </portType> <binding name="OnBehalfOfServicePortBinding" type="tns:OnBehalfOfServiceIface"> <wsp:PolicyReference URI="#AsymmetricSAML2Policy" /> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="sayHello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> <wsp:PolicyReference URI="#Input_Policy" /> </input> <output> <soap:body use="literal"/> <wsp:PolicyReference URI="#Output_Policy" /> </output> </operation> </binding> <service name="OnBehalfOfService"> <port name="OnBehalfOfServicePort" binding="tns:OnBehalfOfServicePortBinding"> <soap:address location="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-onbehalfof/OnBehalfOfService"/> </port> </service> </definitions>
A.8.3.1.2. Web Service Provider インターフェイス
OnBehalfOfServiceIface
Web サービスプロバイダーのインターフェイスクラスは簡単な Web サービス定義です。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof; import javax.jws.WebMethod; import javax.jws.WebService; @WebService ( targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy" ) public interface OnBehalfOfServiceIface { @WebMethod String sayHello(); }
A.8.3.1.3. Web Service Provider の実装
OnBehalfOfServiceImpl
Web サービスプロバイダー実装は単純な POJO です。このアノテーションは標準の WebService
アノテーションを使用して、サービスエンドポイントと Apache CXF ランタイムのエンドポイントを設定するために使用される EndpointProperties
および EndpointProperty
の 2 つ Apache WSS4J アノテーションを定義します。提供される WSS4J 設定情報は、WSS4J の Crypto Merlin 実装用です。
OnBehalfOfServiceImpl
は、ユーザーの代替として動作する ServiceImpl
を呼び出します。setupService
メソッドは、必要な設定を実行します。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; import org.apache.cxf.annotations.EndpointProperties; import org.apache.cxf.annotations.EndpointProperty; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.trust.STSClient; import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service.ServiceIface; import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared.WSTrustAppUtils; import javax.jws.WebService; import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider; import javax.xml.ws.Service; import java.net.*; import java.util.Map; @WebService ( portName = "OnBehalfOfServicePort", serviceName = "OnBehalfOfService", wsdlLocation = "WEB-INF/wsdl/OnBehalfOfService.wsdl", targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy", endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof.OnBehalfOfServiceIface" ) @EndpointProperties(value = { @EndpointProperty(key = "ws-security.signature.username", value = "myactaskey"), @EndpointProperty(key = "ws-security.signature.properties", value = "actasKeystore.properties"), @EndpointProperty(key = "ws-security.encryption.properties", value = "actasKeystore.properties"), @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof.OnBehalfOfCallbackHandler") }) public class OnBehalfOfServiceImpl implements OnBehalfOfServiceIface { public String sayHello() { try { ServiceIface proxy = setupService(); return "OnBehalfOf " + proxy.sayHello(); } catch (MalformedURLException e) { e.printStackTrace(); } return null; } /** * * @return * @throws MalformedURLException */ private ServiceIface setupService()throws MalformedURLException { ServiceIface proxy = null; Bus bus = BusFactory.newInstance().createBus(); try { BusFactory.setThreadDefaultBus(bus); final String serviceURL = "http://" + WSTrustAppUtils.getServerHost() + ":8080/jaxws-samples-wsse-policy-trust/SecurityService"; final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService"); final URL wsdlURL = new URL(serviceURL + "?wsdl"); Service service = Service.create(wsdlURL, serviceName); proxy = (ServiceIface) service.getPort(ServiceIface.class); Map<String, Object> ctx = ((BindingProvider) proxy).getRequestContext(); ctx.put(SecurityConstants.CALLBACK_HANDLER, new OnBehalfOfCallbackHandler()); ctx.put(SecurityConstants.SIGNATURE_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "actasKeystore.properties" )); ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myactaskey" ); ctx.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "../../META-INF/clientKeystore.properties" )); ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey"); STSClient stsClient = new STSClient(bus); Map<String, Object> props = stsClient.getProperties(); props.put(SecurityConstants.USERNAME, "bob"); props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey"); props.put(SecurityConstants.STS_TOKEN_USERNAME, "myactaskey" ); props.put(SecurityConstants.STS_TOKEN_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "actasKeystore.properties" )); props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true"); ctx.put(SecurityConstants.STS_CLIENT, stsClient); } finally { bus.shutdown(true); } return proxy; } }
A.8.3.1.4. OnBehalfOfCallbackHandler クラス
OnBehalfOfCallbackHandler
は、WSS4J Crypto API のコールバックハンドラーです。これは、キーストアの秘密鍵のパスワードを取得するために使用されます。このクラスにより、Apache CXF はメッセージ署名に使用するユーザー名のパスワードを取得できます。このクラスは、このサービス myactaskey
のパスワード、OnBehalfOf
ユーザー、alice
を返すように更新されました。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof; import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler; import java.util.HashMap; import java.util.Map; public class OnBehalfOfCallbackHandler extends PasswordCallbackHandler { public OnBehalfOfCallbackHandler() { super(getInitMap()); } private static Map<String, String> getInitMap() { Map<String, String> passwords = new HashMap<String, String>(); passwords.put("myactaskey", "aspass"); passwords.put("alice", "clarinet"); passwords.put("bob", "trombone"); return passwords; } }
A.8.3.2. Web Service Requester
ここでは、OnBehalfOf
の例の要件に対応するために更新された、基本的な WS-Trust シナリオからの ws-requester
要素について説明します。コンポーネントは次のとおりです。
A.8.3.2.1. OnBehalfOf Web Service Requester Implementation クラス
OnBehalfOf
ws-requester
クライアントは、Web サービスへの参照を最初の 4 行で作成するために標準手順を使用します。エンドポイントのセキュリティー要件に対応するため、Web サービスのリクエストコンテキストは BindingProvider
を使用して設定されます。メッセージ生成に必要な情報は、それを介して提供されます。OnBehalfOf
ユーザー alice
はこのセクションおよび callbackHandler
で宣言され、OnBehalfOf
メッセージ要素 の内容の生成のために UsernameTokenCallbackHandler
が STSClient
に提供されます。この例では、STSClient
オブジェクトが作成され、プロキシーのリクエストコンテキストに提供されます。別の方法として、Basic Scenario クライアントで行なわれたように、.it
接尾辞でタグ付けされた鍵を提供する方法があります。OnBehalfOf
の使用は、stsClient.setOnBehalfOf
呼び出しメソッドによって設定されます。または、SecurityConstants.STS_TOKEN_ON_BEHALF_OF
キーと properties マップの値を使用します。
final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy", "OnBehalfOfService"); final URL wsdlURL = new URL(serviceURL + "?wsdl"); Service service = Service.create(wsdlURL, serviceName); OnBehalfOfServiceIface proxy = (OnBehalfOfServiceIface) service.getPort(OnBehalfOfServiceIface.class); Bus bus = BusFactory.newInstance().createBus(); try { BusFactory.setThreadDefaultBus(bus); Map<String, Object> ctx = proxy.getRequestContext(); ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler()); ctx.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myactaskey"); ctx.put(SecurityConstants.SIGNATURE_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey"); // user and password OnBehalfOf user // UsernameTokenCallbackHandler will extract this information when called ctx.put(SecurityConstants.USERNAME,"alice"); ctx.put(SecurityConstants.PASSWORD, "clarinet"); STSClient stsClient = new STSClient(bus); // Providing the STSClient the mechanism to create the claims contents for OnBehalfOf stsClient.setOnBehalfOf(new UsernameTokenCallbackHandler()); Map<String, Object> props = stsClient.getProperties(); props.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler()); props.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey"); props.put(SecurityConstants.STS_TOKEN_USERNAME, "myclientkey"); props.put(SecurityConstants.STS_TOKEN_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true"); ctx.put(SecurityConstants.STS_CLIENT, stsClient); } finally { bus.shutdown(true); } proxy.sayHello();
A.8.4. シナリオ: ActAs WS-Trust
ActAs
機能は、複合委任が必要なシナリオで使用されます。これは一般的に、ログインユーザーの代わりにサービスが呼び出される、またはサービスが元の呼び出し元の代わりに別のサービスを呼び出す、階層化システムで使用されます。
ActAs
は、RequestSecurityToken
(RST) の新しいサブ要素ではありません。これは、トークンが STS とネゴシエートされる場合の元の呼び出し元に関する追加情報を提供します。ActAs
要素は通常、クライアントがサービスにアクセスするために、アイデンティティー要求 (名前、ロール、承認コードなど) を持つトークンの形式を取ります。
ActAs
シナリオは、基本的な WS-Trust シナリオの拡張機能です。この例では、ActAs
サービスはユーザーの代わりに ws-service
を呼び出します。基本的なシナリオのコードには少数のみ追加されています。ActAs
Web サービスプロバイダーとコールバックハンドラーが追加されました。ActAs
web サービスの WSDL では、ws-provider
と同じセキュリティーポリシーが適用されます。UsernameTokenCallbackHandler
は、ActAs
要素のコンテンツを生成する新しいユーティリティーです。最後に、ActAs
リクエストをサポートするために、STS にコードがいくつか追加されています。
A.8.4.1. Web サービスプロバイダー
ここでは、ActAs
の例のニーズに対応するために変更された基本的な WS-Trust シナリオからの web サービス要素について詳しく説明します。コンポーネントには以下が含まれます。
A.8.4.1.1. Web Service Provider WSDL
ActAs
web サービスプロバイダーの WSDL は ws-provider
WSDL のクローンです。wsp:policy
セクションは同じです。サービスエンドポイント、targetNamespace
、portType
、binding
名、service
の変更があります。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" name="ActAsService" xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsaws="http://www.w3.org/2005/08/addressing" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <types> <xsd:schema> <xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" schemaLocation="ActAsService_schema1.xsd"/> </xsd:schema> </types> <message name="sayHello"> <part name="parameters" element="tns:sayHello"/> </message> <message name="sayHelloResponse"> <part name="parameters" element="tns:sayHelloResponse"/> </message> <portType name="ActAsServiceIface"> <operation name="sayHello"> <input message="tns:sayHello"/> <output message="tns:sayHelloResponse"/> </operation> </portType> <binding name="ActAsServicePortBinding" type="tns:ActAsServiceIface"> <wsp:PolicyReference URI="#AsymmetricSAML2Policy" /> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="sayHello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> <wsp:PolicyReference URI="#Input_Policy" /> </input> <output> <soap:body use="literal"/> <wsp:PolicyReference URI="#Output_Policy" /> </output> </operation> </binding> <service name="ActAsService"> <port name="ActAsServicePort" binding="tns:ActAsServicePortBinding"> <soap:address location="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-actas/ActAsService"/> </port> </service> </definitions>
A.8.4.1.2. Web Service Provider インターフェイス
ActAsServiceIface
Web サービスプロバイダーのインターフェイスクラスは簡単な Web サービス定義です。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas; import javax.jws.WebMethod; import javax.jws.WebService; @WebService ( targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" ) public interface ActAsServiceIface { @WebMethod String sayHello(); }
A.8.4.1.3. Web Service Provider の実装
ActAsServiceImpl
Web サービスプロバイダー実装は単純な POJO です。このアノテーションは標準の WebService
アノテーションを使用して、サービスエンドポイントと Apache CXF ランタイムのエンドポイントを設定するために使用される EndpointProperties
および EndpointProperty
の 2 つ Apache WSS4J アノテーションを定義します。提供される WSS4J 設定情報は、WSS4J の Crypto Merlin 実装用です。
ActAsServiceImpl
は ServiceImpl
をユーザーの代わりに呼び出します。setupService
メソッドは、必要な設定を実行します。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; import org.apache.cxf.annotations.EndpointProperties; import org.apache.cxf.annotations.EndpointProperty; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.trust.STSClient; import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service.ServiceIface; import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared.WSTrustAppUtils; import javax.jws.WebService; import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider; import javax.xml.ws.Service; import java.net.MalformedURLException; import java.net.URL; import java.util.Map; @WebService ( portName = "ActAsServicePort", serviceName = "ActAsService", wsdlLocation = "WEB-INF/wsdl/ActAsService.wsdl", targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy", endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas.ActAsServiceIface" ) @EndpointProperties(value = { @EndpointProperty(key = "ws-security.signature.username", value = "myactaskey"), @EndpointProperty(key = "ws-security.signature.properties", value = "actasKeystore.properties"), @EndpointProperty(key = "ws-security.encryption.properties", value = "actasKeystore.properties"), @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas.ActAsCallbackHandler") }) public class ActAsServiceImpl implements ActAsServiceIface { public String sayHello() { try { ServiceIface proxy = setupService(); return "ActAs " + proxy.sayHello(); } catch (MalformedURLException e) { e.printStackTrace(); } return null; } private ServiceIface setupService()throws MalformedURLException { ServiceIface proxy = null; Bus bus = BusFactory.newInstance().createBus(); try { BusFactory.setThreadDefaultBus(bus); final String serviceURL = "http://" + WSTrustAppUtils.getServerHost() + ":8080/jaxws-samples-wsse-policy-trust/SecurityService"; final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService"); final URL wsdlURL = new URL(serviceURL + "?wsdl"); Service service = Service.create(wsdlURL, serviceName); proxy = (ServiceIface) service.getPort(ServiceIface.class); Map<String, Object> ctx = ((BindingProvider) proxy).getRequestContext(); ctx.put(SecurityConstants.CALLBACK_HANDLER, new ActAsCallbackHandler()); ctx.put(SecurityConstants.SIGNATURE_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource("actasKeystore.properties" )); ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myactaskey" ); ctx.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource("../../META-INF/clientKeystore.properties" )); ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey"); STSClient stsClient = new STSClient(bus); Map<String, Object> props = stsClient.getProperties(); props.put(SecurityConstants.USERNAME, "alice"); props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey"); props.put(SecurityConstants.STS_TOKEN_USERNAME, "myactaskey" ); props.put(SecurityConstants.STS_TOKEN_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource("actasKeystore.properties" )); props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true"); ctx.put(SecurityConstants.STS_CLIENT, stsClient); } finally { bus.shutdown(true); } return proxy; } }
A.8.4.1.4. ActAsCallbackHandler クラス
ActAsCallbackHandler
は WSS4J Crypto API のコールバックハンドラーです。これは、キーストアの秘密鍵のパスワードを取得するために使用されます。このクラスにより、Apache CXF はメッセージ署名に使用するユーザー名のパスワードを取得できます。このクラスは、このサービス myactaskey
のパスワード、ActAs
ユーザー、alice
を返すように更新されました。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas; import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler; import java.util.HashMap; import java.util.Map; public class ActAsCallbackHandler extends PasswordCallbackHandler { public ActAsCallbackHandler() { super(getInitMap()); } private static Map<String, String> getInitMap() { Map<String, String> passwords = new HashMap<String, String>(); passwords.put("myactaskey", "aspass"); passwords.put("alice", "clarinet"); return passwords; } }
A.8.4.1.5. UsernameTokenCallbackHandler
RequestSecurityToken
の ActAs
および OnBeholdOf
サブ要素を WSSE UsernameTokens
として定義する必要があります。このユーティリティーは、適切にフォーマットされた要素を生成します。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.message.Message; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.trust.delegation.DelegationCallback; import org.apache.ws.security.WSConstants; import org.apache.ws.security.message.token.UsernameToken; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.Element; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSSerializer; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import java.io.IOException; import java.util.Map; /** * A utility to provide the 3 different input parameter types for jaxws property * "ws-security.sts.token.act-as" and "ws-security.sts.token.on-behalf-of". * This implementation obtains a username and password via the jaxws property * "ws-security.username" and "ws-security.password" respectively, as defined * in SecurityConstants. It creates a wss UsernameToken to be used as the * delegation token. */ public class UsernameTokenCallbackHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof DelegationCallback) { DelegationCallback callback = (DelegationCallback) callbacks[i]; Message message = callback.getCurrentMessage(); String username = (String)message.getContextualProperty(SecurityConstants.USERNAME); String password = (String)message.getContextualProperty(SecurityConstants.PASSWORD); if (username != null) { Node contentNode = message.getContent(Node.class); Document doc = null; if (contentNode != null) { doc = contentNode.getOwnerDocument(); } else { doc = DOMUtils.createDocument(); } UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc); callback.setToken(usernameToken.getElement()); } } else { throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); } } } /** * Provide UsernameToken as a string. * @param ctx * @return */ public String getUsernameTokenString(Map<String, Object> ctx){ Document doc = DOMUtils.createDocument(); String result = null; String username = (String)ctx.get(SecurityConstants.USERNAME); String password = (String)ctx.get(SecurityConstants.PASSWORD); if (username != null) { UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc); result = toString(usernameToken.getElement().getFirstChild().getParentNode()); } return result; } /** * * @param username * @param password * @return */ public String getUsernameTokenString(String username, String password){ Document doc = DOMUtils.createDocument(); String result = null; if (username != null) { UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc); result = toString(usernameToken.getElement().getFirstChild().getParentNode()); } return result; } /** * Provide UsernameToken as a DOM Element. * @param ctx * @return */ public Element getUsernameTokenElement(Map<String, Object> ctx){ Document doc = DOMUtils.createDocument(); Element result = null; UsernameToken usernameToken = null; String username = (String)ctx.get(SecurityConstants.USERNAME); String password = (String)ctx.get(SecurityConstants.PASSWORD); if (username != null) { usernameToken = createWSSEUsernameToken(username,password, doc); result = usernameToken.getElement(); } return result; } /** * * @param username * @param password * @return */ public Element getUsernameTokenElement(String username, String password){ Document doc = DOMUtils.createDocument(); Element result = null; UsernameToken usernameToken = null; if (username != null) { usernameToken = createWSSEUsernameToken(username,password, doc); result = usernameToken.getElement(); } return result; } private UsernameToken createWSSEUsernameToken(String username, String password, Document doc) { UsernameToken usernameToken = new UsernameToken(true, doc, (password == null)? null: WSConstants.PASSWORD_TEXT); usernameToken.setName(username); usernameToken.addWSUNamespace(); usernameToken.addWSSENamespace(); usernameToken.setID("id-" + username); if (password != null){ usernameToken.setPassword(password); } return usernameToken; } private String toString(Node node) { String str = null; if (node != null) { DOMImplementationLS lsImpl = (DOMImplementationLS) node.getOwnerDocument().getImplementation().getFeature("LS", "3.0"); LSSerializer serializer = lsImpl.createLSSerializer(); serializer.getDomConfig().setParameter("xml-declaration", false); //by default its true, so set it to false to get String without xml-declaration str = serializer.writeToString(node); } return str; } }
A.8.4.1.6. Crypto プロパティーおよびキーストアファイル
ActAs
サービスは、独自の認証情報を提供する必要があります。必須の actasKeystore.properties
プロパティーファイルと actasstore.jks
キーストアが作成されます。
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=aapass org.apache.ws.security.crypto.merlin.keystore.alias=myactaskey org.apache.ws.security.crypto.merlin.keystore.file=actasstore.jks
A.8.4.1.7. デフォルトの MANIFEST.MF
このアプリケーションには、org.jboss.ws.cxf.jbossws-cxf-client
モジュールで提供される JBossWS および Apache CXF API へのアクセスが必要です。また、org.jboss.ws.cxf.sts
モジュールも ActAs
および OnBehalfOf
拡張機能を処理するために必要です。依存関係ステートメントは、デプロイメント時に提供するようサーバーに指示します。
Manifest-Version: 1.0 Dependencies: org.jboss.ws.cxf.jbossws-cxf-client, org.jboss.ws.cxf.sts
A.8.4.2. セキュリティートークンサービス
ここでは、ActAs
の例のニーズに対応するために変更された基本的な WS-Trust シナリオからの STS 要素の詳細を説明します。コンポーネントには以下が含まれます。
A.8.4.2.1. STS 実装クラス
アドレス別に許可されるトークン受信側のセットの宣言が、ActAs
アドレスおよび OnBehalfOf
アドレスを受け入れるように拡張されました。アドレスは reg-ex パターンとして指定されます。
TokenIssueOperation
では、OnBehalfOf
の内容を検証するために UsernameTokenValidator
クラスを提供し、OnBehalfOf
ユーザーの ActAs
のトークン委任要求を処理するために UsernameTokenDelegationHandler
クラスを提供する必要があります。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.sts; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import javax.xml.ws.WebServiceProvider; import org.apache.cxf.annotations.EndpointProperties; import org.apache.cxf.annotations.EndpointProperty; import org.apache.cxf.interceptor.InInterceptors; import org.apache.cxf.sts.StaticSTSProperties; import org.apache.cxf.sts.operation.TokenIssueOperation; import org.apache.cxf.sts.operation.TokenValidateOperation; import org.apache.cxf.sts.service.ServiceMBean; import org.apache.cxf.sts.service.StaticService; import org.apache.cxf.sts.token.delegation.UsernameTokenDelegationHandler; import org.apache.cxf.sts.token.provider.SAMLTokenProvider; import org.apache.cxf.sts.token.validator.SAMLTokenValidator; import org.apache.cxf.sts.token.validator.UsernameTokenValidator; import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider; @WebServiceProvider(serviceName = "SecurityTokenService", portName = "UT_Port", targetNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/", wsdlLocation = "WEB-INF/wsdl/ws-trust-1.4-service.wsdl") //dependency on org.apache.cxf module or on module that exports org.apache.cxf (e.g. org.jboss.ws.cxf.jbossws-cxf-client) is needed, otherwise Apache CXF annotations are ignored @EndpointProperties(value = { @EndpointProperty(key = "ws-security.signature.username", value = "mystskey"), @EndpointProperty(key = "ws-security.signature.properties", value = "stsKeystore.properties"), @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.sts.STSCallbackHandler"), @EndpointProperty(key = "ws-security.validate.token", value = "false") //to let the JAAS integration deal with validation through the interceptor below }) @InInterceptors(interceptors = {"org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingPolicyInterceptor"}) public class SampleSTS extends SecurityTokenServiceProvider { public SampleSTS() throws Exception { super(); StaticSTSProperties props = new StaticSTSProperties(); props.setSignatureCryptoProperties("stsKeystore.properties"); props.setSignatureUsername("mystskey"); props.setCallbackHandlerClass(STSCallbackHandler.class.getName()); props.setIssuer("DoubleItSTSIssuer"); List<ServiceMBean> services = new LinkedList<ServiceMBean>(); StaticService service = new StaticService(); service.setEndpoints(Arrays.asList( "http://localhost:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService", "http://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService", "http://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService", "http://localhost:(\\d)*/jaxws-samples-wsse-policy-trust-actas/ActAsService", "http://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-actas/ActAsService", "http://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-actas/ActAsService", "http://localhost:(\\d)*/jaxws-samples-wsse-policy-trust-onbehalfof/OnBehalfOfService", "http://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-onbehalfof/OnBehalfOfService", "http://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-onbehalfof/OnBehalfOfService" )); services.add(service); TokenIssueOperation issueOperation = new TokenIssueOperation(); issueOperation.setServices(services); issueOperation.getTokenProviders().add(new SAMLTokenProvider()); // required for OnBehalfOf issueOperation.getTokenValidators().add(new UsernameTokenValidator()); // added for OnBehalfOf and ActAs issueOperation.getDelegationHandlers().add(new UsernameTokenDelegationHandler()); issueOperation.setStsProperties(props); TokenValidateOperation validateOperation = new TokenValidateOperation(); validateOperation.getTokenValidators().add(new SAMLTokenValidator()); validateOperation.setStsProperties(props); this.setIssueOperation(issueOperation); this.setValidateOperation(validateOperation); } }
A.8.4.2.2. STSCallbackHandler クラス
ActAs
の例には、ユーザー alice
、および対応するパスワードを追加する必要があります。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.sts; import java.util.HashMap; import java.util.Map; import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler; public class STSCallbackHandler extends PasswordCallbackHandler { public STSCallbackHandler() { super(getInitMap()); } private static Map<String, String> getInitMap() { Map<String, String> passwords = new HashMap<String, String>(); passwords.put("mystskey", "stskpass"); passwords.put("alice", "clarinet"); return passwords; } }
A.8.4.2.3. Web Service Requester
ここでは、ActAs
の例の要件に対応するために更新された、基本的な WS-Trust シナリオからの ws-requester
要素について説明します。コンポーネントは次のとおりです。
A.8.4.2.4. Web Service Requester 実装クラス
ActAs
ws-requester
(クライアント) は、Web サービスへの参照を最初の 4 行で作成するために標準手順を使用します。エンドポイントのセキュリティー要件に対応するため、Web サービスのリクエストコンテキストは BindingProvider
を使用してメッセージの生成に必要な情報を提供します。ActAs
ユーザー myactaskey
はこのセクションで宣言され、UsernameTokenCallbackHandler
は ActAs
要素のコンテンツを STSClient
に提供するために使用されます。この例では、STSClient
オブジェクトが作成され、プロキシーのリクエストコンテキストに提供されます。別の方法として、Basic Scenario クライアントで行なわれたように、.it
接尾辞でタグ付けされた鍵を提供する方法があります。ActAs
の使用は、SecurityConstants .STS_TOKEN_ACT_AS
キーを使用してプロパティーマップで設定します。または、STSClient.setActAs
メソッドを使用します。
final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy", "ActAsService"); final URL wsdlURL = new URL(serviceURL + "?wsdl"); Service service = Service.create(wsdlURL, serviceName); ActAsServiceIface proxy = (ActAsServiceIface) service.getPort(ActAsServiceIface.class); Bus bus = BusFactory.newInstance().createBus(); try { BusFactory.setThreadDefaultBus(bus); Map<String, Object> ctx = proxy.getRequestContext(); ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler()); ctx.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myactaskey"); ctx.put(SecurityConstants.SIGNATURE_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey"); // Generate the ActAs element contents and pass to the STSClient as a string UsernameTokenCallbackHandler ch = new UsernameTokenCallbackHandler(); String str = ch.getUsernameTokenString("alice","clarinet"); ctx.put(SecurityConstants.STS_TOKEN_ACT_AS, str); STSClient stsClient = new STSClient(bus); Map<String, Object> props = stsClient.getProperties(); props.put(SecurityConstants.USERNAME, "bob"); props.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler()); props.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey"); props.put(SecurityConstants.STS_TOKEN_USERNAME, "myclientkey"); props.put(SecurityConstants.STS_TOKEN_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource( "META-INF/clientKeystore.properties")); props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true"); ctx.put(SecurityConstants.STS_CLIENT, stsClient); } finally { bus.shutdown(true); } proxy.sayHello();
Revised on 2023-01-28 12:53:33 +1000