5.3. SOAP Web Service(サーバ/クライアント)¶
目次
5.3.1. Overview¶
本節では、SOAP Web Serviceの基本的な概念とJAX-WSを使用したSOAPサーバ、クライアント双方の開発について説明する。
実装に対する具体的な説明については、
- JAX-WSを使用したSOAP Web Serviceのアプリケーション構成やAPIの実装方法について説明している。
を参照されたい。
5.3.1.1. SOAPとは¶
| 項番 | 説明 | 
|---|---|
| (1) | クライアントは、別のSOAPサーバへの通信を行うWebアプリケーションを想定している。 クライアントと呼んでいるがWebアプリケーション想定なので注意が必要である。 | 
| (2) | SOAPサーバは、Webサービスを公開し、クライアントからのSOAP Web ServiceによるXMLを受信して処理を行う。データベースなどにアクセスを行い、業務処理を行うことを想定している。 | 
| (3) | SOAP Web ServiceではXMLを使用して情報のやり取りを行う。 今回の想定では、SOAPサーバ、クライアントどちらもJavaである想定としているが、他のプラットフォームでも問題なく通信可能である。 | 
5.3.1.2. JAX-WSとは¶
5.3.1.3. JAX-WSを利用したWebサービスの開発について¶
Note
APサーバのJAX-WS実装によって、JAX-WS仕様への対応状況や実際のWebサービスの動作やが異なる場合があり、必ずしも本ガイドラインの実装が全てのAPサーバで同様に動作するわけではない。
開発を始める前には必ず、「アプリケーションの設定」のNoteから使用するAPサーバのマニュアルを確認されたい。
5.3.1.4. Spring FrameworkのJAX-WS連携機能について¶
| 項番 | 説明 | 
|---|---|
| (1) | [クライアント] ControllerがServiceを呼び出す。 通常の呼び出しと変更点は特にない。 | 
| (2) | [クライアント] ServiceがSOAPサーバ提供側で用意したWebServiceインターフェースを呼び出す。 この図では、ServiceがWebServiceインターフェースを呼び出しているが、要件に応じてControllerから直接WebServiceインターフェースを呼び出してもよい。 | 
| (3) | [クライアント] WebServiceインターフェースが呼び出されると実体として「動的プロキシ(Dynamic Proxy)」(以下「プロキシ」)が呼び出される。 このプロキシは org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBeanが生成したWebServiceインターフェースの実装クラスである。この実装クラスがServiceにインジェクションされ、ServiceはWebServiceインターフェースのメソッドを呼び出すだけで、SOAP Web Serviceを利用した処理を行うことができる。 | 
| (4) | プロキシが、SOAPサーバのWebServiceインターフェースを呼び出す。 SOAPサーバとクライアントでの値のやり取りはDomain Objectを使用して行う。 Note 厳密には、SOAPサーバとクライアントはXMLを使用して通信を行っている。 送信時、および受信時にはJAXBを使用して、Domain ObjectとXMLの相互変換が行われているが、SOAP Web Service作成者はXMLをあまり意識せず、開発を行うことができるようになっている。 | 
| (5) | [サーバ] WebServiceインターフェースが呼び出されると実体としてWebService実装クラスが呼び出される。 SOAPサーバでは、WebServiceインターフェースの実装クラスとしてWebService実装クラスを用意する。 このWebService実装クラスは、 org.springframework.web.context.support.SpringBeanAutowiringSupportを継承することで、SpringのDIコンテナ上のBeanを@Autowiredなどでインジェクションすることができる。Note WebService実装クラスは、Spring Frameworkが提供するDispatcherServlet上ではなく、APサーバのJAX-WSエンジンが実装するサーブレットとして動作する。このためガイドラインのアプリケーション層の実装に記載している実装方法とは以下のような違いがあることに注意されたい。 
 また、SOAPサーバは、 
 上記に対して、 | 
| (6) | [サーバ] WebService実装クラスでは、業務処理を行うServiceを呼び出す。 | 
| (7) | [サーバ] Serviceでは、Repositoryなどを使用して業務処理を実行する。 通常の呼び出しと変更点は特にない。 | 
Note
Springでは、ドキュメントドリブンでWebサービスを開発するSpring Web Servicesが提供されているが、ここでは扱わない。 詳細はSpring Web Servicesを参照されたい。
Note
SpringでのJAX-WS実装の詳細は、Spring Framework Reference Documentation -Remoting and web services using Spring(Web services)-を参照されたい。
5.3.1.4.1. JAX-WSを利用したWebサービスのモジュールの構成¶
JAX-WSを利用したWebサービスを作成する場合、既存のブランクプロジェクトとは別に以下2つのプロジェクトを追加することを推奨する。
- modelプロジェクト
- webserviceプロジェクト
本ガイドラインでは、マルチプロジェクトで以下のような構成を用いる。
ここでもクライアントはWebアプリケーションであることを前提とするが、デスクトップアプリケーションやコマンドラインインターフェースから呼び出す場合も基本的な考え方は同じである。
| 項番 | 説明 | 
|---|---|
| (1) | クライアントを作成する場合、従来のマルチプロジェクトにSOAPサーバから提供されるmodelプロジェクトとwebserviceプロジェクトを追加する。 ここではサーバとクライアントをともに開発することを前提としている。 これらのプロジェクトの詳細については「SOAPサーバの作成」で説明する。 追加方法については「SOAPサーバ用にプロジェクトの設定を変更する」を参照されたい。 サーバとクライアントの開発が別々で、modelプロジェクトとwebserviceプロジェクトが提供されない場合、もしくはJava以外でSOAPサーバが作成されている場合には、modelプロジェクト内のDomain Objectとwebserviceプロジェクト内のWebサービスインターフェースを自分で作成する必要がある。 wsimportを使用することで、WSDLから簡単にDomain ObjectとWebサービスインターフェースを作成することができる。 詳細については「wsimportについて」を参照されたい。 | 
| (2) | SOAPサーバを作成する場合、従来のマルチプロジェクトに追加してmodelプロジェクトとwebserviceプロジェクトを追加する。 クライアントにこれら2つのプロジェクトを公開する。 クライアントへのmodelプロジェクト、webserviceプロジェクトの公開方法は、Mavenの依存関係への追加を想定している。 | 
以下は、クライアントのプロジェクト構成である。
5.3.1.5. Webサービスとして公開されるURL¶
- http://AAA.BBB.CCC.DDD:XXXX/コンテキストルート/Webサービス名?wsdl
WSDL内で定義されるエンドポイントアドレスは以下のURLである。
- http://AAA.BBB.CCC.DDD:XXXX/コンテキストルート/Webサービス名
Note
本ガイドラインでは、マルチプロジェクト構成のwebプロジェクトをWARファイル化して、APサーバにデプロイする前提である。その場合、コンテキストルートは基本的に、[server projectName]-webとなる。ただし、APサーバによって異なるので注意すること。
Note
本ガイドラインでは、SOAPサーバ、クライアントともにWebアプリケーションとして公開する前提であるため、クライアントではWSDLのURLを指定している。URLではなく、WSDLをファイルとして用意してクライアントを作成することも可能である。 詳細は、Webサービス クライアントの実装を参照されたい。
Warning
本ガイドラインでは、APサーバ(Tomcatの場合は使用するライブラリ)でコンテキストルートのマッピングを切り替え以下のようなURLでアクセスするように設定している。
- http://AAA.BBB.CCC.DDD:XXXX/[server projectName]-web/ws/TodoWebService?wsdl
このコンテキストルート直下ではないURLにWebサービスをマッピングさせる方法は、APサーバごとに異なる。 詳細は以下を参照してほしい。
項番 APサーバ名 説明 
5.3.2. How to use¶
本節では、SOAP Web Serviceの具体的な作成方法について説明する。
5.3.2.1. SOAPサーバの作成¶
5.3.2.1.1. プロジェクトの構成¶
各プロジェクトの依存関係
| 項番 | プロジェクト名 | 説明 | 
|---|---|---|
| (1) | webプロジェクト | Webサービス実装クラスを配置する。 | 
| (2) | domainプロジェクト | WebServiceの実装クラスから呼び出されるServiceを配置する。 その他、Repositoryなどは従来と同じである。 | 
| (3) | webserviceプロジェクト | 公開するWebServiceのインターフェースをここに配置する。 クライアントはこのインターフェースを使用してWebサービスを実行する。 | 
| (4) | modelプロジェクト | ドメイン層に属するクラスのうち、SOAP Web Serviceで使用するクラスのみをここに配置する。 クライアントからの入力値や返却結果はこのプロジェクト内のクラスを使用する。 | 
5.3.2.1.2. アプリケーションの設定¶
Webサービスを公開する際の初期設定
Note
以下、参考資料として、APサーバのマニュアルを記述しておく。 必ず、使用するバージョンとあっているか確認してから参照すること。
Oracle WebLogic Server 12.2.1: Oracle(R) Fusion Middleware Understanding WebLogic Web Services for Oracle WebLogic Server Features and Standards Supported by WebLogic Web Services
JBoss Enterprise Application Platform 7.0: DEVELOPING JAX-WS WEB SERVICES
JBoss Enterprise Application Platform 6.4: DEVELOPMENT GUIDE JAX-WS WEB SERVICES
WebSphere Application Server 9.0: IBM Knowledge Center - Web services
パッケージのコンポーネントスキャン設定
Webサービスで使用するコンポーネントをスキャンするため、[server projectName]-ws.xmlを作成し、コンポーネントスキャンの定義を行い、Webサービスにインジェクションできるようにする。
[server projectName]-web/src/main/resources/META-INF/spring/[server projectName]-ws.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"
    xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- (1) -->
    <context:component-scan base-package="com.example.ws" />
