spring-ws框架开发webservice服务

目录

  • 开发环境
  • 利用jaxb2的maven插件根据WSDL生成对应的POJO
  • 开发和配置endpoint
  • 配置web.xml
  • 启动servlet容器
  • 验证webservice服务的可用性
    •     检查wsdl
    •     访问webservice
  • 有关spring-ws实现和其他使用的问题
  • 参考资料

 

开发环境

 

 

 

开发环境
eclipse ide 4.3.2
spring 4.0.6
spring-ws-core 2.1.3

 

 开发环境说明:之前没了解过,以为只有spring4.x才有spring-ws的框架支持,后来看一下spring 3.x应该也是有对应版本的spring-ws,所以不一定版本需要用到这么新,也可以根据自己的情况酌情选择其他版本。

 

利用jaxb2的maven插件根据WSDL生成对应的POJO

 

创建项目目录:

 

mkdir -p webservice_sample/src/{main,test}/{java,resources}; mkdir -p webservice_sample/src/main/webapp

 

 

目录结构如下:

 

webservice_sample$ tree
.
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   ├── resources
│   │   └── webapp
│   └── test
│       ├── java
│       └── resources
└── target
    ├── classes
    ├── mvn-eclipse-cache.properties
    └── test-classes

 


在src/main/resources/创建目录 wsdl

 

在目录 src/main/resources/wsdl 当中下载WSDL

 

wget http://webservice.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?WSDL

 

 

进入wsdl目录后,将文件名中的问号改成句点,重命名为 IpAddressSearchWebService.asmx.WSDL

进入目录webservice_sample 编写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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>caesar.com</groupId>
	<artifactId>webservice_sample</artifactId>
	<packaging>jar</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>mock Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<properties>
		<java_source_version>1.6</java_source_version>
		<java_target_version>1.6</java_target_version>
		<maven_compiler_plugin_version>2.5.1</maven_compiler_plugin_version>
		<maven_jaxb2_version>0.9.0</maven_jaxb2_version>
		<maven_jaxb2_forceRegenerate>false</maven_jaxb2_forceRegenerate>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-ws-core</artifactId>
			<version>2.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-xml</artifactId>
			<version>2.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-ws-support</artifactId>
			<version>2.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>1.5.10</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.0.6.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.jdom</groupId>
			<artifactId>jdom2</artifactId>
			<version>2.0.5</version>
		</dependency>
		<dependency>
			<groupId>jaxen</groupId>
			<artifactId>jaxen</artifactId>
			<version>1.1.6</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.jetty.aggregate</groupId>
			<artifactId>jetty-all</artifactId>
			<version>7.2.0.v20101020</version>
		</dependency>

		<dependency>
			<groupId>dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>1.6.1</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.7</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.7</version>
		</dependency>
		<dependency>
			<groupId>apache-log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
		</dependency>
	</dependencies>

	<build>
		<resources>
			<resource>
				<directory>src/main/java/</directory>
			</resource>
			<resource>
				<directory>src/main/resources/</directory>
			</resource>
			<resource>
				<directory>src/main/webapp/</directory>
			</resource>
		</resources>
		<finalName>mock</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<id>copy</id>
						<phase>install</phase>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>7.2.0.v20101020</version>
				<configuration>
					<!-- specify jetty port -->
					<jettyConfig>${basedir}/src/main/resources/jetty.xml</jettyConfig>
				</configuration>
			</plugin>

			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>${maven_compiler_plugin_version}</version>
				<configuration>
					<source>${java_source_version}</source>
					<target>${java_target_version}</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>

			<!-- disable genereate java code from wsdl begin -->
			<!-- wsdl to java code for separate av provider -->
			<plugin>
				<groupId>org.jvnet.jaxb2.maven2</groupId>
				<artifactId>maven-jaxb2-plugin</artifactId>
				<version>${maven_jaxb2_version}</version>
				<executions>
					<execution>
						<id>alibaba_av</id>
						<goals>
							<goal>generate</goal>
						</goals>
						<configuration>
							<schemaLanguage>WSDL</schemaLanguage>
							<generateDirectory>${basedir}/src/main/java/</generateDirectory>
							<generatePackage>cn.com.webxml.webservice.wsdl.ipaddresssearch</generatePackage>
							<forceRegenerate>${maven_jaxb2_forceRegenerate}</forceRegenerate>
							<encoding>UTF-8</encoding>
							<schemas>
								<schema>
									<!-- <url>http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL</url> -->
									<url>file:${basedir}/src/main/resources/wsdl/IpAddressSearchWebService.asmx.WSDL</url>
								</schema>
							</schemas>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<!-- disable genereate java code from wsdl end -->
		</plugins>
	</build>
