以非包装样式调用服务时出现CXF Unmarshalling错误

时间:2015-07-31 16:48:43

标签: java soap junit cxf unmarshalling

我正在使用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)。

1 个答案:

答案 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/