Metro JAX-WS

JAX-WSを利用して、WSDLファイルからソースを自動生成する方法についてですが、
通常、業務系のサービスの場合、WSDLを先に記述することが多いような気がするので、トップダウンで、サービスを開発することが前提になります。
サンプル用のWSDLを準備します。
今回は、サンプルなので、すべての定義をWSDLで行うことにします。
本当は、XML Schemaを定義した方がいいと思います。
リクエストとレスポンスの定義をtypesに下記の様に定義します。

<types>
  <schema xmlns="http://www.w3.org/2001/XMLSchema"
          targetNamespace="http://developer.co.jp/system/dev/schemas/service/message_1/getMessage"
          elementFormDefault="qualified">
          <!-- リクエスト用のスキーマ定義です。 -->
          <element name="messageRequest">
               <complexType>
                   <sequence>
                       <element name="messageId">
                            <simpleType>
                                 <restriction base="int">
                                      <totalDigits value="5"/>
                                 </restriction>
                            </simpleType>
                       </element>
                       <element name="actionDt" type="dateTime"/>
                   </sequence>
               </complexType>
         </element>
         <!-- レスポンス用のスキーマ定義です。 -->
         <element name="messageResponse">
               <complexType>
                   <sequence>
                     <element name="message">
                         <simpleType>
                             <restriction base="string">
                                 <minLength value="1"/>
                                 <maxLength value="1000"/>
                             </restriction>
                         </simpleType>
                     </element>
                 </sequence>
             </complexType>
         </element>
  </schema>
</types>

名前空間をdefinitionsに書いておきます。

<definitions 
    name="MessageService_1_0"
    targetNamespace="http://developer.co.jp/system/dev/wsdl/MessageService_1_0"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://developer.co.jp/system/dev/wsdl/MessageService_1_0"
    xmlns:getMsg="http://developer.co.jp/system/dev/schemas/service/message_1/getMessage">

上記の定義を利用して、メッセージ、ポートタイプ、バインディングを定義します。

<message name="messageRequest">
  <part name="messageRequest" element="getMsg:messageRequest"/>
</message>
<message name="messageResponse">
  <part name="messageResponse" element="getMsg:messageResponse"/>
</message>
<portType name="MessageService">
 <operation name="getMessage">
   <input name="messageRequest" message="tns:messageRequest"/>
   <output name="messageResponse" message="tns:messageResponse"/>
 </operation>
</portType>
<binding name="MessageService" type="tns:MessageService">
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  <operation name="getMessage">
     <soap:operation soapAction="http://developer.co.jp/system/dev/schemas/service/message_1/getMessage"/>
     <input name="messageRequest">
       <soap:body use="literal"/>
     </input>
     <output name="messageResponse">
       <soap:body use="literal"/>
     </output>
  </operation>
</binding>

最後に、サービスを定義します。

<service name="MessageService">
   <port name="MessageService" binding="tns:MessageService">
      <soap:address location="REPLACE_WITH_ACTUAL_URL"/>
   </port>
</service>

最終的に定義されたWSDLは、下記になります。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions 
    name="MessageService_1_0"
    targetNamespace="http://developer.co.jp/system/dev/wsdl/MessageService_1_0"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://developer.co.jp/system/dev/wsdl/MessageService_1_0"
    xmlns:getMsg="http://developer.co.jp/system/dev/schemas/service/message_1/getMessage">
  <types>
      <schema xmlns="http://www.w3.org/2001/XMLSchema"
             targetNamespace="http://developer.co.jp/system/dev/schemas/service/message_1/getMessage"
             elementFormDefault="qualified">
          <!-- リクエスト用のスキーマ定義です。 -->
          <element name="messageRequest">
               <complexType>
                   <sequence>
                       <element name="messageId">
                            <simpleType>
                                 <restriction base="int">
                                      <totalDigits value="5"/>
                                 </restriction>
                            </simpleType>
                       </element>
                       <element name="actionDt" type="dateTime"/>
                   </sequence>
               </complexType>
         </element>
         <!-- レスポンス用のスキーマ定義です。 -->
         <element name="messageResponse">
               <complexType>
                   <sequence>
                     <element name="message">
                         <simpleType>
                             <restriction base="string">
                                 <minLength value="1"/>
                                 <maxLength value="1000"/>
                             </restriction>
                         </simpleType>
                     </element>
                 </sequence>
             </complexType>
         </element>
   </schema>
 </types>
   <message name="messageRequest">
       <part name="messageRequest" element="getMsg:messageRequest"/>
  </message>
  <message name="messageResponse">
       <part name="messageResponse" element="getMsg:messageResponse"/>
   </message>
   <portType name="MessageService">
       <operation name="getMessage">
           <input name="messageRequest" message="tns:messageRequest"/>
           <output name="messageResponse" message="tns:messageResponse"/>
       </operation>
   </portType>
   <binding name="MessageService" type="tns:MessageService">
       <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
       <operation name="getMessage">
           <soap:operation soapAction="http://developer.co.jp/system/dev/schemas/service/message_1/getMessage"/>
           <input name="messageRequest">
               <soap:body use="literal"/>
           </input>
           <output name="messageResponse">
               <soap:body use="literal"/>
           </output>
       </operation>
   </binding>
   <service name="MessageService">
       <port name="MessageService" binding="tns:MessageService">
           <soap:address location="REPLACE_WITH_ACTUAL_URL"/>
       </port>
   </service>