</project>

 

 

在 webservice_sample 目录中执行如下指令,创建eclipse工程

mvn eclipse:clean eclipse:eclipse -DdownloadSources=true

 

导入eclipse中,会看到如下截图:



 

包路径 cn.com.webxml.webservice.wsdl.ipaddresssearch 中的代码就是我们在pom当中如下这段声明生成的(具体配置方法可以参考插件所在的网站文档):

 

<plugin>
      <groupId>org.jvnet.jaxb2.maven2</groupId>
      <artifactId>maven-jaxb2-plugin</artifactId>
……
</plugin>

 

可能有细心的朋友会发现生成WSDL文档对应的pojo代码的声明内容当中注释了一段

 

<!-- <url>http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL</url> -->
 本来是打算用这个webservice来做示例,但是因为执行mvn eclipse:eclipse的时候发生了错误,大家可以自己试验一下看看问题在哪里(我暂时没有去排查这个原因,所以先存疑。 天真

 

开发和配置endpoint

前面只是准备好了wsdl和与xml的对应转换pojo而已,现在要看看如何开发endpoint

打开IpAddressSearchWebService.asmx.WSDL,如下图所示:



 

准备开发一个soap的方法 getCountryCityByIp,我们可以编写如下endpoint:

 

 

package cn.com.webxml.webservice.endpoint;

import java.util.Arrays;

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapMessage;

import cn.com.webxml.webservice.wsdl.ipaddresssearch.ArrayOfString;
import cn.com.webxml.webservice.wsdl.ipaddresssearch.GetCountryCityByIp;
import cn.com.webxml.webservice.wsdl.ipaddresssearch.GetCountryCityByIpResponse;

@Endpoint
public class IpAddrSearchEndpoint {

    private static final String NAMESPACE_URI = "http://WebXml.com.cn/";

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryCityByIp")
    @ResponsePayload
    public GetCountryCityByIpResponse getCountryCityByIp(@RequestPayload GetCountryCityByIp request,
                                                         SoapHeader soapHeader, SoapMessage soapMessage) {
        // output(soapMessage);

        GetCountryCityByIpResponse response = new GetCountryCityByIpResponse();
        ArrayOfString value = new ArrayOfString() {

            {
                this.string = Arrays.asList("hongdulasi", "nibo'er");
            }
        };
        response.setGetCountryCityByIpResult(value);

        return response;
    }

}
 

这里求简,在访问这个方法的时候,默认只反馈一个固定内容的字符串数组。

 

这里需要注意几点:

  1. 这个endpoint类的包路径(后面会用到)是 cn.com.webxml.webservice.endpoint
  2. 类头部上的@Endpoint标注
  3. 方法声明上的 @PayloadRoot 标注中的namespace和localPart分别就是wsdl中的targetNamespace和soap方法名称
  4. @ResponsePayload 和 @RequestPayload 这两个标注的用法,以及它们对应的数据类型就是此前通过maven插件对wsdl定义生成的java类

配置web.xml

虽然有了endpoint,我们依旧无法奔跑(run起来)我们的webservice的服务端,嗯,需要有一个servlet容器以及web.xml配置来衔接我们的spring容器以及endpoint类到运行时状态。

 

准备一个 web-ipaddresssearch.xml

 

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
	<display-name>Archetype Created Web Application</display-name>

	<servlet>
		<servlet-name>ipaddrsearch-spring-ws</servlet-name>
		<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>ipaddrsearch-spring-ws</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>

</web-app>

 

还有一个spring的配置文件 ipaddrsearch-spring-ws-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:sws="http://www.springframework.org/schema/web-services"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:component-scan base-package="cn.com.webxml.webservice.endpoint" /> <!-- 这里是让spring容器扫描这个包路径下的标注,这里就用到上面的endpoint所在的包路径了,当然可以指定更高一级的路径,扩大扫描的范围 -->

	<sws:annotation-driven />

	<sws:static-wsdl id="IpAddressSearchWebService" location="classpath:wsdl/IpAddressSearchWebService.asmx.WSDL"/> <!-- 这里是用来指定静态wsdl定义的配置 -->
</beans>

 

 

需要注意的内容:

 web-ipaddresssearch.xml 和 ipaddrsearch-spring-ws-servlet.xml 之间是有对应关系的。

 web-ipaddresssearch.xml 中的 “servlet-name”的内容就是 ipaddrsearch-spring-ws-servlet.xml 的前半部分。

配置endpoint的文件名称的命名规范可以看成是: <servlet-name>-servlet.xml  【其中<servlet-name>需要用你在web.xml当中配置的servlet-name的名称去替换】

 

启动servlet容器

 

好了都准备好了,该上servlet容器了,我才用了手写代码的笨办法,主要是少点配置,多点代码好调整一些。

 

package mock.webservice.server.main;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.webapp.WebAppContext;

public class RunJetty {

    private static final String JETTY_CONNECTOR_NAME = "webservice_connector";

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Server server = new Server();

        String currentPath = RunJetty.class.getResource("/").getPath();
        System.out.println("currentPath = " + currentPath);

        HandlerList handlerList = new HandlerList();

        SelectChannelConnector connector_8080 = new SelectChannelConnector();
        connector_8080.setPort(8080); // 端口号
        connector_8080.setMaxIdleTime(30000);
        connector_8080.setRequestHeaderSize(8192);
        connector_8080.setName(JETTY_CONNECTOR_NAME);
        server.addConnector(connector_8080);

        WebAppContext customerWebAppContext = new WebAppContext();
        customerWebAppContext.setDescriptor(String.format("%s/WEB-INF/web-ipaddresssearch.xml", currentPath));
        customerWebAppContext.setResourceBase(currentPath);
        customerWebAppContext.setContextPath("/ipaddress"); // context path
        customerWebAppContext.setConnectorNames(new String[] { JETTY_CONNECTOR_NAME });

        handlerList.addHandler(customerWebAppContext);

        server.setHandler(handlerList);

        server.start();
        server.join();
    }
}

 

