INT-4047: TCP: Add contentType Header

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

Polishing - PR Comments
This commit is contained in:
Gary Russell
2016-06-06 17:12:15 -04:00
committed by Artem Bilan
parent ae0939d55f
commit 08718d1919
4 changed files with 134 additions and 6 deletions

View File

@@ -34,6 +34,10 @@ import org.springframework.integration.support.MessageBuilderFactory;
import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.Assert;
import org.springframework.util.InvalidMimeTypeException;
import org.springframework.util.MimeType;
/**
* Maps incoming data from a {@link TcpConnection} to a {@link Message}.
@@ -68,9 +72,14 @@ public class TcpMessageMapper implements
private volatile boolean messageBuilderFactorySet;
private volatile String contentType = "application/octet-stream;charset=" + this.charset;
private volatile boolean addContentTypeHeader;
private BeanFactory beanFactory;
/**
* Set the charset to use when converting outbound String messages to {@code byte[]}.
* @param charset the charset to set
*/
public void setCharset(String charset) {
@@ -93,6 +102,37 @@ public class TcpMessageMapper implements
this.applySequence = applySequence;
}
/**
* Set the content type header value to add to inbound messages when
* {@link #setAddContentTypeHeader(boolean) addContentTypeHeader} is true.
* Default {@code application/octet-stream;charset=UTF-8}. This default is <b>not</b>
* modified by {@link #setCharset(String)}.
* @param contentType the content type header value to set.
* @since 4.3
* @see #setAddContentTypeHeader(boolean)
* @see TcpMessageMapper#setCharset(String)
*/
public void setContentType(String contentType) {
Assert.notNull(contentType, "'contentType' cannot be null");
try {
MimeType.valueOf(contentType);
}
catch (InvalidMimeTypeException e) {
throw new IllegalArgumentException("'contentType' could not be parsed", e);
}
this.contentType = contentType;
}
/**
* Set to true to add a content type header; default false.
* @param addContentTypeHeader true to add a content type header.
* @since 4.3
* @see #setContentType(String)
*/
public void setAddContentTypeHeader(boolean addContentTypeHeader) {
this.addContentTypeHeader = addContentTypeHeader;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
@@ -126,7 +166,8 @@ public class TcpMessageMapper implements
return message;
}
protected final void addStandardHeaders(TcpConnection connection, AbstractIntegrationMessageBuilder<?> messageBuilder) {
protected final void addStandardHeaders(TcpConnection connection,
AbstractIntegrationMessageBuilder<?> messageBuilder) {
String connectionId = connection.getConnectionId();
messageBuilder
.setHeader(IpHeaders.HOSTNAME, connection.getHostName())
@@ -142,9 +183,13 @@ public class TcpMessageMapper implements
.setCorrelationId(connectionId)
.setSequenceNumber((int) connection.incrementAndGetConnectionSequence());
}
if (this.addContentTypeHeader) {
messageBuilder.setHeader(MessageHeaders.CONTENT_TYPE, this.contentType);
}
}
protected final void addCustomHeaders(TcpConnection connection, AbstractIntegrationMessageBuilder<?> messageBuilder) {
protected final void addCustomHeaders(TcpConnection connection,
AbstractIntegrationMessageBuilder<?> messageBuilder) {
Map<String, ?> customHeaders = this.supplyCustomHeaders(connection);
if (customHeaders != null) {
messageBuilder.copyHeadersIfAbsent(customHeaders);

View File

@@ -16,9 +16,12 @@
package org.springframework.integration.ip.tcp.connection;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -48,7 +51,9 @@ import org.springframework.integration.ip.tcp.serializer.MapJsonSerializer;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.converter.MapMessageConverter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.util.MimeType;
/**
* @author Gary Russell
@@ -69,7 +74,6 @@ public class TcpMessageMapperTests {
@Test
public void testToMessage() throws Exception {
TcpMessageMapper mapper = new TcpMessageMapper();
TcpConnection connection = mock(TcpConnection.class);
Socket socket = mock(Socket.class);
@@ -87,11 +91,75 @@ public class TcpMessageMapperTests {
assertEquals("1.1.1.1", message.getHeaders().get(IpHeaders.IP_ADDRESS));
assertEquals(1234, message.getHeaders().get(IpHeaders.REMOTE_PORT));
assertSame(local, message.getHeaders().get(IpHeaders.LOCAL_ADDRESS));
assertNull(message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void testToMessageWithContentType() throws Exception {
TcpMessageMapper mapper = new TcpMessageMapper();
mapper.setAddContentTypeHeader(true);
TcpConnection connection = mock(TcpConnection.class);
Socket socket = mock(Socket.class);
InetAddress local = mock(InetAddress.class);
SocketInfo info = new SocketInfo(socket);
when(socket.getLocalAddress()).thenReturn(local);
when(connection.getPayload()).thenReturn(TEST_PAYLOAD.getBytes());
when(connection.getHostName()).thenReturn("MyHost");
when(connection.getHostAddress()).thenReturn("1.1.1.1");
when(connection.getPort()).thenReturn(1234);
when(connection.getSocketInfo()).thenReturn(info);
Message<?> message = mapper.toMessage(connection);
assertEquals(TEST_PAYLOAD, new String((byte[]) message.getPayload()));
assertEquals("MyHost", message.getHeaders().get(IpHeaders.HOSTNAME));
assertEquals("1.1.1.1", message.getHeaders().get(IpHeaders.IP_ADDRESS));
assertEquals(1234, message.getHeaders().get(IpHeaders.REMOTE_PORT));
assertSame(local, message.getHeaders().get(IpHeaders.LOCAL_ADDRESS));
assertEquals("application/octet-stream;charset=UTF-8", message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
MimeType parseOk = MimeType.valueOf((String) message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
assertEquals(message.getHeaders().get(MessageHeaders.CONTENT_TYPE), parseOk.toString());
}
@Test
public void testToMessageWithCustomContentType() throws Exception {
TcpMessageMapper mapper = new TcpMessageMapper();
mapper.setAddContentTypeHeader(true);
mapper.setContentType("application/octet-stream;charset=ISO-8859-1");
TcpConnection connection = mock(TcpConnection.class);
Socket socket = mock(Socket.class);
InetAddress local = mock(InetAddress.class);
SocketInfo info = new SocketInfo(socket);
when(socket.getLocalAddress()).thenReturn(local);
when(connection.getPayload()).thenReturn(TEST_PAYLOAD.getBytes());
when(connection.getHostName()).thenReturn("MyHost");
when(connection.getHostAddress()).thenReturn("1.1.1.1");
when(connection.getPort()).thenReturn(1234);
when(connection.getSocketInfo()).thenReturn(info);
Message<?> message = mapper.toMessage(connection);
assertEquals(TEST_PAYLOAD, new String((byte[]) message.getPayload()));
assertEquals("MyHost", message.getHeaders().get(IpHeaders.HOSTNAME));
assertEquals("1.1.1.1", message.getHeaders().get(IpHeaders.IP_ADDRESS));
assertEquals(1234, message.getHeaders().get(IpHeaders.REMOTE_PORT));
assertSame(local, message.getHeaders().get(IpHeaders.LOCAL_ADDRESS));
assertEquals("application/octet-stream;charset=ISO-8859-1", message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
MimeType parseOk = MimeType.valueOf((String) message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
assertEquals(message.getHeaders().get(MessageHeaders.CONTENT_TYPE), parseOk.toString());
}
@Test(expected = IllegalArgumentException.class)
public void testToMessageWithBadContentType() throws Exception {
TcpMessageMapper mapper = new TcpMessageMapper();
mapper.setAddContentTypeHeader(true);
try {
mapper.setContentType("");
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("'contentType' could not be parsed"));
throw e;
}
}
@Test
public void testToMessageSequence() throws Exception {
TcpMessageMapper mapper = new TcpMessageMapper();
Socket socket = SocketFactory.getDefault().createSocket();
TcpConnection connection = new TcpConnectionSupport(socket, false, false, null, null) {

View File

@@ -1456,15 +1456,25 @@ The framework includes acknowledgment information in the data packet.
| ip_actualConnectionId
| ACTUAL_ CONNECTION_ID
| For information only - when using a cached or failover client connection factory, contains the actual underlying connection id.
| contentType
| MessageHeaders.CONTENT_TYPE
| An optional content type for inbound messages; see below.
|===
For inbound messages, `ip_hostname`, `ip_address`, `ip_tcp_remotePort` and `ip_connectionId` are mapped by the default
`TcpHeaderMapper`.
Users can add additional headers by subclassing `TcpHeaderMapper`, overriding the method `supplyCustomHeaders`, and
providing an instance to the connection factory using the `mapper` property.
Set the mapper's `addContentTypeHeader` property to `true` and the mapper will set the `contentType` header (`application/octet-stream;charset="UTF-8"`) by default.
You can change the default by setting the `contentType` property.
Users can add additional headers by subclassing `TcpHeaderMapper` and overriding the method `supplyCustomHeaders`.
For example, when using SSL, properties of the `SSLSession` can be added by obtaining the session object from the
`TcpConnection` object which is provided as an argument to the `supplyCustomHeaders` method.
For outbound messages, `String` payloads are converted to `byte[]` using the default (`UTF-8`) charset.
Set the `charset` property to change the default.
When customizing the mapper properties, or subclassing, declare the mapper as a bean and provide an instance to the connection factory using the `mapper` property
[[ip-annotation]]
=== Annotation-Based Configuration

View File

@@ -120,6 +120,11 @@ pooling of the raw buffer into which the data is received, rather than creating
message.
See <<connection-factories>> for more information.
===== TCP Message Mapper
The message mapper now, optionally, sets a configured content type header.
See <<ip-msg-headers>> for more information.
==== File Changes
===== Destination Directory Creation