apache CXF+SpringでWebサービス作成時にWSDLを分離しない方法

よくあることだと思うけど、最近仕事でWebサービスのスタブが必要だったのでapache CXF+SpringでスタブWebサービスを作成してた。
それぞれのクラスや設定ファイルはこんな感じ。

◆サービスインタフェースクラス

package org.ws.sample.stub.service;

import javax.jws.WebService;

@WebService
public interface StubService {
	public String stubMethod(String data);
}

◆サービスクラス

package org.ws.sample.stub.service.impl;

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.jws.soap.SOAPBinding.Style;
import javax.jws.soap.SOAPBinding.Use;

import org.ws.sample.stub.service.StubService;

@WebService(
		serviceName = "StubService", 
		endpointInterface = "org.ws.sample.stub.service.StubService", 
		portName = "StubServicePort")
@SOAPBinding(
		style=Style.DOCUMENT,
		use=Use.LITERAL,
		parameterStyle=ParameterStyle.BARE
		)
public class StubServiceImpl implements StubService {
	public String stubMethod(String data) {
		return data;
	}
}

◆web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/applicationContext.xml</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<servlet>
		<servlet-name>CXFServlet</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>CXFServlet</servlet-name>
		<url-pattern>/ws/*</url-pattern>
	</servlet-mapping>

</web-app>

◆applicationContext.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-3.0.xsd
	        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<import resource="classpath:service-beans.xml" />
</beans>

◆service-bean.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:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

	<bean id="stubService" class="org.ws.sample.stub.service.impl.StubServiceImpl"/>
	<jaxws:endpoint implementor="#stubService" address="/StubService" >
	</jaxws:endpoint>
</beans>

◆pom.xml

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>org.ws.sample</groupId>
	<artifactId>stub</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>${project.artifactId}</name>

	<properties>
		<spring.version>3.0.5.RELEASE</spring.version>
		<cxf.version>2.4.1</cxf.version>
		<slf4j.version>1.6.1</slf4j.version>
		<cglib.version>2.2.2</cglib.version>
	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>maven-jetty-plugin</artifactId>
				<configuration>
					<contextPath>/webapp</contextPath>
					<scanIntervalSeconds>10</scanIntervalSeconds>
					<connectors>
						<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
							<port>8888</port>
							<maxIdleTime>6000</maxIdleTime>
						</connector>
					</connectors>
				</configuration>
			</plugin>
		</plugins>
	</build>
	
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-expression</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http</artifactId>
			<version>${cxf.version}</version>
			<scope>runtime</scope>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-core</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-expression</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-beans</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-aop</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-context</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-web</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxws</artifactId>
			<version>${cxf.version}</version>
		</dependency>

		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib-nodep</artifactId>
			<version>${cglib.version}</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-jdk14</artifactId>
			<version>${slf4j.version}</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
			<scope>runtime</scope>
		</dependency>
	</dependencies>

</project>

ちなみにMavenには「maven-jetty-plugin」を導入して

mvn jetty:run

でこのWebサービスが起動するようにしている。





そしてここで少し問題が発生。
この設定ではサービス定義(WSDL)はこんな感じになる。

<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions name="StubService" targetNamespace="http://impl.service.stub.sample.ws.org/" xmlns:ns1="http://service.stub.sample.ws.org/" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://impl.service.stub.sample.ws.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:import location="http://localhost:8888/webapp/ws/StubService?wsdl=StubService.wsdl" namespace="http://service.stub.sample.ws.org/">
    </wsdl:import>
  <wsdl:binding name="StubServiceSoapBinding" type="ns1:StubService">
 :
 :

なにが問題かというと、

<wsdl:import location="http://localhost:8888/webapp ・・・

の部分。
デフォルトの設定ではcomplexTypeとかportTypeとかが別WSDLへ分離されて定義されてしまうようだ。

これ自体は動作上何の問題もないんだが、システム検証の関係上、1つのWSDL統合したかった。
どうにか出来ないものかと考えていたところ
http://www.mail-archive.com/cxf-user@incubator.apache.org/msg04545.html
を発見、

If all the namespaces are the same, CXF should generate a WSDL that
doesn't use any wsdl imports. 

For the most part, add @WebService(targetNamespace = ".....")
annotations to both the SEI interface and the implementation making sure
the 
targetNamespace is the same for both.   That should allow both the 
logical stuff (interface/portType) and the physical
(impl/service/binding) to be in the same namespace and thus the same
wsdl.

との記述があった。
つまり、Webサービスインタフェースとその実装クラスに同じ「targetNamespace="....."」を指定すれば同じWSDLに定義が吐かれるだろうとのこと。

これを見習って、

◆サービスインタフェースクラス

package org.ws.sample.stub.service;

import javax.jws.WebService;

@WebService(
		serviceName = "StubService", 
		targetNamespace="http://impl.service.stub.sample.ws.org/")
public interface StubService {
	public String stubMethod(String data);
}

◆サービスクラス

package org.ws.sample.stub.service.impl;

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.jws.soap.SOAPBinding.Style;
import javax.jws.soap.SOAPBinding.Use;

import org.ws.sample.stub.service.StubService;

@WebService(
		serviceName = "StubService", 
		portName = "StubServicePort",
		targetNamespace="http://impl.service.stub.sample.ws.org/")
@SOAPBinding(
		style=Style.DOCUMENT,
		use=Use.LITERAL,
		parameterStyle=ParameterStyle.BARE
		)
public class StubServiceImpl implements StubService {
	public String stubMethod(String data) {
		return data;
	}
}

と修正した。

するとWSDL

<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions name="StubService" targetNamespace="http://impl.service.stub.sample.ws.org/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://impl.service.stub.sample.ws.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://impl.service.stub.sample.ws.org/" version="1.0" xmlns:tns="http://impl.service.stub.sample.ws.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 :
 :

のように無事1つに統合された。