直接启动RunJetty类,就能访问我们暴露的webservice的服务了。

 

验证webservice服务的可用性

 

检查wsdl

可以先通过

 

curl http://localhost:8080/ipaddress/IpAddressSearchWebService.wsdl

 

来查看wsdl定义。(注意:spring-ws框架的wsdl的访问路径的固定后缀就是wsdl,而其名称就是前面<sws:static-wsdl/> 中定义的id值)

 

访问webservice

 

首先访问官方的测试文档,打开URL: http://webservice.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?op=getCountryCityByIp

 

拷贝soap1.1中的内容,并稍作调整:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <getCountryCityByIp xmlns="http://WebXml.com.cn/">
      <theIpAddress>test test</theIpAddress>
    </getCountryCityByIp>
  </soap:Body>
</soap:Envelope>

 

将这段内容保存在 /tmp/ipaddrsearch.xml 中,而后在命令行下使用curl访问webservice

 

curl -H "Content-Type:text/xml;charset=utf-8" -d @/tmp/ipaddrsearch.xml http://localhost:8080/ipaddress/IpAddressSearchWebService.asmx > /tmp/xml.tmp;  xmllint --format /tmp/xml.tmp

 

可以得到反馈信息:

 

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <SOAP-ENV:Body>
    <ns2:getCountryCityByIpResponse xmlns:ns2="http://WebXml.com.cn/">
      <ns2:getCountryCityByIpResult>
        <ns2:string>hongdulasi</ns2:string>
        <ns2:string>nibo'er</ns2:string>
      </ns2:getCountryCityByIpResult>
    </ns2:getCountryCityByIpResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

