8

I have configured my WebService like this:

applicationContext:

<sws:annotation-driven />    
 <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping" >
<property name="interceptors">
 <list>
    <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</list>
</property>

Note: the Interceptor is loaded on startup, but doesn´t write anything, if a request is coming in.

I have a class PersonServiceImpl with the method addPersonRequest(). Everything works , if i am using org.dom4j.Element as method parameter;

@Endpoint
public class PersonServiceImpl {
     @PayloadRoot(namespace = "http://www.example.org/person/schema", localPart = "AddPersonRequest")
     @ResponsePayload
       public AddPersonRequest addPersonRequest(@RequestPayload Element element) {
        System.out.println(element.asXML());
        Person response = new Person();
        response.setId(2);
        response.setFirstName("Mad");
        response.setLastName("Mike");
        return response;
     }
}

But if i change my method parameters like shown below (so auto-marshalling of spring-ws should be used) the request.getFirstName() prints null. (JAXB2 is on classpath).

The Person-class is annotated with @XMLType and @XMLRootElement.

Note: The marshalling works fine.

@Endpoint
public class PersonServiceImpl {
     @PayloadRoot(namespace = "http://www.example.org/person/schema", localPart = "AddPersonRequest")
     @ResponsePayload
       public AddPersonRequest addPersonRequest(@RequestPayload Person request, SoapHeader header) {
        System.out.println(header.getName());
        System.out.println(request.getFirstName());
        Person response = new Person();
        response.setId(2);
        response.setFirstName("Mad");
        response.setLastName("Mike");
        return response;
     }
}

Person.java:

@XmlType
@XmlRootElement(namespace="http://www.example.org/person/schema", name="Person")
public class Person implements Serializable {

    private int id;
    private String firstName;
    private String lastName;

    @XmlElement
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @XmlElement
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @XmlAttribute
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}

Test-Request sent via soapUI (generated from wsdl):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://www.example.org/person/schema"> 
<soapenv:Header/>
<soapenv:Body>

  <sch:AddPersonRequest>

     <sch:Person sch:Id="1">

        <sch:FirstName>firstname</sch:FirstName>

        <sch:LastName>lastname</sch:LastName>

     </sch:Person>

  </sch:AddPersonRequest>

</soapenv:Body>
</soapenv:Envelope>
CSan
  • 954
  • 1
  • 10
  • 25
  • do you have a logging interceptor? – Prasanna Talakanti Oct 17 '11 at 14:51
  • No, i didn´t get it to work. Following the spring tutorial tutorial, i changed the Bean-Definition in my applicationContext.(changes made in original post). But my Logging-Level of Spring-WS is set to TRACE and it tells me: o.s.w.s.e.a.m.j.XmlRootElementPayloadMethodProcessor - Unmarshalled payload request to [mypackages.AddPersonRequest@a1557b], so everything seems to work... – CSan Oct 18 '11 at 08:50
  • After playing around a bit, i have get a new Exception: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.example.org/person/schema", local:"AddPersonRequest"). Expected elements are <{http://www.example.org/person/schema}Person> Maybe there is a namespace or local-part-problem, which i do not see?! – CSan Oct 18 '11 at 14:47

3 Answers3

3

I'm not sure if you still need an answer, reading your latest comment. I'm a bit confused with your request and response payload. They seem to be switched. Anyway, that's hard to say without the Person class.

I've dealt with similar issues before though and those were solved by adding JAXBElement<> around the actual class. Like this snippet:

@PayloadRoot(
    localPart = "PutOrganisationUnitRequest", 
    namespace = DEFAULT_NAMESPACE
)
@ResponsePayload public JAXBElement<Response> putOrganisationUnits (
    @RequestPayload JAXBElement<PutOrganisationUnitRequest> organisations,
    MessageContext messageContext) {

One other thing you can check is the namespaces in your jaxb class and in your endpoint definition.

evandongen
  • 1,995
  • 17
  • 19
  • Still need an answer :) What are you confused exactly about? The annotations seem to be right?! I will update my question by adding the person-class. I've tried to the JAXBElement<> around my class, but had no success. The namespace in the XML-File is equal to the namespace mapped in the java-classes. – CSan Oct 18 '11 at 12:48
  • I was confused about the response and request payload. I think you want to receive a AddPersonRequest and respond with a Person. If that's true, you might want to switch those in your method signature. (Especially since you said 'It seems that the unmarshaller tries to parse the to the Person-Object' in yesterdays comment. – evandongen Oct 19 '11 at 15:25
