Search code examples
jsonspringspring-integration

Spring Integration ObjectToJsonTransformer and headers propagation in response


I'm setting up an integration flow which should do the following:

accept an object 🠒 serialize it to JSON 🠒 send it over a TCP socket 🠒 receive a JSON response 🠒 deserialize it to an object (with a different type from the input one)

In order to perform serialization and deserialization, I'm using two @Transformers, namely ObjectToJsonTransformer and JsonToObjectTransformer.

My problem is that ObjectToJsonTransformer creates a Message which contains the type of the serialized object in its headers and these headers get propagated to the response I receive from the remote endpoint. I would expect the received Message to be brand new, but it's not and I don't exactly understand why.

Having these headers still around results in the response being deserialized to the input object type rather than the desired output one.

My workaround has been to add this line to my MessageHandler:

tcpOutboundGateway.setNotPropagatedHeaders(JsonHeaders.HEADERS.toArray(new String[0]));

but I feel like i'm doing something wrong.

Here is what I've done so far:

@EnableIntegration
@IntegrationComponentScan
@Configuration
public class TcpConfiguration {

    private static final String REQUEST_OBJECT_CHANNEL = "REQUEST_OBJECT_CHANNEL";
    private static final String REQUEST_JSON_CHANNEL = "REQUEST_JSON_CHANNEL";
    private static final String RESPONSE_JSON_CHANNEL = "RESPONSE_JSON_CHANNEL";

    @Value(value = "${ip}")
    private String ip;
    @Value(value = "${port}")
    private Integer port;

    @Bean
    public AbstractClientConnectionFactory clientConnectionFactory() {
        return new TcpNetClientConnectionFactory(this.ip, this.port);
    }

    @Bean
    @Transformer(inputChannel = REQUEST_OBJECT_CHANNEL, outputChannel = REQUEST_JSON_CHANNEL)
    public ObjectToJsonTransformer objectToJsonTransformer() {
        return new ObjectToJsonTransformer();
    }

    @Bean
    @ServiceActivator(inputChannel = REQUEST_JSON_CHANNEL)
    public MessageHandler tcpOutputGateway(final AbstractClientConnectionFactory connectionFactory) {
        final var tcpOutboundGateway = new TcpOutboundGateway();
        tcpOutboundGateway.setConnectionFactory(connectionFactory);
        tcpOutboundGateway.setNotPropagatedHeaders(JsonHeaders.HEADERS.toArray(new String[0]));
        tcpOutboundGateway.setOutputChannelName(RESPONSE_JSON_CHANNEL);
        return tcpOutboundGateway;
    }

    @Bean
    @Transformer(inputChannel = RESPONSE_JSON_CHANNEL)
    public JsonToObjectTransformer jsonToObjectTransformer() {
        return new JsonToObjectTransformer(MyResponse.class);
    }

    @MessagingGateway
    public interface StandaloneGateway {

        @Gateway(requestChannel = REQUEST_OBJECT_CHANNEL)
        MyResponse sendRequestAndGetResponse(MyRequest request);

    }
}

With this code I can correctly deserialize the response to MyResponse, but if I remove this line

tcpOutboundGateway.setNotPropagatedHeaders(JsonHeaders.HEADERS.toArray(new String[0]));

then the response get deserialized to MyRequest instead.

Is there a better way to handle this flow?


Solution

  • Gateways always copy inbound headers by default.

    Your solution is correct, but there is an alternative.

    new JsonToObjectTransformer(MyResponse.class);
    

    The type is a fall-back for when headers are not present.

    The valueTypeExpression is consulted first (starting with version 5.2.6), with the default using the headers.

    The alternative is to set a different expression:

    ResolvableType type = ResolvableType.forClass(TestPerson.class);
    transformer.setValueTypeExpression(new ValueExpression<>(type));
    

    The headers will then be ignored.