</definitions>

次に、JAX-WSの実装を下記のサイトからダウンロードしてきます。

https://jax-ws.dev.java.net/2.1.7/

今回は、 Eclipseで開発します。
EclipseでDynamic Web projectを作成してください。
ディレクトリ構成ですが、下記の様な構成とします。
① ソース格納用 ⇒ src
② 自動生成ソース格納用 ⇒ gen
③ JAX-WSライブラリー格納用 ⇒ WebContent/WEB-INF/lib
④ WSDL格納用 ⇒ WebContent/WEB-INF/wsdl
⑤ ANTファイル ⇒
ダウンロードしたライブラリーを WebContent/WEB-INF/lib/の下にコピーしておきます。
作成したWSDLファイルは、WebContent/WEB-INF /wsdl/の下にコピーします。
次にbuild.xml(ANTです。)をプロジェクト直下に作成します。
作成したANTは、下記のような内容です。

<?xml version="1.0" encoding="UTF-8"?>
<project name="MessageService">
  <property name="build.lib" value="WebContent/WEB-INF/lib/"/>
  <property name="build.src" value="src"/>
  <property name="build.gen" value="gen"/>
  <property name="build.bin" value="bin"/>
  <property name="wsdl.dir" value="WebContent/WEB-INF/wsdl/MessageService_1_0.wsdl"/>

  <!-- CLASSPATHにJAX-WSのライブラリーパスを通しておく。 -->
  <path id="classpath">
      <fileset dir="${build.lib}">
          <include name="*.jar"/>
      </fileset>
  </path>

   <!-- wsimportタスクの定義 -->
  <taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport"> 
       <classpath refid="classpath"/>
  </taskdef>
  
  <target name="wsimport">
      <wsimport
            wsdl="${wsdl.dir}"
            destdir="${build.bin}"
            sourcedestdir="${build.gen}"
            keep="true"
            verbose="true"/>
  </target>
</project>

これで準備完了です。
後は、ANTタスクのwsimportを実行すれば、
genディレクトリに自動生成されたソースが作成されるはずです。
JAX-WSは、JAXBでのデータのバインディングがされます。
自動生成されたソースの中で、サービスの実装クラスがimplementsするインターフェイスが生成されています。
そのインターフェイスのパッケージは、WSDLのデフォルト名前空間がパッケージとしてマッピングされたパッケージ配下にあります。
今回は、jp.co.developer.system.dev.wsdl.messageservice_1_0のパッケージ配下にあるMessageServiceがそのインターフェイスになります。
下記がそのインターフェイスになります。

package jp.co.developer.system.dev.wsdl.messageservice_1_0;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;
import jp.co.developer.system.dev.schemas.service.message_1.getmessage.MessageRequest;
import jp.co.developer.system.dev.schemas.service.message_1.getmessage.MessageResponse;
import jp.co.developer.system.dev.schemas.service.message_1.getmessage.ObjectFactory;


/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.1.7-b01-
 * Generated source version: 2.1
 * 
 */
@WebService(name = "MessageService", targetNamespace = "http://developer.co.jp/system/dev/wsdl/MessageService_1_0")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@XmlSeeAlso({
    ObjectFactory.class
})
public interface MessageService {


    /**
     * 
     * @param messageRequest
     * @return
     *     returns jp.co.developer.system.dev.schemas.service.message_1.getmessage.MessageResponse
     */
    @WebMethod(action = "http://developer.co.jp/system/dev/schemas/service/message_1/getMessage")
    @WebResult(name = "messageResponse", targetNamespace = "http://developer.co.jp/system/dev/schemas/service/message_1/getMessage", partName = "messageResponse")
    public MessageResponse getMessage(
        @WebParam(name = "messageRequest", targetNamespace = "http://developer.co.jp/system/dev/schemas/service/message_1/getMessage", partName = "messageRequest")
        MessageRequest messageRequest);

}

上記のインターフェイスを見ると、getMessageのメソットが定義されています。
オペレーション名がそのままメソット名になっています。
また、リクエスト及び、レスポンスは、スキーマ定義した内容がマッピングされたクラスが使われます。
このインターフェイスを実装していきます。

package jp.co.developer.system.dev.wsdl.messageservice_1_0.logic.impl;

import javax.jws.WebMethod;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;