2

You mentioned marshalling is working I don't see any reason why unmarshalling won't work, Have you tested marshall and unmarshall in isloation?

Just to make sure the soap request is good, Can you add logging interceptor and print the actual request that is coming from the client accessing web service, Add this snippet to your context file

  <sws:interceptors>
    <bean class="org.springframework.ws.soap.server.endpoint.interceptor.SoapEnvelopeLoggingInterceptor">
        <property name="logRequest" value="true"></property>
        <property name="logResponse" value="true"></property>
    </bean>
</sws:interceptors>

You should see a log message like this that contains the entire soap request, I am adding a log message request from a saop UI to

   DEBUG [http-8080-2]:endpoint.interceptor.SoapEnvelopeLoggingInterceptor.logMessage - Request:
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://hoost:port/context/patient/schemas">
      <soapenv:Header/>
         <soapenv:Body>
            <sch:addRequest>
              <sch:patient>
                  <sch:id>?</sch:id>
                     <sch:lastname>Joe</sch:lastname>
                </sch:patient>
             </sch:addRequest>
        </soapenv:Body>
      </soapenv:Envelope>

UPDATE of answer

It could be your soap request not sure though

        <sch:FirstName>firstname</sch:FirstName> (this should be)

        <sch:firstName>firstname</sch:firstName>

Another update

The exception is due to the way you defined the end point, In your soap request (sch:AddPersonRequest) you are sending a addPersonRequest not Person as payload so change the end point to reflect that, The @RequestPayload should be AddPersonRequest not Person

     @PayloadRoot(namespace = "http://www.example.org/person/schema", localPart = "AddPersonRequest")
     @ResponsePayload
      public AddPersonRequest addPersonRequest(@RequestPayload AddPersonRequest request, SoapHeader header) {
Prasanna Talakanti
  • 2,354
  • 1
  • 16
  • 16
  • I activated the LoggingInterceptor. It logs exactly the request, i sent via soapUI (i updated the question with the test-request). – CSan Oct 18 '11 at 14:03
  • Please read my last comment on original question. I get a Unmarshalling-Exception. It seems that the unmarshaller tries to parse the to the Person-Object. Maybe i didn´t get the idea of the namespace/local-part-thing right?! – CSan Oct 18 '11 at 14:51
  • @Lodger, Late... but ok, yes, the Unmarshaller could not map the payload to the java bean. Change the @XmlRootElement(name="Person") to @XmlRootElement(name="AddPersonRequest") – DayaMoon Apr 24 '14 at 09:19
1

This Spring webservice implementation is utter garbage. A brain dead idiot could do work better than those "pros".

How hard it was to just follow the JSON principles and restore object tree following offered root type.

NO-ONE needs these namespaces. It never happens that the same request have two elements with the same name but from different namespaces. But these namespaces is endless pain.

Just match XML elements to object fields. This only what is required. Assign namespaces from WSDL when generating responses if anyone cares them on retrieving end but completely ignore them when unmarshaling a request.

When I look at all of this I really want to spend a month and do a NORMAL SOAP webservice framework with XML serialization/deserialization that only cares about element names and auto instantiate request/response objects as long as XML tree is matching object tree.

Stan Sokolov
  • 2,140
  • 1
  • 22
  • 23
  • 1
    I agree that Soap protocol is messy, but Spring simply implements its rules. Your definition of "Normal soap webservice framework" would not respect the soap protocol at all and you wouldn't be able to send valid requests to other applications which respect the soap standard. – Alexandru Severin Nov 14 '19 at 09:44