© 2005-2020 The original authors.
このドキュメントのコピーは、あなた自身の使用および他者への配布のために作成することができますが、そのようなコピーに料金を請求しないこと、さらに、印刷物または電子的に配布されるかどうかにかかわらず、各コピーにこの著作権表示が含まれていることを条件とします。 |
序文
サービス指向アーキテクチャの現在の時代では、以前は接続されていなかったシステムに接続するために Web サービスを使用する人がますます増えています。当初、Web サービスはリモートプロシージャコール(RPC)を実行するための単なる別の方法であると考えられていました。しかし、時間が経つにつれて、RPC と Web サービスの間に大きな違いがあることに人々は気づきました。特に他のプラットフォームとの相互運用性が重要な場合は、リクエストの処理に必要なすべてのデータを含むカプセル化された XML ドキュメントを送信する方がよい場合がよくあります。概念的には、XML ベースの Web サービスは、リモートソリューションよりもメッセージキューに比べて優れています。全体として、XML は、SOA の共通言語であるデータのプラットフォームに依存しない表現と見なす必要があります。Web サービスを開発または使用するときは、Java ではなくこの XML に焦点を当てる必要があります。
Spring Web Services は、これらのドキュメント駆動型 Web サービスの作成に重点を置いています。Spring Web Services は、契約 ファーストの SOAP サービス開発を容易にし、XML ペイロードを操作する多くの方法の 1 つを使用して柔軟な Web サービスを作成できるようにします。Spring-WS は、強力なメッセージディスパッチフレームワーク、既存のアプリケーションセキュリティソリューションと統合する WS-Security ソリューション、おなじみの Spring テンプレートパターンに従うクライアント側 API を提供します。
I. 導入
1. Spring Web Services とは何ですか?
1.1. 導入
Spring Web Services(Spring-WS)は、Spring コミュニティの製品であり、ドキュメント駆動型 Web サービスの作成に重点を置いています。Spring Web Services は、契約優先の SOAP サービス開発を促進することを目的としており、XML ペイロードを操作する多くの方法の 1 つを使用して、柔軟な Web サービスの作成を可能にします。この製品は Spring 自体に基づいています。つまり、Spring の概念(依存性注入など)を Web サービスの不可欠な部分として使用できます。
人々は多くの理由で Spring-WS を使用しますが、ほとんどの人は、Web サービスのベストプラクティスに従うことになると、不足している代替の SOAP スタックを見つけた後にそれに惹かれます。Spring-WS は、ベストプラクティスを簡単なプラクティスにします。これには、WS-I の基本プロファイル、契約優先の開発、契約と実装の間の緩い結合などのプラクティスが含まれます。Spring Web Services の他の重要な機能は次のとおりです。
1.1.2. XML API サポート
受信 XML メッセージは、DOM、SAX、StAX などの標準の JAXP API だけでなく、JDOM、dom4j、XOM、さらにはマーシャリングテクノロジーでも処理できます。
1.1.3. 柔軟な XML マーシャリング
Spring Web Services は、JAXB 1 および 2、Castor、XMLBeans、JiBX、XStream をサポートする Spring Framework のオブジェクト / XML マッピングモジュールに基づいて構築されています。
1.1.4. Spring の専門知識を再利用する
Spring-WS は、すべての構成に Spring アプリケーションコンテキストを使用します。これは、Spring 開発者が迅速に習得できます。また、Spring-WS のアーキテクチャは Spring-MVC のアーキテクチャに似ています。
1.2. ランタイム環境
Spring Web Services には、標準の Java 8 ランタイム環境が必要です。Spring-WS は Spring Framework 4.0.9 上に構築されていますが、より高いバージョンがサポートされています。
Spring-WS は、このセクションの残りの部分で説明するいくつかのモジュールで構成されています。
XML モジュール(
spring-xml.jar
)には、Spring Web Services のさまざまな XML サポートクラスが含まれています。このモジュールは、主に Spring-WS フレームワーク自体を対象としており、Web サービス開発者を対象としていません。コアモジュール (
spring-ws-core.jar
) は、Spring の Web サービス機能の中心部分です。これは、主要なWebServiceMessage
およびSoapMessage
インターフェース、サーバー側フレームワーク (強力なメッセージディスパッチ)、Web サービスエンドポイントを実装するためのさまざまなサポートクラス、およびクライアント側WebServiceTemplate
を提供します。サポートモジュール(
spring-ws-support.jar
)には、追加のトランスポート(JMS、メールなど)が含まれています。セキュリティパッケージ(
spring-ws-security.jar
)は、コア Web サービスパッケージと統合する WS-Security 実装を提供します。署名、復号化、暗号化を行い、SOAP メッセージにプリンシパルトークンを追加できます。さらに、既存の Spring Security セキュリティ実装を認証と認可に使用できます。
次の図は、Spring-WS モジュール間の依存関係を示しています。矢印は依存関係を示します(つまり、Spring-WS Core は Spring-XML および Spring 3 以降にある OXM モジュールに依存します)。
1.3. サポートされている標準
Spring Web Services は、次の標準をサポートしています。
SOAP 1.1 および 1.2
WSDL 1.1 および 2.0 (XSD ベースの生成は、WSDL 1.1 でのみサポートされます。)
WS-I 基本プロファイル 1.0, 1.1, 1.2,, 2.0
WS-Addressing 1.0 および 2004 年 8 月のドラフト
SOAP メッセージセキュリティ 1.1、ユーザー名トークンプロファイル 1.1、X.509 証明書トークンプロファイル 1.1、SAML トークンプロファイル 1.1、Kerberos トークンプロファイル 1.1、基本セキュリティプロファイル 1.1
2. なぜ契約ファーストなのでしょうか?
Web サービスを作成する場合、契約ラストと契約ファーストの 2 つの開発スタイルがあります。契約ラストアプローチを使用する場合は、Java コードから開始し、そこから Web サービス契約(WSDL 内 - サイドバーを参照)を生成します。契約ファーストを使用する場合は、WSDL 契約から開始し、Java を使用して契約を実装します。
Spring-WS は、契約ファーストの開発スタイルのみをサポートします。このセクションでは、その理由について説明します。
2.1. オブジェクト / XML インピーダンスの不一致
オブジェクト / 関係インピーダンスの不一致 [Wikipedia] (英語) がある ORM のフィールドと同様に、Java オブジェクトを XML に変換することにも同様の問題があります。一見すると、O/X マッピングの問題は単純に見えます。Java オブジェクトごとに XML 要素を作成して、すべての Java プロパティとフィールドをサブ要素または属性に変換します。ただし、XML(特に XSD)などの階層言語と Java のグラフモデルには根本的な違いがあるため、見た目ほど単純ではありません。
このセクションの内容のほとんどは、[alpine] および [effective-enterprise-java] に触発されました。 |
2.1.1. XSD 拡張機能
Java では、クラスの動作を変更する唯一の方法は、クラスをサブクラス化して、そのサブクラスに新しい動作を追加することです。XSD では、データ型を制限することで拡張できます。つまり、要素と属性の有効な値を制限します。たとえば、次の例について考えてみます。
<simpleType name="AirportCode">
<restriction base="string">
<pattern value="[A-Z][A-Z][A-Z]"/>
</restriction>
</simpleType>
この型は、正規表現によって XSD 文字列を制限し、大文字を 3 文字のみ許可します。この型を Java に変換すると、通常の java.lang.String
になります。Java ではこの種の拡張機能が許可されていないため、変換プロセスで正規表現が失われます。
2.1.2. ポータブル型
Web サービスの最も重要なゴールの 1 つは、相互運用可能であることです。つまり、Java、.NET、Python などの複数のプラットフォームをサポートすることです。これらの言語はすべて異なるクラスライブラリを持っているため、それらの間で通信するには、いくつかの一般的な言語間形式を使用する必要があります。その形式は XML であり、これらすべての言語でサポートされています。
この変換のため、サービスの実装では必ずポータブル型を使用する必要があります。たとえば、java.util.TreeMap
を返すサービスについて考えてみます。
public Map getFlights() {
// use a tree map, to make sure it's sorted
TreeMap map = new TreeMap();
map.put("KL1117", "Stockholm");
...
return map;
}
間違いなく、このマップのコンテンツはある種の XML に変換できますが、マップを XML で記述する標準的な方法がないため、独自仕様になります。また、XML に変換できる場合でも、多くのプラットフォームには TreeMap
のようなデータ構造がありません。.NET クライアントが Web サービスにアクセスすると、セマンティクスが異なる System.Collections.Hashtable
になってしまう可能性があります。
この問題は、クライアント側で作業する場合にも発生します。サービス契約を説明する次の XSD スニペットについて考えてみます。
<element name="GetFlightsRequest">
<complexType>
<all>
<element name="departureDate" type="date"/>
<element name="from" type="string"/>
<element name="to" type="string"/>
</all>
</complexType>
</element>
この契約は、年、月、日を表す XSD データ型である date
を受け取るリクエストを定義します。このサービスを Java から呼び出す場合は、おそらく java.util.Date
または java.util.Calendar
のいずれかを使用します。ただし、これらのクラスはどちらも、実際には日付ではなく時間を記述します。実際には、2007 年 4 月 4 日の深夜(2007-04-04T00:00:00
)を表すデータを送信することになります。これは 2007-04-04
と同じではありません。
2.1.3. 巡回グラフ
次のクラス構造があると想像してください。
public class Flight {
private String number;
private List<Passenger> passengers;
// getters and setters omitted
}
public class Passenger {
private String name;
private Flight flight;
// getters and setters omitted
}
これは閉路グラフです。Flight
は Passenger
を参照し、Passenger
は再び Flight
を参照します。このような循環グラフは、Java では非常に一般的です。これを XML に変換するために素朴なアプローチを取ると、次のような結果になります。
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
...
このような構造の処理は、このループの停止条件がないため、完了するまでに長い時間がかかる可能性があります。
この問題を解決する 1 つの方法は、すでにマーシャリングされているオブジェクトへの参照を使用することです。
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight href="KL1117" />
</passenger>
...
</passengers>
</flight>
これにより、再帰の課題は解決されますが、新しい問題が発生します。1 つは、XML バリデーターを使用してこの構造を検証することはできません。もう 1 つの課題は、SOAP(RPC/ エンコード)でこれらの参照を使用する標準的な方法が非推奨になり、ドキュメント / リテラルが優先されることです(WS-I 基本プロファイル (英語) を参照)。
これらは、O/X マッピングを処理する際の課題のほんの一部です。Web サービスを作成するときは、これらの課題を考慮することが重要です。考慮する最善の方法は、実装言語として Java を使用しながら、XML に完全に集中することです。これが契約優先です。
2.2. 契約ファースト vs 契約ラスト
前のセクションで説明したオブジェクト / XML マッピングの課題に加えて、契約ファーストの開発スタイルを好む理由は他にもあります。
2.2.1. 脆弱性
前述のように、契約ラスト開発スタイルでは、Java 契約(通常はインターフェース)から Web サービス契約(WSDL および XSD)が生成されます。このアプローチを使用する場合、契約が長期にわたって一定であるという保証はありません。Java 契約を変更して再デプロイするたびに、Web サービス契約が変更される可能性があります。
さらに、すべての SOAP スタックが Java 契約から同じ Web サービス契約を生成するわけではありません。つまり、現在の SOAP スタックを別のスタックに変更すると(何らかの理由で)、Web サービス契約も変更される可能性があります。
Web サービス契約が変更された場合、契約のユーザーは、新しい契約を取得し、契約の変更に対応するためにコードを変更する可能性があることを指示する必要があります。
契約が有用であるためには、可能な限り一定に保たれなければなりません。契約が変更された場合は、サービスのすべてのユーザーに連絡して、新しいバージョンの契約を取得するように指示する必要があります。
2.2.2. パフォーマンス
Java オブジェクトが自動的に XML に変換される場合、ネットワークを介して何が送信されるかを確認する方法はありません。オブジェクトが別のオブジェクトを参照している場合や、別のオブジェクトを参照している場合などがあります。最終的に、仮想マシンのヒープ上のオブジェクトの半分が XML に変換される可能性があり、その結果、レスポンス時間が遅くなります。
契約ファーストを使用する場合は、どの XML がどこに送信されるかを明示的に記述し、それがまさに希望どおりであることを確認します。
2.2.3. 再利用性
別のファイルでスキーマを定義すると、さまざまなシナリオでそのファイルを再利用できます。airline.xsd
というファイルでの AirportCode
の定義について考えてみましょう。
<simpleType name="AirportCode">
<restriction base="string">
<pattern value="[A-Z][A-Z][A-Z]"/>
</restriction>
</simpleType>
import
ステートメントを使用すると、この定義を他のスキーマや WSDL ファイルで再利用できます。
2.2.4. バージョニング
契約は可能な限り一定である必要がありますが、場合によっては変更する必要があります。Java では、これにより通常、AirlineService2
などの新しい Java インターフェースと、そのインターフェースの(新しい)実装が作成されます。もちろん、まだ移行していないクライアントがいる可能性があるため、古いサービスを維持する必要があります。
契約ファーストを使用する場合、契約と実装の間の結合を緩くすることができます。このような緩い結合により、両方のバージョンの契約を 1 つのクラスに実装できます。たとえば、XSLT スタイルシートを使用して、「古いスタイル」のメッセージを「新しいスタイル」のメッセージに変換できます。
3. 契約ファーストの Web サービスを作成
このチュートリアルでは、契約 ファーストの Web サービスを作成する方法、つまり、最初に XML スキーマまたは WSDL 契約で開始し、次に Java コードが続く Web サービスを開発する方法を示します。Spring-WS はこの開発スタイルに焦点を当てており、このチュートリアルは開始するのに役立つはずです。このチュートリアルの最初の部分には、Spring-WS 固有の情報がほとんど含まれていないことに注意してください。主に XML、XSD、WSDL に関するものです。2 番目のパートでは、Spring-WS を使用してこの契約を実装することに焦点を当てます。
契約ファーストの Web サービス開発を行う際に最も重要なことは、XML の観点から考えることです。これは、Java 言語の概念の重要性が低いことを意味します。ネットワークを介して送信されるのは XML であり、それに焦点を当てる必要があります。Web サービスの実装に使用されている Java は、実装の詳細です。
このチュートリアルでは、人事部門によって作成される Web サービスを定義します。クライアントは、このサービスに休日リクエストフォームを送信して、休日を予約できます。
3.1. メッセージ
このセクションでは、Web サービスとの間で送受信される実際の XML メッセージに焦点を当てます。まず、これらのメッセージがどのように見えるかを判断します。
3.1.1. 休日
このシナリオでは、休日のリクエストを処理する必要があるため、XML で休日がどのように見えるかを判断するのは理にかなっています。
<Holiday xmlns="http://mycompany.com/hr/schemas">
<StartDate>2006-07-03</StartDate>
<EndDate>2006-07-07</EndDate>
</Holiday>
休日は、開始日と終了日で構成されます。また、日付には標準の ISO 8601 (英語) 日付形式を使用することにしました。これにより、解析の手間が大幅に軽減されます。また、要素に名前空間を追加して、要素が他の XML ドキュメント内で使用できるようにしました。
3.1.2. 従業員
シナリオには従業員の概念もあります。XML では次のようになります。
<Employee xmlns="http://mycompany.com/hr/schemas">
<Number>42</Number>
<FirstName>Arjen</FirstName>
<LastName>Poutsma</LastName>
</Employee>
以前と同じ名前空間を使用しました。この <Employee/>
要素を他のシナリオで使用できる場合は、http://example.com/employees/schemas (英語)
などの別の名前空間を使用することが理にかなっている可能性があります。
3.1.3. HolidayRequest
holiday
要素と employee
要素の両方を <HolidayRequest/>
に入れることができます。
<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
<Holiday>
<StartDate>2006-07-03</StartDate>
<EndDate>2006-07-07</EndDate>
</Holiday>
<Employee>
<Number>42</Number>
<FirstName>Arjen</FirstName>
<LastName>Poutsma</LastName>
</Employee>
</HolidayRequest>
2 つの要素の順序は重要ではありません。<Employee/>
が最初の要素であった可能性があります。重要なのは、すべてのデータがそこにあるということです。実際、重要なのはデータだけです。データ駆動型のアプローチを採用しています。
3.2. データ契約
使用できる XML データの例をいくつか見てきたため、これをスキーマに形式化することは理にかなっています。このデータ契約は、受け入れるメッセージ形式を定義します。XML のこのような契約を定義するには、次の 4 つの方法があります。
DTD は名前空間のサポートが制限されているため、Web サービスには適していません。RelaxNG と Schematron は XML スキーマよりも簡単です。残念ながら、これらはプラットフォーム間でそれほど広くサポートされていません。その結果、XML スキーマを使用します。
XSD を作成する最も簡単な方法は、サンプルドキュメントから推測することです。優れた XML エディターまたは JavaIDE は、この機能を提供します。基本的に、これらのツールはいくつかのサンプル XML ドキュメントを使用して、それらすべてを検証するスキーマを生成します。最終結果は確かに洗練される必要がありますが、素晴らしい出発点です。
前に説明したサンプルを使用すると、次の生成されたスキーマが作成されます。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas"
xmlns:hr="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:Holiday"/>
<xs:element ref="hr:Employee"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Holiday">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:StartDate"/>
<xs:element ref="hr:EndDate"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="StartDate" type="xs:NMTOKEN"/>
<xs:element name="EndDate" type="xs:NMTOKEN"/>
<xs:element name="Employee">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:Number"/>
<xs:element ref="hr:FirstName"/>
<xs:element ref="hr:LastName"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:NCName"/>
<xs:element name="LastName" type="xs:NCName"/>
</xs:schema>
この生成されたスキーマは改善できます。最初に気付くのは、すべての型にルートレベルの要素宣言があることです。これは、Web サービスがこれらすべての要素をデータとして受け入れることができる必要があることを意味します。これは望ましくありません。<HolidayRequest/>
のみを受け入れたいのです。折り返し要素タグを削除して(したがって型を保持して)結果をインライン化することにより、次のようにこれを実現できます。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://mycompany.com/hr/schemas"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="Holiday" type="hr:HolidayType"/>
<xs:element name="Employee" type="hr:EmployeeType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:NMTOKEN"/>
<xs:element name="EndDate" type="xs:NMTOKEN"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:NCName"/>
<xs:element name="LastName" type="xs:NCName"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
スキーマにはまだ 1 つの問題があります。このようなスキーマでは、次のメッセージが検証されることが期待できます。
<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
<Holiday>
<StartDate>this is not a date</StartDate>
<EndDate>neither is this</EndDate>
</Holiday>
PlainText Section qName:lineannotation level:4, chunks:[<, !-- ... --, >] attrs:[:]
</HolidayRequest>
明らかに、開始日と終了日が実際に日付であることを確認する必要があります。XML スキーマには、使用できる優れた組み込み date
型があります。また、NCName
を string
インスタンスに変更します。最後に、<HolidayRequest/>
の sequence
を all
に変更します。これは、<Holiday/>
と <Employee/>
の順序が重要ではないことを XML パーサーに通知します。最終的な XSD は次のようになります。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://mycompany.com/hr/schemas"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:all>
<xs:element name="Holiday" type="hr:HolidayType"/> (1)
<xs:element name="Employee" type="hr:EmployeeType"/> (1)
</xs:all>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:date"/> (2)
<xs:element name="EndDate" type="xs:date"/> (2)
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:string"/> (3)
<xs:element name="LastName" type="xs:string"/> (3)
</xs:sequence>
</xs:complexType>
</xs:schema>
1 | all は、<Holiday/> と <Employee/> の順序が重要ではないことを XML パーサーに通知します。 |
2 | <StartDate/> と <EndDate/> には、xs:date データ型(年、月、日で構成されます)を使用します。 |
3 | xs:string は、名前と名前に使用されます。 |
このファイルを hr.xsd
として保存します。
3.3. サービス契約
サービス契約は通常、WSDL [W3C] (英語) ファイルとして表現されます。Spring-WS では、手動で WSDL を記述する必要がないことに注意してください。エンドポイントの実装というタイトルのセクションに従って、XSD といくつかの規則に基づいて、Spring-WS は WSDL を作成できます。このセクションの残りの部分では、手動で WSDL を作成する方法を示します。次のセクションにスキップすることもできます。
WSDL を標準のプリアンブルから開始し、既存の XSD をインポートします。スキーマを定義から分離するために、WSDL 定義に別の名前空間 http://mycompany.com/hr/definitions (英語)
を使用します。次のリストは前文を示しています。
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:schema="http://mycompany.com/hr/schemas"
xmlns:tns="http://mycompany.com/hr/definitions"
targetNamespace="http://mycompany.com/hr/definitions">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://mycompany.com/hr/schemas" schemaLocation="hr.xsd"/>
</xsd:schema>
</wsdl:types>
次に、記述されたスキーマ型に基づいてメッセージを追加します。スキーマに配置した <HolidayRequest/>
という 1 つのメッセージしかありません。
<wsdl:message name="HolidayRequest">
<wsdl:part element="schema:HolidayRequest" name="HolidayRequest"/>
</wsdl:message>
メッセージを操作としてポート型に追加します。
<wsdl:portType name="HumanResource">
<wsdl:operation name="Holiday">
<wsdl:input message="tns:HolidayRequest" name="HolidayRequest"/>
</wsdl:operation>
</wsdl:portType>
そのメッセージは、WSDL の抽象部分(いわばインターフェース)を終了し、具体的な部分を残します。具体的な部分は、binding
(定義した操作を呼び出す方法をクライアントに指示する)と service
(クライアントにそれを呼び出す場所を指示する)で構成されます。
具体的なパーツを追加することはかなり標準的です。これを行うには、前に定義した抽象部分を参照し、soap:binding
要素に document/literal
を使用していることを確認し(rpc/encoded
は非推奨)、操作に soapAction
を選択し(この場合、http://mycompany.com/RequestHoliday (英語)
ですが、任意の URI が機能します)、リクエストを到着させたい location
URL(この場合は http://mycompany.com/humanresources (英語)
):
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:schema="http://mycompany.com/hr/schemas"
xmlns:tns="http://mycompany.com/hr/definitions"
targetNamespace="http://mycompany.com/hr/definitions">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://mycompany.com/hr/schemas" (1)
schemaLocation="hr.xsd"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="HolidayRequest"> (2)
<wsdl:part element="schema:HolidayRequest" name="HolidayRequest"/> (3)
</wsdl:message>
<wsdl:portType name="HumanResource"> (4)
<wsdl:operation name="Holiday">
<wsdl:input message="tns:HolidayRequest" name="HolidayRequest"/> (2)
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HumanResourceBinding" type="tns:HumanResource"> (4)(5)
<soap:binding style="document" (6)
transport="http://schemas.xmlsoap.org/soap/http"/> (7)
<wsdl:operation name="Holiday">
<soap:operation soapAction="http://mycompany.com/RequestHoliday"/> (8)
<wsdl:input name="HolidayRequest">
<soap:body use="literal"/> (6)
</wsdl:input>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HumanResourceService">
<wsdl:port binding="tns:HumanResourceBinding" name="HumanResourcePort"> (5)
<soap:address location="http://localhost:8080/holidayService/"/> (9)
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
1 | データ契約で定義されたスキーマをインポートします。 |
2 | portType で使用される HolidayRequest メッセージを定義します。 |
3 | HolidayRequest 型はスキーマで定義されています。 |
4 | binding で使用される HumanResource ポート型を定義します。 |
5 | port で使用される HumanResourceBinding バインディングを定義します。 |
6 | ドキュメント / リテラルスタイルを使用します。 |
7 | リテラル http://schemas.xmlsoap.org/soap/http (英語) は、HTTP トランスポートを意味します。 |
8 | soapAction 属性は、すべてのリクエストで送信される SOAPAction HTTP ヘッダーを示します。 |
9 | http://localhost:8080/holidayService/ アドレスは、Web サービスを呼び出すことができる URL です。 |
上記のリストは、最終的な WSDL を示しています。次のセクションでは、結果のスキーマと WSDL を実装する方法について説明します。
3.4. プロジェクトの作成
このセクションでは、Maven [Apache] (英語) を使用して初期プロジェクト構造を作成します。そうする必要はありませんが、HolidayService をセットアップするために作成する必要のあるコードの量を大幅に削減します。
次のコマンドは、Spring-WS アーキ型(つまり、プロジェクトテンプレート)を使用して、Maven Web アプリケーションプロジェクトを作成します。
mvn archetype:create -DarchetypeGroupId=org.springframework.ws \ -DarchetypeArtifactId=spring-ws-archetype \ -DarchetypeVersion= \ -DgroupId=com.mycompany.hr \ -DartifactId=holidayService
上記のコマンドは、holidayService
という新しいディレクトリを作成します。このディレクトリには、WAR ファイルのルートを含む src/main/webapp
ディレクトリがあります。ここに標準の Web アプリケーションデプロイ記述子('WEB-INF/web.xml'
)があります。これは、Spring-WS MessageDispatcherServlet
を定義し、すべての受信リクエストをこのサーブレットにマップします。
<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">
<display-name>MyCompany HR Holiday Service</display-name>
<!-- take special notice of the name of this servlet -->
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
上記の WEB-INF/web.xml
ファイルに加えて、WEB-INF/spring-ws-servlet.xml
という名前の別の Spring-WS 固有の構成ファイルも必要です。このファイルには、EndPoints
や WebServiceMessageReceivers
などの Spring-WS 固有の Bean がすべて含まれており、新しい Spring コンテナーを作成するために使用されます。このファイルの名前は、-servlet.xml
が追加されたアテンダントサーブレット(この場合は 'spring-ws'
)の名前に由来します。'dynamite'
という名前で MessageDispatcherServlet
を定義すると、Spring-WS 固有の構成ファイルの名前は WEB-INF/dynamite-servlet.xml
になります。
(この例の WEB-INF/spring-ws-servlet.xml
ファイルの内容は [tutorial.example.sws-conf-file] で確認できます)
プロジェクト構造を作成したら、前のセクションのスキーマと WSDL を 'WEB-INF/'
フォルダーに配置できます。
3.5. エンドポイントの実装
Spring-WS では、受信 XML メッセージを処理するためのエンドポイントを実装します。エンドポイントは通常、クラスに @Endpoint
アノテーションを付けることによって作成されます。このエンドポイントクラスでは、受信リクエストを処理する 1 つ以上のメソッドを作成できます。メソッドのシグネチャーは非常に柔軟です。この章の後半で説明するように、受信 XML メッセージに関連するほぼすべての種類のパラメーター型を含めることができます。
3.5.1. XML メッセージの処理
このサンプルアプリケーションでは、JDom 2 (英語) を使用して XML メッセージを処理します。XPath [W3C] (英語) も使用します。これは、厳密なスキーマ準拠を必要とせずに XMLJDOM ツリーの特定の部分を選択できるためです。
次のリストは、休日のエンドポイントを定義するクラスを示しています。
package com.mycompany.hr.ws;
@Endpoint (1)
public class HolidayEndpoint {
private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";
private XPathExpression<Element> startDateExpression;
private XPathExpression<Element> endDateExpression;
private XPathExpression<Element> firstNameExpression;
private XPathExpression<Element> lastNameExpression;
private HumanResourceService humanResourceService;
@Autowired (2)
public HolidayEndpoint(HumanResourceService humanResourceService) throws JDOMException {
this.humanResourceService = humanResourceService;
Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URI);
XPathFactory xPathFactory = XPathFactory.instance();
startDateExpression = xPathFactory.compile("//hr:StartDate", Filters.element(), null, namespace);
endDateExpression = xPathFactory.compile("//hr:EndDate", Filters.element(), null, namespace);
firstNameExpression = xPathFactory.compile("//hr:FirstName", Filters.element(), null, namespace);
lastNameExpression = xPathFactory.compile("//hr:LastName", Filters.element(), null, namespace);
}
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest") (3)
public void handleHolidayRequest(@RequestPayload Element holidayRequest) throws Exception {(4)
Date startDate = parseDate(startDateExpression, holidayRequest);
Date endDate = parseDate(endDateExpression, holidayRequest);
String name = firstNameExpression.evaluateFirst(holidayRequest).getText() + " " + lastNameExpression.evaluateFirst(holidayRequest).getText();
humanResourceService.bookHoliday(startDate, endDate, name);
}
private Date parseDate(XPathExpression<Element> expression, Element element) throws ParseException {
Element result = expression.evaluateFirst(element);
if (result != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.parse(result.getText());
} else {
throw new IllegalArgumentException("Could not evaluate [" + expression + "] on [" + element + "]");
}
}
}
1 | HolidayEndpoint には @Endpoint のアノテーションが付けられています。これにより、クラスは特別な種類の @Component としてマークされ、Spring-WS での XML メッセージの処理に適しており、コンポーネントのスキャンにも適しています。 |
2 | HolidayEndpoint は、HumanResourceService ビジネスサービスが動作する必要があるため、コンストラクターに依存関係を挿入し、@Autowired でアノテーションを付けます。次に、JDOM2API を使用して XPath 式を設定します。4 つの式があります。<StartDate> テキスト値を抽出するための //hr:StartDate 、終了日を抽出するための //hr:EndDate 、従業員の名前を抽出するための 2 つです。 |
3 | @PayloadRoot アノテーションは、handleHolidayRequest メソッドが XML メッセージの処理に適していることを Spring-WS に通知します。このメソッドが処理できるメッセージの種類は、アノテーション値によって示されます。この場合、HolidayRequest ローカル部分と http://mycompany.com/hr/schemas (英語) 名前空間を持つ XML 要素を処理できます。メッセージをエンドポイントにマッピングする方法の詳細については、次のセクションで説明します。 |
4 | handleHolidayRequest(..) メソッドはメインの処理メソッドであり、受信 XML メッセージから <HolidayRequest/> 要素が渡されます。@RequestPayload アノテーションは、holidayRequest パラメーターをリクエストメッセージのペイロードにマップする必要があることを示します。XPath 式を使用して XML メッセージから文字列値を抽出し、SimpleDateFormat (parseData メソッド)を使用してこれらの値を Date オブジェクトに変換します。これらの値を使用して、ビジネスサービスのメソッドを呼び出します。通常、これにより、データベーストランザクションが開始され、データベース内の一部のレコードが変更されます。最後に、void 戻り値の型を定義します。これは、レスポンスメッセージを送信したくないことを Spring-WS に示します。レスポンスメッセージが必要な場合は、レスポンスメッセージのペイロードを表す JDOM 要素を返すことができます。 |
JDOM の使用は、XML を処理するためのオプションの 1 つにすぎません。他のオプションには、DOM、dom4j、XOM、SAX、StAX が含まれますが、次の章で説明するように、JAXB、Castor、XMLBeans、JiBX、XStream などのマーシャリング手法も含まれます。JDOM を選択したのは、生の XML にアクセスできることと、クラスに基づいている (W3C DOM や dom4j のようなインターフェースやファクトリメソッドではない) ためです。XPath を使用するのは、マーシャリングテクノロジよりも脆弱性が低いためです。日付と名前を見つけることができる限り、厳密なスキーマ準拠は必要ありません。
JDOM を使用しているため、プロジェクトディレクトリのルートにある Maven pom.xml
にいくつかの依存関係を追加する必要があります。POM の関連セクションは次のとおりです。
<dependencies>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version></version>
</dependency>
<dependency>
<groupId>jdom</groupId>
<artifactId>jdom</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
コンポーネントスキャンを使用して、spring-ws-servlet.xml
Spring XML 構成ファイルでこれらのクラスを構成する方法を次に示します。また、<sws:annotation-driven>
要素を使用してアノテーション駆動型エンドポイントを使用するように Spring-WS に指示します。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.mycompany.hr"/>
<sws:annotation-driven/>
</beans>
3.5.2. メッセージをエンドポイントにルーティングする
エンドポイントの作成の一環として、@PayloadRoot
アノテーションを使用して、handleHolidayRequest
メソッドで処理できるメッセージの種類を示しました。Spring-WS では、このプロセスは EndpointMapping
の責任です。ここでは、PayloadRootAnnotationMethodEndpointMapping
を使用して、コンテンツに基づいてメッセージをルーティングします。次のリストは、以前に使用したアノテーションを示しています。
@PayloadRoot(namespace = "http://mycompany.com/hr/schemas", localPart = "HolidayRequest")
前の例に示されているアノテーションは、基本的に、名前空間 http://mycompany.com/hr/schemas (英語)
および HolidayRequest
ローカル名で XML メッセージを受信すると、handleHolidayRequest
メソッドにルーティングされることを意味します。構成で <sws:annotation-driven>
要素を使用することにより、@PayloadRoot
アノテーションの検出を有効にします。エンドポイントに複数の関連する処理メソッドがあり、それぞれが異なる XML メッセージを処理する可能性があります(非常に一般的です)。
エンドポイントを XML メッセージにマップする他の方法もあります。これについては、次の章で説明します。
3.5.3. サービスとスタブの実装の提供
エンドポイントができたため、HolidayEndpoint
で使用するための HumanResourceService
とその実装が必要です。次のリストは、HumanResourceService
インターフェースを示しています。
package com.mycompany.hr.service;
public interface HumanResourceService {
void bookHoliday(Date startDate, Date endDate, String name);
}
チュートリアルの目的で、HumanResourceService
の単純なスタブ実装を使用します。
package com.mycompany.hr.service;
@Service (1)
public class StubHumanResourceService implements HumanResourceService {
public void bookHoliday(Date startDate, Date endDate, String name) {
System.out.println("Booking holiday for [" + startDate + "-" + endDate + "] for [" + name + "] ");
}
}
1 | StubHumanResourceService には @Service のアノテーションが付けられています。これにより、クラスがビジネスファサードとしてマークされ、HolidayEndpoint での @Autowired によるインジェクションの候補になります。 |
3.6. WSDL の公開
最後に、WSDL を公開する必要があります。サービス契約で記述されていたように、WSDL を自分で作成する必要はありません。Spring-WS は、いくつかの規則に基づいて生成できます。世代を定義する方法は次のとおりです。
<sws:dynamic-wsdl id="holiday" (1)
portTypeName="HumanResource" (3)
locationUri="/holidayService/" (4)
targetNamespace="http://mycompany.com/hr/definitions"> (5)
<sws:xsd location="/WEB-INF/hr.xsd"/> (2)
</sws:dynamic-wsdl>
1 | id は、WSDL を取得できる URL を決定します。この場合、id は holiday です。これは、サーブレットコンテキストで WSDL を holiday.wsdl として取得できることを意味します。完全な URL は http://localhost:8080/holidayService/holiday.wsdl です。 |
2 | 次に、WSDL ポート型を HumanResource に設定します。 |
3 | サービスに到達できる場所を設定します: /holidayService/ 。相対 URI を使用し、それを動的に絶対 URI に変換するようにフレームワークに指示します。サービスが異なるコンテキストにデプロイされている場合、URI を手動で変更する必要はありません。詳細については、「自動 WSDL 公開」を参照してください。ロケーション変換を機能させるには、web.xml の spring-ws サーブレットに init パラメーターを追加する必要があります (次のリストを参照)。 |
4 | WSDL 定義自体のターゲット名前空間を定義します。この属性を設定する必要はありません。設定されていない場合、WSDL は XSD スキーマと同じ名前空間を持ちます。 |
5 | xsd 要素は、データ契約で定義した人事スキーマを参照します。アプリケーションの WEB-INF ディレクトリにスキーマを配置しました。 |
次のリストは、init パラメーターを追加する方法を示しています。
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
mvn install
を使用して WAR ファイルを作成できます。アプリケーションを (Tomcat、Jetty などに) デプロイし、ブラウザーでこの場所をポイントすると、生成された WSDL が表示されます。この WSDL は、soapUI (英語) やその他の SOAP フレームワークなどのクライアントで使用する準備ができています。
これでこのチュートリアルは終わりです。チュートリアルコードは、Spring-WS の完全なディストリビューションにあります。続行する場合は、ディストリビューションの一部であるエコーサンプルアプリケーションを確認してください。その後、JAXB、WS-Security、Hibernate、トランザクションサービスレイヤーを使用するため、少し複雑な航空会社のサンプルを参照してください。最後に、残りのリファレンスドキュメントを読むことができます。
II. リファレンス
4. 共有コンポーネント
この章では、クライアント側とサーバー側の Spring-WS 開発間で共有されるコンポーネントについて説明します。これらのインターフェースとクラスは Spring-WS の構成要素を表しているため、直接使用しなくても、それらが何をするのかを理解する必要があります。
4.1. Web サービスメッセージ
このセクションでは、Spring-WS が使用するメッセージとメッセージファクトリについて説明します。
4.1.1. WebServiceMessage
Spring Web Services のコアインターフェースの 1 つは WebServiceMessage
です。このインターフェースは、プロトコルに依存しない XML メッセージを表します。インターフェースには、javax.xml.transform.Source
または javax.xml.transform.Result
の形式でメッセージのペイロードへのアクセスを提供するメソッドが含まれています。Source
および Result
は、XML 入力および出力の抽象化を表すタグ付けインターフェースです。次の表に示すように、具体的な実装はさまざまな XML 表現をラップします。
ソースまたは結果の実装 | ラップされた XML 表現 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
ペイロードからの読み取りとペイロードへの書き込みに加えて、Web サービスメッセージはそれ自体を出力ストリームに書き込むことができます。
4.1.2. SoapMessage
SoapMessage
は WebServiceMessage
のサブクラスです。これには、SOAP ヘッダー、SOAP 障害などの取得など、SOAP 固有のメソッドが含まれています。通常、コードは SoapMessage
に依存しないでください。これは、SOAP 本文のコンテンツ(メッセージのペイロード)は、WebServiceMessage
で getPayloadSource()
および getPayloadResult()
を使用して取得できるためです。SOAP 固有のアクション(ヘッダーの追加、添付ファイルの取得など)を実行する必要がある場合にのみ、WebServiceMessage
を SoapMessage
にキャストする必要があります。
4.1.3. メッセージファクトリ
具体的なメッセージの実装は、WebServiceMessageFactory
によって作成されます。このファクトリは、空のメッセージを作成したり、入力ストリームからメッセージを読み取ったりすることができます。WebServiceMessageFactory
には 2 つの具体的な実装があります。1 つは、Java 用の添付ファイル付き SOAPAPI である SAAJ に基づいています。もう 1 つは、Axis 2 の AXIOM(AXis オブジェクトモデル)に基づいています。
SaajSoapMessageFactory
SaajSoapMessageFactory
は、SOAP with Attachments API for Java(SAAJ)を使用して SoapMessage
実装を作成します。SAAJ は J2EE 1.4 の一部であるため、最新のアプリケーションサーバーのほとんどでサポートされているはずです。一般的なアプリケーションサーバーによって提供される SAAJ バージョンの概要は次のとおりです。
アプリケーションサーバー | SAAJ バージョン |
---|---|
BEA WebLogic 8 | 1.1 |
BEA WebLogic 9 | 1.1/1.21 |
IBM WebSphere 6 | 1.2 |
サン Glassfish 1 | 1.3 |
1 Weblogic 9 には、SAAJ 1.2 実装に既知のバグがあります。これは、すべての 1.2 インターフェースを実装しますが、呼び出されると |
さらに、Java SE 6 には SAAJ 1.3 が含まれています。SaajSoapMessageFactory
は次のように接続できます。
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
SAAJ は、ドキュメントオブジェクトモデルである DOM に基づいています。これは、すべての SOAP メッセージがメモリに保存されることを意味します。より大きな SOAP メッセージの場合、これはパフォーマンスが低下する可能性があります。その場合、AxiomSoapMessageFactory の方が適している可能性があります。 |
AxiomSoapMessageFactory
AxiomSoapMessageFactory
は、AXis 2 オブジェクトモデル(AXIOM)を使用して SoapMessage
実装を作成します。AXIOM は、XML 用のストリーミング API である StAX に基づいています。StAX は、XML メッセージを読み取るためのプルベースのメカニズムを提供します。これは、大きなメッセージに対してより効率的です。
AxiomSoapMessageFactory
の読み取りパフォーマンスを向上させるには、payloadCaching
プロパティを false に設定します(デフォルトは true)。そうすることで、SOAP 本体の内容がソケットストリームから直接読み取られます。この設定を有効にすると、ペイロードの読み取りは 1 回のみになります。これは、メッセージの前処理(ロギングまたはその他の作業)がメッセージを消費しないことを確認する必要があることを意味します。
AxiomSoapMessageFactory
は次のように使用できます。
<bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
<property name="payloadCaching" value="true"/>
</bean>
ペイロードキャッシングに加えて、AXIOM は StreamingWebServiceMessage
で定義されているようにフルストリーミングメッセージをサポートします。これは、ペイロードを DOM ツリーまたはバッファーに書き込むのではなく、レスポンスメッセージに直接設定できることを意味します。
AXIOM のフルストリーミングは、ハンドラーメソッドが JAXB2 でサポートされているオブジェクトを返すときに使用されます。このマーシャリングされたオブジェクトを自動的にレスポンスメッセージに設定し、レスポンスが送信されるときに送信ソケットストリームに書き込みます。
フルストリーミングの詳細については、StreamingWebServiceMessage
および StreamingPayload
のクラスレベルの Javadoc を参照してください。
SOAP 1.1 または 1.2
SaajSoapMessageFactory
と AxiomSoapMessageFactory
の両方に soapVersion
プロパティがあり、SoapVersion
定数を挿入できます。デフォルトでは、バージョンは 1.1 ですが、1.2 に設定できます。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
<property name="soapVersion">
<util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
</property>
</bean>
</beans>
前の例では、SOAP 1.2 メッセージのみを受け入れる SaajSoapMessageFactory
を定義します。
両方のバージョンの SOAP の形式は非常に似ていますが、1.2 バージョンは異なる XML 名前空間を使用しているため、1.1 との下位互換性はありません。SOAP 1.1 および 1.2 のその他の主な違いには、障害の構造の違いと、 SOAP バージョン番号(または一般に WS- * 仕様のバージョン番号)で注意すべき重要な点の 1 つは、仕様の最新バージョンは一般に最も人気のあるバージョンではないということです。SOAP の場合、これは(現在)使用するのに最適なバージョンが 1.1 であることを意味します。バージョン 1.2 は将来さらに普及する可能性がありますが、現在 1.1 が最も安全な方法です。 |
4.1.4. MessageContext
通常、メッセージは、リクエストとレスポンスのペアで送信されます。リクエストはクライアント側で作成され、サーバー側にトランスポートを介して送信され、そこでレスポンスが生成されます。このレスポンスはクライアントに返送され、そこで読み取られます。
Spring Web Services では、このような会話は MessageContext
に含まれています。MessageContext
には、リクエストメッセージとレスポンスメッセージを取得するためのプロパティがあります。クライアント側では、メッセージコンテキストは WebServiceTemplate
によって作成されます。サーバー側では、メッセージコンテキストはトランスポート固有の入力ストリームから読み取られます。例: HTTP では、HttpServletRequest
から読み取られ、レスポンスが HttpServletResponse
に書き戻されます。
4.2. TransportContext
SOAP プロトコルの重要な特性の 1 つは、トランスポートに依存しないようにすることです。これが、たとえば、Spring-WS が HTTP リクエスト URL ではなくメッセージコンテンツによるエンドポイントへのメッセージのマッピングをサポートしていない理由です。
ただし、クライアント側またはサーバー側のいずれかで、基盤となるトランスポートにアクセスする必要がある場合があります。このため、Spring Web Services には TransportContext
があります。トランスポートコンテキストにより、基盤となる WebServiceConnection
へのアクセスが許可されます。これは通常、サーバー側の HttpServletConnection
、クライアント側の HttpUrlConnection
または CommonsHttpConnection
です。例: サーバー側のエンドポイントまたはインターセプターで現在のリクエストの IP アドレスを取得できます。
TransportContext context = TransportContextHolder.getTransportContext();
HttpServletConnection connection = (HttpServletConnection )context.getConnection();
HttpServletRequest request = connection.getHttpServletRequest();
String ipAddress = request.getRemoteAddr();
4.3. XPath を使用した XML の処理
XML を処理する最良の方法の 1 つは、XPath を使用することです。[effective-xml] の引用、アイテム 35:
XPath は、プロセッサーがそれらのノードにナビゲートする方法を正確に指定せずに、処理するノードを指定できるようにする第 4 世代の宣言型言語です。XPath のデータモデルは、ほとんどすべての開発者が XML に求めるものを正確にサポートするように非常によく設計されています。たとえば、CDATA セクション内のテキストを含むすべての隣接するテキストをマージし、コメントと処理命令をスキップして、子要素と子孫要素からのテキストを含む値を計算できるようにし、すべての外部エンティティ参照を解決する必要があります。実際には、XPath 式は、入力ドキュメントの予期しない、しかしおそらく重要ではない変更に対してはるかに堅牢になる傾向があります。
Spring Web Services には、アプリケーション内で XPath を使用する 2 つの方法があります。より高速な XPathExpression
またはより柔軟な XPathTemplate
です。
4.3.1. XPathExpression
XPathExpression
は、Java 5 javax.xml.xpath.XPathExpression
インターフェースや Jaxen XPath
クラスなど、コンパイルされた XPath 式を抽象化したものです。アプリケーションコンテキストで式を作成するには、XPathExpressionFactoryBean
を使用できます。次の例では、このファクトリ Bean を使用しています。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="nameExpression" class="org.springframework.xml.xpath.XPathExpressionFactoryBean">
<property name="expression" value="/Contacts/Contact/Name"/>
</bean>
<bean id="myEndpoint" class="sample.MyXPathClass">
<constructor-arg ref="nameExpression"/>
</bean>
</beans>
上記の式は名前空間を使用していませんが、ファクトリ Bean の namespaces
プロパティを使用して名前空間を設定できます。この式は、次のようにコードで使用できます。
package sample;
public class MyXPathClass {
private final XPathExpression nameExpression;
public MyXPathClass(XPathExpression nameExpression) {
this.nameExpression = nameExpression;
}
public void doXPath(Document document) {
String name = nameExpression.evaluateAsString(document.getDocumentElement());
System.out.println("Name: " + name);
}
}
より柔軟なアプローチとして、Spring の JDBC サポートの RowMapper
に類似した NodeMapper
を使用できます。次の例は、その使用方法を示しています。
package sample;
public class MyXPathClass {
private final XPathExpression contactExpression;
public MyXPathClass(XPathExpression contactExpression) {
this.contactExpression = contactExpression;
}
public void doXPath(Document document) {
List contacts = contactExpression.evaluate(document,
new NodeMapper() {
public Object mapNode(Node node, int nodeNum) throws DOMException {
Element contactElement = (Element) node;
Element nameElement = (Element) contactElement.getElementsByTagName("Name").item(0);
Element phoneElement = (Element) contactElement.getElementsByTagName("Phone").item(0);
return new Contact(nameElement.getTextContent(), phoneElement.getTextContent());
}
});
PlainText Section qName; // do something with the list of Contact objects
}
}
Spring JDBC の RowMapper
の行のマッピングと同様に、各結果ノードは匿名の内部クラスを使用してマッピングされます。この場合、後で使用する Contact
オブジェクトを作成します。
4.3.2. XPathTemplate
XPathExpression
では、コンパイル済みの単一の式のみを評価できます。より柔軟ですが、より低速ですが、代替手段は XpathTemplate
です。このクラスは、Spring 全体で使用される共通のテンプレートパターン(JdbcTemplate
、JmsTemplate
など)に従います。次のリストは例を示しています。
package sample;
public class MyXPathClass {
private XPathOperations template = new Jaxp13XPathTemplate();
public void doXPath(Source source) {
String name = template.evaluateAsString("/Contacts/Contact/Name", request);
// do something with name
}
}
4.4. メッセージのログとトレース
Web サービスを開発またはデバッグする場合、(SOAP)メッセージが到着したとき、送信される前にその内容を確認すると非常に便利です。Spring Web Services は、標準の Commons Logging インターフェースを介してこの機能を提供します。
必ず Commons Logging バージョン 1.1 以降を使用してください。以前のバージョンにはクラスの読み込みの課題があり、Log4JTRACE レベルと統合されていません。 |
すべてのサーバー側メッセージをログに記録するには、org.springframework.ws.server.MessageTracing
ロガーレベルを DEBUG
または TRACE
に設定します。DEBUG
レベルでは、ペイロードルート要素のみがログに記録されます。TRACE
レベルでは、メッセージの内容全体がログに記録されます。送信されたメッセージのみをログに記録する場合は、org.springframework.ws.server.MessageTracing.sent
ロガーを使用してください。同様に、org.springframework.ws.server.MessageTracing.received
を使用して、受信したメッセージのみをログに記録できます。
クライアント側には、org.springframework.ws.client.MessageTracing.sent
と org.springframework.ws.client.MessageTracing.received
という同様のロガーが存在します。
次の log4j.properties
構成ファイルの例では、クライアント側で送信されたメッセージの全内容と、クライアント側で受信されたメッセージのペイロードルート要素のみがログに記録されます。サーバー側では、ペイロードルートは送信メッセージと受信メッセージの両方についてログに記録されます。
log4j.rootCategory=INFO, stdout
log4j.logger.org.springframework.ws.client.MessageTracing.sent=TRACE
log4j.logger.org.springframework.ws.client.MessageTracing.received=DEBUG
log4j.logger.org.springframework.ws.server.MessageTracing=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%p [%c{3}] %m%n
この構成では、一般的な出力は次のとおりです。
TRACE [client.MessageTracing.sent] Sent request [<SOAP-ENV:Envelope xmlns:SOAP-ENV="... DEBUG [server.MessageTracing.received] Received request [SaajSoapMessage {http://example.com}request] ... DEBUG [server.MessageTracing.sent] Sent response [SaajSoapMessage {http://example.com}response] ... DEBUG [client.MessageTracing.received] Received response [SaajSoapMessage {http://example.com}response] ...
5. Spring-WS を使用した Web サービスの作成
Spring-WS のサーバー側サポートは、受信メッセージをエンドポイントにディスパッチする MessageDispatcher
を中心に設計されており、構成可能なエンドポイントマッピング、レスポンス生成、エンドポイントインターセプトを備えています。エンドポイントには通常、@Endpoint
アノテーションが付けられ、1 つ以上の処理メソッドがあります。これらのメソッドは、メッセージの一部(通常はペイロード)をインスペクションして受信 XML リクエストメッセージを処理し、ある種のレスポンスを作成します。メソッドに別のアノテーション(通常は @PayloadRoot
)を付けて、処理できるメッセージの種類を示すことができます。
Spring-WS の XML 処理は非常に柔軟です。エンドポイントは、Spring-WS でサポートされている多数の XML 処理ライブラリから選択できます。
DOM ファミリー: W3C DOM、JDOM、dom4j、XOM
SAX または StAX: より速いパフォーマンスのために
XPath: メッセージから情報を抽出するには
マーシャリング手法(JAXB、Castor、XMLBeans、JiBX、XStream): XML をオブジェクトに変換する(またはその逆)
5.1. MessageDispatcher
Spring-WS のサーバー側は、受信 XML メッセージをエンドポイントにディスパッチする主要クラスを中心に設計されています。Spring-WS の MessageDispatcher
は非常に柔軟性があり、Spring IoC コンテナーで構成できる限り、あらゆる種類のクラスをエンドポイントとして使用できます。ある意味で、メッセージディスパッチャーは Spring の DispatcherServlet
、Spring Web MVC で使用される「フロントコントローラー」に似ています。
次のシーケンス図は、MessageDispatcher
の処理とディスパッチのフローを示しています。
MessageDispatcher
が使用できるようにセットアップされ、その特定のディスパッチャーにリクエストが来ると、MessageDispatcher
はリクエストの処理を開始します。次のプロセスでは、MessageDispatcher
がリクエストを処理する方法について説明します。
構成された
EndpointMapping(s)
は、適切なエンドポイントを検索します。エンドポイントが見つかると、エンドポイント(プリプロセッサー、ポストプロセッサー、エンドポイント)に関連付けられた呼び出しチェーンが呼び出され、レスポンスが作成されます。エンドポイントに適切なアダプターが見つかりました。
MessageDispatcher
は、このアダプターに委譲してエンドポイントを呼び出します。レスポンスが返されると、途中で送信されます。レスポンスが返されない場合(たとえば、セキュリティ上の理由で、プリプロセッサーまたはポストプロセッサーがリクエストをインターセプトしたことが原因である可能性があります)、レスポンスは送信されません。
リクエストの処理中にスローされた例外は、アプリケーションコンテキストで宣言されたエンドポイント例外リゾルバーのいずれかによって取得されます。これらの例外リゾルバーを使用すると、そのような例外がスローされた場合のカスタム動作(SOAP 障害の返送など)を定義できます。
MessageDispatcher
には、エンドポイントアダプター、マッピング、例外リゾルバーを設定するためのいくつかのプロパティがあります。ただし、ディスパッチャーはアプリケーションコンテキストに登録されているすべての型を自動的に検出するため、これらのプロパティを設定する必要はありません。これらのプロパティは、検出をオーバーライドする必要がある場合にのみ設定する必要があります。
メッセージディスパッチャーは、トランスポート固有の入力ストリームおよび出力ストリームではなく、メッセージコンテキストで動作します。その結果、トランスポート固有のリクエストは MessageContext
に読み込む必要があります。HTTP の場合、これは WebServiceMessageReceiverHandlerAdapter
(これは Spring Web HandlerInterceptor
です) を使用して行われるため、MessageDispatcher
は標準の DispatcherServlet
に接続できます。ただし、これを行うためのより便利な方法が MessageDispatcherServlet
に示されています。
5.2. トランスポート
Spring Web Services は、複数のトランスポートプロトコルをサポートしています。最も一般的なのは HTTP トランスポートで、カスタムサーブレットが提供されますが、JMS やメールを介してメッセージを送信することもできます。
5.2.1. MessageDispatcherServlet
MessageDispatcherServlet
は、標準の Spring Web DispatcherServlet
から便利に拡張され、MessageDispatcher
をラップする標準の Servlet
です。その結果、これらの属性を 1 つに結合します。MessageDispatcher
として、前のセクションで説明したのと同じリクエスト処理フローに従います。サーブレットとして、MessageDispatcherServlet
は Web アプリケーションの web.xml
で構成されます。MessageDispatcherServlet
で処理するリクエストは、同じ web.xml
ファイル内の URL マッピングによってマップする必要があります。これは、標準の Java EE サーブレット構成です。次の例は、そのような MessageDispatcherServlet
宣言とマッピングを示しています。
<web-app>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
前の例では、すべてのリクエストは spring-ws
MessageDispatcherServlet
によって処理されます。Spring-WS フレームワークで使用されるさまざまなコンポーネント Bean も構成する必要があるため、これは Spring Web Services をセットアップするための最初のステップにすぎません。この構成は、標準の Spring XML <bean/>
定義で構成されています。MessageDispatcherServlet
は標準の Spring DispatcherServlet
であるため、Web アプリケーションの WEB-INF
ディレクトリで [servlet-name] -servlet.xml という名前のファイルを検索し、そこで定義された Bean を Spring コンテナーに作成します。前の例では、"/WEB-INF/spring-ws-servlet.xml" を検索します。このファイルには、エンドポイント、マーシャラーなど、すべての Spring Web Services Bean が含まれています。
web.xml
の代わりに、Servlet 3+ 環境で実行する場合は、Spring-WS をプログラムで構成できます。この目的のために、Spring-WS は、Spring Framework にある WebApplicationInitializer
インターフェースを継承するいくつかの抽象基本クラスを提供します。Bean 定義に @Configuration
クラスも使用する場合は、AbstractAnnotationConfigMessageDispatcherServletInitializer
を継承する必要があります。
public class MyServletInitializer
extends AbstractAnnotationConfigMessageDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{MyRootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MyEndpointConfig.class};
}
}
前の例では、エンドポイント Bean 定義が MyEndpointConfig
クラス(@Configuration
クラス)にあることを Spring に通知します。その他の Bean 定義(通常はサービス、リポジトリなど)は、MyRootConfig
クラスにあります。デフォルトでは、AbstractAnnotationConfigMessageDispatcherServletInitializer
はサーブレットを /services
と *.wsdl
の 2 つのパターンにマップしますが、getServletMappings()
メソッドをオーバーライドすることでこれを変更できます。MessageDispatcherServlet
のプログラム構成の詳細については、AbstractMessageDispatcherServletInitializer
(Javadoc) および AbstractAnnotationConfigMessageDispatcherServletInitializer
(Javadoc) の Javadoc を参照してください。
自動 WSDL 公開
MessageDispatcherServlet
は、Spring コンテナーで定義されている WsdlDefinition
Bean を自動的に検出します。WsdlDefinition
で検出されたすべての Bean は、WsdlDefinitionHandlerAdapter
を介して公開されます。これは、いくつかの Bean を定義することにより、WSDL をクライアントに公開するための便利な方法です。
例として、Spring-WS 構成ファイル(/WEB-INF/[servlet-name]-servlet.xml
)で定義されている次の <static-wsdl>
定義について考えてみます。id
属性の値に注意してください。これは、WSDL を公開するときに使用されるためです。
<sws:static-wsdl id="orders" location="orders.wsdl"/>
または、@Configuration
クラスの @Bean
メソッドにすることもできます。
@Bean
public SimpleWsdl11Definition orders() {
return new SimpleWsdl11Definition(new ClassPathResource("orders.wsdl"));
}
次の形式の URL への GET
リクエストを介して、クラスパス上の orders.wsdl
ファイルで定義された WSDL にアクセスできます(必要に応じて、ホスト、ポート、サーブレットのコンテキストパスに置き換えてください)。
http://localhost:8080/spring-ws/orders.wsdl
すべての WsdlDefinition Bean 定義は、`.wsdl` の接尾辞が付いた Bean 名で MessageDispatcherServlet によって公開されます。Bean 名が echo 、ホスト名が server 、サーブレットコンテキスト(war 名)が spring-ws の場合、WSDL は http://server/spring-ws/echo.wsdl (英語) にあります。 |
MessageDispatcherServlet
(より正確には WsdlDefinitionHandlerAdapter
)のもう 1 つの優れた機能は、公開するすべての WSDL の location
の値を変換して、受信リクエストの URL を反映できることです。
この location
変換機能はデフォルトでオフになっていることに注意してください。この機能をオンにするには、MessageDispatcherServlet
に初期化パラメーターを指定する必要があります。
<web-app>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
AbstractAnnotationConfigMessageDispatcherServletInitializer
を使用する場合、変換の有効化は、isTransformWsdlLocations()
メソッドをオーバーライドして true
を返すのと同じくらい簡単です。
変換プロセス全体の詳細については、WsdlDefinitionHandlerAdapter
(Javadoc) クラスのクラスレベルの Javadoc を参照してください。
手動で WSDL を記述し、<static-wsdl>
で公開する代わりに、Spring Web Services は XSD スキーマから WSDL を生成することもできます。これは WSDL の公開に示されているアプローチです。次のアプリケーションコンテキストスニペットは、このような動的 WSDL ファイルを作成する方法を示しています。
<sws:dynamic-wsdl id="orders"
portTypeName="Orders"
locationUri="http://localhost:8080/ordersService/">
<sws:xsd location="Orders.xsd"/>
</sws:dynamic-wsdl>
または、Java @Bean
メソッドを使用することもできます。
@Bean
public DefaultWsdl11Definition orders() {
DefaultWsdl11Definition definition = new DefaultWsdl11Definition();
definition.setPortTypeName("Orders");
definition.setLocationUri("http://localhost:8080/ordersService/");
definition.setSchema(new SimpleXsdSchema(new ClassPathResource("echo.xsd")));
return definition;
}
<dynamic-wsdl>
エレメントは、DefaultWsdl11Definition
クラスに依存します。この定義クラスは、org.springframework.ws.wsdl.wsdl11.provider
(Javadoc) パッケージの WSDL プロバイダーと ProviderBasedWsdl4jDefinition
(Javadoc) クラスを使用して、最初にリクエストされたときに WSDL を生成します。必要に応じて、このメカニズムを継承する方法については、これらのクラスのクラスレベルの Javadoc を参照してください。
DefaultWsdl11Definition
(したがって、<dynamic-wsdl>
タグ)は、規則を使用して XSD スキーマから WSDL を構築します。スキーマで見つかったすべての element
要素を反復処理し、すべての要素に対して message
を作成します。次に、定義されたリクエストまたはレスポンスサフィックスで終わるすべてのメッセージに対して WSDL operation
を作成します。デフォルトのリクエストサフィックスは Request
です。デフォルトのレスポンスサフィックスは Response
ですが、これらは <dynamic-wsdl />
でそれぞれ requestSuffix
属性と responseSuffix
属性を設定することで変更できます。また、操作に基づいて portType
、binding
、service
を構築します。
たとえば、Orders.xsd
スキーマが GetOrdersRequest
要素と GetOrdersResponse
要素を定義している場合、<dynamic-wsdl>
は GetOrdersRequest
および GetOrdersResponse
メッセージと GetOrders
操作を作成します。これは Orders
ポート型に入れられます。
インクルードまたはインポートのいずれかによって複数のスキーマを使用するには、CommonsXMLSchema をクラスパスに配置できます。Commons XMLSchema がクラスパス上にある場合、<dynamic-wsdl>
要素はすべての XSD インポートに従い、単一の XSD として WSDL に含めてインライン化します。これにより、スキーマのデプロイが大幅に簡素化され、スキーマを個別に編集できるようになります。
XSD から実行時に WSDL を作成するのは便利ですが、このアプローチにはいくつかの欠点があります。まず、リリース間で WSDL 生成プロセスの一貫性を保つように努めていますが、それでも(わずかに)変更される機能があります。次に、生成は少し遅いですが、生成されると、WSDL は後で参照できるようにキャッシュされます。 |
<dynamic-wsdl>
は、プロジェクトの開発段階でのみ使用する必要があります。ブラウザーを使用して、生成された WSDL をダウンロードし、プロジェクトに保存して、<static-wsdl>
で公開することをお勧めします。これは、WSDL が時間の経過とともに変化しないことを実際に確認する唯一の方法です。
5.2.2. DispatcherServlet
で Spring-WS を接続する
MessageDispatcherServlet
の代わりに、標準の Spring-Web MVC DispatcherServlet
で MessageDispatcher
を接続できます。デフォルトでは、DispatcherServlet
は Controllers
にのみ委譲できますが、サーブレットの Web アプリケーションコンテキストに WebServiceMessageReceiverHandlerAdapter
を追加することにより、MessageDispatcher
に委譲するように指示できます。
<beans>
<bean class="org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="defaultHandler" ref="messageDispatcher"/>
</bean
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>
...
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
</beans>
WebServiceMessageReceiverHandlerAdapter
を明示的に追加すると、ディスパッチャーサーブレットはデフォルトのアダプターをロードせず、標準の Spring-MVC @Controllers
を処理できないことに注意してください。最後に RequestMappingHandlerAdapter
を追加します。
同様の方法で、WsdlDefinitionHandlerAdapter
を接続して、DispatcherServlet
が WsdlDefinition
インターフェースの実装を処理できることを確認できます。
<beans>
<bean class="org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter"/>
<bean class="org.springframework.ws.transport.http.WsdlDefinitionHandlerAdapter"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="*.wsdl">myServiceDefinition</prop>
</props>
</property>
<property name="defaultHandler" ref="messageDispatcher"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>
<bean id="myServiceDefinition" class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<prop name="wsdl" value="/WEB-INF/myServiceDefintion.wsdl"/>
</bean>
...
</beans>
5.2.3. JMS トランスポート
Spring Web Services は、Spring フレームワークで提供される JMS 機能を介してサーバー側の JMS 処理をサポートします。Spring Web Services は、MessageListenerContainer
にプラグインするための WebServiceMessageListener
を提供します。このメッセージリスナーが動作するには、WebServiceMessageFactory
と MessageDispatcher
が必要です。次の構成例はこれを示しています。
<beans>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false"/>
</bean>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="RequestQueue"/>
<property name="messageListener">
<bean class="org.springframework.ws.transport.jms.WebServiceMessageListener">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>
</property>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
5.2.4. メールトランスポート
HTTP と JMS に加えて、Spring Web Services はサーバー側のメール処理も提供します。この機能は、MailMessageReceiver
クラスを通じて提供されます。このクラスは、POP3 または IMAP フォルダーを監視し、メールを WebServiceMessage
に変換し、SMTP を使用してレスポンスを送信します。ホスト名は、リクエストを監視するメールフォルダー(通常は POP3 または IMAP フォルダー)を示す storeUri
、およびレスポンスの送信に使用するサーバー(通常は SMTP サーバー)を示す transportUri
を介して構成できます。
プラグ可能な戦略である MonitoringStrategy
を使用して、MailMessageReceiver
が受信メッセージを監視する方法を構成できます。デフォルトでは、ポーリング戦略が使用されます。この戦略では、受信フォルダーが 5 分ごとに新しいメッセージについてポーリングされます。ストラテジーの pollingInterval
プロパティを設定することにより、この間隔を変更できます。デフォルトでは、すべての MonitoringStrategy
実装は処理されたメッセージを削除します。この設定は、deleteMessages
プロパティを設定することで変更できます。
非常に非効率的なポーリングアプローチの代替として、IMAPIDLE を使用する監視戦略があります。IDLE コマンドは、メールサーバーが新しいメッセージの更新を MailMessageReceiver
に非同期で送信できるようにする IMAP メールプロトコルのオプションの拡張です。IDLE コマンドをサポートする IMAP サーバーを使用している場合は、ImapIdleMonitoringStrategy
を monitoringStrategy
プロパティにプラグインできます。サポートするサーバーに加えて、JavaMail バージョン 1.4.1 以降を使用する必要があります。
次の構成は、サーバー側のメールサポートを使用する方法を示しており、デフォルトのポーリング間隔をオーバーライドして 30 秒(30.000 ミリ秒)ごとにチェックします。
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="messagingReceiver" class="org.springframework.ws.transport.mail.MailMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="from" value="Spring-WS SOAP Server <[email protected] (英語) >"/>
<property name="storeUri" value="imap://server:[email protected] (英語) /INBOX"/>
<property name="transportUri" value="smtp://smtp.example.com"/>
<property name="messageReceiver" ref="messageDispatcher"/>
<property name="monitoringStrategy">
<bean class="org.springframework.ws.transport.mail.monitor.PollingMonitoringStrategy">
<property name="pollingInterval" value="30000"/>
</bean>
</property>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
5.2.5. 組み込み HTTP サーバートランスポート
Spring Web Services は、Sun の JRE 1.6 Http サーバー (英語) に基づくトランスポートを提供します。組み込み HTTP サーバーは、構成が簡単なスタンドアロンサーバーです。これは、従来のサーブレットコンテナーよりも軽量な代替手段を提供します。
組み込み HTTP サーバーを使用する場合、外部デプロイ記述子(web.xml
)は必要ありません。サーバーのインスタンスを定義し、受信リクエストを処理するように構成するだけで済みます。Core Spring Framework のリモートモジュールには、HTTP サーバー用の便利なファクトリ Bean である SimpleHttpServerFactoryBean
が含まれています。最も重要なプロパティは contexts
で、コンテキストパスを対応する HttpHandler
インスタンスにマップします。
Spring Web Services は、HttpHandler
インターフェースの 2 つの実装(WsdlDefinitionHttpHandler
(Javadoc) と WebServiceMessageReceiverHttpHandler
(Javadoc) )を提供します。前者は、受信 GET リクエストを WsdlDefinition
にマップします。後者は、Web サービスメッセージの POST リクエストの処理を担当するため、そのタスクを実行するには WebServiceMessageFactory
(通常は SaajSoapMessageFactory
)と WebServiceMessageReceiver
(通常は SoapMessageDispatcher
)が必要です。
サーブレットの世界との類似点を描くために、contexts
プロパティは web.xml
のサーブレットマッピングのロールを果たし、WebServiceMessageReceiverHttpHandler
は MessageDispatcherServlet
と同等です。
次のスニペットは、HTTP サーバートランスポートの構成例を示しています。
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="messageReceiver" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings" ref="endpointMapping"/>
</bean>
<bean id="endpointMapping" class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint" ref="stockEndpoint"/>
</bean>
<bean id="httpServer" class="org.springframework.remoting.support.SimpleHttpServerFactoryBean">
<property name="contexts">
<map>
<entry key="/StockService.wsdl" value-ref="wsdlHandler"/>
<entry key="/StockService" value-ref="soapHandler"/>
</map>
</property>
</bean>
<bean id="soapHandler" class="org.springframework.ws.transport.http.WebServiceMessageReceiverHttpHandler">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageReceiver"/>
</bean>
<bean id="wsdlHandler" class="org.springframework.ws.transport.http.WsdlDefinitionHttpHandler">
<property name="definition" ref="wsdlDefinition"/>
</bean>
</beans>
SimpleHttpServerFactoryBean
の詳細については、Javadoc (英語) を参照してください。
5.2.6. XMPP トランスポート
Spring Web Services 2.0 は、Jabber とも呼ばれる XMPP のサポートを導入しました。サポートはスマック (英語) ライブラリに基づいています。
XMPP の Spring Web Services サポートは、他のトランスポートと非常によく似ています。WebServiceTemplate
用の XmppMessageSender
と、MessageDispatcher
で使用する XmppMessageReceiver
があります。
次の例は、サーバー側の XMPP コンポーネントを設定する方法を示しています。
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean">
<property name="host" value="jabber.org"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="messagingReceiver" class="org.springframework.ws.transport.xmpp.XmppMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="connection" ref="connection"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
5.2.7. MTOM
MTOM [Wikipedia] (英語) は、Web サービスとの間でバイナリデータを送受信するためのメカニズムです。MTOM サンプル [GitHub] (英語) を介して Spring WS でこれを実装する方法を見ることができます。
5.3. エンドポイント
エンドポイントは、Spring-WS のサーバー側サポートの中心的な概念です。エンドポイントは、アプリケーションの動作へのアクセスを提供します。これは通常、ビジネスサービスインターフェースによって定義されます。エンドポイントは XML リクエストメッセージを解釈し、その入力を使用して(通常)ビジネスサービスのメソッドを呼び出します。そのサービス呼び出しの結果は、レスポンスメッセージとして表されます。Spring-WS にはさまざまなエンドポイントがあり、さまざまな方法を使用して XML メッセージを処理し、レスポンスを作成します。
クラスに @Endpoint
アノテーションを付けることで、エンドポイントを作成できます。このクラスでは、さまざまなパラメーター型(DOM 要素、JAXB2 オブジェクトなど)を使用して、受信 XML リクエストを処理する 1 つ以上のメソッドを定義します。別のアノテーション(通常は @PayloadRoot
)を使用して、メソッドが処理できるメッセージの種類を示すことができます。
次のサンプルエンドポイントについて考えてみます。
package samples;
@Endpoint (1)
public class AnnotationOrderEndpoint {
private final OrderService orderService;
@Autowired (2)
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@PayloadRoot(localPart = "order", namespace = "http://samples") (5)
public void order(@RequestPayload Element orderElement) { (3)
Order order = createOrder(orderElement);
orderService.createOrder(order);
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples") (5)
@ResponsePayload
public Order getOrder(@RequestPayload OrderRequest orderRequest, SoapHeader header) { (4)
checkSoapHeaderForSomething(header);
return orderService.getOrder(orderRequest.getId());
}
...
}
1 | クラスには @Endpoint のアノテーションが付けられ、Spring-WS エンドポイントとしてマークされます。 |
2 | コンストラクターは @Autowired でマークされているため、OrderService ビジネスサービスはこのエンドポイントに注入されます。 |
3 | order メソッドは、パラメーターとして Element (@RequestPayload でアノテーションが付けられている)を取ります。これは、メッセージのペイロードが DOM 要素としてこのメソッドに渡されることを意味します。このメソッドには void 戻り値の型があり、レスポンスメッセージが送信されないことを示します。エンドポイントメソッドの詳細については、@Endpoint の取り扱い方法を参照してください。 |
4 | getOrder メソッドは、パラメーターとして OrderRequest (@RequestPayload のアノテーションも付けられています)を取ります。このパラメーターは、JAXB2 でサポートされているオブジェクトです(@XmlRootElement でアノテーションが付けられています)。これは、メッセージのペイロードがマーシャリングされていないオブジェクトとしてこのメソッドに渡されることを意味します。SoapHeader 型もパラメーターとして指定されます。呼び出し時に、このパラメーターにはリクエストメッセージの SOAP ヘッダーが含まれます。このメソッドには @ResponsePayload のアノテーションも付けられており、戻り値(Order )がレスポンスメッセージのペイロードとして使用されていることを示しています。エンドポイントメソッドの詳細については、@Endpoint の取り扱い方法を参照してください。 |
5 | このエンドポイントの 2 つの処理メソッドは @PayloadRoot でマークされ、メソッドで処理できるリクエストメッセージの種類を示します。getOrder メソッドは、orderRequest ローカル名と http://samples (英語) 名前空間 URI を持つリクエストに対して呼び出されます。order メソッドは、order ローカル名を持つリクエストに対して呼び出されます。@PayloadRoot の詳細については、エンドポイントマッピングを参照してください。 |
@Endpoint
および関連する Spring-WS アノテーションのサポートを有効にするには、Spring アプリケーションコンテキストに以下を追加する必要があります。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/web-services
http://www.springframework.org/schema/web-services/web-services.xsd">
*<sws:annotation-driven />
</beans>
または、Spring XML の代わりに @Configuration
クラスを使用する場合は、構成クラスに @EnableWs
アノテーションを付けることができます。
@EnableWs
@Configuration
public class EchoConfig {
// @Bean definitions go here
}
@EnableWs
構成をカスタマイズするには、WsConfigurer
を実装するか、さらに良いことに、WsConfigurerAdapter
を継承します。
@Configuration
@EnableWs
@ComponentScan(basePackageClasses = { MyConfiguration.class })
public class MyConfiguration extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyInterceptor());
}
@Override
public void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyArgumentResolver());
}
// More overridden methods ...
}
次のいくつかのセクションでは、@Endpoint
プログラミングモデルのより詳細な説明を示します。
エンドポイントは、他の Spring Bean と同様に、デフォルトでシングルトンとしてスコープされます。つまり、Bean 定義の 1 つのインスタンスがコンテナーごとに作成されます。シングルトンであるということは、複数のスレッドが同時にそれを使用できることを意味するため、エンドポイントはスレッドセーフである必要があります。プロトタイプなど、別のスコープを使用する場合は、Spring リファレンスドキュメントを参照してください。 |
Note that all abstract base classes provided in Spring-WS are thread safe, unless otherwise indicated in the class-level Javadoc.
5.3.1. @Endpoint
の取り扱い方法
エンドポイントが実際に受信 XML メッセージを処理するには、1 つ以上の処理メソッドが必要です。処理メソッドは、さまざまなパラメーターと戻り値型を受け取ることができます。ただし、通常、メッセージペイロードを含むパラメーターが 1 つあり、レスポンスメッセージのペイロード(存在する場合)を返します。このセクションでは、サポートされているパラメーターと戻り値の型について説明します。
メソッドが処理できるメッセージの種類を示すために、メソッドには通常、@PayloadRoot
または @SoapAction
アノテーションが付けられます。これらのアノテーションについて詳しくは、エンドポイントマッピングを参照してください。
次の例は、処理方法を示しています。
@PayloadRoot(localPart = "order", namespace = "http://samples")
public void order(@RequestPayload Element orderElement) {
Order order = createOrder(orderElement);
orderService.createOrder(order);
}
order
メソッドは、パラメーターとして Element
(@RequestPayload
でアノテーションが付けられている)を取ります。これは、メッセージのペイロードが DOM 要素としてこのメソッドに渡されることを意味します。このメソッドには void
戻り値の型があり、レスポンスメッセージが送信されないことを示します。
メソッドパラメーターの処理
処理方法には通常、受信 XML メッセージのさまざまな部分を参照する 1 つ以上のパラメーターがあります。最も一般的には、処理メソッドにはメッセージのペイロードにマップする単一のパラメーターがありますが、SOAP ヘッダーなどのリクエストメッセージの他の部分にマップすることもできます。このセクションでは、メソッド署名の処理に使用できるパラメーターについて説明します。
パラメーターをリクエストメッセージのペイロードにマップするには、このパラメーターに @RequestPayload
アノテーションを付ける必要があります。このアノテーションは、パラメーターをリクエストペイロードにバインドする必要があることを Spring-WS に通知します。
次の表に、サポートされているパラメーター型を示します。サポートされている型、パラメーターに @RequestPayload
アノテーションを付ける必要があるかどうか、および追加の注記が表示されます。
名前 | サポートされているパラメーター型 | @RequestPayload が必要ですか? | その他の注意事項 |
---|---|---|---|
TrAX |
|
Yes |
Enabled by default. |
W3C DOM |
| はい | デフォルトで有効 |
dom4j |
| はい | dom4j がクラスパス上にある場合に有効になります。 |
JDOM |
| はい | JDOM がクラスパス上にある場合に有効になります。 |
XOM |
| はい | XOM がクラスパス上にある場合に有効になります。 |
StAX |
| はい | StAX がクラスパス上にある場合に有効になります。 |
XPath | Spring 変換サービスによって | いいえ | デフォルトで有効になっています。 |
メッセージコンテキスト |
| いいえ | デフォルトで有効になっています。 |
SOAP |
|
No |
Enabled by default. |
JAXB2 |
Any type that is annotated with |
Yes |
Enabled when JAXB2 is on the classpath. |
OXM |
Any type supported by a Spring OXM |
Yes |
Enabled when the |
The next few examples show possible method signatures. The following method is invoked with the payload of the request message as a DOM org.w3c.dom.Element
:
public void handle(@RequestPayload Element element)
The following method is invoked with the payload of the request message as a javax.xml.transform.dom.DOMSource
. The header
parameter is bound to the SOAP header of the request message.
public void handle(@RequestPayload DOMSource domSource, SoapHeader header)
The following method is invoked with the payload of the request message unmarshalled into a MyJaxb2Object
(which is annotated with @XmlRootElement
). The payload of the message is also given as a DOM Element
. The whole message context is passed on as the third parameter.
public void handle(@RequestPayload MyJaxb2Object requestObject, @RequestPayload Element element, Message messageContext)
As you can see, there are a lot of possibilities when it comes to defining how to handle method signatures. You can even extend this mechanism to support your own parameter types. See the Javadoc of DefaultMethodEndpointAdapter
(Javadoc) and MethodArgumentResolver
(Javadoc) to see how.
@XPathParam
One parameter type needs some extra explanation: @XPathParam
. The idea here is that you annotate one or more method parameters with an XPath expression and that each such annotated parameter is bound to the evaluation of the expression. The following example shows how to do so:
package samples;
@Endpoint
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
@Namespace(prefix = "s", uri="http://samples")
public Order getOrder(@XPathParam("/s:orderRequest/@id") int orderId) {
Order order = orderService.getOrder(orderId);
// create Source from order and return it
}
}
Since we use the s
prefix in our XPath expression, we must bind it to the http://samples (英語)
namespace. This is accomplished with the @Namespace
annotation. Alternatively, we could have placed this annotation on the type-level to use the same namespace mapping for all handler methods or even the package-level (in package-info.java
) to use it for multiple endpoints.
By using the @XPathParam
, you can bind to all the data types supported by XPath:
-
boolean
orBoolean
double
またはDouble
String
Node
NodeList
このリストに加えて、Spring 変換サービスによって String
から変換できる任意の型を使用できます。
メソッドの戻り型の処理
レスポンスメッセージを送信するには、処理で戻り値の型を指定する必要があります。レスポンスメッセージが不要な場合、メソッドは void
戻り値の型を宣言できます。最も一般的には、return 型は、レスポンスメッセージのペイロードを作成するために使用されます。ただし、レスポンスメッセージの他の部分にマップすることもできます。このセクションでは、メソッドシグネチャーの処理で使用できる戻り値の型について説明します。
戻り値をレスポンスメッセージのペイロードにマップするには、メソッドに @ResponsePayload
アノテーションを付ける必要があります。このアノテーションは、戻り値をレスポンスペイロードにバインドする必要があることを Spring-WS に通知します。
次の表に、サポートされている戻り値の型を示します。サポートされている型、パラメーターに @ResponsePayload
アノテーションを付ける必要があるかどうか、および追加の注記が表示されます。
名前 | サポートされている戻り値の型 | @ResponsePayload が必要ですか? | その他の注意事項 |
---|---|---|---|
レスポンスなし |
| いいえ | デフォルトで有効になっています。 |
TrAX |
|
Yes |
Enabled by default. |
W3C DOM |
| はい | デフォルトで有効 |
dom4j |
| はい | dom4j がクラスパス上にある場合に有効になります。 |
JDOM |
| はい | JDOM がクラスパス上にある場合に有効になります。 |
XOM |
| はい | XOM がクラスパス上にある場合に有効になります。 |
JAXB2 |
| はい | JAXB2 がクラスパス上にある場合に有効になります。 |
OXM | Spring OXM | はい |
|
処理メソッドのシグネチャーの定義に関しては、多くの可能性があります。このメカニズムを継承して、独自のパラメーター型をサポートすることも可能です。方法については、DefaultMethodEndpointAdapter
(Javadoc) および MethodReturnValueHandler
(Javadoc) のクラスレベルの Javadoc を参照してください。
5.4. エンドポイントマッピング
エンドポイントマッピングは、受信メッセージを適切なエンドポイントにマッピングするロールを果たします。一部のエンドポイントマッピングはデフォルトで有効になっています(たとえば、PayloadRootAnnotationMethodEndpointMapping
または SoapActionAnnotationMethodEndpointMapping
)。ただし、最初に EndpointMapping
の一般的な概念を検討する必要があります。
EndpointMapping
は EndpointInvocationChain
を配信します。これには、受信リクエストに一致するエンドポイントが含まれ、リクエストとレスポンスに適用されるエンドポイントインターセプターのリストも含まれる場合があります。リクエストが届くと、MessageDispatcher
はそれをエンドポイントマッピングに渡してリクエストをインスペクションし、適切な EndpointInvocationChain
を考え出します。次に、MessageDispatcher
は、チェーン内のエンドポイントとすべてのインターセプターを呼び出します。
オプションでインターセプターを含めることができる(次に、リクエスト、レスポンス、その両方を操作できる)構成可能なエンドポイントマッピングの概念は非常に強力です。多くのサポート機能をカスタム EndpointMapping
実装に組み込むことができます。例: カスタムエンドポイントマッピングでは、メッセージの内容だけでなく、特定の SOAP ヘッダー(または実際には複数の SOAP ヘッダー)に基づいてエンドポイントを選択できます。
ほとんどのエンドポイントマッピングは、使用するインターセプターのリストである「インターセプター」プロパティを提供する AbstractEndpointMapping
を継承しています。EndpointInterceptors
はリクエストのインターセプト — EndpointInterceptor
インターフェースで説明されています。さらに、defaultEndpoint
があります。これは、このエンドポイントマッピングでエンドポイントが一致しない場合に使用するデフォルトのエンドポイントです。
エンドポイントに従って、@Endpoint
スタイルでは、1 つのエンドポイントクラスで複数のリクエストを処理できます。これは MethodEndpointMapping
の責任です。このマッピングは、受信リクエストメッセージに対して呼び出されるメソッドを決定します。
メソッドにリクエストを送信できるエンドポイントマッピングは 2 つあります。PayloadRootAnnotationMethodEndpointMapping
と SoapActionAnnotationMethodEndpointMapping
アプリケーションコンテキストで <sws:annotation-driven/>
を使用して、両方のメソッドを有効にできます。
PayloadRootAnnotationMethodEndpointMapping
は、@PayloadRoot
アノテーションを localPart
および namespace
要素とともに使用して、特定の修飾名でメソッドをマークします。ペイロードルート要素のこの修飾名を持つメッセージが届くたびに、メソッドが呼び出されます。例については、上記を参照してください。
または、SoapActionAnnotationMethodEndpointMapping
は @SoapAction
アノテーションを使用して、特定の SOAP アクションでメソッドをマークします。この SOAPAction
ヘッダーを含むメッセージが届くたびに、メソッドが呼び出されます。
5.4.1. WS-Addressing
WS-Addressing は、トランスポートニュートラルルーティングメカニズムを指定します。これは、To
ヘッダーと Action
SOAP ヘッダーに基づいており、SOAP メッセージの宛先と意図をそれぞれ示します。さらに、WS-Addressing を使用すると、リターンアドレス(通常のメッセージおよび障害用)と、相関に使用できる一意のメッセージ識別子を定義できます。WS-Addressing の詳細については、https://en.wikipedia.org/wiki/WS-Addressing (英語) を参照してください。次の例は、WS-Addressing メッセージを示しています。
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<SOAP-ENV::Header>
<wsa:MessageID>urn:uuid:21363e0d-2645-4eb7-8afd-2f5ee1bb25cf</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://example.com/business/client1</wsa:Address>
</wsa:ReplyTo>
<wsa:To S:mustUnderstand="true">http://example/com/fabrikam</wsa:To>
<wsa:Action>http://example.com/fabrikam/mail/Delete</wsa:Action>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<f:Delete xmlns:f="http://example.com/fabrikam">
<f:maxCount>42</f:maxCount>
</f:Delete>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
前の例では、宛先は http://example/com/fabrikam (英語)
に設定され、アクションは http://example.com/fabrikam/mail/Delete (英語)
に設定されています。さらに、メッセージ識別子と返信先アドレスがあります。デフォルトでは、このアドレスは「匿名」アドレスであり、リクエストと同じチャネルを使用してレスポンスを送信する必要があることを示します(つまり、HTTP レスポンス)が、この例に示すように、別のアドレスにすることもできます。
Spring Web Services では、WS-Addressing はエンドポイントマッピングとして実装されています。このマッピングを使用して、前述の SoapActionAnnotationMethodEndpointMapping
と同様に、WS-Addressing アクションをエンドポイントに関連付けます。
AnnotationActionEndpointMapping
を使用する
AnnotationActionEndpointMapping
は SoapActionAnnotationMethodEndpointMapping
に似ていますが、SOAP アクショントランスポートヘッダーの代わりに WS-Addressing ヘッダーを使用します。
AnnotationActionEndpointMapping
を使用するには、@Endpoint
の取り扱い方法およびエンドポイントマッピングで説明されている @PayloadRoot
および @SoapAction
アノテーションと同様に、処理メソッドに @Action
アノテーションを付けます。次の例は、その方法を示しています。
package samples;
@Endpoint
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@Action("http://samples/RequestOrder")
public Order getOrder(OrderRequest orderRequest) {
return orderService.getOrder(orderRequest.getId());
}
@Action("http://samples/CreateOrder")
public void order(Order order) {
orderService.createOrder(order);
}
}
上記のマッピングは、http://samples/RequestOrder (英語)
の WS-Addressing Action
を持つリクエストを getOrder
メソッドにルーティングします。http://samples/CreateOrder (英語)
を使用したリクエストは、order
メソッドにルーティングされます。
デフォルトでは、AnnotationActionEndpointMapping
は 1.0(2006 年 5 月)と 2004 年 8 月版の WS-Addressing の両方をサポートします。これらの 2 つのバージョンは最も人気があり、Axis 1 および 2、JAX-WS、XFire、Windows Communication Foundation(WCF)、および Windows Services Enhancements(WSE)3.0 と相互運用可能です。必要に応じて、仕様の特定のバージョンを versions
プロパティに挿入できます。
@Action
アノテーションに加えて、@Address
アノテーションでクラスにアノテーションを付けることができます。設定されている場合、値は受信メッセージの To
ヘッダープロパティと比較されます。
最後に、messageSenders
プロパティがあります。これは、匿名ではない範囲外のアドレスにレスポンスメッセージを送信するために必要です。WebServiceTemplate
の場合と同じように、このプロパティで MessageSender
の実装を設定できます。URI とトランスポートを参照してください。
5.4.2. リクエストのインターセプト — EndpointInterceptor
インターフェース
エンドポイントマッピングメカニズムには、エンドポイントインターセプターの概念があります。これらは、特定の機能を特定のリクエストに適用する場合に非常に役立ちます。たとえば、セキュリティ関連の SOAP ヘッダーの処理や、リクエストとレスポンスのメッセージのログ記録などです。
エンドポイントインターセプターは通常、アプリケーションコンテキストで <sws:interceptors>
要素を使用して定義されます。この要素では、そのアプリケーションコンテキストで定義されたすべてのエンドポイントに適用されるエンドポイントインターセプター Bean を定義できます。または、<sws:payloadRoot>
または <sws:soapAction>
要素を使用して、インターセプターが適用するペイロードルート名または SOAP アクションを指定することもできます。次の例は、その方法を示しています。
<sws:interceptors>
<bean class="samples.MyGlobalInterceptor"/>
<sws:payloadRoot namespaceUri="http://www.example.com">
<bean class="samples.MyPayloadRootInterceptor"/>
</sws:payloadRoot>
<sws:soapAction value="http://www.example.com/SoapAction">
<bean class="samples.MySoapActionInterceptor1"/>
<ref bean="mySoapActionInterceptor2"/>
</sws:soapAction>
</sws:interceptors>
<bean id="mySoapActionInterceptor2" class="samples.MySoapActionInterceptor2"/>
前の例では、すべてのリクエストとレスポンスをインターセプトする 1 つの「グローバル」インターセプター(MyGlobalInterceptor
)を定義します。また、ペイロードルート名前空間として http://www.example.com (英語)
を持つ XML メッセージにのみ適用されるインターセプターを定義します。namespaceUri
に加えて localPart
属性を定義して、インターセプターが適用されるメッセージをさらに制限することもできます。最後に、メッセージに http://www.example.com/SoapAction (英語)
SOAP アクションがある場合に適用される 2 つのインターセプターを定義します。2 番目のインターセプターが実際には <interceptors>
要素の外側の Bean 定義への参照であることに注意してください。Bean 参照は、<interceptors>
要素内のどこでも使用できます。
@Configuration
クラスを使用する場合、WsConfigurerAdapter
から拡張してインターセプターを追加できます。
@Configuration
@EnableWs
public class MyWsConfiguration extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyPayloadRootInterceptor());
}
}
インターセプターは、org.springframework.ws.server
パッケージから EndpointInterceptor
インターフェースを実装する必要があります。このインターフェースは、実際のエンドポイントが処理される前にリクエストメッセージを処理するために使用できる方法、通常のレスポンスメッセージを処理するために使用できる方法、および障害メッセージを処理するために使用できる方法の 3 つの方法を定義します。次の 2 つは、エンドポイントが処理された後に呼び出されます。これらの 3 つの方法は、あらゆる種類の前処理と後処理を実行するのに十分な柔軟性を提供する必要があります。
インターセプターの handleRequest(..)
メソッドはブール値を返します。このメソッドを使用して、呼び出しチェーンの処理を中断または続行できます。このメソッドが true
を返すと、エンドポイント処理チェーンが続行されます。false
を返す場合、MessageDispatcher
はこれを、インターセプター自体が処理を行い、呼び出しチェーン内の他のインターセプターと実際のエンドポイントの処理を続行しないことを意味すると解釈します。handleResponse(..)
メソッドと handleFault(..)
メソッドにもブール値の戻り値があります。これらのメソッドが false
を返す場合、レスポンスはクライアントに返送されません。
Web サービスで使用できる標準の EndpointInterceptor
実装がいくつかあります。さらに、XwsSecurityInterceptor
で説明されている XwsSecurityInterceptor
があります。
PayloadLoggingInterceptor
および SoapEnvelopeLoggingInterceptor
Web サービスを開発するときは、受信および送信 XML メッセージをログに記録すると便利です。Spring WS は、PayloadLoggingInterceptor
クラスと SoapEnvelopeLoggingInterceptor
クラスでこれを容易にします。前者は、メッセージのペイロードのみを Commons Logging ログに記録します。後者は、SOAP ヘッダーを含む SOAP エンベロープ全体をログに記録します。次の例は、エンドポイントマッピングで PayloadLoggingInterceptor
を定義する方法を示しています。
<sws:interceptors>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</sws:interceptors>
これらのインターセプターには両方とも、logRequest
と logResponse
の 2 つのプロパティがあり、これらを false
に設定して、リクエストメッセージまたはレスポンスメッセージのロギングを無効にすることができます。
前述のように、PayloadLoggingInterceptor
にも WsConfigurerAdapter
アプローチを使用できます。
PayloadValidatingInterceptor
契約ファースト開発スタイルを使用する利点の 1 つは、スキーマを使用して受信および発信 XML メッセージを検証できることです。Spring-WS は、PayloadValidatingInterceptor
を使用してこれを容易にします。このインターセプターは、1 つ以上の W3CXML または RELAXNG スキーマへの参照を必要とし、リクエスト、レスポンス、その両方を検証するように設定できます。
リクエストの検証は良い考えのように聞こえるかもしれませんが、結果として得られる Web サービスは非常に厳密になることに注意してください。通常、エンドポイントがリクエストを満たすのに十分な情報を取得できる場合にのみ、リクエストが検証されるかどうかはそれほど重要ではありません。エンドポイントはそのスキーマに準拠する必要があるため、レスポンスを検証することをお勧めします。ポステルの法則を忘れないでください。「自分の行動には保守的であり、他人から受け入れることには寛大である」。 |
次の例では、PayloadValidatingInterceptor
を使用しています。この例では、/WEB-INF/orders.xsd
のスキーマを使用してレスポンスを検証しますが、リクエストは検証しません。PayloadValidatingInterceptor
は、schemas
プロパティを設定することにより、複数のスキーマを受け入れることもできることに注意してください。
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schema" value="/WEB-INF/orders.xsd"/>
<property name="validateRequest" value="false"/>
<property name="validateResponse" value="true"/>
</bean>
もちろん、前述のように、PayloadValidatingInterceptor
にも WsConfigurerAdapter
アプローチを使用できます。
PayloadTransformingInterceptor
を使用する
ペイロードを別の XML 形式に変換するために、Spring Web Services は PayloadTransformingInterceptor
を提供します。このエンドポイントインターセプターは XSLT スタイルシートに基づいており、古いメッセージ形式を新しい形式に変換できるため、Web サービスの複数のバージョンをサポートする場合に特に役立ちます。次の例では、PayloadTransformingInterceptor
を使用しています。
<bean id="transformingInterceptor"
class="org.springframework.ws.server.endpoint.interceptor.PayloadTransformingInterceptor">
<property name="requestXslt" value="/WEB-INF/oldRequests.xslt"/>
<property name="responseXslt" value="/WEB-INF/oldResponses.xslt"/>
</bean>
前の例では、/WEB-INF/oldRequests.xslt
を使用してリクエストを変換し、/WEB-INF/oldResponses.xslt
を使用してレスポンスメッセージを変換します。エンドポイントインターセプターはエンドポイントマッピングレベルで登録されるため、「古いスタイル」のメッセージに適用されるエンドポイントマッピングを作成し、そのマッピングにインターセプターを追加できることに注意してください。変換はこれらの「古いスタイル」のメッセージにのみ適用されます。
前述のように、PayloadTransformingInterceptor
にも WsConfigurerAdapter
アプローチを使用できます。
5.5. 例外の処理
Spring-WS は EndpointExceptionResolvers
を提供して、メッセージがリクエストに一致するエンドポイントによって処理されている間に発生する予期しない例外の苦痛を和らげます。エンドポイント例外リゾルバーは、Web アプリケーション記述子 web.xml
で定義できる例外マッピングにいくぶん似ています。ただし、例外を処理するためのより柔軟な方法を提供します。これらは、例外がスローされたときに呼び出されたエンドポイントに関する情報を提供します。さらに、プログラムで例外を処理する方法により、適切に対応する方法についてさらに多くのオプションが提供されます。例外とスタックトレースを指定してアプリケーションの内部を公開するのではなく、特定の障害コードと文字列を含む SOAP 障害を返すなど、任意の方法で例外を処理できます。
エンドポイント例外リゾルバーは MessageDispatcher
によって自動的に取得されるため、明示的な構成は必要ありません。
resolveException(MessageContext, endpoint, Exception)
メソッドの実装のみの問題である EndpointExceptionResolver
インターフェースの実装に加えて、提供されている実装の 1 つを使用することもできます。最も単純な実装は SimpleSoapExceptionResolver
です。これは、SOAP 1.1 サーバーまたは SOAP1.2 レシーバー障害を作成し、障害文字列として例外メッセージを使用します。SimpleSoapExceptionResolver
がデフォルトですが、別のリゾルバーを明示的に追加することでオーバーライドできます。
5.5.1. SoapFaultMappingExceptionResolver
SoapFaultMappingExceptionResolver
はより洗練された実装です。このリゾルバーを使用すると、スローされる可能性のある例外のクラス名を取得して、SOAP 障害にマップできます。
<beans>
<bean id="exceptionResolver"
class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
<property name="defaultFault" value="SERVER"/>
<property name="exceptionMappings">
<value>
org.springframework.oxm.ValidationFailureException=CLIENT,Invalid request
</value>
</property>
</bean>
</beans>
キー値とデフォルトのエンドポイントは faultCode,faultString,locale
の形式を使用し、障害コードのみが必要です。障害文字列が設定されていない場合、デフォルトで例外メッセージになります。言語が設定されていない場合、デフォルトで英語になります。上記の構成では、次のように、型 ValidationFailureException
の例外を Invalid request
の障害文字列を持つクライアント側の SOAP 障害にマップします。
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>Invalid request</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
その他の例外が発生した場合は、デフォルトの障害が返されます。サーバー側の障害で、障害文字列として例外メッセージが表示されます。
5.5.2. SoapFaultAnnotationExceptionResolver
を使用する
例外クラスに @SoapFault
アノテーションを付けて、その例外がスローされるたびに返される必要がある SOAP 障害を示すこともできます。これらのアノテーションを取得するには、アプリケーションコンテキストに SoapFaultAnnotationExceptionResolver
を追加する必要があります。アノテーションの要素には、障害コードの列挙、障害の文字列または理由、言語が含まれます。次の例は、そのような例外を示しています。
package samples;
@SoapFault(faultCode = FaultCode.SERVER)
public class MyBusinessException extends Exception {
public MyClientException(String message) {
super(message);
}
}
エンドポイントの呼び出し中にコンストラクター文字列 "Oops!"
とともに MyBusinessException
がスローされると、次のレスポンスが返されます。
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Oops!</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
5.6. サーバー側のテスト
Web サービスエンドポイントのテストに関しては、2 つの可能なアプローチがあります。
エンドポイントが消費する(モック)引数を提供する単体テストを記述します。
このアプローチの利点は、非常に簡単に実行できることです(特に、
@Endpoint
でアノテーションが付けられたクラスの場合)。欠点は、ネットワーク経由で送信される XML メッセージの正確な内容を実際にテストしていないことです。メッセージの内容をテストする統合テストを記述します。
最初のアプローチは、EasyMock、JMock などのモックフレームワークを使用して簡単に実現できます。次のセクションでは、Spring Web Services 2.0 で導入されたテスト機能を使用して、統合テストを作成することに焦点を当てます。
5.6.1. サーバー側統合テストの作成
Spring Web Services 2.0 は、エンドポイント統合テストを作成するためのサポートを導入しました。このコンテキストでは、エンドポイントは(SOAP)メッセージを処理するクラスです(エンドポイントを参照)。
統合テストのサポートは、org.springframework.ws.test.server
パッケージに含まれています。そのパッケージのコアクラスは MockWebServiceClient
です。基本的な考え方は、このクライアントがリクエストメッセージを作成し、それを標準の MessageDispatcherServlet
アプリケーションコンテキストで構成されているエンドポイントに送信することです(MessageDispatcherServlet
を参照)。これらのエンドポイントはメッセージを処理し、レスポンスを作成します。次に、クライアントはこのレスポンスを受信し、登録された期待に照らして検証します。
MockWebServiceClient
の一般的な使用箇所は次のとおりです。
MockWebServiceClient.createClient(ApplicationContext)
またはMockWebServiceClient.createClient(WebServiceMessageReceiver, WebServiceMessageFactory)
を呼び出して、MockWebServiceClient
インスタンスを作成します。sendRequest(RequestCreator)
を呼び出して、おそらくRequestCreators
で提供されているデフォルトのRequestCreator
実装(静的にインポート可能)を使用して、リクエストメッセージを送信します。おそらく
ResponseMatchers
で提供されるデフォルトのResponseMatcher
実装(静的にインポート可能)を使用して、andExpect(ResponseMatcher)
を呼び出すことにより、レスポンスの期待値を設定します。andExpect(ResponseMatcher)
呼び出しを連鎖させることにより、複数の期待値を設定できます。
MockWebServiceClient (および関連するクラス)は「流れるような」API を提供するため、通常は IDE のコード補完機能を使用して、モックサーバーのセットアッププロセスをガイドできます。 |
また、単体テストでは Spring Web Services で利用可能な標準のロギング機能を利用できることにも注意してください。特定のテストが失敗した理由を見つけるために、リクエストメッセージまたはレスポンスメッセージを調べることが役立つ場合があります。詳細については、メッセージのログとトレースを参照してください。 |
たとえば、次の Web サービスエンドポイントクラスについて考えてみます。
@Endpoint (1)
public class CustomerEndpoint {
@ResponsePayload (2)
public CustomerCountResponse getCustomerCount( (2)
@RequestPayload CustomerCountRequest request) { (2)
CustomerCountResponse response = new CustomerCountResponse();
response.setCustomerCount(10);
return response;
}
}
1 | @Endpoint でアノテーションが付けられた CustomerEndpoint 。エンドポイントを参照してください。 |
2 | getCustomerCount() メソッドは、引数として CustomerCountRequest を取り、CustomerCountResponse を返します。これらのクラスは両方とも、マーシャラーによってサポートされるオブジェクトです。たとえば、JAXB2 でサポートされる @XmlRootElement アノテーションを付けることができます。 |
次の例は、CustomerEndpoint
の一般的なテストを示しています。
@RunWith(SpringJUnit4ClassRunner.class) (2)
@ContextConfiguration("spring-ws-servlet.xml") (2)
public class CustomerEndpointIntegrationTest {
@Autowired
private ApplicationContext applicationContext; (3)
private MockWebServiceClient mockClient;
@Before
public void createClient() {
mockClient = MockWebServiceClient.createClient(applicationContext); (4)
}
@Test
public void customerEndpoint() throws Exception {
Source requestPayload = new StringSource(
"<customerCountRequest xmlns='http://springframework.org/spring-ws'>" +
"<customerName>John Doe</customerName>" +
"</customerCountRequest>");
Source responsePayload = new StringSource(
"<customerCountResponse xmlns='http://springframework.org/spring-ws'>" +
"<customerCount>10</customerCount>" +
"</customerCountResponse>");
mockClient.sendRequest(withPayload(requestPayload)). (5)
andExpect(payload(responsePayload)); (5)
}
}
1 | CustomerEndpointIntegrationTest は MockWebServiceClient をインポートし、RequestCreators と ResponseMatchers を静的にインポートします。 |
2 | このテストでは、Spring Framework で提供される標準のテスト機能を使用します。これは必須ではありませんが、一般的にテストを設定する最も簡単な方法です。 |
3 | アプリケーションコンテキストは、標準の Spring-WS アプリケーションコンテキスト(MessageDispatcherServlet を参照)であり、spring-ws-servlet.xml から読み取られます。この場合、アプリケーションコンテキストには、CustomerEndpoint の Bean 定義が含まれています(または、<context:component-scan /> が使用されている可能性があります)。 |
4 | @Before メソッドでは、createClient ファクトリメソッドを使用して MockWebServiceClient を作成します。 |
5 | 静的にインポートされた RequestCreators によって提供される withPayload() RequestCreator を使用して sendRequest() を呼び出すことにより、リクエストを送信します(RequestCreator と RequestCreators の使用を参照)。 また、静的にインポートされた テストのこのパートは少し紛らわしいように見えるかもしれませんが、IDE のコード補完機能は非常に役立ちます。 |
5.6.2. RequestCreator
および RequestCreators
の使用
最初に、MockWebServiceClient
は、エンドポイントが消費するリクエストメッセージを作成する必要があります。クライアントは、この目的のために RequestCreator
戦略インターフェースを使用します。
public interface RequestCreator {
WebServiceMessage createRequest(WebServiceMessageFactory messageFactory)
throws IOException;
}
メッセージファクトリを使用してリクエストメッセージを作成し、このインターフェースの独自の実装を作成できますが、必ずしもそうする必要はありません。RequestCreators
クラスは、withPayload()
メソッドの特定のペイロードに基づいて RequestCreator
を作成する方法を提供します。通常、RequestCreators
を静的にインポートします。
5.6.3. ResponseMatcher
および ResponseMatchers
の使用
リクエストメッセージがエンドポイントによって処理され、レスポンスが受信されると、MockWebServiceClient
は、このレスポンスメッセージが特定の期待を満たしているかどうかを検証できます。クライアントは、この目的のために ResponseMatcher
戦略インターフェースを使用します。
public interface ResponseMatcher {
void match(WebServiceMessage request,
WebServiceMessage response)
throws IOException, AssertionError;
}
繰り返しになりますが、このインターフェースの独自の実装を記述して、メッセージが期待を満たさない場合に AssertionError
インスタンスをスローできますが、ResponseMatchers
クラスはテストで使用する標準の ResponseMatcher
実装を提供するため、そうする必要はありません。通常、このクラスは静的にインポートします。
ResponseMatchers
クラスは、次のレスポンスマッチャーを提供します。
ResponseMatchers メソッド | 説明 |
---|---|
| 指定されたレスポンスペイロードを期待します。 |
| レスポンスペイロードが特定の XSD スキーマに対して検証されることを期待します。 |
| 指定された XPath 式が存在するか、存在しないか、指定された値に評価されることを期待します。 |
| 指定された SOAP ヘッダーがレスポンスメッセージに存在することを想定しています。 |
| レスポンスメッセージに SOAP 障害が含まれていないことを想定しています。 |
| レスポンスメッセージに特定の SOAP 障害が含まれていることを想定しています。 |
andExpect()
呼び出しを連鎖させることにより、複数のレスポンス期待値を設定できます。
mockClient.sendRequest(...).
andExpect(payload(expectedResponsePayload)).
andExpect(validPayload(schemaResource));
ResponseMatchers
が提供するレスポンスマッチャーの詳細については、Javadoc を参照してください。
6. クライアントで Spring Web Services を使用する
Spring-WS は、Web サービスへの一貫した XML 駆動型アクセスを可能にするクライアント側の Web サービス API を提供します。また、マーシャラーとアンマーシャラーの使用にも対応しているため、サービス層コードは Java オブジェクトのみを処理できます。
org.springframework.ws.client.core
パッケージは、クライアント側アクセス API を使用するためのコア機能を提供します。これには、コア Spring JdbcTemplate
が JDBC に対して行うのと同様に、Web サービスの使用を簡素化するテンプレートクラスが含まれています。Spring テンプレートクラスに共通の設計原則は、一般的な操作を実行するためのヘルパーメソッドを提供し、より高度な使用箇所のために、ユーザーが実装したコールバックインターフェースに委譲することです。Web サービステンプレートは同じデザインに従います。クラスは、さまざまな便利なメソッドを提供します
XML メッセージの送受信
送信する前にオブジェクトを XML にマーシャリングする
複数の輸送オプションを可能にする
6.1. クライアント側 API の使用
このセクションでは、クライアント側 API の使用方法について説明します。サーバー側 API の使用方法については、Spring-WS を使用した Web サービスの作成を参照してください。
6.1.1. WebServiceTemplate
WebServiceTemplate
は、Spring-WS のクライアント側 Web サービスアクセスのコアクラスです。これには、Source
オブジェクトを送信し、Source
または Result
としてレスポンスメッセージを受信するためのメソッドが含まれています。さらに、トランスポートを介してオブジェクトを送信する前にオブジェクトを XML にマーシャリングし、レスポンス XML をオブジェクトに再度マーシャリングすることができます。
URI とトランスポート
WebServiceTemplate
クラスは、メッセージの宛先として URI を使用します。テンプレート自体に defaultUri
プロパティを設定するか、テンプレートのメソッドを呼び出すときに URI を明示的に指定することができます。URI は WebServiceMessageSender
に解決されます。WebServiceMessageSender
は、トランスポート層を介して XML メッセージを送信するロールを果たします。WebServiceTemplate
クラスの messageSender
または messageSenders
プロパティを使用して、1 つ以上のメッセージ送信者を設定できます。
HTTP トランスポート
HTTP を介してメッセージを送信するための WebServiceMessageSender
インターフェースの 2 つの実装があります。デフォルトの実装は HttpUrlConnectionMessageSender
で、Java 自体が提供する機能を使用します。別の方法は、Apache HttpComponents HttpClient (英語) を使用する HttpComponentsMessageSender
です。より高度で使いやすい機能(認証、HTTP 接続プールなど)が必要な場合は、後者を使用してください。
HTTP トランスポートを使用するには、defaultUri
を http://example.com/services (英語)
のようなものに設定するか、いずれかのメソッドに uri
パラメーターを指定します。
次の例は、HTTP トランスポートのデフォルト構成を使用する方法を示しています。
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="defaultUri" value="http://example.com/WebService"/>
</bean>
</beans>
次の例は、デフォルト設定をオーバーライドし、Apache HttpClient を使用して HTTP 認証で認証する方法を示しています。
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="credentials">
<bean class="org.apache.http.auth.UsernamePasswordCredentials">
<constructor-arg value="john:secret"/>
</bean>
</property>
</bean>
</property>
<property name="defaultUri" value="http://example.com/WebService"/>
</bean>
JMS トランスポート
JMS 経由でメッセージを送信するために、Spring Web Services は JmsMessageSender
を提供します。このクラスは、Spring フレームワークの機能を使用して WebServiceMessage
を JMS Message
に変換し、Queue
または Topic
で送信し、レスポンス (存在する場合) を受信します。
JmsMessageSender
を使用するには、defaultUri
または uri
パラメーターを JMS URI に設定する必要があります。JMS URI は、少なくとも jms:
プレフィックスと宛先名で構成されます。JMS URI の例には、jms:SomeQueue
、jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT
、jms:RequestQueue?replyToName=ResponseName
があります。この URI 構文の詳細については、JmsMessageSender
の Javadoc を参照してください。
デフォルトでは、JmsMessageSender
は JMS BytesMessage
を送信しますが、JMS URI の messageType
パラメーターを使用して TextMessages
を使用するようにこれをオーバーライドできます (例: jms:Queue?messageType=TEXT_MESSAGE
)。TextMessages
は添付ファイルと文字エンコーディングを確実にサポートしていないため、BytesMessages
が推奨される型であることに注意してください。
次の例は、JMS トランスポートを ActiveMQ 接続ファクトリと組み合わせて使用する方法を示しています。
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false"/>
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.jms.JmsMessageSender">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
</property>
<property name="defaultUri" value="jms:RequestQueue?deliveryMode=NON_PERSISTENT"/>
</bean>
</beans>
メールトランスポート
Spring Web Services は、SMTP 経由で Web サービスメッセージを送信し、POP3 または IMAP 経由で取得するために使用できるメールトランスポートも提供します。クライアント側のメール機能は、MailMessageSender
クラスに含まれています。このクラスは、リクエスト WebServiceMessage
からメールメッセージを作成し、SMTP 経由で送信します。次に、レスポンスメッセージが受信 POP3 または IMAP サーバーに到着するのを待ちます。
MailMessageSender
を使用するには、defaultUri
または uri
パラメーターを mailto
URI (たとえば、mailto:[email protected] (英語)
または mailto:server@localhost?subject=SOAP%20Test
) に設定します。メッセージ送信者が、リクエストの送信に使用するサーバー (通常は SMTP サーバー) を示す transportUri
と、レスポンスをポーリングするサーバー (通常は POP3 または IMAP サーバー) を示す storeUri
で適切に構成されていることを確認してください。
次の例は、メールトランスポートの使用方法を示しています。
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.mail.MailMessageSender">
<property name="from" value="Spring-WS SOAP Client <[email protected] (英語) >"/>
<property name="transportUri" value="smtp://client:[email protected] (英語) "/>
<property name="storeUri" value="imap://client:[email protected] (英語) /INBOX"/>
</bean>
</property>
<property name="defaultUri" value="mailto:[email protected] (英語) ?subject=SOAP%20Test"/>
</bean>
</beans>
XMPP トランスポート
Spring Web Services 2.0 では、XMPP 経由で Web サービスメッセージを送受信するために使用できる XMPP (Jabber) トランスポートが導入されました。クライアント側の XMPP 機能は、XmppMessageSender
クラスに含まれています。このクラスは、リクエスト WebServiceMessage
から XMPP メッセージを作成し、XMPP 経由で送信します。次に、レスポンスメッセージが到着するのを待ちます。
XmppMessageSender
を使用するには、defaultUri
または uri
パラメーターを xmpp
URI(たとえば、xmpp:[email protected] (英語)
)に設定します。また、送信者は XMPPConnection
が機能することを必要とします。これは、org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean
を使用して簡単に作成できます。
次の例は、XMPP トランスポートの使用方法を示しています。
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean">
<property name="host" value="jabber.org"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.xmpp.XmppMessageSender">
<property name="connection" ref="connection"/>
</bean>
</property>
<property name="defaultUri" value="xmpp:[email protected] (英語) "/>
</bean>
</beans>
6.1.2. WebServiceMessage
の送受信
WebServiceTemplate
には、Web サービスメッセージを送受信するための多くの便利なメソッドが含まれています。Source
を受け入れて返すメソッドと、Result
を返すメソッドがあります。さらに、オブジェクトを XML にマーシャリングおよびアンマーシャルするメソッドがあります。次の例では、単純な XML メッセージを Web サービスに送信します。
public class WebServiceClient {
private static final String MESSAGE =
"<message xmlns=\"http://tempuri.org\">Hello, Web Service World</message>";
private final WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
public void setDefaultUri(String defaultUri) {
webServiceTemplate.setDefaultUri(defaultUri);
}
// send to the configured default URI
public void simpleSendAndReceive() {
StreamSource source = new StreamSource(new StringReader(MESSAGE));
StreamResult result = new StreamResult(System.out);
webServiceTemplate.sendSourceAndReceiveToResult(source, result);
}
// send to an explicit URI
public void customSendAndReceive() {
StreamSource source = new StreamSource(new StringReader(MESSAGE));
StreamResult result = new StreamResult(System.out);
webServiceTemplate.sendSourceAndReceiveToResult("http://localhost:8080/AnotherWebService",
source, result);
}
}
<beans xmlns="http://www.springframework.org/schema/beans">
<bean id="webServiceClient" class="WebServiceClient">
<property name="defaultUri" value="http://localhost:8080/WebService"/>
</bean>
</beans>
前の例では、WebServiceTemplate
を使用して、http://localhost:8080/WebService
にある Web サービスに "Hello, World" メッセージを送信し (simpleSendAndReceive()
メソッドの場合)、結果をコンソールに書き込みます。WebServiceTemplate
には、Java コードで明示的に指定された URI がないために使用されるデフォルト URI が挿入されます。
WebServiceTemplate
クラスは、一度構成するとスレッドセーフになることに注意してください (その依存関係もすべてスレッドセーフであると仮定します。これは、Spring-WS に同梱されているすべての依存関係に当てはまります)。そのため、複数のオブジェクトが同じ共有 WebServiceTemplate
を使用できます。インスタンス。WebServiceTemplate
は、引数ゼロのコンストラクターと、インスタンスの構築に使用できる messageFactory
および messageSender
Bean プロパティを公開します (Spring コンテナーまたはプレーン Java コードを使用)。または、Spring-WS の WebServiceGatewaySupport
コンビニエンス基本クラスから派生することを検討してください。これは、便利な Bean プロパティを公開して構成を簡単にします。(この基本クラスを継承する必要はありません。コンビニエンスクラスのみの提供です。)
6.1.3. POJO の送受信 — マーシャリングとアンマーシャリング
プレーン Java オブジェクトの送信を容易にするために、WebServiceTemplate
には、メッセージのデータコンテンツの引数として Object
を受け取る多数の send(..)
メソッドがあります。WebServiceTemplate
クラスのメソッド marshalSendAndReceive(..)
は、リクエストオブジェクトの XML から Marshaller
への変換と、レスポンス XML からオブジェクトへの変換を Unmarshaller
に委譲します。(マーシャリングとアンマーシャラーの詳細については、Spring Framework リファレンスドキュメントを参照してください) マーシャラーを使用することで、アプリケーションコードは送受信されるビジネスオブジェクトに集中でき、XML として表現される方法の詳細には関心がありません。マーシャリング機能を使用するには、WebServiceTemplate
クラスの marshaller
および unmarshaller
プロパティを使用してマーシャラーとアンマーシャラーを設定する必要があります。
6.1.4. WebServiceMessageCallback
を使用する
メッセージの SOAP ヘッダーの設定やその他の設定に対応するために、WebServiceMessageCallback
インターフェースは、メッセージが作成された後、送信される前にメッセージにアクセスできるようにします。次の例は、オブジェクトのマーシャリングによって作成されたメッセージに SOAP アクションヘッダーを設定する方法を示しています。
public void marshalWithSoapActionHeader(MyObject o) {
webServiceTemplate.marshalSendAndReceive(o, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
((SoapMessage)message).setSoapAction("http://tempuri.org/Action");
}
});
}
org.springframework.ws.soap.client.core.SoapActionCallback を使用して SOAP アクションヘッダーを設定することもできます。 |
WS-Addressing
サーバー側の WS-Addressing サポートに加えて、Spring Web Services はクライアント側でもこの仕様をサポートしています。
クライアントで WS-Addressing ヘッダーを設定するには、org.springframework.ws.soap.addressing.client.ActionCallback
を使用できます。このコールバックは、目的のアクションヘッダーをパラメーターとして受け取ります。また、WS-Addressing バージョンと To
ヘッダーを指定するためのコンストラクターもあります。指定されていない場合、To
ヘッダーはデフォルトで確立されている接続の URL になります。
次の例では、Action
ヘッダーを http://samples/RequestOrder (英語)
に設定します。
webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));
6.1.5. WebServiceMessageExtractor
を使用する
WebServiceMessageExtractor
インターフェースは、受信した WebServiceMessage
から Object
を抽出するプロセスを完全に制御できる低レベルのコールバックインターフェースです。WebServiceTemplate
は、提供されたリソースへの基本的な接続がまだ開いている間に、提供された WebServiceMessageExtractor
で extractData(..)
メソッドを呼び出します。次の例は、動作中の WebServiceMessageExtractor
を示しています。
public void marshalWithSoapActionHeader(final Source s) {
final Transformer transformer = transformerFactory.newTransformer();
webServiceTemplate.sendAndReceive(new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
transformer.transform(s, message.getPayloadResult());
},
new WebServiceMessageExtractor() {
public Object extractData(WebServiceMessage message) throws IOException {
// do your own transforms with message.getPayloadResult()
// or message.getPayloadSource()
}
}
});
}
6.2. クライアント側のテスト
Web サービスクライアント (つまり、WebServiceTemplate
を使用して Web サービスにアクセスするクラス) をテストする場合、2 つの可能なアプローチがあります。
WebServiceTemplate
クラス、WebServiceOperations
インターフェース、または完全なクライアントクラスをモックする単体テストを作成します。このアプローチの利点は、達成が容易なことです。欠点は、特にクライアントクラス全体をモックする場合、ネットワーク経由で送信される XML メッセージの正確な内容を実際にテストしていないことです。
メッセージの内容をテストする統合テストを作成します。
最初のアプローチは、EasyMock、JMock などのモックフレームワークを使用して簡単に実現できます。次のセクションでは、Spring Web Services 2.0 で導入されたテスト機能を使用した統合テストの作成に焦点を当てます。
6.2.1. クライアント側の統合テストの作成
Spring Web Services 2.0 は、Web サービスクライアント統合テストを作成するためのサポートを導入しました。このコンテキストでは、クライアントは WebServiceTemplate
を使用して Web サービスにアクセスするクラスです。
統合テストのサポートは org.springframework.ws.test.client
パッケージに含まれています。そのパッケージのコアクラスは MockWebServiceServer
です。基本的な考え方は、Web サービステンプレートがこのモックサーバーに接続してリクエストメッセージを送信し、モックサーバーが登録された期待に照らして検証するというものです。期待が満たされると、モックサーバーはレスポンスメッセージを準備し、テンプレートに送り返します。
MockWebServiceServer
の一般的な使用箇所は次のとおりです。
MockWebServiceServer.createServer(WebServiceTemplate)
、MockWebServiceServer.createServer(WebServiceGatewaySupport)
、MockWebServiceServer.createServer(ApplicationContext)
を呼び出して、MockWebServiceServer
インスタンスを作成します。おそらく
RequestMatchers
で提供されるデフォルトのRequestMatcher
実装(静的にインポート可能)を使用して、expect(RequestMatcher)
を呼び出すことにより、リクエストの期待値を設定します。andExpect(RequestMatcher)
呼び出しを連鎖させることにより、複数の期待値を設定できます。andRespond(ResponseCreator)
を呼び出して、おそらくResponseCreators
で提供されているデフォルトのResponseCreator
実装(静的にインポート可能)を使用して、適切なレスポンスメッセージを作成します。WebServiceTemplate
は、クライアントコードを介して直接、または通常どおりに使用します。MockWebServiceServer.verify()
を呼び出して、すべての期待値が満たされていることを確認します。
MockWebServiceServer (および関連クラス) は「流れるような」API を提供するため、通常、IDE のコード補完機能を使用して、モックサーバーのセットアッププロセスをガイドできます。 |
また、単体テストでは Spring Web Services で利用可能な標準のロギング機能を利用できることにも注意してください。特定のテストが失敗した理由を見つけるために、リクエストメッセージまたはレスポンスメッセージを調べることが役立つ場合があります。詳細については、メッセージのログとトレースを参照してください。 |
たとえば、次の Web サービスクライアントクラスについて考えてみましょう。
public class CustomerClient extends WebServiceGatewaySupport { (1)
public int getCustomerCount() {
CustomerCountRequest request = new CustomerCountRequest(); (2)
request.setCustomerName("John Doe");
CustomerCountResponse response =
(CustomerCountResponse) getWebServiceTemplate().marshalSendAndReceive(request); (3)
return response.getCustomerCount();
}
}
1 | CustomerClient は WebServiceGatewaySupport を継承し、webServiceTemplate プロパティを提供します。 |
2 | CustomerCountRequest は、マーシャラーによってサポートされるオブジェクトです。たとえば、JAXB2 でサポートされる @XmlRootElement アノテーションを持つことができます。 |
3 | CustomerClient は、WebServiceGatewaySupport によって提供される WebServiceTemplate を使用して、リクエストオブジェクトを SOAP メッセージにマーシャリングし、それを Web サービスに送信します。レスポンスオブジェクトは CustomerCountResponse に非整列化されます。 |
次の例は、CustomerClient
の一般的なテストを示しています。
@RunWith(SpringJUnit4ClassRunner.class) (2)
@ContextConfiguration("integration-test.xml") (2)
public class CustomerClientIntegrationTest {
@Autowired
private CustomerClient client; (3)
private MockWebServiceServer mockServer; (4)
@Before
public void createServer() throws Exception {
mockServer = MockWebServiceServer.createServer(client);
}
@Test
public void customerClient() throws Exception {
Source requestPayload = new StringSource(
"<customerCountRequest xmlns='http://springframework.org/spring-ws'>" +
"<customerName>John Doe</customerName>" +
"</customerCountRequest>");
Source responsePayload = new StringSource(
"<customerCountResponse xmlns='http://springframework.org/spring-ws'>" +
"<customerCount>10</customerCount>" +
"</customerCountResponse>");
mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload));(5)
int result = client.getCustomerCount(); (6)
assertEquals(10, result); (6)
mockServer.verify(); (7)
}
}
1 | CustomerClientIntegrationTest は MockWebServiceServer をインポートし、RequestMatchers と ResponseCreators を静的にインポートします。 |
2 | このテストでは、Spring Framework で提供される標準のテスト機能を使用します。これは必須ではありませんが、一般的にテストを設定する最も簡単な方法です。 |
3 | CustomerClient は integration-test.xml で構成され、@Autowired を使用してこのテストに接続されます。 |
4 | @Before メソッドでは、createServer ファクトリメソッドを使用して MockWebServiceServer を作成します。 |
5 | 静的にインポートされた RequestMatchers によって提供される payload() RequestMatcher を使用して expect() を呼び出すことにより、期待値を定義します(RequestMatcher と RequestMatchers の使用を参照)。 また、静的にインポートされた テストのこのパートは少し混乱しているように見えるかもしれませんが、IDE のコード補完機能は非常に役立ちます。 |
6 | CustomerClient で getCustomerCount() を呼び出し、WebServiceTemplate を使用します。テンプレートは「テストモード」に設定されているため、このメソッド呼び出しでは実際の (HTTP) 接続は確立されません。メソッド呼び出しの結果に基づいて、いくつかの JUnit アサーションも作成します。 |
7 | MockWebServiceServer で verify() を呼び出し、予期したメッセージが実際に受信されたことを確認します。 |
6.2.2. RequestMatcher
および RequestMatchers
の使用
リクエストメッセージが特定の期待を満たしているかどうかを確認するために、MockWebServiceServer
は RequestMatcher
戦略インターフェースを使用します。このインターフェースで定義される契約は次のとおりです。
public interface RequestMatcher {
void match(URI uri,
WebServiceMessage request)
throws IOException,
AssertionError;
}
このインターフェースの独自の実装を作成して、メッセージが期待を満たさない場合に AssertionError
例外をスローすることができますが、その必要はありません。RequestMatchers
クラスは、テストで使用できる標準の RequestMatcher
実装を提供します。通常、このクラスは静的にインポートします。
RequestMatchers
クラスは、次のリクエストマッチャーを提供します。
RequestMatchers メソッド | 説明 |
---|---|
| あらゆる種類のリクエストを期待します。 |
| 指定されたリクエストペイロードを期待します。 |
| リクエストペイロードが指定された XSD スキーマに対して検証されることを期待します。 |
| 指定された XPath 式が存在するか、存在しないか、指定された値に評価されることを期待します。 |
| 指定された SOAP ヘッダーがリクエストメッセージに存在することを想定しています。 |
| 指定された URL への接続が必要です。 |
andExpect()
呼び出しを連鎖させることで、複数のリクエストの期待を設定できます。
mockServer.expect(connectionTo("http://example.com")).
andExpect(payload(expectedRequestPayload)).
andExpect(validPayload(schemaResource)).
andRespond(...);
RequestMatchers
によって提供されるリクエストマッチャーの詳細については、Javadoc を参照してください。
6.2.3. ResponseCreator
および ResponseCreators
の使用
リクエストメッセージが検証され、定義された期待を満たしている場合、MockWebServiceServer
は WebServiceTemplate
が消費するレスポンスメッセージを作成します。サーバーは、この目的のために ResponseCreator
戦略インターフェースを使用します。
public interface ResponseCreator {
WebServiceMessage createResponse(URI uri,
WebServiceMessage request,
WebServiceMessageFactory messageFactory)
throws IOException;
}
ここでも、メッセージファクトリを使用してレスポンスメッセージを作成し、このインターフェースの独自の実装を作成できますが、ResponseCreators
クラスはテストで使用できる標準の ResponseCreator
実装を提供するため、その必要はありません。通常、このクラスは静的にインポートします。
ResponseCreators
クラスは、次のレスポンスを提供します。
ResponseCreators メソッド | 説明 |
---|---|
| 指定されたペイロードでレスポンスメッセージを作成します。 |
| レスポンス接続でエラーを作成します。この方法では、エラー処理をテストする機会が与えられます。 |
| レスポンス接続から読み取るときに例外をスローします。このメソッドは、例外処理をテストする機会を提供します。 |
| 指定された SOAP 障害を含むレスポンスメッセージを作成します。この方法では、障害処理をテストする機会が与えられます。 |
RequestMatchers
によって提供されるリクエストマッチャーの詳細については、Javadoc を参照してください。
7. Spring-WS で Web サービスを保護する
この章では、WS-Security アスペクトを Web サービスに追加する方法について説明します。WS-Security の 3 つの異なる領域に焦点を当てます。
認証 : これは、本人が本人であるかどうかを判断するプロセスです。このコンテキストでは、「プリンシパル」とは、通常、アプリケーションでアクションを実行できるユーザー、デバイス、その他のシステムを意味します。
デジタル署名 : メッセージのデジタル署名は、ドキュメントと署名者の秘密鍵の両方に基づく情報です。これは、ハッシュ関数と秘密署名関数(署名者の秘密鍵で暗号化)を使用して作成されます。
暗号化と復号化 : 暗号化とは、適切なキーがなければデータを読み取ることができない形式にデータを変換するプロセスです。主に、意図していない人に情報を隠しておくために使用されます。復号化は、暗号化の逆です。これは、暗号化されたデータを読み取り可能な形式に変換するプロセスです。
これらの 3 つの領域は、それぞれ XwsSecurityInterceptor
および Wss4jSecurityInterceptor
の使用で説明する XwsSecurityInterceptor
または Wss4jSecurityInterceptor
を使用して実装されます。
WS-Security (特に暗号化と署名) は大量のメモリを必要とし、パフォーマンスを低下させる可能性があることに注意してください。パフォーマンスが重要な場合は、WS-Security または HTTP ベースのセキュリティを使用しないことを検討してください。 |
7.1. XwsSecurityInterceptor
XwsSecurityInterceptor
は、SUN の XML および Web サービスセキュリティパッケージ(XWSS)に基づく EndpointInterceptor
(リクエストのインターセプト — EndpointInterceptor
インターフェースを参照)です。この WS-Security 実装は、Java Web Services Developer Pack(Java WSDP (英語) )の一部です。
他のエンドポイントインターセプターと同様に、エンドポイントマッピングで定義されます ( エンドポイントマッピングを参照)。これは、WS-Security サポートの追加を選択できることを意味します。一部のエンドポイントマッピングではこれが必要ですが、他のエンドポイントマッピングでは必要ありません。
XWSS には、SUN 1.5 JDK と SUNSAAJ リファレンス実装の両方が必要であることに注意してください。WSS4J インターセプターにはこれらの要件はありません(Wss4jSecurityInterceptor を使用するを参照)。 |
XwsSecurityInterceptor
が動作するには、セキュリティポリシーファイルが必要です。この XML ファイルは、受信 SOAP メッセージに必要なセキュリティアスペクトと、送信メッセージに追加するアスペクトをインターセプターに通知します。ポリシーファイルの基本的な形式については次のセクションで説明しますが、より詳細なチュートリアルはこちら にあります (英語) 。Spring リソースを必要とする policyConfiguration
プロパティを使用してポリシーを設定できます。ポリシーファイルには、複数の要素を含めることができます。たとえば、受信メッセージにユーザー名トークンを要求し、すべての送信メッセージに署名します。これには、ルートとして SecurityConfiguration
要素 (JAXRPCSecurity
要素ではない) が含まれます。
さらに、セキュリティインターセプターが動作するには、1 つ以上の CallbackHandler
インスタンスが必要です。これらのハンドラーは、証明書、秘密鍵の取得、ユーザー資格情報の検証などに使用されます。Spring-WS は、最も一般的なセキュリティ問題に対応するハンドラーを提供します。たとえば、Spring Security 認証マネージャーに対する認証や、X509 証明書に基づく送信メッセージの署名などです。次のセクションでは、どのセキュリティ上の問題にどのコールバックハンドラーを使用するかを示します。callbackHandler
または callbackHandlers
プロパティを使用して、コールバックハンドラーを設定できます。
XwsSecurityInterceptor
を接続する方法を示す次の例:
<beans>
<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="certificateHandler"/>
<ref bean="authenticationHandler"/>
</list>
</property>
</bean>
...
</beans>
このインターセプターは、クラスパス上の securityPolicy.xml
ファイルを使用して構成されます。後でファイルで定義される 2 つのコールバックハンドラーを使用します。
7.1.1. キーストア
ほとんどの暗号化操作では、標準の java.security.KeyStore
オブジェクトを使用できます。これらの操作には、証明書の検証、メッセージの署名、署名の検証、暗号化が含まれます。ユーザー名とタイムスタンプの確認は除外されます。このセクションの目的は、キーストアと、キーと証明書をキーストアファイルに保存するために使用できる Java ツールに関する背景知識を提供することです。この情報は、ほとんど Spring-WS に関連するものではなく、Java の一般的な暗号機能に関連するものです。
java.security.KeyStore
クラスは、暗号化キーと証明書のストレージ機能を表します。これには、次の 3 種類の要素を含めることができます。
秘密鍵 : これらのキーは、自己認証に使用されます。秘密鍵には、対応する公開鍵の証明書 チェーンが付属しています。WS-Security のフィールド内で、これはメッセージの署名とメッセージの復号化を説明します。
対称鍵 : 対称 (または秘密) キーは、メッセージの暗号化と復号化にも使用されます。違いは、両方の側 (送信者と受信者) が同じ秘密鍵を共有することです。
トラステッド証明書 : これらの X509 証明書は「信頼できる証明書」と呼ばれます。これは、キーストアの所有者が、証明書の公開鍵が実際に証明書の所有者のものであると信頼しているためです。WS-Security 内では、これらの証明書は証明書の検証、署名の検証、暗号化に使用されます。
keytool
を使用する
キーおよび証明書管理ユーティリティである keytool
プログラムは、Java 仮想マシンに付属しています。このツールを使用して、新しいキーストアを作成したり、新しい秘密鍵と証明書をそれらに追加したりできます。keytool
コマンドの完全なリファレンスを提供することはこのドキュメントの範囲を超えていますが、ここで、またはコマンドラインで keytool -help
コマンドを使用してリファレンスを見つけることができます。
KeyStoreFactoryBean
を使用する
Spring 構成を使用してキーストアを簡単にロードするには、KeyStoreFactoryBean
を使用できます。ロードするキーストアのパスを指すように設定できるリソースロケーションプロパティがあります。キーストアデータの整合性をチェックするためにパスワードを指定できます。パスワードが指定されていない場合、整合性チェックは実行されません。次のリストは KeyStoreFactoryBean
を構成します。
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="password" value="password"/>
<property name="location" value="classpath:org/springframework/ws/soap/security/xwss/test-keystore.jks"/>
</bean>
場所のプロパティを指定しない場合、新しい空のキーストアが作成されますが、これはおそらく必要なものではありません。 |
KeyStoreCallbackHandler
XwsSecurityInterceptor
内でキーストアを使用するには、KeyStoreCallbackHandler
を定義する必要があります。このコールバックには、型 keystore
: (keyStore
、trustStore
、symmetricStore
) の 3 つのプロパティがあります。ハンドラーによって使用される正確なストアは、このハンドラーによって実行される暗号化操作によって異なります。秘密鍵の操作には keyStore
を使用します。対称鍵の操作には、symmetricStore
が使用されます。信頼関連を決定するために、trustStore
が使用されます。次の表はこれを示しています。
暗号化操作 | 使用したキーストア |
---|---|
証明書の検証 | 最初に |
秘密鍵に基づく復号化 |
|
対称鍵に基づく復号化 |
|
公開鍵証明書に基づく暗号化 |
|
対称鍵に基づく暗号化 |
|
署名 |
|
署名の検証 |
|
さらに、KeyStoreCallbackHandler
には privateKeyPassword
プロパティがあり、`keyStore` に含まれている秘密鍵のロックを解除するように設定する必要があります。
symmetricStore
が設定されていない場合、デフォルトで keyStore
になります。キーまたはトラストストアが設定されていない場合、コールバックハンドラーは標準の Java メカニズムを使用してそれをロードまたは作成します。このメカニズムがどのように機能するかは、KeyStoreCallbackHandler
の JavaDoc を参照してください。
たとえば、KeyStoreCallbackHandler
を使用して受信証明書または署名を検証する場合は、トラストストアを使用できます。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
これを使用して受信証明書を復号化したり、送信メッセージに署名したりする場合は、キーストアを使用できます。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
次のセクションでは、KeyStoreCallbackHandler
を使用できる場所と、特定の暗号化操作のために設定するプロパティを示します。
7.1.2. 認証
この章の冒頭で記述されていたように、認証とは、本人が本人であるかどうかを判断する作業です。WS-Security 内では、認証は 2 つの形式を取ることができます。ユーザー名とパスワードトークンを使用する (プレーンテキストパスワードまたはパスワードダイジェストを使用) か、X509 証明書を使用します。
プレーンテキストのユーザー名認証
ユーザー名認証の最も単純な形式では、プレーンテキストのパスワードを使用します。このシナリオでは、SOAP メッセージに UsernameToken
要素が含まれています。この要素自体には、Username
要素と、プレーンテキストのパスワードを含む Password
要素が含まれています。プレーンテキスト認証は、HTTP サーバーによって提供される基本認証と比較できます。
プレーンテキストのパスワードはあまり安全ではないことに注意してください。トランスポート層を使用する場合は、常に追加のセキュリティ対策をトランスポート層に追加する必要があります (たとえば、プレーンな HTTP の代わりに HTTPS を使用するなど)。 |
すべての受信メッセージにプレーンテキストのパスワードを持つ UsernameToken
が含まれていることを要求するには、セキュリティポリシーファイルに RequireUsernameToken
要素が含まれ、passwordDigestRequired
属性が false
に設定されている必要があります。可能な子要素のリファレンスは ここにあります (英語) 。次のリストは、RequireUsernameToken
要素を含める方法を示しています。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="false"/>
...
</xwss:SecurityConfiguration>
ユーザー名トークンが存在しない場合、XwsSecurityInterceptor
は SOAP エラーを送信者に返します。存在する場合、登録されたハンドラーに対して PlainTextPasswordRequest
を使用して PasswordValidationCallback
を起動します。Spring-WS 内には、この特定のコールバックを処理する 3 つのクラスがあります。
SimplePasswordValidationCallbackHandler
を使用する
最も単純なパスワード検証ハンドラーは SimplePasswordValidationCallbackHandler
です。このハンドラーは、メモリ内の Properties
オブジェクトに対してパスワードを検証します。これは、users
プロパティを使用して指定できます。
<bean id="passwordValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="Bert">Ernie</prop>
</props>
</property>
</bean>
この場合、ユーザー "Bert" のみがパスワード "Ernie" を使用してログインすることを許可しています。
SpringPlainTextPasswordValidationCallbackHandler
を使用する
SpringPlainTextPasswordValidationCallbackHandler
は Spring Security を使用してユーザーを認証します。Spring Security について説明することはこのドキュメントの範囲を超えていますが、これは本格的なセキュリティフレームワークです。Spring Security リファレンスドキュメントでそれについてさらに読むことができます。
SpringPlainTextPasswordValidationCallbackHandler
の動作には AuthenticationManager
が必要です。このマネージャーを使用して、作成した UsernamePasswordAuthenticationToken
に対して認証を行います。認証が成功すると、トークンは SecurityContextHolder
に格納されます。authenticationManager
プロパティを使用して、認証マネージャーを設定できます。
<beans>
<bean id="springSecurityHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringPlainTextPasswordValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<bean class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
JaasPlainTextPasswordValidationCallbackHandler
を使用する
JaasPlainTextPasswordValidationCallbackHandler
は、標準の Java 認証および認可サービス (英語) に基づいています。JAAS の完全な導入を提供することはこのドキュメントの範囲を超えていますが、優れたチュートリアル (英語) が利用可能です。
JaasPlainTextPasswordValidationCallbackHandler
の動作には loginContextName
のみが必要です。この名前を使用して新しい JAAS LoginContext
を作成し、SOAP メッセージで提供されるユーザー名とパスワードを使用して標準の JAAS NameCallback
および PasswordCallback
を処理します。これは、このコールバックハンドラーが、標準の動作である login()
フェーズ中にこれらのコールバックを起動する JAAS LoginModule
と統合されることを意味します。
JaasPlainTextPasswordValidationCallbackHandler
は次のように接続できます。
<bean id="jaasValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.jaas.JaasPlainTextPasswordValidationCallbackHandler">
<property name="loginContextName" value="MyLoginModule" />
</bean>
この場合、コールバックハンドラーは MyLoginModule
という名前の LoginContext
を使用します。このモジュールは、前述のチュートリアル (英語) で説明されているように、jaas.config
ファイルで定義する必要があります。
ダイジェストユーザー名認証
パスワードダイジェストを使用する場合、SOAP メッセージには UsernameToken
要素も含まれ、この要素自体に Username
要素と Password
要素が含まれます。違いは、パスワードがプレーンテキストではなくダイジェストとして送信されることです。受信者は、このダイジェストをユーザーの既知のパスワードから計算したダイジェストと比較し、それらが同じ場合、ユーザーは認証されます。この方法は、HTTP サーバーが提供するダイジェスト認証に相当します。
すべての受信メッセージにパスワードダイジェストを含む UsernameToken
要素が含まれていることを要求するには、セキュリティポリシーファイルに RequireUsernameToken
要素が含まれ、passwordDigestRequired
属性が true
に設定されている必要があります。さらに、nonceRequired
属性を true
に設定する必要があります。可能な子要素の参照 (英語) はここにあります。次のリストは、RequireUsernameToken
エレメントを定義する方法を示しています。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireUsernameToken passwordDigestRequired="true" nonceRequired="true"/>
...
</xwss:SecurityConfiguration>
ユーザー名トークンが存在しない場合、XwsSecurityInterceptor
は SOAP エラーを送信者に返します。存在する場合、登録されたハンドラーに対して DigestPasswordRequest
を使用して PasswordValidationCallback
を起動します。Spring-WS 内では、SimplePasswordValidationCallbackHandler
と SpringDigestPasswordValidationCallbackHandler
の 2 つのクラスがこの特定のコールバックを処理します。
SimplePasswordValidationCallbackHandler
を使用する
SimplePasswordValidationCallbackHandler
は、プレーンテキストのパスワードとパスワードダイジェストの両方を処理できます。SimplePasswordValidationCallbackHandler
を使用するで説明されています。
SpringDigestPasswordValidationCallbackHandler
を使用する
SpringDigestPasswordValidationCallbackHandler
の動作には Spring Security UserDetailService
が必要です。このサービスを使用して、トークンで指定されたユーザーのパスワードを取得します。この詳細オブジェクトに含まれるパスワードのダイジェストは、メッセージ内のダイジェストと比較されます。それらが等しい場合、ユーザーは正常に認証されており、UsernamePasswordAuthenticationToken
が SecurityContextHolder
に格納されています。userDetailsService
プロパティを使用してサービスを設定できます。さらに、userCache
プロパティを設定して、ロードされたユーザーの詳細をキャッシュすることができます。次の例は、その方法を示しています。
<beans>
<bean class="org.springframework.ws.soap.security.xwss.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
証明書認証
より安全な認証方法では、X509 証明書を使用します。このシナリオでは、SOAP メッセージに `BinarySecurityToken` が含まれています。これには、Base64 でエンコードされたバージョンの X509 証明書が含まれています。証明書は、受信者が認証するために使用します。メッセージに格納されている証明書は、メッセージの署名にも使用されます(署名の検証を参照)。
すべての受信 SOAP メッセージに `BinarySecurityToken` が含まれていることを確認するには、セキュリティポリシーファイルに RequireSignature
要素が含まれている必要があります。この要素は、署名の検証でカバーされている他の要素をさらに運ぶことができます。可能な子要素のリファレンスは ここにあります (英語) 。次のリストは、RequireSignature
要素を定義する方法を示しています。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireSignature requireTimestamp="false">
...
</xwss:SecurityConfiguration>
証明書を含まないメッセージが到着すると、XwsSecurityInterceptor
は SOAP エラーを送信者に返します。存在する場合、CertificateValidationCallback
を起動します。Spring-WS 内の 3 つのハンドラーは、認証のためにこのコールバックを処理します。
有効な証明書のみに対して認証を行う必要があるため、ほとんどの場合、証明書の認証の前に証明書の検証を行う必要があります。有効期限が過ぎた証明書や、信頼できる証明書のストアにない証明書など、無効な証明書は無視する必要があります。 Spring-WS の用語では、これは
この設定を使用して、インターセプターは最初にメッセージ内の証明書がキーストアを使用して有効であるかどうかを判断し、次にそれに対して認証します。 |
KeyStoreCallbackHandler
を使用する
KeyStoreCallbackHandler
は、標準の Java キーストアを使用して証明書を検証します。この証明書検証プロセスは、次の手順で構成されています。
ハンドラーは、証明書がプライベート
keyStore
にあるかどうかを確認します。そうである場合、有効です。証明書が秘密鍵ストアにない場合、ハンドラーは現在の日時が証明書で指定された有効期間内であるかどうかを確認します。そうでない場合、証明書は無効です。そうである場合は、最後のステップに進みます。
証明書の証明書パスが作成されます。これは基本的に、ハンドラーが証明書が
trustStore
のいずれかの認証局によって発行されたかどうかを判断することを意味します。証明書パスを正常に構築できる場合、証明書は有効です。それ以外の場合、証明書は無効です。
証明書の検証目的で KeyStoreCallbackHandler
を使用するには、ほとんどの場合、trustStore
プロパティのみを設定する必要があります。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
前の例に示したセットアップを使用すると、検証される証明書がトラストストア自体にあるか、証明書を発行した認証局がトラストストアに含まれている必要があります。
SpringCertificateValidationCallbackHandler
を使用する
SpringCertificateValidationCallbackHandler
の動作には Spring Security AuthenticationManager
が必要です。このマネージャーを使用して、作成した X509AuthenticationToken
に対して認証を行います。構成された認証マネージャーは、このトークンを処理できるプロバイダー (通常は X509AuthenticationProvider
のインスタンス) を提供することが期待されています。認証が成功すると、トークンは SecurityContextHolder
に格納されます。authenticationManager
プロパティを使用して、認証マネージャーを設定できます。
<beans>
<bean id="springSecurityCertificateHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringCertificateValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="authenticationManager"
class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<bean class="org.springframework.ws.soap.security.x509.X509AuthenticationProvider">
<property name="x509AuthoritiesPopulator">
<bean class="org.springframework.ws.soap.security.x509.populator.DaoX509AuthoritiesPopulator">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
この場合、カスタムユーザーの詳細サービスを使用して、証明書に基づいて認証の詳細を取得します。X509 証明書に対する認証の詳細については、Spring Security リファレンスドキュメント (英語) を参照してください。
JaasCertificateValidationCallbackHandler
を使用する
JaasCertificateValidationCallbackHandler
の動作には loginContextName
が必要です。この名前と証明書の X500Principal
を使用して、新しい JAAS LoginContext
を作成します。これは、このコールバックハンドラーが、X500 プリンシパルを処理する JAAS LoginModule
と統合されることを意味します。
JaasCertificateValidationCallbackHandler
は次のように接続できます。
<bean id="jaasValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.jaas.JaasCertificateValidationCallbackHandler">
<property name="loginContextName">MyLoginModule</property>
</bean>
この場合、コールバックハンドラーは MyLoginModule
という名前の LoginContext
を使用します。このモジュールは jaas.config
ファイルで定義する必要があり、X500 プリンシパルに対して認証できる必要があります。
7.1.3. デジタル署名
メッセージのデジタル署名は、ドキュメントと署名者の秘密鍵の両方に基づく情報です。WS-Security の署名には、署名の検証とメッセージへの署名という 2 つの主要なタスクが関連しています。
署名の検証
証明書ベースの認証と同様に、署名されたメッセージには BinarySecurityToken
が含まれます。これには、メッセージの署名に使用される証明書が含まれます。さらに、メッセージのどの部分が署名されたかを示す SignedInfo
ブロックが含まれています。
すべての受信 SOAP メッセージに BinarySecurityToken
が含まれていることを確認するには、セキュリティポリシーファイルに RequireSignature
要素が含まれている必要があります。また、SignatureTarget
要素を含めることもできます。これは、署名されることが期待されていたターゲットメッセージ部分とその他のさまざまなサブ要素を指定します。使用する秘密鍵エイリアス、秘密鍵の代わりに対称鍵を使用するかどうか、その他の多くのプロパティを定義することもできます。可能な子要素のリファレンスは ここにあります (英語) 。次のリストは、RequireSignature
要素を構成します。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireSignature requireTimestamp="false"/>
</xwss:SecurityConfiguration>
署名が存在しない場合、XwsSecurityInterceptor
は SOAP エラーを送信者に返します。存在する場合、登録されたハンドラーに対して SignatureVerificationKeyCallback
を起動します。Spring-WS 内では、1 つのクラスがこの特定のコールバックを処理します: KeyStoreCallbackHandler
。
KeyStoreCallbackHandler
を使用する
KeyStoreCallbackHandler に従って、KeyStoreCallbackHandler
は、署名の検証を含むさまざまな暗号化コールバックを処理するために java.security.KeyStore
を使用します。署名の検証のために、ハンドラーは trustStore
プロパティを使用します。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:org/springframework/ws/soap/security/xwss/test-truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
メッセージへの署名
メッセージに署名するとき、XwsSecurityInterceptor
は BinarySecurityToken
をメッセージに追加します。また、メッセージのどの部分が署名されたかを示す SignedInfo
ブロックも追加します。
すべての送信 SOAP メッセージに署名するには、セキュリティポリシーファイルに Sign
要素が含まれている必要があります。また、SignatureTarget
要素を含めることもできます。これは、署名されることが期待されていたターゲットメッセージ部分とその他のさまざまなサブ要素を指定します。使用する秘密鍵エイリアス、秘密鍵の代わりに対称鍵を使用するかどうか、その他の多くのプロパティを定義することもできます。可能な子要素のリファレンスは ここにあります (英語) 。次の例には、Sign
要素が含まれています。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:Sign includeTimestamp="false" />
</xwss:SecurityConfiguration>
XwsSecurityInterceptor
は、登録されたハンドラーに SignatureKeyCallback
を発行します。Spring-WS 内では、KeyStoreCallbackHandler
クラスがこの特定のコールバックを処理します。
KeyStoreCallbackHandler
を使用する
KeyStoreCallbackHandler に従って、KeyStoreCallbackHandler
は java.security.KeyStore
を使用して、メッセージの署名を含むさまざまな暗号化コールバックを処理します。署名を追加するために、ハンドラーは keyStore
プロパティを使用します。さらに、署名に使用される秘密鍵のロックを解除するには、privateKeyPassword
プロパティを設定する必要があります。次の例では、KeyStoreCallbackHandler
を使用しています。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
7.1.4. 復号化と暗号化
暗号化すると、メッセージは適切なキーでのみ読み取れる形式に変換されます。メッセージを解読して、元の読み取り可能なメッセージを明らかにすることができます。
復号化
受信 SOAP メッセージを復号化するには、セキュリティポリシーファイルに RequireEncryption
要素が含まれている必要があります。この要素はさらに、メッセージのどの部分を暗号化する必要があるかを示す EncryptionTarget
要素と、メッセージの復号化に通常の秘密鍵の代わりに共有秘密を使用する必要があることを示す SymmetricKey
を運ぶことができます。ここで他の要素の説明を読むことができます。次の例では、RequireEncryption
要素を使用しています。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireEncryption />
</xwss:SecurityConfiguration>
受信メッセージが暗号化されていない場合、XwsSecurityInterceptor
は SOAP ault を送信者に返します。存在する場合、登録されたハンドラーに対して DecryptionKeyCallback
を起動します。Spring-WS 内では、KeyStoreCallbackHandler
クラスがこの特定のコールバックを処理します。
KeyStoreCallbackHandler
を使用する
KeyStoreCallbackHandler に従って、KeyStoreCallbackHandler
は java.security.KeyStore
を使用して、復号化を含むさまざまな暗号化コールバックを処理します。復号化のために、ハンドラーは keyStore
プロパティを使用します。さらに、privateKeyPassword
プロパティを設定して、復号化に使用される秘密鍵のロックを解除する必要があります。対称キーに基づく復号化には、symmetricStore
を使用します。次の例では、KeyStoreCallbackHandler
を使用しています。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
暗号化
送信 SOAP メッセージを暗号化するには、セキュリティポリシーファイルに Encrypt
要素が含まれている必要があります。この要素はさらに、メッセージのどの部分を暗号化する必要があるかを示す EncryptionTarget
要素と、メッセージの暗号化に通常の公開鍵の代わりに共有秘密を使用する必要があることを示す SymmetricKey
を運ぶことができます。ここで他の要素の説明を読むことができます。次の例では、Encrypt
要素を使用しています。
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:Encrypt />
</xwss:SecurityConfiguration>
XwsSecurityInterceptor
は、暗号化情報を取得するために、登録されたハンドラーに対して EncryptionKeyCallback
を起動します。Spring-WS 内では、KeyStoreCallbackHandler
クラスがこの特定のコールバックを処理します。
KeyStoreCallbackHandler
を使用する
KeyStoreCallbackHandler に従って、KeyStoreCallbackHandler
は java.security.KeyStore
を使用して、暗号化を含むさまざまな暗号化コールバックを処理します。公開鍵に基づく暗号化の場合、ハンドラーは trustStore
プロパティを使用します。対称鍵に基づく暗号化には symmetricStore
を使用します。次の例では、KeyStoreCallbackHandler
を使用しています。
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
7.1.5. セキュリティ例外処理
保護または検証アクションが失敗すると、XwsSecurityInterceptor
はそれぞれ WsSecuritySecurementException
または WsSecurityValidationException
をスローします。これらの例外は、標準の例外処理メカニズムをバイパスしますが、インターセプター自体によって処理されます。
WsSecuritySecurementException
例外は、XwsSecurityInterceptor
の handleSecurementException
メソッドによって処理されます。デフォルトでは、このメソッドはエラーをログに記録し、メッセージの処理を停止します。
同様に、WsSecurityValidationException
例外は XwsSecurityInterceptor
の handleValidationException
メソッドによって処理されます。デフォルトでは、このメソッドは SOAP 1.1 クライアントまたは SOAP 1.2 送信側障害を作成し、それをレスポンスとして送り返します。
handleSecurementException と handleValidationException はどちらも protected メソッドであり、オーバーライドしてデフォルトの動作を変更できます。 |
7.2. Wss4jSecurityInterceptor
を使用する
Wss4jSecurityInterceptor
は、Apache の WSS4J (英語) に基づく EndpointInterceptor
(リクエストのインターセプト — EndpointInterceptor
インターフェースを参照)です。
WSS4J は、次の標準を実装しています。
OASIS Web サービスセキュリティ: SOAP メッセージセキュリティ 1.0 標準 200401、2004 年 3 月
ユーザー名トークンプロファイル V1.0
X.509 トークンプロファイル V1.0
このインターセプターは、AxiomSoapMessageFactory
および SaajSoapMessageFactory
によって作成されたメッセージをサポートします。
7.2.1. Wss4jSecurityInterceptor
の構成
WSS4J は外部構成ファイルを使用しません。インターセプターは完全にプロパティによって構成されます。このインターセプターによって呼び出される検証および保護アクションは、それぞれ validationActions
および securementActions
プロパティを介して指定されます。アクションは、スペース区切りの文字列として渡されます。次のリストは、構成例を示しています。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="UsernameToken Encrypt"/>
...
<property name="securementActions" value="Encrypt"/>
...
</bean>
次の表は、使用可能な検証アクションを示しています。
検証アクション | 説明 |
---|---|
| ユーザー名トークンを検証します |
| タイムスタンプを検証します |
| メッセージを復号化します |
| 署名を検証します |
| アクションは実行されません |
次の表は、使用可能なセキュリティ保護アクションを示しています。
固定アクション | 説明 |
---|---|
| ユーザー名トークンを追加します |
| ユーザー名トークンと署名ユーザー名トークン秘密鍵を追加します |
| タイムスタンプを追加します |
| レスポンスを暗号化します |
| レスポンスに署名します |
| アクションは実行されません |
アクションの順序は重要であり、インターセプターによって強制されます。セキュリティアクションが `validationActions` で指定された順序とは異なる順序で実行された場合、インターセプターは受信 SOAP メッセージを拒否します。
7.2.2. デジタル証明書の処理
キーストアとの相互作用または証明書処理(署名、暗号化、復号化操作)を必要とする暗号化操作の場合、WSS4J には `org.apache.ws.security.components.crypto.Crypto` のインスタンスが必要です。
Crypto
インスタンスは、WSS4J の CryptoFactory
から、または Spring-WS `CryptoFactoryBean` を使用してより便利に取得できます。
CryptoFactoryBean
Spring-WS は、厳密に型指定されたプロパティ (推奨) または Properties
オブジェクトを介して Crypto
インスタンスを構築および構成する便利なファクトリ Bean、CryptoFactoryBean
を提供します。
デフォルトでは、CryptoFactoryBean
は org.apache.ws.security.components.crypto.Merlin
のインスタンスを返します。これを変更するには、cryptoProvider
プロパティ (またはそれに相当する org.apache.ws.security.crypto.provider
文字列プロパティ) を設定します。
次の構成例では、CryptoFactoryBean
を使用しています。
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="mypassword"/>
<property name="keyStoreLocation" value="file:/path_to_keystore/keystore.jks"/>
</bean>
7.2.3. 認証
このセクションでは、Wss4jSecurityInterceptor
を使用して認証を行う方法について説明します。
ユーザー名トークンの検証
Spring-WS は、Spring Security と統合するための一連のコールバックハンドラーを提供します。さらに、メモリ内の Properties
オブジェクトを使用してユーザーとパスワードを構成するために、単純なコールバックハンドラー SimplePasswordValidationCallbackHandler
が提供されます。
コールバックハンドラーは、Wss4jSecurityInterceptor
プロパティの validationCallbackHandler
を介して構成されます。
SimplePasswordValidationCallbackHandler
を使用する
SimplePasswordValidationCallbackHandler
は、プレーンテキストとダイジェストユーザー名トークンをメモリ内の Properties
オブジェクトに対して検証します。次のように構成できます。
<bean id="callbackHandler"
class="org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="Bert">Ernie</prop>
</props>
</property>
</bean>
SpringSecurityPasswordValidationCallbackHandler
を使用する
SpringSecurityPasswordValidationCallbackHandler
は、Spring Security UserDetailService
を使用して操作することにより、プレーンテキストとダイジェストパスワードを検証します。このサービスを使用して、トークンで指定されたユーザーのパスワード(またはパスワードのダイジェスト)を取得します。次に、この詳細オブジェクトに含まれているパスワード(またはパスワードのダイジェスト)が、メッセージのダイジェストと比較されます。それらが等しい場合、ユーザーは正常に認証され、UsernamePasswordAuthenticationToken
は `SecurityContextHolder` に保管されます。userDetailsService
を使用してサービスを設定できます。さらに、次のように、userCache
プロパティを設定して、ロードされたユーザーの詳細をキャッシュできます。
<beans>
<bean class="org.springframework.ws.soap.security.wss4j.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
ユーザー名トークンの追加
送信メッセージへのユーザー名トークンの追加は、Wss4jSecurityInterceptor
の securementActions
プロパティに UsernameToken
を追加し、securementUsername
と `securementPassword` を指定するのと同じくらい簡単です。
パスワードの種類は、securementPasswordType
プロパティを設定することで設定できます。可能な値は、プレーンテキストパスワードの PasswordText
またはダイジェストパスワードの PasswordDigest
です。これがデフォルトです。
次の例では、ダイジェストパスワードを使用してユーザー名トークンを生成します。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="UsernameToken"/>
<property name="securementUsername" value="Ernie"/>
<property name="securementPassword" value="Bert"/>
</bean>
プレーンテキストのパスワード型を選択した場合、securementUsernameTokenElements
プロパティを設定することにより、インターセプターに Nonce
および Created
要素を追加するよう指示することができます。値は、必要な要素の名前をスペースで区切ったリストである必要があります (大文字と小文字が区別されます)。
次の例では、プレーンテキストのパスワード、Nonce
、Created
要素を持つユーザー名トークンを生成します。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="UsernameToken"/>
<property name="securementUsername" value="Ernie"/>
<property name="securementPassword" value="Bert"/>
<property name="securementPasswordType" value="PasswordText"/>
<property name="securementUsernameTokenElements" value="Nonce Created"/>
</bean>
証明書認証
証明書認証はデジタル署名に似ているため、WSS4J は署名の検証と保護の一部としてそれを処理します。具体的には、X509 証明書を含む BinarySecurityToken
要素を生成し、それを送信メッセージに含めるように WSS4J に指示するには、securementSignatureKeyIdentifier
プロパティを DirectReference
に設定する必要があります。次の例に示すように、証明書の名前とパスワードは、それぞれ securementUsername
および securementPassword
プロパティを介して渡されます。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Signature"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference"/>
<property name="securementUsername" value="mycert"/>
<property name="securementPassword" value="certpass"/>
<property name="securementSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
証明書の検証には、通常の署名の検証が適用されます。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
検証の最後に、インターセプターは、デフォルトの WSS4J 実装に委譲することにより、証明書の有効性を自動的に検証します。必要に応じて、verifyCertificateTrust
メソッドを再定義することでこの動作を変更できます。
詳しくはデジタル署名へ。
7.2.4. セキュリティタイムスタンプ
このセクションでは、Wss4jSecurityInterceptor
で使用できるさまざまなタイムスタンプオプションについて説明します。
タイムスタンプの検証
タイムスタンプを検証するには、Timestamp
を validationActions
プロパティに追加します。timestampStrict
を true
に設定し、timeToLive
プロパティを設定してサーバー側の存続時間 (デフォルト: 300) を指定することにより、SOAP メッセージのイニシエーターによって指定されたタイムスタンプのセマンティクスをオーバーライドできます。インターセプターは、timeToLive
の値に関係なく、すでに期限切れのタイムスタンプを常に拒否します。
次の例では、インターセプターがタイムスタンプの有効期間を 10 秒に制限し、その期間外の有効なタイムスタンプトークンを拒否します。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Timestamp"/>
<property name="timestampStrict" value="true"/>
<property name="timeToLive" value="10"/>
</bean>
タイムスタンプの追加
Timestamp
を securementActions
プロパティに追加すると、送信メッセージにタイムスタンプヘッダーが生成されます。timestampPrecisionInMilliseconds
プロパティは、生成されるタイムスタンプの精度がミリ秒単位かどうかを指定します。デフォルト値は true
です。次のリストはタイムスタンプを追加します。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Timestamp"/>
<property name="timestampPrecisionInMilliseconds" value="true"/>
</bean>
7.2.5. デジタル署名
このセクションでは、Wss4jSecurityInterceptor
で使用できるさまざまな署名オプションについて説明します。
署名の検証
Wss4jSecurityInterceptor
に指示するには、validationActions
に Signature
アクションが含まれている必要があります。さらに、validationSignatureCrypto
プロパティは、イニシエーターの公開証明書を含むキーストアを指す必要があります。
<bean id="wsSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
メッセージへの署名
発信メッセージへの署名は、Signature
アクションを securementActions
に追加することで有効になります。使用する秘密鍵のエイリアスとパスワードは、それぞれ securementUsername
プロパティと securementPassword
プロパティで指定します。securementSignatureCrypto
は、秘密鍵を含むキーストアを指す必要があります。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Signature"/>
<property name="securementUsername" value="mykey"/>
<property name="securementPassword" value="123456"/>
<property name="securementSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
さらに、securementSignatureAlgorithm
プロパティを設定することで署名アルゴリズムを定義できます。
securementSignatureKeyIdentifier
プロパティを設定することで、使用するキー識別子の型をカスタマイズできます。署名は IssuerSerial
と DirectReference
のみが有効です。
securementSignatureParts
プロパティは、メッセージのどの部分が署名されるかを制御します。このプロパティの値は、署名する要素を識別するセミコロンで区切られた要素名のリストです。署名部分の一般的な形式は {}{namespace}Element
です。最初の空の 括弧 は暗号化部分のみに使用されることに注意してください。デフォルトの動作では、SOAP 本文に署名します。
次の例は、Spring Web Services エコーサンプルの echoResponse
要素に署名する方法を示しています。
<property name="securementSignatureParts"
value="{}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
名前空間のない要素を指定するには、文字列 Null
(大文字と小文字を区別) を名前空間名として使用します。
リクエスト内の他の要素に Body
のローカル名がない場合、SOAP 名前空間識別子は空 ({}
) にすることができます。
署名の確認
enableSignatureConfirmation
を true
に設定することで署名確認が可能になります。署名確認アクションは、リクエストとレスポンスにまたがることに注意してください。これは、対応するセキュリティアクションがない場合でも、secureResponse
および validateRequest
を true
(デフォルト値) に設定する必要があることを意味します。次の例では、enableSignatureConfirmation
プロパティを true
に設定します。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="enableSignatureConfirmation" value="true"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="file:/keystore.jks"/>
</bean>
</property>
</bean>
7.2.6. 復号化と暗号化
このセクションでは、Wss4jSecurityInterceptor
で使用できるさまざまな復号化および暗号化オプションについて説明します。
復号化
受信 SOAP メッセージの暗号化を解除するには、Encrypt
アクションを validationActions
プロパティに追加する必要があります。残りの構成は、メッセージに表示されるキー情報によって異なります。(これは、WSS4J が暗号化されたキーに必要なのは暗号化のみであるのに対し、埋め込まれたキー名の検証はコールバックハンドラーに委譲されるためです。)
埋め込まれた暗号化対称キー (xenc:EncryptedKey
要素) を使用してメッセージを復号化するには、validationDecryptionCrypto
は、復号化秘密鍵を含むキーストアを指す必要があります。さらに、validationCallbackHandler
には、キーのパスワードを指定する org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler
を挿入する必要があります。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Encrypt"/>
<property name="validationDecryptionCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="privateKeyPassword" value="mykeypass"/>
</bean>
</property>
</bean>
キー名 ( ds:KeyName
要素) が埋め込まれたメッセージの復号化をサポートするために、対称秘密鍵を使用してキーストアを指す KeyStoreCallbackHandler
を構成できます。symmetricKeyPassword
プロパティはキーのパスワードを示し、キー名は ds:KeyName
要素で指定されたものです。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Encrypt"/>
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="keyStore">
<bean class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="type" value="JCEKS"/>
<property name="password" value="123456"/>
</bean>
</property>
<property name="symmetricKeyPassword" value="mykeypass"/>
</bean>
</property>
</bean>
暗号化
Encrypt
を securementActions
に追加すると、送信メッセージの暗号化が可能になります。securementEncryptionUser
プロパティを設定することで、暗号化に使用する証明書のエイリアスを設定できます。証明書が存在するキーストアには、securementEncryptionCrypto
プロパティを介してアクセスします。暗号化は公開証明書に依存しているため、パスワードを渡す必要はありません。次の例では、securementEncryptionCrypto
プロパティを使用しています。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Encrypt"/>
<property name="securementEncryptionUser" value="mycert"/>
<property name="securementEncryptionCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="file:/keystore.jks"/>
</bean>
</property>
</bean>
暗号化はいくつかの方法でカスタマイズできます。使用するキー識別子の型は、securementEncryptionKeyIdentifier
プロパティによって定義されます。可能な値は IssuerSerial
、X509KeyIdentifier
、DirectReference
、Thumbprint
、SKIKeyIdentifier
、EmbeddedKeyName
です。
EmbeddedKeyName
型を選択した場合は、暗号化に使用する秘密鍵を指定する必要があります。キーのエイリアスは、他のキー識別子型と同様に securementEncryptionUser
プロパティに設定されます。ただし、WSS4J では、秘密鍵を取得するためにコールバックハンドラーが必要です。適切なキーストアを指す KeyStoreCallbackHandler
を securementCallbackHandler
に提供する必要があります。デフォルトでは、結果の WS-Security ヘッダーの ds:KeyName
要素は securementEncryptionUser
プロパティの値を取ります。別の名前を示すために、securementEncryptionEmbeddedKeyName
に希望の値を設定できます。次の例では、発信メッセージは secretKey
というエイリアスのキーで暗号化されていますが、myKey
は ds:KeyName
要素に表示されます。
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Encrypt"/>
<property name="securementEncryptionKeyIdentifier" value="EmbeddedKeyName"/>
<property name="securementEncryptionUser" value="secretKey"/>
<property name="securementEncryptionEmbeddedKeyName" value="myKey"/>
<property name="securementCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="symmetricKeyPassword" value="keypass"/>
<property name="keyStore">
<bean class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="file:/keystore.jks"/>
<property name="type" value="jceks"/>
<property name="password" value="123456"/>
</bean>
</property>
</bean>
</property>
</bean>
securementEncryptionKeyTransportAlgorithm
プロパティは、生成された対称鍵を暗号化するために使用するアルゴリズムを定義します。サポートされている値は、デフォルトの http://www.w3.org/2001/04/xmlenc#rsa-1_5 [W3C] (英語)
と http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p [W3C] (英語)
です。
securementEncryptionSymAlgorithm
プロパティを設定することにより、使用する対称暗号化アルゴリズムを設定できます。サポートされている値は http://www.w3.org/2001/04/xmlenc#aes128-cbc [W3C] (英語)
(デフォルト)、http://www.w3.org/2001/04/xmlenc#tripledes-cbc [W3C] (英語)
、http://www.w3.org/2001/04/xmlenc#aes256-cbc [W3C] (英語)
、http://www.w3.org/2001/04/xmlenc#aes192-cbc [W3C] (英語)
です。
最後に、securementEncryptionParts
プロパティは、メッセージのどの部分が暗号化されるかを定義します。このプロパティの値は、暗号化する要素を識別するセミコロンで区切られた要素名のリストです。暗号化モードの指定子と名前空間の識別は、それぞれが中の 括弧 のペアの内側にあり、各要素名の前にある場合があります。暗号化モード指定子は {Content}
または {Element}
要素とコンテンツの暗号化の違いについては、W3C XML 暗号化仕様を参照してください。次の例は、エコーサンプルから echoResponse
を識別します。
<property name="securementEncryptionParts"
value="{Content}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
要素名、名前空間識別子、暗号化修飾子では大文字と小文字が区別されることに注意してください。暗号化修飾子と名前空間識別子は省略できます。これを行うと、暗号化モードはデフォルトで Content
になり、名前空間は SOAP 名前空間に設定されます。
名前空間のない要素を指定するには、名前空間名として値 Null
(大文字と小文字を区別) を使用します。リストが指定されていない場合、ハンドラーはデフォルトで SOAP 本文を Content
モードで暗号化します。
7.2.7. セキュリティ例外処理
Wss4jSecurityInterceptor
の例外処理は、XwsSecurityInterceptor
の例外処理と同じです。詳細については、セキュリティ例外処理を参照してください。
III. その他のリソース
参考文献
[waldo-94] ジムウォルド、アンヴォルラス、サムケンダル。分散コンピューティングに関する注記。シュプリンガーバーラグ。1994 年
[alpine] Steve Loughran & Edmund Smith. Rethinking the Java SOAP Stack. May 17, 2005. © 2005 IEEE Telephone Laboratories, Inc.
[effective-enterprise-java] テッドヌワード。スコットマイヤーズ。効果的なエンタープライズ Java。アディソン - ウェスリー。2004 年
[effective-xml] エリオットラスティハロルド。スコットマイヤーズ。効果的な XML。アディソン - ウェスリー。2004 年