</beans>
| 項番 | 説明 | 
|---|---|
| (1) | Webサービスで使用するコンポーネントが格納されているパッケージを指定する。 | 
[server projectName]-web/src/main/webapp/WEB-INF/web.xml
<context-param>
    <param-name>contextConfigLocation</param-name>
    <!-- Root ApplicationContext -->
    <!-- (1) -->
    <param-value>
        classpath*:META-INF/spring/applicationContext.xml
        classpath*:META-INF/spring/spring-security.xml
        classpath*:META-INF/spring/[server projectName]-ws.xml
    </param-value>
</context-param>
| 項番 | 説明 | 
|---|---|
| (1) | [server projectName]-ws.xmlをルートApplicationContext生成時の読み込み対象に加える。 | 
入力チェックを行うための定義
[server projectName]-web/src/main/resources/META-INF/spring/applicationContext.xml
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
    <property name="validator" ref="validator" />
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
5.3.2.1.3. Webサービスの実装¶
以下の作成を行う。
- Domain Objectの作成
- WebServiceインターフェイスの作成
- WebService実装クラスの作成
Domain Objectの作成
java.io.Serializableインターフェースを実装した一般のJavaBeanと特に変わりはない。[server projectName]-model/src/main/java/com/example/domain/model/Todo.java
package com.example.domain.model;
import java.io.Serializable;
import java.util.Date;
public class Todo implements Serializable {
    private String todoId;
    private String title;
    private String description;
    private boolean finished;
    private Date createdAt;
    // omitted setter and getter
}
WebServiceインターフェイスの作成
webserviceプロジェクト内にWebサービスを呼び出すインターフェースを作成する。
[server projectName]-webservice/src/main/java/com/example/ws/todo/TodoWebService.java
package com.example.ws.todo;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import com.example.domain.model.Todo;
import com.example.ws.webfault.WebFaultException;
@WebService(targetNamespace = "http://example.com/todo") // (1)
public interface TodoWebService {
    @WebMethod // (2)
    @WebResult(name = "todo") // (3)
    Todo getTodo(@WebParam(name = "todoId") /* (4) */ String todoId) throws WebFaultException;
}
| 項番 | 説明 | 
|---|---|
| (1) | @WebServiceを付けることで、WebServiceインターフェースであることを宣言する。targetNamespace属性には、名前空間を定義するが、これは作成するWebサービスのパッケージ名と合わせることを推奨する。Warning 
 Note 
 | 
| (2) | Webサービスのメソッドとして公開するメソッドに @WebMethodを付ける。このアノテーションを付けることにより、WSDL上にメソッドが公開され、外部から使用することが可能になる。 | 
| (3) | 返り値に @WebResultを付け、名前をname属性に指定する。返り値がない場合は不要。このアノテーションを付けることにより、WSDL上に返り値として公開される。 | 
| (4) | 引数に @WebParamを付け、名前をname属性に指定する。このアノテーションを付けることにより、WSDL上に引数が公開され、外部から呼び出すときの必要なパラメータとして定義される。 WebFaultExceptionの詳細は「例外ハンドリングの実装」を参照されたい。 | 
Note
パッケージ名および、ネームスペースの付け方について
パッケージ名が以下のような形式になっている場合
- 【ドメイン】.【アプリケーション名(システム名)】.ws.【ユースケース名】
本ガイドラインでは、以下のようなネームスペースにすることを推奨する。
- http://【ドメイン】/【アプリケーション名(システム名)】
Note
ネームスペースとパッケージ名の関係
ドメインをcom.example、アプリケーション名をtodoとした場合、Namespaceは以下のようなJavaのパッケージと紐づけられる。
仕様ではないが、Namespaceとパッケージの命名について、XML Namespace Mapping(Red Hat JBoss Fuse)にまとまっている。
WebService実装クラスの作成
webプロジェクト内にWebServiceインターフェースの実装クラスを作成する。
[server projectName]-web/src/main/java/com/example/ws/todo/TodoWebServiceImpl.java
package com.example.ws.todo;
import java.util.List;
import javax.jws.HandlerChain;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import com.example.domain.model.Todo;
import com.example.domain.service.TodoService;
import com.example.ws.webfault.WebFaultException;
import com.example.ws.exception.WsExceptionHandler;
import com.example.ws.todo.TodoWebService;
@WebService(
        portName = "TodoWebPort",
        serviceName = "TodoWebService",
        targetNamespace = "http://example.com/todo",
        endpointInterface = "com.example.ws.todo.TodoWebService") // (1)
