Question

tl;dr

I have a REST service that responds with an XML body. I want to unmarshall the response using an HTTP Message Converter. However, the XML returned from the service does not always map to a single Java type. How do I configure a HTTP outbound gateway to expect more than one response type?


I am using

  • Java 1.6
  • Spring Integration 3.0.0.RELEASE
  • Spring Integration HTTP 3.0.0.RELEASE
  • Spring OXM 4.0.1.RELEASE

I am trying to setup an int-http:outbound-gateway that calls a REST service. The REST service responds with 'text/xml'. I want to be able to use a MarshallingHttpMessageConverter to marshall the response to a Java Object.

The REST service can respond with either a 'successful' XML response or an 'error' XML response. For example, it may return:

<?xml version="1.0" encoding="UTF-8"?>
<success>
    <yay>You're call was successful, here is the information you wanted.</yay>
    <information>very important stuff</information>
</success>

or it may return:

<?xml version="1.0" encoding="UTF-8"?>
<failure>
    <boo>You're call was unsuccessful, go in the corner and cry.</boo>
    <error>very important error code</error>
</failure>

Therefore, I need to have 2 Java Objects setup to map these different responses to. So I have created

Success.java:

@XmlRootElement(name = "success")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Success{

    private String yay;
    private String information;

    @XmlElement(name = "yay")
    public String getYay() {
        return yay;
    }

    @XmlElement(name = "information")
    public String getInformation() {
        return information;
    }
    //setters omitted for brevity.
}

Failure.java:

@XmlRootElement(name = "failure")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Failure{

    private String boo;
    private String information;

    @XmlElement(name = "boo")
    public String getBoo() {
        return boo;
    }

    @XmlElement(name = "error")
    public String getError() {
        return error;
    }
    //setters omitted for brevity.
}

Now, in order to setup the outbound Gateway I have a RestTemplate configured with a MarshallingHttpMessageConverter. On the MarshallingHttpMessageConverter, I inject an OXM unmarshaller that has bound the Success and Failure objects:

<oxm:jaxb2-marshaller id="myRestMarshaller">
    <oxm:class-to-be-bound name="com.example.Success" />
    <oxm:class-to-be-bound name="com.example.Failure" />
</oxm:jaxb2-marshaller>

The problem is that when I try to set up my int-http:outbound-gateway, I can only put one of com.example.Success or com.example.Failure into the expected-response-type attribute. If I choose to use Success, it will throw an exception if the REST service responds with a Failure and vice-versa.

<int-http:outbound-gateway
    id="myRestServiceGateway"
    url-expression="'http://localhost:8855/webapp/ws/testId/{testId}'"
    http-method="GET" 
    request-channel="restRequest"
    reply-channel="restResponse" 
    rest-template="restTemplate"
    expected-response-type="com.example.Success">

    <int-http:uri-variable name="testId" expression="payload"/>

</int-http:outbound-gateway>

How do I tell the outbound gateway that the response type could be com.example.Success or com.example.Failure?

Was it helpful?

Solution 2

  1. You can't specify several expected-response-type since it is a part of RestTemplate to convert response via ResponseExtractor.

  2. It is true that RestTemplate uses HttpMessageConverter to convert the response to the appropriate Java object.

  3. Since you expect XML as response and want to convert it to the POJO, using MarshallingHttpMessageConverter is the right way to go.

So how can you handle your requirement to convert both the success and failure responses?

We can't configure several types for the RestTemplate, but we can do it for JaxbMarshaller. There is a trick to deceive the RestTemplate and MarshallingHttpMessageConverter.

You can create a super-class for both Success and Failure and configure it as the expected-response-type. Add it as another class-to-be-bound for JaxbMarshaller in addition to Success and Failure.

And don't forget to mark the new super-class with @XmlRootElement, because JaxbMarshaller uses checkForXmlRootElement = true by default.

OTHER TIPS

Artem Bilan's answer answers my question as asked. However, I ended up using another method to accomplish my goal.

I set up my outbound gateway to receive the xml as a String and I did not use MarshallingHttpMessageConverter. Instead I placed my outbound gateway in a chain where the gateway is the first endpoint and an unmarshalling transformer is the second endpoint. So my configuration looks like this:

<oxm:jaxb2-marshaller id="myRestMarshaller">
    <oxm:class-to-be-bound name="com.example.Success" />
    <oxm:class-to-be-bound name="com.example.Failure" />
</oxm:jaxb2-marshaller>

<int:chain input-channel="restRequest" output-channel="restResponse">
    <int-http:outbound-gateway
        id="myRestServiceGateway"
        url-expression="'http://localhost:8855/webapp/ws/testId/{testId}'"
        http-method="GET" 
        rest-template="restTemplate"
        expected-response-type="java.lang.String">

        <int-http:uri-variable name="testId" expression="payload"/>

    </int-http:outbound-gateway>

    <int-xml:unmarshalling-transformer unmarshaller="myRestMarshaller"/>
</int:chain>

I like this method better because I think it provides a little more separation of concerns. Now the gateway is only concerned with communicating with the HTTP service and the marshaller is concerned with translating the response.

I also don't need to introduce an extra class to my model that will never be used.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top