JAX-WSでの例外

JAX-WSを使用したWebサービスでサーバ側で例外を投げる場合にはどうすべきなのか試してみました。とりあえず、JavaのコードからWSDLを自動生成してWebサービスを構築した場合(ボトムアップアプローチ?)で試します。JAX-WSWSDLの仕様を読むべきだと思いますが、ほとんど読んでません。ごめんなさい。

この前、入力された名前を返すだけのHelloというWebサービスをサンプルとして使いましたが(Tomcat+JAX-WSでWebサービス)、これに例外を投げるメソッドを追加しました。

package com.azuki3.study.ws;

import javax.jws.WebService;

@WebService
public class Hello {
	public String sayHello(String name){
		System.out.println(name);
		return "Hello " + name + "!";
	}

	public String sayHello2(String name) throws IllegalArgumentException{
		return "Hello " + name + "!";
	}

	public String sayHello3(String name) throws HelloException{
		return "Hello " + name + "!";
	}
}

sayHello2では、IllegalArgumentExceptionを、sayHello3では、Exceptionを継承したHelloExceptionを投げます(宣言だけですが)。HelloExceptionは以下のように、codeとnameというプロパティがあります。

package com.azuki3.study.ws;

public class HelloException extends Exception {
	private String code;
	private String name;

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

これらのJavaコードから自動生成されるWSDLは以下のようになります。例外に対応したSOAPのFaultを自動で追加してくれるようです。

<?xml version="1.0" encoding="UTF-8"?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. 
	RI's version is JAX-WS RI 2.1.4-b01-. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. 
	RI's version is JAX-WS RI 2.1.4-b01-. -->
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://ws.study.azuki3.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://ws.study.azuki3.com/"
	name="HelloService">
	<types>
		<xsd:schema>
			<xsd:import namespace="http://ws.study.azuki3.com/"
				schemaLocation="http://localhost:8080/jaxws-sample/hello.ws?xsd=1" />
		</xsd:schema>
	</types>
	<message name="sayHello">
		<part name="parameters" element="tns:sayHello" />
	</message>
	<message name="sayHelloResponse">
		<part name="parameters" element="tns:sayHelloResponse" />
	</message>
	<message name="sayHello2">
		<part name="parameters" element="tns:sayHello2" />
	</message>
	<message name="sayHello2Response">
		<part name="parameters" element="tns:sayHello2Response" />
	</message>
	<message name="IllegalArgumentException">
		<part name="fault" element="tns:IllegalArgumentException" />
	</message>
	<message name="sayHello3">
		<part name="parameters" element="tns:sayHello3" />
	</message>
	<message name="sayHello3Response">
		<part name="parameters" element="tns:sayHello3Response" />
	</message>
	<message name="HelloException">
		<part name="fault" element="tns:HelloException" />
	</message>
	<portType name="Hello">
		<operation name="sayHello">
			<input message="tns:sayHello" />
			<output message="tns:sayHelloResponse" />
		</operation>
		<operation name="sayHello2">
			<input message="tns:sayHello2" />
			<output message="tns:sayHello2Response" />
			<fault message="tns:IllegalArgumentException" name="IllegalArgumentException" />
		</operation>
		<operation name="sayHello3">
			<input message="tns:sayHello3" />
			<output message="tns:sayHello3Response" />
			<fault message="tns:HelloException" name="HelloException" />
		</operation>
	</portType>
	<binding name="HelloPortBinding" type="tns:Hello">
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
			style="document" />
		<operation name="sayHello">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
		</operation>
		<operation name="sayHello2">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
			<fault name="IllegalArgumentException">
				<soap:fault name="IllegalArgumentException" use="literal" />
			</fault>
		</operation>
		<operation name="sayHello3">
			<soap:operation soapAction="" />
			<input>
				<soap:body use="literal" />
			</input>
			<output>
				<soap:body use="literal" />
			</output>
			<fault name="HelloException">
				<soap:fault name="HelloException" use="literal" />
			</fault>
		</operation>
	</binding>
	<service name="HelloService">
		<port name="HelloPort" binding="tns:HelloPortBinding">
			<soap:address location="http://localhost:8080/jaxws-sample/hello.ws" />
		</port>
	</service>
</definitions>

同じく自動生成されるXMLスキーマは以下のようになります。faultについては、IllegalArgumentException(Excptionも同様)の場合にはmessageが、独自例外の場合にはそれに加え作成したプロパティが利用できるようです(getterが存在するものが生成される?)。

<?xml version="1.0" encoding="UTF-8"?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net.
	RI's version is JAX-WS RI 2.1.4-b01-. -->
<xs:schema xmlns:tns="http://ws.study.azuki3.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
	version="1.0" targetNamespace="http://ws.study.azuki3.com/">

