INT-1807 Add Mechanism For Headers with TCP
TCP streams have no standard message structure. Therefore, the
TCP implementation previously only transferred the message
payload.
If someone wanted to convey header information, they would have
to write their own wrapper and/or use Java serialization for
the entire message.
This change provides a strategy to allow users to determine
which headers are transferred, and how.
A MessageConvertingMessageMapper is now provided that invokes
any MessageConverter. A MapMessageConverter is provided that
converts the payload, and selected heades to a Map with two
entries ("payload") and ("headers").
A MapJsonSerializer is provided that converts a Map to/from
JSON. Jackson can't delimit multiple objects in a stream
so another serializer is required to encode/decode structure.
A ByteArrayLfSerializer is used by default, inserting a
linefeed between JSON objects.
The combination of these elements now allows header
information to be transferred over TCP. Of course, users
can implment their own (de)serializer to format the
bits on the wire exactly as needed by their application.
INT-1807 Polishing
Add a test that uses a Map MessageConverter with a
Java (de)serializer.
INT-1807: Polishing
INT-1807: Rebased and polished
Change `MapJsonSerializer` to use `JsonObjectMapper` abstraction
Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
1847eaa194
commit
0699fdc6cf
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.support.converter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Converts to/from a Map with 2 keys ('headers' and 'payload').
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public class MapMessageConverter implements MessageConverter {
|
||||
|
||||
private volatile String[] headerNames;
|
||||
|
||||
private volatile boolean filterHeadersInToMessage;
|
||||
|
||||
/**
|
||||
* Headers to be converted in {@link #fromMessage(Message)}.
|
||||
* {@link #toMessage(Object)} will populate all headers found in
|
||||
* the map, unless {@link #filterHeadersInToMessage} is true.
|
||||
* @param headerNames
|
||||
*/
|
||||
public void setHeaderNames(String... headerNames) {
|
||||
this.headerNames = headerNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default all headers on Map passed to {@link #toMessage(Object)}
|
||||
* will be mapped. Set this property
|
||||
* to 'true' if you wish to limit the inbound headers to those in
|
||||
* the #headerNames.
|
||||
* @param filterHeadersInToMessage
|
||||
*/
|
||||
public void setFilterHeadersInToMessage(boolean filterHeadersInToMessage) {
|
||||
this.filterHeadersInToMessage = filterHeadersInToMessage;
|
||||
}
|
||||
|
||||
public <P> Message<P> toMessage(Object object) {
|
||||
Assert.isInstanceOf(Map.class, object, "This converter expects a Map");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) object;
|
||||
Object payload = map.get("payload");
|
||||
Assert.notNull(payload, "'payload' entry cannot be null");
|
||||
MessageBuilder<?> messageBuilder = MessageBuilder.withPayload(payload);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> headers = (Map<String, ?>) map.get("headers");
|
||||
if (headers != null) {
|
||||
if (this.filterHeadersInToMessage) {
|
||||
headers.keySet().retainAll(Arrays.asList(this.headerNames));
|
||||
}
|
||||
messageBuilder.copyHeaders(headers);
|
||||
/*for (Entry<String, ?> entry : headers.entrySet()) {
|
||||
if (this.filterHeadersInToMessage ? this.headerNames.contains(entry.getKey()) : true) {
|
||||
messageBuilder.setHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<P> convertedMessage = (Message<P>) messageBuilder.build();
|
||||
return convertedMessage;
|
||||
}
|
||||
|
||||
public <P> Object fromMessage(Message<P> message) {
|
||||
Map<String,Object> map = new HashMap<String, Object>();
|
||||
map.put("payload", message.getPayload());
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
for (String headerName : headerNames) {
|
||||
Object header = message.getHeaders().get(headerName);
|
||||
if (header != null) {
|
||||
headers.put(headerName, header);
|
||||
}
|
||||
}
|
||||
map.put("headers", headers);
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.support.converter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public class MapMessageConverterTests {
|
||||
|
||||
@Test
|
||||
public void testFromMessageToMessage() throws Exception {
|
||||
Message<String> message = MessageBuilder.withPayload("foo")
|
||||
.setHeader("bar", "baz")
|
||||
.setHeader("baz", "qux")
|
||||
.build();
|
||||
MapMessageConverter converter = new MapMessageConverter();
|
||||
converter.setHeaderNames("bar");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) converter.fromMessage(message);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> headers = (Map<String, Object>) map.get("headers");
|
||||
|
||||
assertNotNull(headers);
|
||||
assertNotNull(map.get("payload"));
|
||||
assertEquals("foo", map.get("payload"));
|
||||
assertNotNull(headers.get("bar"));
|
||||
assertEquals("baz", headers.get("bar"));
|
||||
assertNull(headers.get("baz"));
|
||||
|
||||
headers.put("baz", "qux");
|
||||
message = converter.toMessage(map);
|
||||
assertEquals("foo", message.getPayload());
|
||||
assertEquals("baz", message.getHeaders().get("bar"));
|
||||
assertEquals("qux", message.getHeaders().get("baz"));
|
||||
|
||||
converter.setFilterHeadersInToMessage(true);
|
||||
|
||||
message = converter.toMessage(map);
|
||||
assertEquals("foo", message.getPayload());
|
||||
assertEquals("baz", message.getHeaders().get("bar"));
|
||||
assertNull(message.getHeaders().get("baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalid() throws Exception {
|
||||
Message<String> message = MessageBuilder.withPayload("foo")
|
||||
.setHeader("bar", "baz")
|
||||
.setHeader("baz", "qux")
|
||||
.build();
|
||||
MapMessageConverter converter = new MapMessageConverter();
|
||||
converter.setHeaderNames("bar");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) converter.fromMessage(message);
|
||||
|
||||
map.remove("payload");
|
||||
|
||||
try {
|
||||
converter.toMessage(map);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertEquals("'payload' entry cannot be null", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoHeaders() throws Exception {
|
||||
Message<String> message = MessageBuilder.withPayload("foo")
|
||||
.build();
|
||||
MapMessageConverter converter = new MapMessageConverter();
|
||||
converter.setHeaderNames("bar");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) converter.fromMessage(message);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> headers = (Map<String, Object>) map.get("headers");
|
||||
|
||||
assertNotNull(headers);
|
||||
assertEquals(0, headers.size());
|
||||
map.remove("headers");
|
||||
message = converter.toMessage(map);
|
||||
assertEquals("foo", message.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotIncludedIfNull() throws Exception {
|
||||
Message<String> message = MessageBuilder.withPayload("foo")
|
||||
.setHeader("bar", null)
|
||||
.build();
|
||||
MapMessageConverter converter = new MapMessageConverter();
|
||||
converter.setHeaderNames("bar");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) converter.fromMessage(message);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> headers = (Map<String, Object>) map.get("headers");
|
||||
|
||||
assertNotNull(headers);
|
||||
assertNotNull(map.get("payload"));
|
||||
assertEquals("foo", map.get("payload"));
|
||||
assertFalse(headers.keySet().contains("bar"));
|
||||
assertEquals(0, headers.size());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user