© 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. 導入

リファレンスドキュメントのこの最初の部分は、Spring Web Services とその基礎となる概念の概要です。その後、Spring-WS が紹介され、契約 ファーストの Web サービス開発の背後にある概念が説明されます。

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.1. 強力なマッピング

メッセージペイロード、SOAP アクションヘッダー、XPath 式に応じて、受信 XML リクエストを任意のオブジェクトに配布できます。

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.1.5. WS-Security のサポート

WS-Security を使用すると、SOAP メッセージへの署名、メッセージの暗号化と復号化、メッセージに対する認証を行うことができます。

1.1.6. Spring Security との統合

Spring Web Services の WS-Security 実装は、Spring Security との統合を提供します。これは、SOAP サービスにも既存の Spring Security 構成を使用できることを意味します。

1.1.7. Apache ライセンス

プロジェクトで Spring-WS を自信を持って使用できます。

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 モジュールに依存します)。

spring deps

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 を使用して契約を実装します。

WSDL とは何ですか?

WSDL は Web サービス記述言語の略です。WSDL ファイルは、Web サービスを説明する XML ドキュメントです。これは、サービスの場所と、サービスが公開する操作(またはメソッド)を指定します。WSDL の詳細については、WSDL 仕様 [W3C] (英語) を参照してください。

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>
1all は、<Holiday/> と <Employee/> の順序が重要ではないことを XML パーサーに通知します。
2<StartDate/> と <EndDate/> には、xs:date データ型(年、月、日で構成されます)を使用します。
3xs: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 データ契約で定義されたスキーマをインポートします。
2portType で使用される HolidayRequest メッセージを定義します。
3HolidayRequest 型はスキーマで定義されています。
4binding で使用される HumanResource ポート型を定義します。
5port で使用される HumanResourceBinding バインディングを定義します。
6 ドキュメント / リテラルスタイルを使用します。
7 リテラル http://schemas.xmlsoap.org/soap/http (英語)  は、HTTP トランスポートを意味します。
8soapAction 属性は、すべてのリクエストで送信される SOAPAction HTTP ヘッダーを示します。
9http://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;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;

import com.mycompany.hr.service.HumanResourceService;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.filter.Filters;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;

@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 + "]");
        }
    }

}
1HolidayEndpoint には @Endpoint のアノテーションが付けられています。これにより、クラスは特別な種類の @Component としてマークされ、Spring-WS での XML メッセージの処理に適しており、コンポーネントのスキャンにも適しています。
2HolidayEndpoint は、HumanResourceService ビジネスサービスが動作する必要があるため、コンストラクターに依存関係を挿入し、@Autowired でアノテーションを付けます。次に、JDOM2API を使用して XPath 式を設定します。4 つの式があります。<StartDate> テキスト値を抽出するための //hr:StartDate、終了日を抽出するための //hr:EndDate、従業員の名前を抽出するための 2 つです。
3@PayloadRoot アノテーションは、handleHolidayRequest メソッドが XML メッセージの処理に適していることを Spring-WS に通知します。このメソッドが処理できるメッセージの種類は、アノテーション値によって示されます。この場合、HolidayRequest ローカル部分と http://mycompany.com/hr/schemas (英語)  名前空間を持つ XML 要素を処理できます。メッセージをエンドポイントにマッピングする方法の詳細については、次のセクションで説明します。
4handleHolidayRequest(..) メソッドはメインの処理メソッドであり、受信 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;

import java.util.Date;

public interface HumanResourceService {
    void bookHoliday(Date startDate, Date endDate, String name);
}

チュートリアルの目的で、HumanResourceService の単純なスタブ実装を使用します。

package com.mycompany.hr.service;

import java.util.Date;

import org.springframework.stereotype.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 + "] ");
    }
}
1StubHumanResourceService には @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>
1id は、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 パラメーターを追加する必要があります (次のリストを参照)。
4WSDL 定義自体のターゲット名前空間を定義します。この属性を設定する必要はありません。設定されていない場合、WSDL は XSD スキーマと同じ名前空間を持ちます。
5xsd 要素は、データ契約で定義した人事スキーマを参照します。アプリケーションの 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. リファレンス