import jp.co.developer.system.dev.schemas.service.message_1.getmessage.MessageRequest;
import jp.co.developer.system.dev.schemas.service.message_1.getmessage.MessageResponse;
import jp.co.developer.system.dev.schemas.service.message_1.getmessage.ObjectFactory;
import jp.co.developer.system.dev.wsdl.messageservice_1_0.MessageService;
@WebService(name = "MessageService", targetNamespace = "http://developer.co.jp/system/dev/wsdl/MessageService_1_0")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@XmlSeeAlso({
    ObjectFactory.class
})
public class MessageServiceImpl implements MessageService {

	@WebMethod(action = "http://developer.co.jp/system/dev/schemas/service/message_1/getMessage")
	@WebResult(name = "messageResponse", targetNamespace = "http://developer.co.jp/system/dev/schemas/service/message_1/getMessage", partName = "messageResponse")
	public MessageResponse getMessage(MessageRequest messageRequest) {
		MessageResponse response = new MessageResponse();
		if (messageRequest.getMessageId() == 1) {
			response.setMessage("テストメッセージ1です。");
		} else {
			response.setMessage("テストメッセージ2です。");
		}
		return response;
	}
}

これで実装は、完了です。
なぜか?インターフェイスに定義されているアノテーションが実装クラスに引き継がれないみたいな?
GlassFishにデプロイしてみましたが、サービスとして認識されないようです。
今回は、実装クラスにも同様のアノテーションを追加しています。
これで、サービスとして認識されました。
デプロイには、web.xmlとsun-jaxws.xmlの2つの定義が必要になります。
まず、web.xmlですが、リスナーを定義します。
利用するリスナーは、下記のリスナーです。

<listener>
	<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>

次にサーブレットの設定をします。

<servlet>
	<servlet-name>MessageService_1_0</servlet-name>
	<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>MessageService_1_0</servlet-name>
	<url-pattern>/MessageService</url-pattern>
</servlet-mapping>

定義したweb.xmlは、下記になります。

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" 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">
	<display-name>MessageService</display-name>
	<listener>
		<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
	</listener>
	<servlet>
		<servlet-name>MessageService_1_0</servlet-name>
		<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>MessageService_1_0</servlet-name>
		<url-pattern>/MessageService</url-pattern>
	</servlet-mapping>
	<session-config>
		<session-timeout>60</session-timeout>
	</session-config>
</web-app>

sun-jaxws.xmlには、エンドポイントやインターフェイス及び実装クラスなでの定義を記述します。
記述内容は、下記の様になります。

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
    <endpoint
        name="MessageService"
        interface="jp.co.developer.system.dev.wsdl.messageservice_1_0.MessageService"
        implementation="jp.co.developer.system.dev.wsdl.messageservice_1_0.logic.impl.MessageServiceImpl"
        wsdl="WEB-INF/wsdl/MessageService_1_0.wsdl"
        service="{http://developer.co.jp/system/dev/wsdl/MessageService_1_0}MessageService"
        port="{http://developer.co.jp/system/dev/wsdl/MessageService_1_0}MessageService"
        url-pattern="/MessageService" />
</endpoints>

WARファイルを作成するタスクをANTファイルに追加します。
WARファイルを作成する下記のタスクを追加定義します。

	<target name="war">
		<mkdir dir="${build.dist}"/>
		<mkdir dir="${tmp.WebContent}"/>
		<mkdir dir="${tmp.src}"/>
		<mkdir dir="${tmp.classes}"/>
		<mkdir dir="${tmp.lib}"/>
		<mkdir dir="${tmp.wsdl}"/>
		<copy todir="${tmp.src}">
			<fileset dir="${build.src}"/>
			<fileset dir="${build.gen}"/>
		</copy>
		<copy todir="${tmp.lib}">
			<fileset dir="${build.lib}"/>
		</copy>
		<copy todir="${tmp.wsdl}">
			<fileset dir="${build.wsdl}"/>
		</copy>
		<copy file="WebContent/WEB-INF/web.xml" tofile="tmp/WebContent/WEB-INF/web.xml"/>
		<copy file="WebContent/WEB-INF/sun-jaxws.xml" tofile="tmp/WebContent/WEB-INF/sun-jaxws.xml"/>
		<javac
			srcdir="${tmp.src}"
			destdir="${tmp.classes}"
			source="1.6"
			encoding="UTF-8">
			<classpath>
				<path refid="classpath"/>
			</classpath>
		</javac>
		<jar destfile="${tmp.lib}/${project.name}_${version}_${revision}.jar" 
				basedir="${tmp.classes}"/>
		<war destfile="${build.dist}/${project.name}_${version}_${revision}.war" 
				basedir="tmp/WebContent" 
				webxml="tmp/WebContent/WEB-INF/web.xml">
			<webinf dir="tmp/WebContent/WEB-INF/" includes="sun-jaxws.xml"/>
		</war>
	</target>

これで、WARを作成して、GlassFishにデプロイします。
デプロイ後、http://localhost:8080/MessageService_1_0/MessageService?WSDLへアクセスして、WSDLが表示されれば成功です。