Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CXF Unmarshalling Error when calling a service in non-wrapper style

I am developing a web service from a WSDL using CXF and I want to be able to run test sample requests using JUnit.

My Test class creates a mock server, then acts as a client to test the server.

I don't understand why I keep getting Unmarshalling Error: unexpected element exceptions.

I isolated the issue into a minimal project, but even this didn't help. I'm convinced that the problem comes from some stupid mistake I have done... It's been two days and I can't find it...

The error message is: org.apache.cxf.interceptor.Fault: Unmarshalling Error: unexpected element (uri:"http://my.project.service", local:"myProperty"). Expected elements are <{http://my.project.service}myObject>

It's like if it was expecting an other myObject element inside myObject... Everything is generated using CXF, why wouldn't it be able to unmarshal something that it just marshalled a few milliseconds ago?

I tried soap 1.1 / 1.2, I tried different versions of CXF up to 3.0.5, I tried playing with the namespaces, but it is always the same error.

Here is the entire content of the minimal project that reproduces the error:

<?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>

The pom.xml for dependencies:

<?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>

The implementation:

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;
  }

}

The test class:

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());

  }
}

And the trace:

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}

Sorry for the fuzziness, I'm willing to improve this question when I'll know more...

Thank you for your time.

Edit 2015-08-18: In the wsdl, if I rename the element MyOperationRequest into MyOperation (the name of the operation), CXF will enable the wrapper style and the test will pass.

My issue is that I cannot alter the wsdl, I need to make the mock service working with the wrapper style turned off.

Edit 2015-08-21: Setting the wsdl style as rpc allows me to bypass the problem, but this solution is not acceptable in my situation. Maybe I could alter the wsdl, but the request and the response structure must not change.

I'm removing my solution (set the style to rpc).

like image 727
boumbh Avatar asked Oct 31 '22 22:10

boumbh


1 Answers

I think that the error is in the wsdl, here is my understanding:

The wrapper style assumes that the operation name matches the wrapper element name, but it is not the case in my wsdl.

The non-wrapper style assumes that the wsdl style is rpc, but it is not the case in my wsdl.

In order to be compatible with one of the two wrapper styles, I think that I must alter the wsdl. Setting the wsdl style to rpc is not acceptable because the structure of the response would change.

So my solution was to change the name of the operation, I will need to update the name of the implementation method in Java but the request and response messages will be unchanged.

<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>

The article which helped me: https://myarch.com/wrappernon-wrapper-web-service-styles-things-you-need-to-know/

like image 106
boumbh Avatar answered Nov 15 '22 04:11

boumbh