我们期望的结果出现了。

 

需要注意的是:

其实我们的访问URL并没有特别的约束,其核心部分是:

 

curl -H "Content-Type:text/xml;charset=utf-8" -d @/tmp/ipaddrsearch.xml http://localhost:8080/ipaddress/IpAddressSearchWebService.asmx

 

这里的URL后面的“IpAddressSearchWebService.asmx”这段可以改成其他任何字符串都是ok的。

因为在 /tmp/ipaddrsearch.xml 当中的请求内容已经将webservice请求的namespace和soap方法说明的比较清楚了,spring-ws框架已经能够定位到我们所编写的endpoint类。

 

有关spring-ws实现和其他使用的问题

 

【实现】spring-ws是如何定位到endpoint类其中的方法的?

【使用】文中没有提到一个很常用的场景——soapheader进行权限验证应该如何实现?

 

参考资料

spring官方文档: http://docs.spring.io/spring-ws/docs/2.2.0.RELEASE/reference/htmlsingle/

 

如果需要观察webservice调用情况,可以通过tcpdump获取抓包的内容(比如文中端口是8080,网卡假定名称为eth1,操作系统为linux)写入一个固定为(比如下面指令的 /tmp/capture),则可以使用如下指令:

sudo tcpdump -i eth1 port 8080 -w /tmp/capture

这篇文档讲了如何将jdk的DomSource、String类型的xml文档输出成稍微有点缩进的样子(虽然不够pretty,但是也还好了)

http://stackoverflow.com/questions/139076/how-to-pretty-print-xml-from-java

自动拷贝依赖包(如果需要将文中的代码打包放到某台固定机器的话,会需要所有依赖包合并的到一起,方便启动)

http://www.ibm.com/developerworks/cn/java/j-5things13/

目录

 

开发环境

 

 

 

开发环境
eclipse ide 4.3.2
spring 4.0.6
spring-ws-core 2.1.3

 

 开发环境说明:之前没了解过,以为只有spring4.x才有spring-ws的框架支持,后来看一下spring 3.x应该也是有对应版本的spring-ws,所以不一定版本需要用到这么新,也可以根据自己的情况酌情选择其他版本。

 

利用jaxb2的maven插件根据WSDL生成对应的POJO

 

创建项目目录:

 

mkdir -p webservice_sample/src/{main,test}/{java,resources}; mkdir -p webservice_sample/src/main/webapp

 

 

目录结构如下:

 

webservice_sample$ tree
.
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   ├── resources
│   │   └── webapp
│   └── test
│       ├── java
│       └── resources
└── target
    ├── classes
    ├── mvn-eclipse-cache.properties
    └── test-classes

 


在src/main/resources/创建目录 wsdl

 

在目录 src/main/resources/wsdl 当中下载WSDL

 

wget http://webservice.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?WSDL

 

 

进入wsdl目录后,将文件名中的问号改成句点,重命名为 IpAddressSearchWebService.asmx.WSDL

