我正在使用CXF从WSDL开发Web服务,我希望能够使用JUnit运行测试样本请求。
My Test类创建一个模拟服务器,然后充当客户端来测试服务器。
我不明白为什么我会继续Unmarshalling Error: unexpected element
例外。
我将问题分解为一个最小的项目,但即便如此也无济于事。我确信问题来自于我做过的一些愚蠢的错误......已经有两天了,我找不到它......
错误消息为:org.apache.cxf.interceptor.Fault: Unmarshalling Error: unexpected element (uri:"http://my.project.service", local:"myProperty"). Expected elements are <{http://my.project.service}myObject>
就好像它期待myObject
内的其他myObject
元素...所有东西都是使用CXF生成的,为什么它不能解组几毫秒之前它只是编组的东西?
我试过soap 1.1 / 1.2,我尝试了不同版本的CXF高达3.0.5,我尝试使用命名空间,但总是出现同样的错误。
以下是重现错误的最小项目的全部内容:
<?xml version="1.0" ?>
<definitions targetNamespace="http://my.project.service"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:tns="http://my.project.service">
<types>
<xsd:schema attributeFormDefault="qualified"
elementFormDefault="qualified" targetNamespace="http://my.project.service"
xmlns:tns="http://my.project.service" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="MyObject">
<xsd:sequence>
<xsd:element name="myProperty" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="MyOperationRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="myObject" type="tns:MyObject" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="MyOperationResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="myMessage" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</types>
<message name="MyOperationResponse">
<part element="tns:MyOperationResponse" name="parameters" />
</message>
<message name="MyOperationRequest">
<part element="tns:MyOperationRequest" name="parameters" />
</message>
<portType name="MyServicePortType">
<operation name="MyOperation">
<input message="tns:MyOperationRequest" name="MyOperationRequest" />
<output message="tns:MyOperationResponse" name="MyOperationResponse" />
</operation>
</portType>
<binding name="MyServiceBinding" type="tns:MyServicePortType">
<soap12:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="MyOperation">
<soap12:operation soapAction="" style="document" />
<input name="MyOperationRequest">
<soap12:body parts="parameters" use="literal" />
</input>
<output name="MyOperationResponse">
<soap12:body parts="parameters" use="literal" />
</output>
</operation>
</binding>
<service name="MyService">
<port binding="tns:MyServiceBinding" name="MyServicePort">
<soap12:address location="http://my-server:my-port/" />
</port>
</service>
</definitions>
依赖项的pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<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>my.project</groupId>
<artifactId>project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>My project</name>
<description>My project description</description>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
<version>2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>2.5.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
</plugin>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated-sources/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${project.basedir}/src/main/resources/wsdl/my-service.wsdl</wsdl>
<extraargs>
<extraarg>-p</extraarg>
<extraarg>http://my.project.service=my.project.service</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
实施:
package my.project.service;
@javax.jws.WebService(serviceName = "MyService", portName = "MyServicePort",
targetNamespace = "http://my.project.service",
wsdlLocation = "src/main/resources/wsdl/my-service.wsdl",
endpointInterface = "my.project.service.MyServicePortType")
public class MyServicePortTypeImpl implements MyServicePortType {
public my.project.service.MyOperationResponse myOperation(MyOperationRequest parameters) {
MyOperationResponse myOperationResponse = new MyOperationResponse();
myOperationResponse.setMyMessage("YOUPI!");
return myOperationResponse;
}
}
测试类:
package my.project.test;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.frontend.ServerFactoryBean;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import my.project.service.MyService;
import my.project.service.MyServicePortTypeImpl;
import my.project.service.MyObject;
import my.project.service.ObjectFactory;
import my.project.service.MyServicePortType;
import my.project.service.MyOperationRequest;
import my.project.service.MyOperationResponse;
public class MyWebServiceClientTest {
private static final Log LOG = LogFactory.getLog(MyWebServiceClientTest.class);
private static Server myMockServer;
private static final String MY_MOCK_SERVICE_ADDRESS = "http://localhost:9091/MyService";
private static final String MY_WSDL_FILE_PATH = "classpath:wsdl/my-service.wsdl";
private static final String MY_NAMESPACE = "http://my.project.service";
private static final QName MY_SERVICE_QNAME = new QName(MY_NAMESPACE, "MyService");
private static final QName MY_SERVICE_PORT_QNAME = new QName(MY_NAMESPACE, "MyServicePort");
public static Server createMockServer(String mockWebServiceAddress, boolean logging) {
ServerFactoryBean serverFactoryBean = new ServerFactoryBean();
serverFactoryBean.setAddress(mockWebServiceAddress);
serverFactoryBean.setServiceClass(MyServicePortTypeImpl.class);
serverFactoryBean.setWsdlLocation(MY_WSDL_FILE_PATH);
serverFactoryBean.setServiceName(MY_SERVICE_QNAME);
serverFactoryBean.setEndpointName(MY_SERVICE_PORT_QNAME);
Server server = serverFactoryBean.create();
if (logging) {
Endpoint endpoint = server.getEndpoint();
LoggingInInterceptor loggingInInterceptor = new LoggingInInterceptor();
loggingInInterceptor.setPrettyLogging(true);
endpoint.getBinding().getInInterceptors().add(loggingInInterceptor);
LoggingOutInterceptor loggingOutInterceptor = new LoggingOutInterceptor();
loggingOutInterceptor.setPrettyLogging(true);
endpoint.getBinding().getOutInterceptors().add(loggingOutInterceptor);
}
return server;
}
@BeforeClass
public static void setUp() throws FileNotFoundException, IOException {
myMockServer = createMockServer(MY_MOCK_SERVICE_ADDRESS, true);
LOG.info("Starting my mock server on " + MY_MOCK_SERVICE_ADDRESS);
myMockServer.start();
}
@AfterClass
public static void tearDown() {
try {
if (myMockServer != null) {
LOG.info("Stopping my mock server");
myMockServer.stop();
}
} catch (Throwable t) {
LOG.error("Could not stop my mock server: ", t);
}
}
@Test
public void testMedwsClientSOAPCallWorks() {
URL wsdlURL = null;
try {
wsdlURL = new URL(MY_MOCK_SERVICE_ADDRESS + "?wsdl");
} catch (MalformedURLException e) {
LOG.error("Could not create the URL for MED WSDL", e);
return;
}
MyService myService = new MyService(wsdlURL, MY_SERVICE_QNAME);
MyServicePortType port = myService.getMyServicePort();
LOG.info("Invoking myOperation...");
MyOperationRequest myOperationRequest = new MyOperationRequest();
ObjectFactory objectFactory = new ObjectFactory();
MyObject myObject = objectFactory.createMyObject();
myObject.setMyProperty("Go go go!");
myOperationRequest.setMyObject(myObject);
MyOperationResponse myOperationResponse = port.myOperation(myOperationRequest);
LOG.info(myOperationResponse.getMyMessage());
}
}
跟踪:
31 juil. 2015 17:55:38 org.apache.cxf.service.factory.ReflectionServiceFactoryBean checkServiceClassAnnotations
ATTENTION: A JAX-WS Annotation was found on my.project.service.MyServicePortTypeImpl while using the Simple frontend. For better results, use the JAX-WS frontend.
31 juil. 2015 17:55:38 org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://my.project.service}MyService from WSDL: classpath:wsdl/my-service.wsdl
31 juil. 2015 17:55:39 org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be http://localhost:9091/MyService
2015-07-31 17:55:39.054:INFO:oejs.Server:jetty-7.5.3.v20111011
2015-07-31 17:55:39.085:INFO:oejs.AbstractConnector:Started SelectChannelConnector@localhost:9091 STARTING
2015-07-31 17:55:39.101:INFO:oejsh.ContextHandler:started o.e.j.s.h.ContextHandler{,null}
2015-07-31 17:55:39 INFO MyWebServiceClientTest:73 - Starting my mock server on http://localhost:9091/MyService
31 juil. 2015 17:55:39 org.apache.cxf.services.MyService.MyServicePort.MyServicePortType
INFO: Inbound Message
----------------------------
ID: 1
Address: http://localhost:9091/MyService?wsdl
Encoding: UTF-8
Http-Method: GET
Content-Type: text/xml
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], content-type=[text/xml], Host=[localhost:9091], Pragma=[no-cache], User-Agent=[Apache CXF 2.5.1]}
--------------------------------------
31 juil. 2015 17:55:39 org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://my.project.service}MyService from WSDL: http://localhost:9091/MyService?wsdl
2015-07-31 17:55:39 INFO MyWebServiceClientTest:102 - Invoking myOperation...
31 juil. 2015 17:55:39 org.apache.cxf.services.MyService.MyServicePort.MyServicePortType
INFO: Inbound Message
----------------------------
ID: 2
Address: http://localhost:9091/MyService
Encoding: UTF-8
Http-Method: POST
Content-Type: application/soap+xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[237], content-type=[application/soap+xml; charset=UTF-8], Host=[localhost:9091], Pragma=[no-cache], User-Agent=[Apache CXF 2.5.1]}
Payload: <?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<MyOperationRequest xmlns="http://my.project.service">
<myObject>
<myProperty>Go go go!</myProperty>
</myObject>
</MyOperationRequest>
</soap:Body>
</soap:Envelope>
--------------------------------------
31 juil. 2015 17:55:39 org.apache.cxf.phase.PhaseInterceptorChain doDefaultLogging
ATTENTION: Interceptor for {http://my.project.service}MyService#{http://my.project.service}MyOperation has thrown exception, unwinding now
org.apache.cxf.interceptor.Fault: Unmarshalling Error: unexpected element (uri:"http://my.project.service", local:"myProperty"). Expected elements are <{http://my.project.service}myObject>
at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:823)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:644)
at org.apache.cxf.jaxb.io.DataReaderImpl.read(DataReaderImpl.java:156)
at org.apache.cxf.interceptor.DocLiteralInInterceptor.getPara(DocLiteralInInterceptor.java:260)
at org.apache.cxf.interceptor.DocLiteralInInterceptor.handleMessage(DocLiteralInInterceptor.java:127)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:123)
at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.serviceRequest(JettyHTTPDestination.java:323)
at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:289)
at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:72)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:942)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:878)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
at org.eclipse.jetty.server.Server.handle(Server.java:345)
at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:441)
at org.eclipse.jetty.server.HttpConnection$RequestHandler.content(HttpConnection.java:936)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:801)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:218)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:52)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
at java.lang.Thread.run(Thread.java:662)
Caused by: javax.xml.bind.UnmarshalException
- with linked exception:
[javax.xml.bind.UnmarshalException: unexpected element (uri:"http://my.project.service", local:"myProperty"). Expected elements are <{http://my.project.service}myObject>]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:434)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:371)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:348)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.doUnmarshal(JAXBEncoderDecoder.java:784)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.access$100(JAXBEncoderDecoder.java:96)
at org.apache.cxf.jaxb.JAXBEncoderDecoder$1.run(JAXBEncoderDecoder.java:812)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:810)
... 25 more
Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://my.project.service", local:"myProperty"). Expected elements are <{http://my.project.service}myObject>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:662)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:258)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:253)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:120)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.childElement(Loader.java:105)
at com.sun.xml.bind.v2.runtime.unmarshaller.StructureLoader.childElement(StructureLoader.java:251)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:498)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:480)
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleStartElement(StAXStreamConnector.java:247)
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:181)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:369)
... 31 more
Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://my.project.service", local:"myProperty"). Expected elements are <{http://my.project.service}myObject>
... 42 more
31 juil. 2015 17:55:39 org.apache.cxf.binding.soap.interceptor.Soap12FaultOutInterceptor$Soap12FaultOutInterceptorInternal handleMessage
INFO: class org.apache.cxf.binding.soap.interceptor.Soap12FaultOutInterceptor$Soap12FaultOutInterceptorInternalapplication/soap+xml
2015-07-31 17:55:39 INFO MyWebServiceClientTest:81 - Stopping my mock server
2015-07-31 17:55:39.367:INFO:oejsh.ContextHandler:stopped o.e.j.s.h.ContextHandler{,null}
抱歉模糊不清,当我知道更多时,我愿意改善这个问题......
感谢您的时间。
编辑2015-08-18 :在wsdl中,如果我将元素MyOperationRequest
重命名为MyOperation
(操作名称),CXF将启用包装器风格和测试将通过。
我的问题是我无法改变wsdl,我需要让关闭包装器样式的模拟服务。
编辑2015-08-21 :将wsdl样式设置为rpc
可以绕过问题,但在我的情况下这个解决方案是不可接受的。也许我可以改变wsdl,但请求和响应结构不能改变。
我正在删除我的解决方案(将样式设置为rpc
)。
答案 0 :(得分:0)
我认为错误发生在wsdl中,这是我的理解:
包装器样式假定操作名称与包装器元素名称匹配,但在我的wsdl中不是这样。
非包装样式假定wsdl样式为rpc
,但在我的wsdl中却不是这样。
为了与两种包装样式中的一种兼容,我认为我必须改变wsdl。将wsdl样式设置为rpc
是不可接受的,因为响应的结构会发生变化。
所以我的解决方案是更改操作的名称,我需要在Java中更新实现方法的名称,但请求和响应消息将保持不变。
<portType name="MyServicePortType">
<operation name="MyOperationRequest">
<input message="tns:MyOperationRequest" name="MyOperationRequest" />
<output message="tns:MyOperationResponse" name="MyOperationResponse" />
</operation>
</portType>
<binding name="MyServiceBinding" type="tns:MyServicePortType">
<soap12:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="MyOperationRequest">
<soap12:operation soapAction="" style="document" />
[...]
</operation>
</binding>
帮助我的文章:https://myarch.com/wrappernon-wrapper-web-service-styles-things-you-need-to-know/