@BindingType(SOAPBinding.SOAP12HTTP_BINDING) // (2)
public class TodoWebServiceImpl extends SpringBeanAutowiringSupport implements TodoWebService { // (3)
    @Autowired // (4)
    TodoService todoService;
    @Override // (5)
    public Todo getTodo(String todoId) throws WebFaultException {
        return todoService.getTodo(todoId);
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | @WebServiceを付けることで、WebServiceの実装クラスであることを宣言する。portName属性は、WSDL上のポート名として公開される。serviceName属性は、WSDL上のサービス名として公開される。targetNamespace属性は、WSDL上で使用されるネームスペース。endpointInterface属性は、このクラスが実装しているWebサービスのインターフェース名を定義する。Note 
 | 
| (2) | @BindingTypeを付けることで、バインディングの方式を設定する。SOAPBinding.SOAP12HTTP_BINDINGを定義するとSOAP1.2でのバインディングとなる。何もつけない場合は、SOAP1.1でのバインディングとなる。 Note 使用するAPサーバのJAX-WS実装により、バインディング方式で挙動が異なる場合があるため注意すること。 たとえば、WebSphere Application Serverの特定のバージョンではSOAP1.2でのバインディングの場合にWSDLが自動生成されない。詳細についてはIBM Knowledge Center - Using annotations to create web servicesを参照されたい。 | 
| (3) | 先ほど作成した TodoWebServiceインターフェースを実装する。org.springframework.web.context.support.SpringBeanAutowiringSupportを継承することで、SpringのBeanをDIできるようにする。 | 
| (4) | Serviceをインジェクションする。 通常のControllerでServiceを呼び出す場合と変わりはない。 | 
| (5) | Serviceを呼び出して業務処理を実行する。 通常のControllerでServiceを呼び出す場合と変わりはない。 | 
Note
Webサービス関連のクラスはwsパッケージ配下にまとめることを推奨する。これは、アプリケーション層のクラスはappパッケージ配下に配置することを推奨しており、それらと区別をしやすくするためである。
5.3.2.1.4. 入力チェックの実装¶
[server projectName]-domain/src/main/java/com/example/domain/service/todo/TodoService.java
package com.example.domain.service.todo;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.groups.Default;
import org.springframework.validation.annotation.Validated;
import com.example.domain.model.Todo;
@Validated // (1)
public interface TodoService {
    Todo getTodo(@NotNull String todoId); // (2)
    Todo createTodo(@Valid Todo todo); // (3)
    @Validated({ Default.class, Todo.Update.class }) // (4)
    Todo updateTodo(@Valid Todo todo);
}
| 項番 | 説明 | 
|---|---|
| (1) | @Validatedを付けることで、このインターフェースの実装クラスが入力チェック対象であることを宣言する。 | 
| (2) | 引数をチェックする場合には、引数自体にアノテーションを付ける。 | 
| (3) | JavaBeanの入力チェックを行う場合も、引数に @Validを付ける。 | 
| (4) | @Validatedにグループを指定し、特定の条件を絞って入力チェックすることも可能である。グループの詳細は次のJavaBeanの説明で記述する。 | 
[server projectName]-model/src/main/java/com/example/domain/model/Todo.java
package com.example.domain.model;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import java.io.Serializable;
import java.util.Date;
// (1)
public class Todo implements Serializable {
    // (2)
    public interface Create {
    }
    public interface Update {
    }
    @Null(groups = Create.class)
    @NotNull(groups = Update.class)
    private String todoId;
    @NotNull
    private String title;
    private String description;
    private boolean finished;
    @Null(groups = Create.class)
    private Date createdAt;
    // omitted setter and getter
}
| 項番 | 説明 | 
|---|---|
| (1) | Bean ValidationでJavaBeanの入力チェックを定義する。 詳細は「入力チェック」を参照されたい。 | 
| (2) | バリデーションのグループ化を行うために使用するインターフェースを定義する。 | 
5.3.2.1.5. セキュリティ対策¶
認証処理
以下にSOAP Web Serviceに対して、Basic認証を行うSpring Securityの設定例を示す。
[server projectName]-web/src/main/resources/META-INF/spring/spring-security.xml
<!-- (1) -->
<sec:http pattern="/ws/**"
          create-session="stateless">
   <sec:csrf disabled="true" />
   <sec:http-basic />
</sec:http>
<!-- (2) -->
<sec:authentication-manager>
   <sec:authentication-provider
       user-service-ref="sampleUserDetailsService">
       <sec:password-encoder ref="passwordEncoder" />
   </sec:authentication-provider>
</sec:authentication-manager>
| 項番 | 説明 | 
|---|---|
| (1) | sec:http-basicタグを記述するとBasic認証を行うことができる。pattern属性を使用して、Webサービスを実行する部分のみ認証を行う。 | 
| (2) | authentication-providerを利用して、認証方式を定義する。実際の認証およびユーザ情報取得は UserDetailsServiceを作成して実施する必要がある。詳細は「認証」を参照されたい。 | 
認可処理
[server projectName]-web/src/main/resources/META-INF/spring/spring-security.xml
<sec:global-method-security pre-post-annotations="enabled" /> <!-- (1) -->
| 項番 | 説明 | 
|---|---|
| (1) | <sec:global-method-security>要素のpre-post-annotations属性をenabledに指定する。 | 
[server projectName]-domain/src/main/java/com/example/domain/service/todo/TodoServiceImpl.java
public class TodoServiceImpl implements TodoService {
    // omitted
    // (1)
    @PreAuthorize("isAuthenticated()")
    public List<Todo> getTodos() {
        // omitted
    }
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public Todo createTodo(Todo todo) {
        // omitted
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | 認可処理を行うメソッドに org.springframework.security.access.prepost.PreAuthorizeアノテーションを設定する。 | 
CSRF対策
[server projectName]-web/src/main/resources/META-INF/spring/spring-security.xml
<!-- (1) -->
<sec:http pattern="/ws/**"
    create-session="stateless">
    <sec:http-basic />
    <sec:csrf disabled="true" />
</sec:http>
| 項番 | 説明 | 
|---|---|
| (1) | SOAP Web Service用のSpring Securityの定義を追加する。 <sec:http>要素のpattern属性にSOAP Web Service用のリクエストパスのURLパターンを指定する。このコード例では、 /ws/で始まるリクエストパスをSOAP Web Service用のリクエストパスとしている。また、 create-session属性をstatelessとする事で、Spring Securityの処理でセッションが使用されなくなる。CSRF対策を無効化するために、 <sec:csrf>要素のdisabled属性をtrueに指定する。 | 
5.3.2.1.6. 例外ハンドリングの実装¶
SOAPサーバで発生する例外
SOAPサーバで発生した例外はこれから記述する例外を実装したクラス(SOAPFault)を使用することで、クライアントへの通知メッセージを決定することができる。
具体的には以下のクラスを作成する。
| 項番 | クラス名 | 概要 | 
|---|---|---|
| (1) | ErrorBean | 発生した例外のコードとメッセージなどを保持するクラス。 | 
| (2) | WebFaultType | 例外の種類を判別するために使用する列挙型。 | 
| (3) | WebFaultBean | ErrorBeanとWebFaultTypeを保持するクラス。ErrorBeanをListで保持して例外情報を複数保持できる。 | 
| (4) | WebFaultException | WebFaultBeanを保持する例外クラス。 | 
これらの例外はSOAPサーバ、クライアントで共用するため、[server projectName]-webserviceに配置する。
[server projectName]-webservice/src/main/java/com/example/ws/webfault/ErrorBean.java
package com.example.ws.webfault;
public class ErrorBean { // (1)
    private String code;
    private String message;
    private String path;
    // omitted setter and getter
}
| 項番 | 説明 | 
|---|---|
| (1) | 例外のメッセージなどを保持するクラスを作成する。 | 
[server projectName]-webservice/src/main/java/com/example/ws/webfault/WebFaultType.java
package com.example.ws.webfault;
public enum WebFaultType { // (2)
    AccessDeniedFault,
    BusinessFault,
    ResourceNotFoundFault,
    ValidationFault,
}
| 項番 | 説明 | 
|---|---|
| (1) | 例外の種類を判別するために使用する列挙型を定義する。 | 
[server projectName]-webservice/src/main/java/com/example/ws/webfault/WebFaultBean.java
package com.example.ws.webfault;
import java.util.ArrayList;
import java.util.List;
public class WebFaultBean { // (3)
    private WebFaultType type;
    private List<ErrorBean> errors = new ArrayList<ErrorBean>();
    public WebFaultBean(WebFaultType type) {
        this.type = type;
    }
    public void addError(String code, String message) {
        addError(code, message, null);
    }
    public void addError(String code, String message, String path) {
        errors.add(new ErrorBean(code, message, path));
    }
    // omitted setter and getter
}
| 項番 | 説明 | 
|---|---|
| (1) | ErrorBeanとWebFaultTypeを保持するクラスを作成する。 | 
[server projectName]-webservice/src/main/java/com/example/ws/webfault/WebFaultException.java
package com.example.ws.webfault;
import java.util.List;
import javax.xml.ws.WebFault;
@WebFault(name = "WebFault", targetNamespace = "http://example.com/todo") // (1)
public class WebFaultException extends Exception {
    private WebFaultBean faultInfo; // (2)
    public WebFaultException() {
    }
    public WebFaultException(String message, WebFaultBean faultInfo) {
        super(message);
        this.faultInfo = faultInfo;
    }
    public WebFaultException(String message, WebFaultBean faultInfo, Throwable e) {
        super(message, e);
        this.faultInfo = faultInfo;
    }
    public List<ErrorBean> getErrors() {
        return this.faultInfo.getErrors();
    }
    public WebFaultType getType() {
        return this.faultInfo.getType();
    }
    // omitted setter and getter
}
| 項番 | 説明 | 
|---|---|
| (1) | Exception継承クラスに @WebFaultを付けて、SOAPFaultであることを宣言する。name属性には、クライアントに送信するSOAPFaultのname属性を設定する。targetNamespace属性には、使用するネームスペースを設定する。Webサービスと同じにする必要がある。 | 
| (2) | faultInfoをフィールドに保持させるとともに、コード例のように以下のようなコンストラクタとメソッドを持たせる。 
 | 
Note
WebFaultExceptionにRuntimeExceptionではなく、Exceptionを継承させている理由
WebFaultExceptionの親クラスをRuntimeExceptionにすれば、例外の処理をもっと簡略化することができそうに見える。しかし、親クラスをRuntimeExceptionにしてはいけない。JSR 224: JavaTM API for XML-Based Web Servicesでも明確にしてはいけないと宣言されている。実際に試してみても、APサーバのJAX-WS実装次第ではあるが、クライアントで@WebFaultを付けた例外クラス(WebFaultException)を取得することができず、エラーの種類やメッセージを取得することができなくなる。AOPを使用して例外処理を実施していないのもExceptionを継承しているためである。
Warning
WebFaultExceptionのコンストラクタとフィールドについて
WebFaultExceptionには、デフォルトコンストラクタと各フィールドに対応するsetterが必須となる。これは、クライアントの内部処理で、WebFaultExceptionを作成する際に使用するためである。そのため、各フィールドをfinalにすることも不可能である。
WebFaultExceptionを継承し、クライアントへ伝えたい種類分、子クラスを作成する。- 業務エラー例外
- 入力エラー例外
- リソース未検出エラー例外
- 排他エラー例外
- 認可エラー例外
- システムエラー例外
下記は、業務エラー例外の例である。
[server projectName]-webservice/src/main/java/com/example/ws/webfault/BusinessFaultException.java
package com.example.ws.webfault;
import javax.xml.ws.WebFault;
@WebFault(name = "BusinessFault", targetNamespace = "http://example.com/todo") // (1)
public class BusinessFaultException extends WebFaultException {
    public BusinessFaultException(String message, WebFaultBean faultInfo) {
        super(message, faultInfo);
    }
    public BusinessFaultException(String message, WebFaultBean faultInfo, Throwable e) {
        super(message, faultInfo, e);
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | WebFaultExceptionを継承し、コンストラクタのみ作成する。フィールドやその他メソッドは親クラスのメソッドを使用するため記述不要である。 | 
発生する例外をSOAPFaultでラップする例外ハンドラー
Serviceから発生する実行時例外をSOAPFaultでラップするために例外ハンドラークラスを作成する。 本ガイドラインではWebService実装クラスがこのハンドラーを用いて例外を変換してスローする方針とする。
Serviceからスローされる例外は以下を想定している。必要に応じて追加されたい。
| 例外名 | 内容 | 
|---|---|
| org.springframework.security.access.AccessDeniedException | 認可エラー時の例外 | 
| javax.validation.ConstraintViolationException | 入力チェックエラー時の例外 | 
| org.terasoluna.gfw.common.exception.ResourceNotFoundException | リソースが見つからない場合の例外 | 
| org.terasoluna.gfw.common.exception.BusinessException | 業務例外 | 
[server projectName]-web/src/main/java/com/example/ws/exception/WsExceptionHandler.java
package com.example.ws.exception;
import java.util.Iterator;
import java.util.Locale;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Component;
import org.terasoluna.gfw.common.exception.BusinessException;
import org.terasoluna.gfw.common.exception.ExceptionCodeResolver;
import org.terasoluna.gfw.common.exception.ExceptionLogger;
import org.terasoluna.gfw.common.exception.ResourceNotFoundException;
import org.terasoluna.gfw.common.exception.SystemException;
import org.terasoluna.gfw.common.message.ResultMessage;
import org.terasoluna.gfw.common.message.ResultMessages;
import com.example.ws.webfault.WebFaultBean;
import com.example.ws.webfault.WebFaultException;
import com.example.ws.webfault.WebFaultType;
@Component  // (1)
public class WsExceptionHandler {
    @Autowired
    MessageSource messageSource; // (2)
    @Autowired
    ExceptionCodeResolver exceptionCodeResolver; // (3)
    @Autowired
    ExceptionLogger exceptionLogger; // (4)
    // (5)
    public void translateException(Exception e) throws WebFaultException {
        loggingException(e);
        WebFaultBean faultInfo = null;
        if (e instanceof AccessDeniedException) {
            faultInfo = new WebFaultBean(WebFaultType.AccessDeniedFault);
            faultInfo.addError(e.getClass().getName(), e.getMessage());
        } else if (e instanceof ConstraintViolationException) {
            faultInfo = new WebFaultBean(WebFaultType.ValidationFault);
            this.addErrors(faultInfo, ((ConstraintViolationException) e).getConstraintViolations());
        } else if (e instanceof ResourceNotFoundException) {
            faultInfo = new WebFaultBean(WebFaultType.ResourceNotFoundFault);
            this.addErrors(faultInfo, ((ResourceNotFoundException) e).getResultMessages());
        } else if (e instanceof BusinessException) {
            faultInfo = new WebFaultBean(WebFaultType.BusinessFault);
            this.addErrors(faultInfo, ((BusinessException) e).getResultMessages());
        } else {
            // not translate.
            throw new SystemException("e.ex.fw.9001", e);
        }
        throw new WebFaultException(e.getMessage(), faultInfo, e.getCause());
    }
    private void loggingException(Exception e) {
        exceptionLogger.log(e);
    }
    private void addErrors(WebFaultBean faultInfo, Set<ConstraintViolation<?>> constraintViolations) {
        for (ConstraintViolation<?> v : constraintViolations) {
            Iterator<Path.Node> pathIt = v.getPropertyPath().iterator();
            pathIt.next(); // method name node (skip)
            Path.Node methodArgumentNameNode = pathIt.next();
            faultInfo.addError(
                v.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(),
                v.getMessage(),
                pathIt.hasNext() ? pathIt.next().toString() : methodArgumentNameNode.toString());
        }
    }
    private void addErrors(WebFaultBean faultInfo, ResultMessages resultMessages) {
        Locale locale = Locale.getDefault();
        for (ResultMessage message : resultMessages) {
            faultInfo.addError(
                message.getCode(),
                messageSource.getMessage(message.getCode(), message.getArgs(), message.getText(), locale));
        }
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | 本クラスをDIコンテナに管理をさせるため、 @Componentを付ける。 | 
| (2) | 出力するメッセージを取得するために MessageSourceを使用する。 | 
| (3) | 共通ライブラリが提供する ExceptionCodeResolverMessageSourceを使用して例外の種類と例外コードをマッピングする。詳細は、「例外ハンドリング」を参照されたい。 | 
| (4) | 共通ライブラリが提供する ExceptionLoggerを使用して例外情報を例外に出力する。詳細は、「例外ハンドリング」を参照されたい。 | 
| (5) | Serviceから発生しうる各例外について、 SOAPFaultへのラップを行う。例外のマッピングは冒頭の表を参考されたい。 | 
Note
その他の例外の扱いについて
その他の例外発生時(上記のtranslateExceptionメソッドのelse部分)では、クライアントでは詳細な例外の内容は通知されず、com.sun.xml.internal.ws.fault.ServerSOAPFaultExceptionが発生するのみとなる。他の例外同様にラップしてクライアント側に通知することも可能である。
Serviceで発生した例外をWebサービス内から例外ハンドラーを呼び出し、ラップする
Webサービスクラスにて、例外ハンドラーを呼び出す。以下はその例である。
[server projectName]-web/src/main/java/com/example/ws/todo/TodoWebServiceImpl.java
@WebService(
        portName = "TodoWebPort",
        serviceName = "TodoWebService",
        targetNamespace = "http://example.com/todo",
        endpointInterface = "com.example.ws.todo.TodoWebService")
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public class TodoWebServiceImpl extends SpringBeanAutowiringSupport implements TodoWebService {
    @Autowired
    TodoService todoService;
    @Autowired
    WsExceptionHandler handler; // (1)
    @Override
    public Todo getTodo(String todoId) throws WebFaultException /* (2) */ {
        try {
            return todoService.getTodo(todoId);
        } catch (RuntimeException e) {
            handler.translateException(e); // (3)
        }
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | 例外ハンドラーをインジェクションする。 | 
| (2) | WebFaultExceptionにラップしてスローするため、throws句を付ける。 | 
| (3) | 実行時例外が発生した場合は、例外ハンドラークラスに処理を委譲する。 | 
5.3.2.1.7. MTOMを利用した大容量のバイナリデータを扱う方法¶
[server projectName]-webservice/src/main/java/com/example/ws/todo/TodoWebService.java
package com.example.ws.todo;
import java.util.List;
import javax.activation.DataHandler;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlMimeType;
import com.example.domain.model.Todo;
import com.example.ws.webfault.WebFaultException;
@WebService(targetNamespace = "http://example.com/todo")
public interface TodoWebService {
    // omitted
    @WebMethod
    void uploadFile(@XmlMimeType("application/octet-stream") /* (1) */ DataHandler dataHandler) throws WebFaultException;
}
| 項番 | 説明 | 
|---|---|
| (1) | バイナリデータを処理する javax.activation.DataHandlerに対して@XmlMimeTypeを付ける。 | 
[server projectName]-web/src/main/java/com/example/ws/todo/TodoWebServiceImpl.java
package com.example.ws.todo;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.activation.DataHandler;
import javax.jws.HandlerChain;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.soap.MTOM;
import javax.xml.ws.soap.SOAPBinding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import org.terasoluna.gfw.common.exception.SystemException;
import com.example.domain.model.Todo;
import com.example.domain.service.TodoService;
import com.example.ws.webfault.WebFaultException;
import com.example.ws.exception.WsExceptionHandler;
// (1)
@MTOM
@WebService(
        portName = "TodoWebPort",
        serviceName = "TodoWebService",
        targetNamespace = "http://example.com/todo",
        endpointInterface = "com.example.ws.todo.TodoWebService")
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public class TodoWebServiceImpl extends SpringBeanAutowiringSupport implements TodoWebService {
    @Autowired
    TodoService todoService;
    // omitted
    @Override
    public void uploadFile(DataHandler dataHandler) throws WebFaultException {
        try (InputStream inputStream = dataHandler.getInputStream()){ // (2)
            todoService.uploadFile(inputStream);
        } catch (Exception e) {
            handler.translateException(e);
        }
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | @MTOMを付けて、MTOMに準拠した実装を使用することを宣言する。 | 
| (2) | javax.activation.DataHandlerからjava.io.InputStreamを取得してファイルを扱う。 | 
5.3.2.2. クライアントの作成¶
5.3.2.2.1. プロジェクトの構成¶
「JAX-WSを利用したWebサービスの開発について」で述べたとおり、modelプロジェクトとwebserviceプロジェクトをSOAPサーバから受領する前提である。
| 項番 | プロジェクト名 | 説明 | 
|---|---|---|
| (1) | webプロジェクト | Controllerを作成する。 通常の画面遷移時のControllerと特に変更点はない。 | 
| (2) | domainプロジェクト | Serviceクラスからwebserviceプロジェクトで用意されたWebServeインターフェースを使用してWebサービスを呼び出す。 SOAPサーバと通信する際に使用するWebServiceインターフェースを実装したプロキシを定義する。 | 
| (3) | webserviceプロジェクト | SOAPサーバと同じ資材を配置する。 クライアントはこのインターフェースを使用してWebサービスを実行する。 | 
| (4) | modelプロジェクト | SOAPサーバと同じ資材を配置する。 SOAPサーバに渡す入力値や返却結果はこのプロジェクト内のクラスを使用する。 | 
| (5) | envプロジェクト | domainプロジェクトで定義したプロキシの環境依存する値を定義する。 プロキシの定義から環境依存する値をプロパティファイルに集約し、プロパティファイルのみenvプロジェクトに配置する。 | 
Note
プロキシの定義ついて
試験用SOAPサーバ、本番用SOAPサーバ等、複数環境向けのプロキシを定義する際に発生する重複部分を排除し、管理を容易にするために、当ガイドラインではプロキシの定義はdomainプロジェクトで行い、環境依存する値はプロパティファイルに集約、プロパティファイルのみenvプロジェクトに配置することを推奨する。
ユニットテストでプロキシのスタブやモックを使用する場合は、ユニットテスト用のコンポーネントを定義するためのBean定義ファイル(test-context.xml)にBeanを定義する。
5.3.2.2.2. Webサービス クライアントの実装¶
以下のクラスの実装を行う。
- WebServiceインターフェースを実装したプロキシの定義
- ServiceクラスからWebServiceインターフェース経由でWebサービスを呼び出す。
WebServiceインターフェースを実装したプロキシの作成
WebServiceインターフェースを実装したプロキシを生成するorg.springframework.remoting.jaxws.JaxWsPortProxyFactoryBeanの定義を行う。
[client projectName]-domain/src/main/resources/META-INF/spring/[client projectName]-domain.xml
<bean id="todoWebService"
    class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean"><!-- (1) -->
    <property name="serviceInterface" value="com.example.ws.todo.TodoWebService" /><!-- (2) -->
    <!-- (3) -->
    <property name="serviceName" value="TodoWebService" />
    <property name="portName" value="TodoWebPort" />
    <property name="namespaceUri" value="http://example.com/todo" />
    <property name="wsdlDocumentResource" value="${webservice.todoWebService.wsdlDocumentResource}" /><!-- (4) -->
    <property name="lookupServiceOnStartup" value="false" /><!-- (5) -->
</bean>
[client projectName]-env/src/main/resources/META-INF/spring/[client projectName]-infra.properties
# (6)
webservice.todoWebService.wsdlDocumentResource=http://AAA.BBB.CCC.DDD:XXXX/[server projectName]-web/ws/TodoWebService?wsdl
| 項番 | 説明 | 
|---|---|
| (1) | org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBeanを定義する。このクラスが生成するプロキシを経由してSOAPサーバにアクセスできる。 | 
| (2) | serviceInterfaceプロパティに本来このWebサービスが実装すべきインターフェースを定義する。 | 
| (3) | serviceName、portName、namespaceUriプロパティにそれぞれSOAPサーバ側で定義している同じ内容を定義する必要がある。 | 
| (4) | wsdlDocumentResourceプロパティに公開されているWDSLのURLを設定する。ここでは後述するプロパティファイルにURLを記述するため、プロパティのキーを指定している。 | 
| (5) | lookupServiceOnStartupプロパティにBean生成する時、SOAPサーバからWSDLファイルを取得するかどうかを設定する。falseの場合はBeanが初めて使用されるタイミングでWSDLファイルの取得が行われる。SOAPサーバからWSDLファイルの取得が不可能な場合でもWebサービス クライアントを起動させるために、 lookupServiceOnStartupプロパティにfalseを指定することを推奨する。ただし、WSDLファイルをWebサービス クライアントで保持している場合は設定不要である。 | 
| (6) | [client projectName]-domain.xmlで定義したプロパティのキーの値を設定する。WSDLのURLを記述する。Note wsdlDocumentResourceへのWSDLファイルのURL以外の指定 上記の例では、SOAPサーバがWSDLファイルを公開している前提である。 | 
Note
エンドポイントアドレスの上書き指定
WSDLファイルには、Webサービス実行時のアクセスURL(エンドポイントアドレス)が記述されているため、クライアントではアクセスURLの設定は不要である。
ただし、WSDLファイルに記述されているURLではないURLにアクセスする場合、org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBeanのendpointAddressプロパティを設定することで上書きすることができる。
テストなどで、環境を切り替える場合に使用するとよい。
以下はその設定例である。
[client projectName]-domain/src/main/resources/META-INF/spring/[client projectName]-domain.xml
<bean id="todoWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean"> <property name="serviceInterface" value="com.example.ws.todo.TodoWebService" /> <property name="serviceName" value="TodoWebService" /> <property name="portName" value="TodoWebPort" /> <property name="namespaceUri" value="http://example.com/todo" /> <property name="wsdlDocumentResource" value="${webservice.todoWebService.wsdlDocumentResource}" /> <property name="endpointAddress" value="${webservice.todoWebService.endpointAddress}" /><!-- (1) --> <property name="lookupServiceOnStartup" value="false" /> </bean>
[client projectName]-env/src/main/resources/META-INF/spring/[client projectName]-infra.properties
# (2) webservice.todoWebService.endpointAddress=http://AAA.BBB.CCC.DDD:XXXX/[server projectName]-web/ws/TodoWebService
項番 説明 [client projectName]-domain.xmlで定義したプロパティのキーの値を設定する。エンドポイントアドレスを記述する。
ServiceからWebサービスを呼び出す
上記で作成したWebサービスをServiceでインジェクションして実行する。
[client projectName]-domain/src/main/java/com/example/domain/service/todo/TodoServiceImpl.java
package com.example.soap.domain.service.todo;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.domain.model.Todo;
import com.example.ws.webfault.WebFaultException;
import com.example.ws.todo.TodoWebService;
@Service
public class TodoServiceImpl implements TodoService {
    @Autowired
    TodoWebService todoWebService;
    @Override
    public void createTodo(Todo todo) {
        // (1)
        try {
            todoWebService.createTodo(todo);
        } catch (WebFaultException e) {
            // (2)
            // handle exception…
        }
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | TodoWebServiceをインジェクションして、実行対象のServiceを呼び出す。 | 
| (2) | 
Note
レスポンスの情報取得
リトライを考慮するなど、レスポンスの情報をクライアントで取得したい場合、以下のようにjavax.xml.ws.BindingProviderクラスにキャストすることで取得できる。
BindingProvider provider = (BindingProvider) todoWebService; int status = (int) provider.getResponseContext().get(MessageContext.HTTP_RESPONSE_CODE);
BindingProviderの詳細については The Java API for XML-Based Web Services(JAX-WS) 2.2 -4.2 javax.xml.ws.BindingProvider-を参照されたい。
ただし、クライアントの依存関係にApatch CXFライブラリが含まれる場合、通信エラー時に上記の方法でレスポンスの情報を取得することができない。 これは、依存関係にApatch CXFライブラリが含まれる場合は自動的にApatch CXFのプロキシが使用されるため、およびApache CXFのプロキシは通信エラーが発生した場合にレスポンスの情報をレスポンスコンテキストに保持しないためである。 Apache CXFのエラー処理についてはApache CXF Software Architecture Guide -Fault Handling-を参照されたい。
Webサービスと別のWebサービスへのクライアントを持つ中継サービスのように、どうしてもクライアントにApache CXFライブラリの依存関係を含んでしまう場合は制限事項として注意されたい。
5.3.2.2.3. セキュリティ対策¶
認証処理
org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBeanを使用している場合でBasic認証を使用しているSOAPサーバと通信をする場合には、bean定義にユーザ名とパスワードを追加するだけで認証を行うことができる。
[client projectName]-domain/src/main/resources/META-INF/spring/[client projectName]-domain.xml
<bean id="todoWebService"
    class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="serviceInterface" value="com.example.ws.todo.TodoWebService" />
    <property name="serviceName" value="TodoWebService" />
    <property name="portName" value="TodoWebPort" />
    <property name="namespaceUri" value="http://example.com/todo" />
    <property name="wsdlDocumentResource" value="${webservice.todoWebService.wsdlDocumentResource}" />
    <!-- (1) -->
    <property name="username" value="${webservice.todoWebService.username}" />
    <property name="password" value="${webservice.todoWebService.password}" />
</bean>
[client projectName]-env/src/main/resources/META-INF/spring/[client projectName]-infra.properties
# (2)
webservice.todoWebService.username=testuser
webservice.todoWebService.password=password
| 項番 | 説明 | 
|---|---|
| (1) | org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBeanのbean定義にusernameとpasswordを加えることでBasic認証における、認証情報を送信することができる。ユーザ名とパスワードをプロパティファイルに切り出した場合のサンプルである。 | 
| (2) | [client projectName]-domain.xmlで定義したプロパティのキーの値を設定する。認証に使用するユーザ名とパスワードを記述する。 | 
5.3.2.2.4. 例外ハンドリングの実装¶
WebFaultExceptionに例外をラップして、スローすることを推奨している。WebFaultExceptionをキャッチして、その原因例外を判定してそれぞれの処理を行う。@Override
public void createTodo(Todo todo) {
    try {
        // (1)
        todoWebService.createTodo(todo);
    } catch (WebFaultException e) {
        // (2)
        switch (e.getFaultInfo().getType()) {
        case ValidationFault:
            // handle exception…
            break;
        case BusinessFault:
            // handle exception…
            break;
        default:
            // handle exception…
            break;
        }
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | Webサービスを呼び出す。throwsがついているため、 WebFaultExceptionをキャッチする必要がある。 | 
| (2) | faultInfoの種別で例外を判定し、それぞれの処理を記述する(画面にメッセージを出す、例外をスローするなど) | 
5.3.2.2.5. タイムアウトの設定¶
クライアントで指定できるタイムアウトは大きく以下の2つがある。
- SOAPサーバとのコネクションタイムアウト
- SOAPサーバへのリクエストタイムアウト
org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBeanのカスタムプロパティに指定する必要がある。[client projectName]-domain/src/main/resources/META-INF/spring/[client projectName]-domain.xml
<bean id="todoWebService"
    class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="serviceInterface" value="com.example.ws.todo.TodoWebService" />
    <property name="serviceName" value="TodoWebService" />
    <property name="portName" value="TodoWebPort" />
    <property name="namespaceUri" value="http://example.com/todo" />
    <property name="wsdlDocumentResource" value="${webservice.todoWebService.wsdlDocumentResource}" />
    <!-- (1) -->
    <property name="customProperties">
        <map>
            <!-- (2) -->
            <entry key="com.sun.xml.internal.ws.connect.timeout" value="${webservice.connect.timeout}"/>
            <entry key="com.sun.xml.internal.ws.request.timeout" value="${webservice.request.timeout}"/>
        </map>
    </property>
</bean>
[client projectName]-env/src/main/resources/META-INF/spring/[client projectName]-infra.properties
# (3)
webservice.request.timeout=3000
webservice.connect.timeout=3000
| 項番 | 説明 | 
|---|---|
| (1) | customPropertiesプロパティにMapを指定することでカスタムプロパティを定義する。 | 
| (2) | コネクションタイムアウトとリクエストタイムアウトを定義する。 それぞれの値をプロパティファイルに切り出した場合のサンプルである。 Warning タイムアウト定義に使用するキーについて それぞれのタイムアウトを定義するキーはJAX-WSの実装により異なる値を設定する必要がある。 詳細はJAX_WS-1166 Standardize timeout settingsを参照されたい。 Note WebLogicで該当キーを指定する場合は、 指定をしない場合、WebLogicのJAX-WS実装ライブラリが
 設定の方法は以下のとおりである。 
 | 
| (3) | [client projectName]-domain.xmlで定義したプロパティのキーの値を設定する。コネクションタイムアウトとリクエストタイムアウトを記述する。 | 
5.3.3. Appendix¶
5.3.3.1. SOAPサーバ用にプロジェクトの設定を変更する¶
artifactId
├── pom.xml
├── artifactId-domain
├── artifactId-env
├── artifactId-initdb
├── artifactId-selenium
└── artifactId-web
以下のようなプロジェクト構成にする。
artifactId
├── pom.xml
├── artifactId-domain
├── artifactId-env
├── artifactId-initdb
├── artifactId-selenium
├── artifactId-web
├── artifactId-model
└── artifactId-webservice
5.3.3.1.1. 既存プロジェクトの変更¶
5.3.3.1.2. modelプロジェクトの作成¶
modelプロジェクトの構成について説明する。
artifactId-model
    ├── pom.xml  ... (1)
| 項番 | 説明 | 
|---|---|
| (1) | modelモジュールの構成を定義するPOM(Project Object Model)ファイル。 このファイルでは、以下の定義を行う。 
 | 
pom.xmlは以下のようなイメージになる。必要に応じて編集する必要がある。<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>artifactId-model</artifactId>
    <packaging>jar</packaging>
    <parent>
        <groupId>groupId</groupId>
        <artifactId>artifactId</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <dependencies>
        <!-- == Begin TERASOLUNA == -->
        <dependency>
            <groupId>org.terasoluna.gfw</groupId>
            <artifactId>terasoluna-gfw-common-dependencies</artifactId>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.terasoluna.gfw</groupId>
            <artifactId>terasoluna-gfw-jodatime-dependencies</artifactId>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.terasoluna.gfw</groupId>
            <artifactId>terasoluna-gfw-security-core-dependencies</artifactId>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.terasoluna.gfw</groupId>
            <artifactId>terasoluna-gfw-recommended-dependencies</artifactId>
            <type>pom</type>
        </dependency>
        <!-- == End TERASOLUNA == -->
    </dependencies>
</project>
5.3.3.1.3. webserviceプロジェクトの作成¶
webserviceプロジェクトの構成について説明する。
artifactId-webservice
    ├── pom.xml  ... (1)
| 項番 | 説明 | 
|---|---|
| (1) | webserviceモジュールの構成を定義するPOM(Project Object Model)ファイル。 このファイルでは、以下の定義を行う。 
 | 
pom.xmlは以下のようなイメージになる。必要に応じて編集する必要がある。<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>artifactId-webservice</artifactId>
    <packaging>jar</packaging>
    <parent>
        <groupId>groupId</groupId>
        <artifactId>artifactId</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>artifactId-model</artifactId>
        </dependency>
        <!-- == Begin TERASOLUNA == -->
        <dependency>
            <groupId>org.terasoluna.gfw</groupId>
            <artifactId>terasoluna-gfw-common-dependencies</artifactId>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.terasoluna.gfw</groupId>
            <artifactId>terasoluna-gfw-jodatime-dependencies</artifactId>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.terasoluna.gfw</groupId>
            <artifactId>terasoluna-gfw-security-core-dependencies</artifactId>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.terasoluna.gfw</groupId>
            <artifactId>terasoluna-gfw-recommended-dependencies</artifactId>
            <type>pom</type>
        </dependency>
        <!-- == End TERASOLUNA == -->
    </dependencies>
</project>
5.3.3.2. SOAPサーバのパッケージ構成¶
| プロジェクト名 | 説明 | 
|---|---|
| [server projectName]-domain | SOAPサーバのドメイン層に関するクラス・設定ファイルを格納するプロジェクト | 
| [server projectName]-web | SOAPサーバのアプリケーション層に関するクラス・設定ファイルを格納するプロジェクト | 
| [server projectName]-env | SOAPサーバの環境に依存するファイル等を格納するプロジェクト | 
| [server projectName]-model | SOAPサーバのドメイン層に関するクラスの中で、Webサービス実行時に使用し、クライアントと共有するクラスを格納するプロジェクト | 
| [server projectName]-webservice | SOAPサーバが提供するWebサービスのインターフェースを格納するプロジェクト | 
5.3.3.2.1. [server projectName]-domain¶
[server projectName]-modelの依存関係を追加するため、pom.xmlに以下を追加する。
<dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>artifactId-model</artifactId>
</dependency>
その他のパッケージ構成は、通常のdomainプロジェクトと変わらないため、「アプリケーションのレイヤ化 の プロジェクト構成」を参照されたい。
5.3.3.2.2. [server projectName]-web¶
[server projectName]-webserviceの依存関係を追加するため、pom.xmlに以下を追加する。
<dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>artifactId-webservice</artifactId>
</dependency>
Note
依存性の解決について
[server projectName]-modelの依存関係の定義は不要である。これは[server projectName]-webserviceから[server projectName]-modelへの依存関係が定義されているため、推移的に依存関係が追加されるためである。
[server projectName]-webのプロジェクト推奨構成を、以下に示す。
[server projectName]-web
  └src
      └main
          ├java
          │  └com
          │      └example
          │          ├app...(1)
          │          └ws...(2)
          │            ├exception...(3)
          │            │  └WsExceptionHandler.java
          │            ├abc
          │            │  └AbcWebServiceImpl.java
          │            └def
          │                └DefWebServiceImpl.java
          ├resources
          │  ├META-INF
          │  │  └spring
          │  │      ├applicationContext.xml...(4)
          │  │      ├application.properties...(5)
          │  │      ├spring-mvc.xml ...(6)
          │  │      ├spring-security.xml...(7)
          │  │      └[server projectName]-ws.xml...(8)
          │  └i18n
          │      └application-messages.properties...(9)
          └webapp
              ├resources...(10)
              └WEB-INF
                  ├views ...(11)
                  └web.xml...(12)
| 項番 | 説明 | 
|---|---|
| (1) | アプリケーション層の構成要素を格納するパッケージ。 Webサービスのみ作成する場合は削除してもよい。 | 
| (2) | Webサービスの関連クラスを格納するパッケージ。 | 
| (3) | Webサービスの例外ハンドラーなどを格納するパッケージ。 | 
| (4) | アプリケーション全体に関するBean定義を行う。 | 
| (5) | アプリケーションで使用するプロパティを定義する。 | 
| (6) | Spring MVCの設定を行うBean定義を行う。 Webサービスのみ作成する場合は削除してもよい。 | 
| (7) | Spring Securityの設定を行うBean定義を行う。 | 
| (8) | Webサービスに関するBean定義を行う。 | 
| (9) | 画面表示用のメッセージ(国際化対応)定義を行う。 | 
| (10) | 静的リソース(css、js、画像など)を格納する。 Webサービスのみ作成する場合は削除してもよい。 | 
| (11) | View(jsp)を格納する。 Webサービスのみ作成する場合は削除してもよい。 | 
| (12) | Servletのデプロイメント定義を行う。 | 
Note
SOAPサーバの不要なファイル
SOAPサーバで、Webサービスのみを作成する場合、ブランクプロジェクトに存在するSpring MVCの設定ファイルなどは不要となるため、削除したほうが望ましい。
5.3.3.2.3. [server projectName]-env¶
[server projectName]-envについては、通常のenvプロジェクトと変わらないため、「アプリケーションのレイヤ化 の プロジェクト構成」を参照されたい。
5.3.3.2.4. [server projectName]-model¶
[server projectName]-modelのプロジェクト推奨構成を、以下に示す。
[server projectName]-model
  └src
      └main
          └java
              └com
                  └example
                      └domain ...(1)
                          └model ...(2)
                              ├Xxx.java
                              ├Yyy.java
                              └Zzz.java
| 項番 | 説明 | 
|---|---|
| (1) | ドメイン層の構成要素を格納するパッケージ。 | 
| (2) | Domain Objectの中でWebサービス実行時に使用するクラスを格納するパッケージ。 | 
5.3.3.2.5. [server projectName]-webservice¶
[server projectName]-webserviceのプロジェクト推奨構成を、以下に示す。
[server projectName]-webservice
  └src
      └main
          └java
              └com
                  └example
                      └ws...(1)
                        ├webfault...(2)
                        ├abc
                        │  └AbcWebService.java
                        └def
                            └DefWebService.java
| 項番 | 説明 | 
|---|---|
| (1) | Webサービスのインターフェースを格納するパッケージ。 | 
| (2) | Webサービスのwebfaultを格納するパッケージ。 | 
5.3.3.3. クライアントのパッケージ構成¶
| プロジェクト名 | 説明 | 
|---|---|
| [client projectName]-domain | クライアントのドメイン層に関するクラス・設定ファイルを格納するプロジェクト | 
| [client projectName]-web | クライアントのアプリケーション層に関するクラス・設定ファイルを格納するプロジェクト | 
| [client projectName]-env | クライアントの環境に依存するファイル等を格納するプロジェクト | 
Note
[server projectName]-modelと[server projectName]-webserviceについては、前述の「 SOAPサーバのパッケージ構成」を参照されたい。
5.3.3.3.1. [client projectName]-domain¶
SOAPサーバから提供される[server projectName]-webserviceの依存関係を追加するため、pom.xmlに以下を追加する。
<dependency>
    <groupId>${project.groupId}</groupId>
    <artifactId>artifactId-webservice</artifactId>
</dependency>
Note
依存性の解決について
[server projectName]-webと同様に、このpom.xmlには、[server projectName]-modelの依存関係の定義は不要である。これは[server projectName]-webserviceから[server projectName]-modelへの依存関係が定義されているため、推移的に依存関係が追加されるためである。
その他のパッケージ構成は、通常のdomainプロジェクトと変わらないため、「アプリケーションのレイヤ化 の プロジェクト構成」を参照されたい。
5.3.3.3.2. [client projectName]-web¶
[client projectName]-webについては、通常のwebプロジェクトと変わらないため、「アプリケーションのレイヤ化 の プロジェクト構成」を参照されたい。
5.3.3.3.3. [client projectName]-env¶
[client projectName]-envのプロジェクト推奨構成を、以下に示す。
[projectName]-env
  ├configs ...(1)
  │   └[envName] ...(2)
  │       └resources ...(3)
  └src
      └main
          └resources ...(4)
             ├META-INF
             │  └spring
             │      ├[projectName]-env.xml ...(5)
             │      └[projectName]-infra.properties ...(6)
             ├dozer.properties
             ├log4jdbc.properties
             └logback.xml ...(7)
| 項番 | 説明 | 
|---|---|
| (1) | 全環境の環境依存ファイルを管理するためのディレクトリ。 | 
| (2) | 環境毎の環境依存ファイルを管理するためのディレクトリ。 ディレクトリ名は、環境を識別する名前を指定する。 | 
| (3) | 環境毎の設定ファイルを管理するためのディレクトリ。 サブディレクトリの構成や管理する設定ファイルは、(4)と同様。 | 
| (4) | ローカル開発環境用の設定ファイルを管理するためのディレクトリ。 | 
| (5) | ローカル開発環境用のBean定義を行う。 | 
| (6) | ローカル開発環境用のプロパティを定義する。 WSDLのURLなど環境ごとに変更の可能性がある値を設定する。 | 
| (7) | ローカル開発環境用のログ出力定義を行う。 | 
5.3.3.4. wsimportについて¶
5.3.3.4.1. wsimportの使い道¶
5.3.3.4.2. wsimportの使い方¶
# (1)
wsimport -keep -p [出力するソースのパッケージ名] -s [出力するソースを格納する場所] [wsdlのURL]
| 項番 | 説明 | 
|---|---|
| (1) | wsimportの引数としてWSDLのURLを指定する。 オプションとして以下を使用する。 
 その他オプションについては、Java Platform, Standard Edition Tools Reference -Web Services(wsimport)-を参照されたい。 | 
Note
wsimportはデフォルトの挙動としてclassファイルのみが出力される。動かすだけなら問題はないが、デバッグなどを実行したい場合に備えkeepオプションを付けてソースも保存することを推奨する。
例えば、以下のようなコマンドとなる。
wsimport -keep -p com.example.ws.todo -s c:/tmp http://AAA.BBB.CCC.DDD:XXXX/soap-web/ws/TodoWebService?wsdl
作成されるソースは公開されているWebサービスに依存するが、本ガイドラインで使用している以下のJavaクラスが出力される。
- Webサービスインターフェース(ソース例ではTodoWebService.java)
- Domain Object(ソース例ではTodo.java)
Note
出力されるJavaクラスは上記以外にも出力される。出力されたソースのみでクライアントを作成可能なソースである。ただし、本ガイドラインではクライアントは、org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBeanを使用する方針であるため、その他のJavaクラスは使用しないことを推奨する。
5.3.3.5. Tomcat上でのWebサービス開発¶
CXFServletを使用する必要がある。- POJOでWebサービス実装クラスを記述する方式
- SpringBeanAutowiringSupportを継承してWebサービス実装クラスを作成する方式 (これまで説明してきた方法)
5.3.3.5.1. CXFServletを使用する場合の設定¶
CXFServletを使用するため、pom.xmlにライブラリの設定を記述する。
<!-- (1) -->
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.1.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.1.4</version>
</dependency>
| 項番 | 説明 | 
|---|---|
| (1) | CXFServletを使用するため、Apache CXFライブラリへの依存関係を追加する。 | 
次にweb.xmlにSOAP Web Serviceを受け付けるCXFServletを定義する。
<!-- (1) -->
<servlet>
    <servlet-name>cxfServlet</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <init-param>
        <param-name>config-location</param-name>
        <param-value>classpath:/META-INF/spring/cxf-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<!-- (2) -->
<servlet-mapping>
    <servlet-name>cxfServlet</servlet-name>
    <url-pattern>/ws/*</url-pattern>
</servlet-mapping>
| 項番 | 説明 | 
|---|---|
| (1) | org.apache.cxf.transport.servlet.CXFServletのサーブレット定義を行う。config-locationには、後述するcxf-servlet.xmlのパスを指定する。 | 
| (2) | 定義したサーブレットへのマッピングを定義する。この場合、コンテキスト名/ws配下にWebサービスが作成される。 | 
5.3.3.5.2. POJO方式で必要な設定¶
Webサービス実装クラスをエンドポイントとして設定する。
[server projectName]-web/src/main/resources/META-INF/spring/cxf-servlet.xml
<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:jaxws="http://cxf.apache.org/jaxws" xmlns:soap="http://cxf.apache.org/bindings/soap"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd
         http://cxf.apache.org/jaxws
         http://cxf.apache.org/schemas/jaxws.xsd
         http://cxf.apache.org/bindings/soap
         http://cxf.apache.org/schemas/configuration/soap.xsd">
    <!-- (1) -->
    <jaxws:endpoint id="todoWebEndpoint" implementor="#todoWebServiceImpl"
        address="/TodoWebService" />
</beans>
| 項番 | 説明 | 
|---|---|
| (1) | 公開するエンドポイントを定義する。 implementor属性に、DIコンテナに登録済みのWebサービスクラスのbean名(「#bean名」形式)を指定する。address属性にWebサービスを公開するアドレスを指定する。アドレスは、公開するエンドポイントのパス部分のみ記述する。 属性の詳細についてはApache CXF JAX-WS Configurationを参照されたい。 | 
TodoWebServiceImplをPOJOとして作成する。
[server projectName]-web/src/main/java/com/example/ws/todo/TodoWebServiceImpl.java
package com.example.ws.todo;
import java.util.List;
import javax.jws.HandlerChain;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import org.springframework.stereotype.Component;
import com.example.domain.model.Todo;
import com.example.domain.service.TodoService;
import com.example.ws.webfault.WebFaultException;
import com.example.ws.exception.WsExceptionHandler;
import com.example.ws.todo.TodoWebService;
// (1)
@Component
@WebService(
      portName = "TodoWebPort",
      serviceName = "TodoWebService",
      targetNamespace = "http://example.com/todo",
      endpointInterface = "com.example.ws.todo.TodoWebService")
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
// (2)
public class TodoWebServiceImpl implements TodoWebService {
    // omitted
}
| 項番 | 説明 | 
|---|---|
| (1) | @Componentを付けて、DIコンテナへの登録を行う。 | 
| (2) | コンポーネントスキャンにてDIコンテナへの登録が可能であるため、POJOとして作成する。つまり、 org.springframework.web.context.support.SpringBeanAutowiringSupportを継承する必要がなくなる。 | 
5.3.3.5.3. SpringBeanAutowiringSupportを継承する方式で必要な設定¶
CXFServlet用のBean定義ファイルに、SOAPのエンドポイントとなるクラス名およびアドレスを定義する。
[server projectName]-web/src/main/resources/META-INF/spring/cxf-servlet.xml
<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:jaxws="http://cxf.apache.org/jaxws" xmlns:soap="http://cxf.apache.org/bindings/soap"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd
         http://cxf.apache.org/jaxws
         http://cxf.apache.org/schemas/jaxws.xsd
         http://cxf.apache.org/bindings/soap
         http://cxf.apache.org/schemas/configuration/soap.xsd">
    <!-- (1) -->
    <jaxws:endpoint id="todoWebEndpoint" implementor="com.example.ws.todo.TodoWebServiceImpl"
        address="/TodoWebService" />
</beans>
| 項番 | 説明 | 
|---|---|
| (1) | 公開するエンドポイントを定義する。 implementor属性に公開するWebサービスの実装クラスを指定する。address属性にWebサービスを公開するアドレスを指定する。アドレスは、公開するエンドポイントのパス部分のみ記述する。 属性の詳細についてはApache CXF JAX-WS Configurationを参照されたい。 | 