进入目录webservice_sample 编写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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>caesar.com</groupId>
	<artifactId>webservice_sample</artifactId>
	<packaging>jar</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>mock Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<properties>
		<java_source_version>1.6</java_source_version>
		<java_target_version>1.6</java_target_version>
		<maven_compiler_plugin_version>2.5.1</maven_compiler_plugin_version>
		<maven_jaxb2_version>0.9.0</maven_jaxb2_version>
		<maven_jaxb2_forceRegenerate>false</maven_jaxb2_forceRegenerate>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-ws-core</artifactId>
			<version>2.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-xml</artifactId>
			<version>2.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-ws-support</artifactId>
			<version>2.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.ws</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>1.5.10</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.0.6.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.jdom</groupId>
			<artifactId>jdom2</artifactId>
			<version>2.0.5</version>
		</dependency>
		<dependency>
			<groupId>jaxen</groupId>
			<artifactId>jaxen</artifactId>
			<version>1.1.6</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.jetty.aggregate</groupId>
			<artifactId>jetty-all</artifactId>
			<version>7.2.0.v20101020</version>
		</dependency>

		<dependency>
			<groupId>dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>1.6.1</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.7</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.7</version>
		</dependency>
		<dependency>
			<groupId>apache-log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
		</dependency>
	</dependencies>

	<build>
		<resources>
			<resource>
				<directory>src/main/java/</directory>
			</resource>
			<resource>
				<directory>src/main/resources/</directory>
			</resource>
			<resource>
				<directory>src/main/webapp/</directory>
			</resource>
		</resources>
		<finalName>mock</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<id>copy</id>
						<phase>install</phase>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>7.2.0.v20101020</version>
				<configuration>
					<!-- specify jetty port -->
					<jettyConfig>${basedir}/src/main/resources/jetty.xml</jettyConfig>
				</configuration>
			</plugin>

			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>${maven_compiler_plugin_version}</version>
				<configuration>
					<source>${java_source_version}</source>
					<target>${java_target_version}</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>

			<!-- disable genereate java code from wsdl begin -->
			<!-- wsdl to java code for separate av provider -->
			<plugin>
				<groupId>org.jvnet.jaxb2.maven2</groupId>
				<artifactId>maven-jaxb2-plugin</artifactId>
				<version>${maven_jaxb2_version}</version>
				<executions>
					<execution>
						<id>alibaba_av</id>
						<goals>
							<goal>generate</goal>
						</goals>
						<configuration>
							<schemaLanguage>WSDL</schemaLanguage>
							<generateDirectory>${basedir}/src/main/java/</generateDirectory>
							<generatePackage>cn.com.webxml.webservice.wsdl.ipaddresssearch</generatePackage>
							<forceRegenerate>${maven_jaxb2_forceRegenerate}</forceRegenerate>
							<encoding>UTF-8</encoding>
							<schemas>
								<schema>
									<!-- <url>http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL</url> -->
									<url>file:${basedir}/src/main/resources/wsdl/IpAddressSearchWebService.asmx.WSDL</url>
								</schema>
							</schemas>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<!-- disable genereate java code from wsdl end -->
		</plugins>
	</build>
</project>

 

 

在 webservice_sample 目录中执行如下指令,创建eclipse工程

mvn eclipse:clean eclipse:eclipse -DdownloadSources=true

 

导入eclipse中,会看到如下截图:



 

包路径 cn.com.webxml.webservice.wsdl.ipaddresssearch 中的代码就是我们在pom当中如下这段声明生成的(具体配置方法可以参考插件所在的网站文档):

 

<plugin>
      <groupId>org.jvnet.jaxb2.maven2</groupId>
      <artifactId>maven-jaxb2-plugin</artifactId>
……
</plugin>

 

可能有细心的朋友会发现生成WSDL文档对应的pojo代码的声明内容当中注释了一段

 