リファレンスドキュメントのこのパートでは、Spring Web Services を構成するさまざまなコンポーネントについて詳しく説明します。これには、クライアント側とサーバー側の両方の WS に共通する部分について説明するサーバー側の Web サービスの作成 の詳細に特化した章、クライアント側での Web サービスの使用に関する章、および WS-Security の使用に関する章が含まれます。

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 表現

javax.xml.transform.dom.DOMSource

org.w3c.dom.Node

javax.xml.transform.dom.DOMResult

org.w3c.dom.Node

javax.xml.transform.sax.SAXSource

org.xml.sax.InputSource and org.xml.sax.XMLReader

javax.xml.transform.sax.SAXResult

org.xml.sax.ContentHandler

javax.xml.transform.stream.StreamSource

java.io.File, java.io.InputStream, or java.io.Reader

javax.xml.transform.stream.StreamResult

java.io.File, java.io.OutputStream, or java.io.Writer

ペイロードからの読み取りとペイロードへの書き込みに加えて、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 インターフェースを実装しますが、呼び出されると UnsupportedOperationException をスローします。Spring Web Services には回避策があります。WebLogic 9 で動作する場合は SAAJ 1.1 を使用します。

さらに、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 のその他の主な違いには、障害の構造の違いと、SOAPAction HTTP ヘッダーは引き続き機能しますが、事実上非推奨であるという事実が含まれます。

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 全体で使用される共通のテンプレートパターン(JdbcTemplateJmsTemplate など)に従います。次のリストは例を示しています。

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 の処理とディスパッチのフローを示しています。

sequence

MessageDispatcher が使用できるようにセットアップされ、その特定のディスパッチャーにリクエストが来ると、MessageDispatcher はリクエストの処理を開始します。次のプロセスでは、MessageDispatcher がリクエストを処理する方法について説明します。

  1. 構成された EndpointMapping(s) は、適切なエンドポイントを検索します。エンドポイントが見つかると、エンドポイント(プリプロセッサー、ポストプロセッサー、エンドポイント)に関連付けられた呼び出しチェーンが呼び出され、レスポンスが作成されます。

  2. エンドポイントに適切なアダプターが見つかりました。MessageDispatcher は、このアダプターに委譲してエンドポイントを呼び出します。

  3. レスポンスが返されると、途中で送信されます。レスポンスが返されない場合(たとえば、セキュリティ上の理由で、プリプロセッサーまたはポストプロセッサーがリクエストをインターセプトしたことが原因である可能性があります)、レスポンスは送信されません。

