记录一下springboot整合webservice遇到的问题:
首先,项目需求是,客户那边希望通过webservice来发送数据到本地的服务器,而在本地是希望使用springboot框架,打成的war包在tomcat中启动服务。所以在这里就准备使用springboot整合webservice来完成这一需求。
在网上找了大部分的资料,大多都是关于用webservice的CXF框架,打成war包之后,在运行过程中项目直接启动失败,报了这样一个问题:
java.lang.ClassNotFoundException:com.sun.xml.ws.transport.http.servlet.WSServletContextListener
查了很多原因,发现都是与依赖相关,将相关jar包导入之后仍然报各种问题,所以此方法舍弃。
查看到springboot官网中https://spring.io/guides/gs/consuming-web-service/ 与spring-ws的列子,尝试使用此方法,由于没有跑demo,直接照着上面用了,主要的两个类如下:
首先,schema文件(xsd) service.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://webservice.server.test.com"
targetNamespace="http://webservice.server.test.com" elementFormDefault="qualified">
<xs:element name="getUserInfoRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="type" type="xs:string"/>
<xs:element name="value" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getUserInfoResponse" type="xs:string"/>
</xs:schema>
WebServiceConfig.java
package com.test.server.webservice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
/**
*
* @author wyj
*/
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private final static Logger LOGGER = LoggerFactory.getLogger(WebServiceConfig.class);
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
@Bean(name = "test")
public DefaultWsdl11Definition defaultWsdl11Definition() {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("ServicePort");
wsdl11Definition.setLocationUri("/ws/test.wsdl");
wsdl11Definition.setSchema(serviceSchema());
return wsdl11Definition;
}
@Bean
public XsdSchema serviceSchema() {
return new SimpleXsdSchema(new ClassPathResource("service.xsd"));
}
}
ServiceEndpoint.java
package com.test.server.webservice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
/**
*
* @author wyj
*/
@Endpoint
public class ServiceEndpoint {
private final static Logger LOGGER = LoggerFactory.getLogger(ServiceEndpoint.class);
private static final String NAMESPACE_URI = "http://webservice.server.test.com";
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getUserInfo")
@ResponsePayload
public String getUserInfo(@RequestPayload GetUserInfoRequest request) {
LOGGER.info("getUserInfo type:[{}],value [{}]", request.getType(), request.getValue());
String dataValue = Integration.getInstance().getWebServiceResult(request.getType(), request.getValue());
LOGGER.info("getUserInfo result:[{}]", dataValue);
return dataValue ;
}
}
项目可以正常运行,浏览器打开能够正常访问http://localhost:8080/test/ws/test.wsdl
于是跑了个客户端的java项目,代码如下:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.test;
import com.ankon.his.integration.server.webservice.GetUserInfoRequest;
import com.ankon.his.integration.server.webservice.GetUserInfoResponse;
import com.ankon.his.integration.server.webservice.ServicePort;
import com.ankon.his.integration.server.webservice.ServicePortService;
import java.net.MalformedURLException;
import java.net.URL;
/**
*
* @author wyj
*/
public class NewClass {
public static void main(String args[]) throws MalformedURLException {
ServicePortService service = new ServicePortService(new URL("http://localhost:81/test/ws/test.wsdl"));
GetUserInfoRequest request = new GetUserInfoRequest ();
request.setType("aaa");
request.setValue("aaa");
ServicePort servicePort = service.getServicePortSoap11();
System.err.println(servicePort );
String response = ankonService.getUserInfo(request);
System.err.println(response );
}
}
报如下问题:
JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e: Stub for http://localhost:81/test/ws/test.wsdl
Exception in thread "main" com.sun.xml.internal.ws.client.ClientTransportException: 服务器发送了 HTTP 状态代码 404: null
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.checkStatusCode(HttpTransportPipe.java:310)
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.createResponsePacket(HttpTransportPipe.java:259)
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:217)
at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java:130)
at com.sun.xml.internal.ws.transport.DeferredTransportPipe.processRequest(DeferredTransportPipe.java:95)
at com.sun.xml.internal.ws.api.pipe.Fiber.__doRun(Fiber.java:1121)
网上找了很多地方,没有找到解决方案,于是与官网上的demo仔细比对,发现ServiceEndpoint.java中localPart 设置为getUserInfo没有与xsd文件中的定义的request对应,导致了这一问题,修改为getUserInfoRequest问题解决。
继续调试,遇到了新的问题:
JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e: Stub for http://localhost:81/test/ws/test.wsdl
Exception in thread "main" com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client received SOAP Fault from server: No adapter for endpoint [public java.lang.String com.ankon.test.server.webservice.ServiceEndpoint.getUserInfo(com.ankon.test.server.webservice.GetUserInfoRequest)]: Is your endpoint annotated with @Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint? Please see the server log to find more detail regarding exact cause of the failure.
at com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:178)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:116)
at com.sun.xml.internal.ws.client.sei.StubHandler.readResponse(StubHandler.java:238)
at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:189)
at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:276)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:104)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:77)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:147)
at com.sun.proxy.$Proxy30.getUserInfo(Unknown Source)
再回过头来运行官网的demo并无此问题,多次尝试后,发现与返回值有关:
定义的方法getUserInfo返回值为String,而官网上不管是request还是response都是定义了一个简单对象,所有再次尝试,将String的返回值重新包装成一个对象,修改了xsd文件及ServiceEndpoint,问题解决。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://webservice.server.test.com"
targetNamespace="http://webservice.server.test.com" elementFormDefault="qualified">
<xs:element name="getUserInfoRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="type" type="xs:string"/>
<xs:element name="value" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getUserInfoResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="data" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getUserInfoRequest")
@ResponsePayload
public GetUserInfoResponse getUserInfo(@RequestPayload GetUserInfoRequest request) {
LOGGER.info("getUserInfo type:[{}],value [{}]", request.getType(), request.getValue());
GetUserInfoResponse data = new GetUserInfoResponse ();
String dataValue = Integration.getInstance().getWebServiceResult(request.getType(), request.getValue());
LOGGER.info("getUserInfo result:[{}]", dataValue);
data.setData(dataValue);
return data;
}