INT-4255: JSON Embedded Headers Message Mapper

JIRA: https://jira.spring.io/browse/INT-4255

- Support embedding headers for transports that don't support headers (TCP, Kafka, etc)
- Use the new message-aware Jackson ObjectMapper
- Provide a mechanism to more efficiently support byte[] payloads (avoid Base64 encoding)
- Support decoding "legacy" SCSt embedded headers

Polishing; PR Comments - Add Support to MQTT and TCP

Switch to simple patterns instead of regex

* Fix JavaDoc typo
* Upgrade to Jackson 2.9.1
This commit is contained in:
Gary Russell
2017-05-21 12:56:41 -04:00
committed by Artem Bilan
parent 0d495294ed
commit 73d9c6b6c5
10 changed files with 602 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.integration.ip.IpHeaders;
import org.springframework.integration.mapping.BytesMessageMapper;
import org.springframework.integration.mapping.InboundMessageMapper;
import org.springframework.integration.mapping.OutboundMessageMapper;
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
@@ -78,6 +79,8 @@ public class TcpMessageMapper implements
private BeanFactory beanFactory;
private BytesMessageMapper bytesMessageMapper;
/**
* Set the charset to use when converting outbound String messages to {@code byte[]}.
* @param charset the charset to set
@@ -89,7 +92,9 @@ public class TcpMessageMapper implements
/**
* Sets whether outbound String payloads are to be converted
* to byte[]. Default is true.
* Ignored if a {@link BytesMessageMapper} is provided.
* @param stringToBytes The stringToBytes to set.
* @see #setBytesMessageMapper(BytesMessageMapper)
*/
public void setStringToBytes(boolean stringToBytes) {
this.stringToBytes = stringToBytes;
@@ -138,6 +143,18 @@ public class TcpMessageMapper implements
this.beanFactory = beanFactory;
}
/**
* Set a {@link BytesMessageMapper} to use when mapping byte[].
* {@link #setStringToBytes(boolean)} is ignored when a {@link BytesMessageMapper}
* is provided.
* @param bytesMessageMapper the mapper.
* @since 5.0
* @see #setStringToBytes(boolean)
*/
public void setBytesMessageMapper(BytesMessageMapper bytesMessageMapper) {
this.bytesMessageMapper = bytesMessageMapper;
}
protected MessageBuilderFactory getMessageBuilderFactory() {
if (!this.messageBuilderFactorySet) {
if (this.beanFactory != null) {
@@ -148,12 +165,20 @@ public class TcpMessageMapper implements
return this.messageBuilderFactory;
}
@SuppressWarnings("unchecked")
@Override
public Message<?> toMessage(TcpConnection connection) throws Exception {
Message<Object> message = null;
Object payload = connection.getPayload();
if (payload != null) {
AbstractIntegrationMessageBuilder<Object> messageBuilder = getMessageBuilderFactory().withPayload(payload);
AbstractIntegrationMessageBuilder<Object> messageBuilder;
if (this.bytesMessageMapper != null && payload instanceof byte[]) {
messageBuilder = (AbstractIntegrationMessageBuilder<Object>) getMessageBuilderFactory()
.fromMessage(this.bytesMessageMapper.toMessage((byte[]) payload));
}
else {
messageBuilder = getMessageBuilderFactory().withPayload(payload);
}
this.addStandardHeaders(connection, messageBuilder);
this.addCustomHeaders(connection, messageBuilder);
message = messageBuilder.build();
@@ -208,6 +233,9 @@ public class TcpMessageMapper implements
@Override
public Object fromMessage(Message<?> message) throws Exception {
if (this.bytesMessageMapper != null) {
return this.bytesMessageMapper.fromMessage(message);
}
if (this.stringToBytes) {
return getPayloadAsBytes(message);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ import org.springframework.integration.ip.IpHeaders;
import org.springframework.integration.ip.tcp.serializer.MapJsonSerializer;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.converter.MapMessageConverter;
import org.springframework.integration.support.json.EmbeddedJsonHeadersMessageMapper;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.MessageConverter;
@@ -437,4 +438,28 @@ public class TcpMessageMapperTests {
assertEquals("someId", message.getHeaders().get(IpHeaders.CONNECTION_ID));
}
@Test
public void testWithBytesMapper() throws Exception {
Message<String> outMessage = MessageBuilder.withPayload("foo")
.setHeader("bar", "baz")
.build();
TcpMessageMapper mapper = new TcpMessageMapper();
mapper.setBytesMessageMapper(new EmbeddedJsonHeadersMessageMapper());
byte[] bytes = (byte[]) mapper.fromMessage(outMessage);
TcpConnection connection = mock(TcpConnection.class);
when(connection.getPayload()).thenReturn(bytes);
when(connection.getHostName()).thenReturn("someHost");
when(connection.getHostAddress()).thenReturn("1.1.1.1");
when(connection.getPort()).thenReturn(1234);
when(connection.getConnectionId()).thenReturn("someId");
Message<?> message = mapper.toMessage(connection);
assertEquals("foo", message.getPayload());
assertEquals("baz", message.getHeaders().get("bar"));
assertEquals("someHost", message.getHeaders().get(IpHeaders.HOSTNAME));
assertEquals("1.1.1.1", message.getHeaders().get(IpHeaders.IP_ADDRESS));
assertEquals(1234, message.getHeaders().get(IpHeaders.REMOTE_PORT));
assertEquals("someId", message.getHeaders().get(IpHeaders.CONNECTION_ID));
}
}