リクエストの処理中にスローされた例外は、アプリケーションコンテキストで宣言されたエンドポイント例外リゾルバーのいずれかによって取得されます。これらの例外リゾルバーを使用すると、そのような例外がスローされた場合のカスタム動作(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-wsMessageDispatcherServlet によって処理されます。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 属性を設定することで変更できます。また、操作に基づいて portTypebindingservice を構築します。

たとえば、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 &lt;[email protected] (英語)  &gt;"/>
        <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;

import org.w3c.dom.Element;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.soap.SoapHeader;

@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 ビジネスサービスはこのエンドポイントに注入されます。
3order メソッドは、パラメーターとして Element (@RequestPayload でアノテーションが付けられている)を取ります。これは、メッセージのペイロードが DOM 要素としてこのメソッドに渡されることを意味します。このメソッドには void 戻り値の型があり、レスポンスメッセージが送信されないことを示します。エンドポイントメソッドの詳細については、@Endpoint の取り扱い方法を参照してください。
4getOrder メソッドは、パラメーターとして 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

javax.xml.transform.Source and sub-interfaces (DOMSource, SAXSource, StreamSource, and StAXSource)

Yes

Enabled by default.

W3C DOM

org.w3c.dom.Element

はい

デフォルトで有効

dom4j

org.dom4j.Element

はい

dom4j がクラスパス上にある場合に有効になります。

JDOM

org.jdom.Element

はい

JDOM がクラスパス上にある場合に有効になります。

XOM

nu.xom.Element

はい

XOM がクラスパス上にある場合に有効になります。

StAX

javax.xml.stream.XMLStreamReader and javax.xml.stream.XMLEventReader

はい

StAX がクラスパス上にある場合に有効になります。

XPath

Spring 変換サービスによって String から変換でき、@XPathParam でアノテーションが付けられているブール値、double 型、Stringorg.w3c.Nodeorg.w3c.dom.NodeList 型、型。

いいえ

デフォルトで有効になっています。XPathParam を参照してください。

メッセージコンテキスト

org.springframework.ws.context.MessageContext

いいえ

デフォルトで有効になっています。

SOAP

org.springframework.ws.soap.SoapMessage, org.springframework.ws.soap.SoapBody, org.springframework.ws.soap.SoapEnvelope, org.springframework.ws.soap.SoapHeader, and org.springframework.ws.soap.SoapHeaderElement`s when used in combination with the `@SoapHeader annotation.

No

Enabled by default.

JAXB2

Any type that is annotated with javax.xml.bind.annotation.XmlRootElement, and javax.xml.bind.JAXBElement.

Yes

Enabled when JAXB2 is on the classpath.

OXM

Any type supported by a Spring OXM Unmarshaller.

Yes

Enabled when the unmarshaller attribute of <sws:annotation-driven/> is specified.

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;

import javax.xml.transform.Source;

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.Namespace;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.XPathParam;

@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 or Boolean

  • double または Double

  • String

  • Node

  • NodeList

このリストに加えて、Spring 変換サービスによって String から変換できる任意の型を使用できます。

メソッドの戻り型の処理

レスポンスメッセージを送信するには、処理で戻り値の型を指定する必要があります。レスポンスメッセージが不要な場合、メソッドは void 戻り値の型を宣言できます。最も一般的には、return 型は、レスポンスメッセージのペイロードを作成するために使用されます。ただし、レスポンスメッセージの他の部分にマップすることもできます。このセクションでは、メソッドシグネチャーの処理で使用できる戻り値の型について説明します。

戻り値をレスポンスメッセージのペイロードにマップするには、メソッドに @ResponsePayload アノテーションを付ける必要があります。このアノテーションは、戻り値をレスポンスペイロードにバインドする必要があることを Spring-WS に通知します。

次の表に、サポートされている戻り値の型を示します。サポートされている型、パラメーターに @ResponsePayload アノテーションを付ける必要があるかどうか、および追加の注記が表示されます。

名前 サポートされている戻り値の型 @ResponsePayload が必要ですか? その他の注意事項

レスポンスなし

void

いいえ

デフォルトで有効になっています。

TrAX

javax.xml.transform.Source and sub-interfaces (DOMSource, SAXSource, StreamSource, and StAXSource)

Yes

Enabled by default.

W3C DOM

org.w3c.dom.Element

はい

デフォルトで有効

dom4j

org.dom4j.Element

はい

dom4j がクラスパス上にある場合に有効になります。

JDOM

org.jdom.Element

はい

JDOM がクラスパス上にある場合に有効になります。

XOM

nu.xom.Element

はい

XOM がクラスパス上にある場合に有効になります。

JAXB2

javax.xml.bind.annotation.XmlRootElement および javax.xml.bind.JAXBElement でアノテーションが付けられているすべての型。

はい

JAXB2 がクラスパス上にある場合に有効になります。

OXM

Spring OXM Marshaller でサポートされているすべての型。

はい

<sws:annotation-driven/> の marshaller 属性が指定されている場合に有効になります。

処理メソッドのシグネチャーの定義に関しては、多くの可能性があります。このメカニズムを継承して、独自のパラメーター型をサポートすることも可能です。方法については、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;

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.soap.addressing.server.annotation.Action

@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;

import org.springframework.ws.soap.server.endpoint.annotation.FaultCode;
import org.springframework.ws.soap.server.endpoint.annotation.SoapFault;

@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 の一般的な使用箇所は次のとおりです。

  1. MockWebServiceClient.createClient(ApplicationContext) または MockWebServiceClient.createClient(WebServiceMessageReceiver, WebServiceMessageFactory) を呼び出して、MockWebServiceClient インスタンスを作成します。

  2. sendRequest(RequestCreator) を呼び出して、おそらく RequestCreators で提供されているデフォルトの RequestCreator 実装(静的にインポート可能)を使用して、リクエストメッセージを送信します。

  3. おそらく ResponseMatchers で提供されるデフォルトの ResponseMatcher 実装(静的にインポート可能)を使用して、andExpect(ResponseMatcher) を呼び出すことにより、レスポンスの期待値を設定します。andExpect(ResponseMatcher) 呼び出しを連鎖させることにより、複数の期待値を設定できます。

MockWebServiceClient (および関連するクラス)は「流れるような」API を提供するため、通常は IDE のコード補完機能を使用して、モックサーバーのセットアッププロセスをガイドできます。
また、単体テストでは Spring Web Services で利用可能な標準のロギング機能を利用できることにも注意してください。特定のテストが失敗した理由を見つけるために、リクエストメッセージまたはレスポンスメッセージを調べることが役立つ場合があります。詳細については、メッセージのログとトレースを参照してください。

たとえば、次の Web サービスエンドポイントクラスについて考えてみます。

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

@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エンドポイントを参照してください。
2getCustomerCount() メソッドは、引数として CustomerCountRequest を取り、CustomerCountResponse を返します。これらのクラスは両方とも、マーシャラーによってサポートされるオブジェクトです。たとえば、JAXB2 でサポートされる @XmlRootElement アノテーションを付けることができます。

次の例は、CustomerEndpoint の一般的なテストを示しています。

import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.xml.transform.StringSource;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.ws.test.server.MockWebServiceClient;                       (1)
import static org.springframework.ws.test.server.RequestCreators.*;                   (1)
import static org.springframework.ws.test.server.ResponseMatchers.*;                  (1)

@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)
  }
}
1CustomerEndpointIntegrationTest は 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 の使用を参照)。

