INT-4047: TCP: Add contentType Header
JIRA: https://jira.spring.io/browse/INT-4047 Polishing - PR Comments
This commit is contained in:
committed by
Artem Bilan
parent
ae0939d55f
commit
08718d1919
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user