	<xs:element name="HelloException" type="tns:HelloException" />

	<xs:element name="IllegalArgumentException" type="tns:IllegalArgumentException" />

	<xs:element name="sayHello" type="tns:sayHello" />

	<xs:element name="sayHello2" type="tns:sayHello2" />

	<xs:element name="sayHello2Response" type="tns:sayHello2Response" />

	<xs:element name="sayHello3" type="tns:sayHello3" />

	<xs:element name="sayHello3Response" type="tns:sayHello3Response" />

	<xs:element name="sayHelloResponse" type="tns:sayHelloResponse" />

	<xs:complexType name="sayHello2">
		<xs:sequence>
			<xs:element name="arg0" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="sayHello2Response">
		<xs:sequence>
			<xs:element name="return" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="IllegalArgumentException">
		<xs:sequence>
			<xs:element name="message" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="sayHello3">
		<xs:sequence>
			<xs:element name="arg0" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="sayHello3Response">
		<xs:sequence>
			<xs:element name="return" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="HelloException">
		<xs:sequence>
			<xs:element name="code" type="xs:string" minOccurs="0" />
			<xs:element name="message" type="xs:string" minOccurs="0" />
			<xs:element name="name" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="sayHello">
		<xs:sequence>
			<xs:element name="arg0" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="sayHelloResponse">
		<xs:sequence>
			<xs:element name="return" type="xs:string" minOccurs="0" />
		</xs:sequence>
	</xs:complexType>
</xs:schema>

特に当たり前の内容になってしまいまし。でも簡単でいいですね。

例外を投げるとどうなるのか?

サーバで実際に例外を発生させてレスポンスのSOAPをtcpmonで見てみました。この場合のレスポンスコードは500です。

<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Body>
		<S:Fault xmlns:ns3="http://www.w3.org/2003/05/soap-envelope">
			<faultcode>S:Server</faultcode>
			<faultstring>com.azuki3.study.ws.HelloException</faultstring>
			<detail>
				<ns2:HelloException xmlns:ns2="http://ws.study.azuki3.com/">
					<code>1</code>
					 <name>world</name>
				</ns2:HelloException>
				<ns2:exception xmlns:ns2="http://jax-ws.dev.java.net/"
					class="com.azuki3.study.ws.HelloException"
					note="To disable this feature, set com.sun.xml.ws.fault.SOAPFaultBuilder.disableCaptureStackTrace system property to false">
					<ns2:stackTrace>
					<!-- ここにスタックトレース-->
					</ns2:stackTrace>
				</ns2:exception>
			</detail>
		</S:Fault>
	</S:Body>
</S:Envelope>

非チェック例外(NPE)の場合。

<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Body>
		<S:Fault xmlns:ns3="http://www.w3.org/2003/05/soap-envelope">
			<faultcode>S:Server</faultcode>
			<faultstring>java.lang.NullPointerException</faultstring>
			<detail>
				<ns2:exception xmlns:ns2="http://jax-ws.dev.java.net/"
					class="java.lang.NullPointerException"
					note="To disable this feature, set com.sun.xml.ws.fault.SOAPFaultBuilder.disableCaptureStackTrace system property to false">
					<ns2:stackTrace>
					<!-- ここにスタックトレース-->						
					</ns2:stackTrace>
				</ns2:exception>
			</detail>
		</S:Fault>
	</S:Body>
</S:Envelope>

detailの中身が変わってますね。例外クラス名が入っていますが、クライアントがJavaの場合はよいですが、.net系だったりすることを考えるとどうなのでしょうか?他言語からアクセスするサンプルとかも試してみたいところです。

参考

このあたりの話題はdeveloperWorksが充実してますね。今回のエントリは一つめのURLに書いてある内容を試しています。