また、静的にインポートされた ResponseMatchers によって提供される payload()ResponseMatcher を使用して andExpect() を呼び出すことにより、レスポンスの期待値を設定します(ResponseMatcher と ResponseMatchers の使用を参照)。

テストのこのパートは少し紛らわしいように見えるかもしれませんが、IDE のコード補完機能は非常に役立ちます。sendRequest( と入力した後、静的にインポートされた RequestCreators の場合、IDE は可能なリクエスト作成戦略のリストを提供できます。ResponseMatchers を静的にインポートした場合、同じことが andExpect() にも当てはまります。

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 メソッド 説明

payload()

指定されたレスポンスペイロードを期待します。

validPayload()

レスポンスペイロードが特定の XSD スキーマに対して検証されることを期待します。

xpath()

指定された XPath 式が存在するか、存在しないか、指定された値に評価されることを期待します。

soapHeader()

指定された SOAP ヘッダーがレスポンスメッセージに存在することを想定しています。

noFault()

レスポンスメッセージに SOAP 障害が含まれていないことを想定しています。

mustUnderstandFault(), clientOrSenderFault(), serverOrReceiverFault(), and versionMismatchFault()

レスポンスメッセージに特定の 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:SomeQueuejms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENTjms: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 &lt;[email protected] (英語)  &gt;"/>
                <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>
メッセージファクトリ

メッセージ送信者に加えて、WebServiceTemplate には Web サービスメッセージファクトリが必要です。SOAP には、SaajSoapMessageFactory と AxiomSoapMessageFactory の 2 つのメッセージファクトリがあります。(messageFactory プロパティを設定して) メッセージファクトリが指定されていない場合、Spring-WS はデフォルトで SaajSoapMessageFactory を使用します。

6.1.2. WebServiceMessage の送受信

WebServiceTemplate には、Web サービスメッセージを送受信するための多くの便利なメソッドが含まれています。Source を受け入れて返すメソッドと、Result を返すメソッドがあります。さらに、オブジェクトを XML にマーシャリングおよびアンマーシャルするメソッドがあります。次の例では、単純な XML メッセージを Web サービスに送信します。

import java.io.StringReader;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.WebServiceMessageSender;

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 の一般的な使用箇所は次のとおりです。

  1. MockWebServiceServer.createServer(WebServiceTemplate)MockWebServiceServer.createServer(WebServiceGatewaySupport)MockWebServiceServer.createServer(ApplicationContext) を呼び出して、MockWebServiceServer インスタンスを作成します。

  2. おそらく RequestMatchers で提供されるデフォルトの RequestMatcher 実装(静的にインポート可能)を使用して、expect(RequestMatcher) を呼び出すことにより、リクエストの期待値を設定します。andExpect(RequestMatcher) 呼び出しを連鎖させることにより、複数の期待値を設定できます。

  3. andRespond(ResponseCreator) を呼び出して、おそらく ResponseCreators で提供されているデフォルトの ResponseCreator 実装(静的にインポート可能)を使用して、適切なレスポンスメッセージを作成します。

  4. WebServiceTemplate は、クライアントコードを介して直接、または通常どおりに使用します。

  5. MockWebServiceServer.verify() を呼び出して、すべての期待値が満たされていることを確認します。

MockWebServiceServer (および関連クラス) は「流れるような」API を提供するため、通常、IDE のコード補完機能を使用して、モックサーバーのセットアッププロセスをガイドできます。
また、単体テストでは Spring Web Services で利用可能な標準のロギング機能を利用できることにも注意してください。特定のテストが失敗した理由を見つけるために、リクエストメッセージまたはレスポンスメッセージを調べることが役立つ場合があります。詳細については、メッセージのログとトレースを参照してください。

たとえば、次の Web サービスクライアントクラスについて考えてみましょう。

import org.springframework.ws.client.core.support.WebServiceGatewaySupport;

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();
  }

}
1CustomerClient は WebServiceGatewaySupport を継承し、webServiceTemplate プロパティを提供します。
2CustomerCountRequest は、マーシャラーによってサポートされるオブジェクトです。たとえば、JAXB2 でサポートされる @XmlRootElement アノテーションを持つことができます。
3CustomerClient は、WebServiceGatewaySupport によって提供される WebServiceTemplate を使用して、リクエストオブジェクトを SOAP メッセージにマーシャリングし、それを Web サービスに送信します。レスポンスオブジェクトは CustomerCountResponse に非整列化されます。