<!-- <url>http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL</url> -->
 本来是打算用这个webservice来做示例,但是因为执行mvn eclipse:eclipse的时候发生了错误,大家可以自己试验一下看看问题在哪里(我暂时没有去排查这个原因,所以先存疑。 天真

 

开发和配置endpoint

前面只是准备好了wsdl和与xml的对应转换pojo而已,现在要看看如何开发endpoint

打开IpAddressSearchWebService.asmx.WSDL,如下图所示:



 

准备开发一个soap的方法 getCountryCityByIp,我们可以编写如下endpoint:

 

 

package cn.com.webxml.webservice.endpoint;

import java.util.Arrays;

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapMessage;

import cn.com.webxml.webservice.wsdl.ipaddresssearch.ArrayOfString;
import cn.com.webxml.webservice.wsdl.ipaddresssearch.GetCountryCityByIp;
import cn.com.webxml.webservice.wsdl.ipaddresssearch.GetCountryCityByIpResponse;

@Endpoint
public class IpAddrSearchEndpoint {

    private static final String NAMESPACE_URI = "http://WebXml.com.cn/";

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryCityByIp")
    @ResponsePayload
    public GetCountryCityByIpResponse getCountryCityByIp(@RequestPayload GetCountryCityByIp request,
                                                         SoapHeader soapHeader, SoapMessage soapMessage) {
        // output(soapMessage);

        GetCountryCityByIpResponse response = new GetCountryCityByIpResponse();
        ArrayOfString value = new ArrayOfString() {

            {
                this.string = Arrays.asList("hongdulasi", "nibo'er");
            }
        };
        response.setGetCountryCityByIpResult(value);

        return response;
    }

}
 

这里求简,在访问这个方法的时候,默认只反馈一个固定内容的字符串数组。

 

这里需要注意几点:

  1. 这个endpoint类的包路径(后面会用到)是 cn.com.webxml.webservice.endpoint
  2. 类头部上的@Endpoint标注
  3. 方法声明上的 @PayloadRoot 标注中的namespace和localPart分别就是wsdl中的targetNamespace和soap方法名称
  4. @ResponsePayload 和 @RequestPayload 这两个标注的用法,以及它们对应的数据类型就是此前通过maven插件对wsdl定义生成的java类

配置web.xml

虽然有了endpoint,我们依旧无法奔跑(run起来)我们的webservice的服务端,嗯,需要有一个servlet容器以及web.xml配置来衔接我们的spring容器以及endpoint类到运行时状态。

 

准备一个 web-ipaddresssearch.xml

 

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
	<display-name>Archetype Created Web Application</display-name>

	<servlet>
		<servlet-name>ipaddrsearch-spring-ws</servlet-name>
		<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>ipaddrsearch-spring-ws</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>

</web-app>

 

还有一个spring的配置文件 ipaddrsearch-spring-ws-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:sws="http://www.springframework.org/schema/web-services"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:component-scan base-package="cn.com.webxml.webservice.endpoint" /> <!-- 这里是让spring容器扫描这个包路径下的标注,这里就用到上面的endpoint所在的包路径了,当然可以指定更高一级的路径,扩大扫描的范围 -->

	<sws:annotation-driven />

	<sws:static-wsdl id="IpAddressSearchWebService" location="classpath:wsdl/IpAddressSearchWebService.asmx.WSDL"/> <!-- 这里是用来指定静态wsdl定义的配置 -->
</beans>

 

 

需要注意的内容:

 web-ipaddresssearch.xml 和 ipaddrsearch-spring-ws-servlet.xml 之间是有对应关系的。

 web-ipaddresssearch.xml 中的 “servlet-name”的内容就是 ipaddrsearch-spring-ws-servlet.xml 的前半部分。

配置endpoint的文件名称的命名规范可以看成是: <servlet-name>-servlet.xml  【其中<servlet-name>需要用你在web.xml当中配置的servlet-name的名称去替换】

 

启动servlet容器

 

好了都准备好了,该上servlet容器了,我才用了手写代码的笨办法,主要是少点配置,多点代码好调整一些。

 

package mock.webservice.server.main;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.webapp.WebAppContext;

public class RunJetty {

    private static final String JETTY_CONNECTOR_NAME = "webservice_connector";

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Server server = new Server();

        String currentPath = RunJetty.class.getResource("/").getPath();
        System.out.println("currentPath = " + currentPath);

        HandlerList handlerList = new HandlerList();

        SelectChannelConnector connector_8080 = new SelectChannelConnector();
        connector_8080.setPort(8080); // 端口号
        connector_8080.setMaxIdleTime(30000);
        connector_8080.setRequestHeaderSize(8192);
        connector_8080.setName(JETTY_CONNECTOR_NAME);
        server.addConnector(connector_8080);

        WebAppContext customerWebAppContext = new WebAppContext();
        customerWebAppContext.setDescriptor(String.format("%s/WEB-INF/web-ipaddresssearch.xml", currentPath));
        customerWebAppContext.setResourceBase(currentPath);
        customerWebAppContext.setContextPath("/ipaddress"); // context path
        customerWebAppContext.setConnectorNames(new String[] { JETTY_CONNECTOR_NAME });

        handlerList.addHandler(customerWebAppContext);

        server.setHandler(handlerList);

        server.start();
        server.join();
    }
}

 

