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つに統合された。