次の例は、CustomerClient の一般的なテストを示しています。

import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.xml.transform.StringSource;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;

import org.springframework.ws.test.client.MockWebServiceServer;                         (1)
import static org.springframework.ws.test.client.RequestMatchers.*;                     (1)
import static org.springframework.ws.test.client.ResponseCreators.*;                    (1)

@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)
  }

}
1CustomerClientIntegrationTest は MockWebServiceServer をインポートし、RequestMatchers と ResponseCreators を静的にインポートします。
2 このテストでは、Spring Framework で提供される標準のテスト機能を使用します。これは必須ではありませんが、一般的にテストを設定する最も簡単な方法です。
3CustomerClient は integration-test.xml で構成され、@Autowired を使用してこのテストに接続されます。
4@Before メソッドでは、createServer ファクトリメソッドを使用して MockWebServiceServer を作成します。
5 静的にインポートされた RequestMatchers によって提供される payload()RequestMatcher を使用して expect() を呼び出すことにより、期待値を定義します(RequestMatcher と RequestMatchers の使用を参照)。

また、静的にインポートされた ResponseCreators によって提供される withPayload()ResponseCreator を使用して andRespond() を呼び出すことにより、レスポンスを設定します(ResponseCreator と ResponseCreators の使用を参照)。

テストのこのパートは少し混乱しているように見えるかもしれませんが、IDE のコード補完機能は非常に役立ちます。expect( と入力すると、静的にインポートされた RequestMatchers であれば、IDE は可能なリクエストマッチング戦略のリストを提供できます。ResponseCreators を静的にインポートした場合、同じことが andRespond( にも当てはまります。

6CustomerClient で getCustomerCount() を呼び出し、WebServiceTemplate を使用します。テンプレートは「テストモード」に設定されているため、このメソッド呼び出しでは実際の (HTTP) 接続は確立されません。メソッド呼び出しの結果に基づいて、いくつかの JUnit アサーションも作成します。
7MockWebServiceServer で 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 メソッド 説明

anything()

あらゆる種類のリクエストを期待します。

payload()

指定されたリクエストペイロードを期待します。

validPayload()

リクエストペイロードが指定された XSD スキーマに対して検証されることを期待します。

xpath()

指定された XPath 式が存在するか、存在しないか、指定された値に評価されることを期待します。

soapHeader()

指定された SOAP ヘッダーがリクエストメッセージに存在することを想定しています。

connectionTo()

指定された 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 メソッド 説明

withPayload()

指定されたペイロードでレスポンスメッセージを作成します。

withError()

レスポンス接続でエラーを作成します。この方法では、エラー処理をテストする機会が与えられます。

withException()

レスポンス接続から読み取るときに例外をスローします。このメソッドは、例外処理をテストする機会を提供します。

withMustUnderstandFault(), withClientOrSenderFault(), withServerOrReceiverFault(), or withVersionMismatchFault()

指定された 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: (keyStoretrustStoresymmetricStore) の 3 つのプロパティがあります。ハンドラーによって使用される正確なストアは、このハンドラーによって実行される暗号化操作によって異なります。秘密鍵の操作には keyStore を使用します。対称鍵の操作には、symmetricStore が使用されます。信頼関連を決定するために、trustStore が使用されます。次の表はこれを示しています。

暗号化操作 使用したキーストア

証明書の検証

最初に keyStore、次に trustStore

秘密鍵に基づく復号化

keyStore

対称鍵に基づく復号化

symmetricStore

公開鍵証明書に基づく暗号化

trustStore

対称鍵に基づく暗号化

symmetricStore

署名

keyStore

署名の検証

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 の用語では、これは SpringCertificateValidationCallbackHandler または JaasCertificateValidationCallbackHandler の前に KeyStoreCallbackHandler を付ける必要があることを意味します。これは、XwsSecurityInterceptor の構成で callbackHandlers プロパティの順序を設定することで実現できます。

<bean id="wsSecurityInterceptor"
    class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
    <property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
    <property name="callbackHandlers">
        <list>
            <ref bean="keyStoreHandler"/>
            <ref bean="springSecurityHandler"/>
        </list>
    </property>
</bean>

この設定を使用して、インターセプターは最初にメッセージ内の証明書がキーストアを使用して有効であるかどうかを判断し、次にそれに対して認証します。

KeyStoreCallbackHandler を使用する

KeyStoreCallbackHandler は、標準の Java キーストアを使用して証明書を検証します。この証明書検証プロセスは、次の手順で構成されています。

  1. ハンドラーは、証明書がプライベート keyStore にあるかどうかを確認します。そうである場合、有効です。

  2. 証明書が秘密鍵ストアにない場合、ハンドラーは現在の日時が証明書で指定された有効期間内であるかどうかを確認します。そうでない場合、証明書は無効です。そうである場合は、最後のステップに進みます。

  3. 証明書の証明書パスが作成されます。これは基本的に、ハンドラーが証明書が 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>

次の表は、使用可能な検証アクションを示しています。

検証アクション 説明

UsernameToken

ユーザー名トークンを検証します

Timestamp

タイムスタンプを検証します

Encrypt

メッセージを復号化します

Signature

署名を検証します

NoSecurity

アクションは実行されません

次の表は、使用可能なセキュリティ保護アクションを示しています。

固定アクション 説明

UsernameToken

ユーザー名トークンを追加します

UsernameTokenSignature

ユーザー名トークンと署名ユーザー名トークン秘密鍵を追加します

Timestamp

タイムスタンプを追加します

Encrypt

レスポンスを暗号化します

Signature

レスポンスに署名します

NoSecurity

アクションは実行されません

アクションの順序は重要であり、インターセプターによって強制されます。セキュリティアクションが `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 要素を追加するよう指示することができます。値は、必要な要素の名前をスペースで区切ったリストである必要があります (大文字と小文字が区別されます)。

次の例では、プレーンテキストのパスワード、NonceCreated 要素を持つユーザー名トークンを生成します。

<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 プロパティによって定義されます。可能な値は IssuerSerialX509KeyIdentifierDirectReferenceThumbprintSKIKeyIdentifierEmbeddedKeyName です。

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. その他のリソース

このリファレンスドキュメントに加えて、他の多くのリソースが Spring Web Services の使用方法を学習するのに役立つ場合があります。これらの追加のサードパーティリソースは、このセクションに列挙されています。

参考文献

  • [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 年