直接启动RunJetty类,就能访问我们暴露的webservice的服务了。

 

验证webservice服务的可用性

 

检查wsdl

可以先通过

 

curl http://localhost:8080/ipaddress/IpAddressSearchWebService.wsdl

 

来查看wsdl定义。(注意:spring-ws框架的wsdl的访问路径的固定后缀就是wsdl,而其名称就是前面<sws:static-wsdl/> 中定义的id值)

 

访问webservice

 

首先访问官方的测试文档,打开URL: http://webservice.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?op=getCountryCityByIp

 

拷贝soap1.1中的内容,并稍作调整:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <getCountryCityByIp xmlns="http://WebXml.com.cn/">
      <theIpAddress>test test</theIpAddress>
    </getCountryCityByIp>
  </soap:Body>
</soap:Envelope>

 

将这段内容保存在 /tmp/ipaddrsearch.xml 中,而后在命令行下使用curl访问webservice

 

curl -H "Content-Type:text/xml;charset=utf-8" -d @/tmp/ipaddrsearch.xml http://localhost:8080/ipaddress/IpAddressSearchWebService.asmx > /tmp/xml.tmp;  xmllint --format /tmp/xml.tmp

 

可以得到反馈信息:

 

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <SOAP-ENV:Body>
    <ns2:getCountryCityByIpResponse xmlns:ns2="http://WebXml.com.cn/">
      <ns2:getCountryCityByIpResult>
        <ns2:string>hongdulasi</ns2:string>
        <ns2:string>nibo'er</ns2:string>
      </ns2:getCountryCityByIpResult>
    </ns2:getCountryCityByIpResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

我们期望的结果出现了。

 

需要注意的是:

其实我们的访问URL并没有特别的约束,其核心部分是:

 

curl -H "Content-Type:text/xml;charset=utf-8" -d @/tmp/ipaddrsearch.xml http://localhost:8080/ipaddress/IpAddressSearchWebService.asmx

 

这里的URL后面的“IpAddressSearchWebService.asmx”这段可以改成其他任何字符串都是ok的。

因为在 /tmp/ipaddrsearch.xml 当中的请求内容已经将webservice请求的namespace和soap方法说明的比较清楚了,spring-ws框架已经能够定位到我们所编写的endpoint类。

 

有关spring-ws实现和其他使用的问题

 

【实现】spring-ws是如何定位到endpoint类其中的方法的?

【使用】文中没有提到一个很常用的场景——soapheader进行权限验证应该如何实现?

 

参考资料

spring官方文档: http://docs.spring.io/spring-ws/docs/2.2.0.RELEASE/reference/htmlsingle/

 

如果需要观察webservice调用情况,可以通过tcpdump获取抓包的内容(比如文中端口是8080,网卡假定名称为eth1,操作系统为linux)写入一个固定为(比如下面指令的 /tmp/capture),则可以使用如下指令:

sudo tcpdump -i eth1 port 8080 -w /tmp/capture

这篇文档讲了如何将jdk的DomSource、String类型的xml文档输出成稍微有点缩进的样子(虽然不够pretty,但是也还好了)

http://stackoverflow.com/questions/139076/how-to-pretty-print-xml-from-java

自动拷贝依赖包(如果需要将文中的代码打包放到某台固定机器的话,会需要所有依赖包合并的到一起,方便启动)

http://www.ibm.com/developerworks/cn/java/j-5things13/

猜你喜欢

转载自gkbusy.iteye.com/blog/2104448
今日推荐