AMQP-432: Properly convert BasicProperties values to and from

MessageProperties

JIRA: https://jira.spring.io/browse/AMQP-432

Previously `DefaultMessagePropertiesConverter` converted just
`LongString`,
but BasicProperties may contain List and Map values with nested
LongStrings. Values unsupported by AMQP which are contained in Lists or
Maps are also not converted from `MessageProperties` to
`BasicProperties`.

Add logic to properly convert from LongString values nested in
Lists/Maps.
Add logic to properly convert unsupported values nested in Lists/Maps to
Strings.

**Cherry-pick to 1.3.x**

Code style polishing
This commit is contained in:
Soeren Unruh
2014-10-17 16:03:35 +02:00
committed by Artem Bilan
parent 1b14c5f0fd
commit d144db36d7
2 changed files with 199 additions and 6 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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
@@ -15,6 +15,7 @@ package org.springframework.amqp.rabbit.support;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -35,6 +36,7 @@ import com.rabbitmq.client.LongString;
*
* @author Mark Fisher
* @author Gary Russell
* @author Soeren Unruh
* @since 1.0
*/
public class DefaultMessagePropertiesConverter implements MessagePropertiesConverter {
@@ -45,11 +47,7 @@ public class DefaultMessagePropertiesConverter implements MessagePropertiesConve
Map<String, Object> headers = source.getHeaders();
if (!CollectionUtils.isEmpty(headers)) {
for (Map.Entry<String, Object> entry : headers.entrySet()) {
Object value = entry.getValue();
if (value instanceof LongString) {
value = this.convertLongString((LongString) value, charset);
}
target.setHeader(entry.getKey(), value);
target.setHeader(entry.getKey(), convertLongStringIfNecessary(entry.getValue(), charset));
}
}
target.setTimestamp(source.getTimestamp());
@@ -130,6 +128,11 @@ public class DefaultMessagePropertiesConverter implements MessagePropertiesConve
return writableHeaders;
}
/**
* Converts a header value to a String if the value type is unsupported by AMQP, also handling values
* nested inside Lists or Maps.
* <p> {@code null} values are passed through, although Rabbit client will throw an IllegalArgumentException.
*/
private Object convertHeaderValueIfNecessary(Object value) {
boolean valid = (value instanceof String) || (value instanceof byte[]) || (value instanceof Boolean)
|| (value instanceof LongString) || (value instanceof Integer) || (value instanceof Long)
@@ -139,6 +142,22 @@ public class DefaultMessagePropertiesConverter implements MessagePropertiesConve
if (!valid && value != null) {
value = value.toString();
}
else if (value instanceof List<?>) {
List<Object> writableList = new ArrayList<Object>(((List<?>) value).size());
for (Object listValue : (List<?>) value) {
writableList.add(convertHeaderValueIfNecessary(listValue));
}
value = writableList;
}
else if (value instanceof Map<?, ?>) {
@SuppressWarnings("unchecked")
Map<String, Object> originalMap = (Map<String, Object>) value;
Map<String, Object> writableMap = new HashMap<String, Object>(originalMap.size());
for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
writableMap.put(entry.getKey(), this.convertHeaderValueIfNecessary(entry.getValue()));
}
value = writableMap;
}
return value;
}
@@ -158,4 +177,31 @@ public class DefaultMessagePropertiesConverter implements MessagePropertiesConve
}
}
/**
* Converts a LongString value using {@link #convertLongString(LongString, String)}, also handling values
* nested in Lists or Maps.
*/
private Object convertLongStringIfNecessary(Object value, String charset) {
if (value instanceof LongString) {
value = convertLongString((LongString) value, charset);
}
else if (value instanceof List<?>) {
List<Object> convertedList = new ArrayList<Object>(((List<?>) value).size());
for (Object listValue : (List<?>) value) {
convertedList.add(this.convertLongStringIfNecessary(listValue, charset));
}
value = convertedList;
}
else if (value instanceof Map<?, ?>) {
@SuppressWarnings("unchecked")
Map<String, Object> originalMap = (Map<String, Object>) value;
Map<String, Object> convertedMap = new HashMap<String, Object>();
for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
convertedMap.put(entry.getKey(), this.convertLongStringIfNecessary(entry.getValue(), charset));
}
value = convertedMap;
}
return value;
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2014 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.amqp.rabbit.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.amqp.core.MessageProperties;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.LongString;
import com.rabbitmq.client.impl.LongStringHelper;
/**
* @author Soeren Unruh
* @since 1.3
*/
public class DefaultMessagePropertiesConverterTests {
private final MessagePropertiesConverter messagePropertiesConverter = new DefaultMessagePropertiesConverter();
private final Envelope envelope = new Envelope(0, false, null, null);
private final LongString longString = LongStringHelper.asLongString("longString");
private String longStringString;
@Before
public void init() throws UnsupportedEncodingException {
longStringString = new String(longString.getBytes(), "UTF-8");
}
@Test
public void testToMessagePropertiesLongString() {
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("longString", longString);
BasicProperties source = new BasicProperties.Builder()
.headers(headers)
.build();
MessageProperties messageProperties = messagePropertiesConverter.toMessageProperties(source, envelope, "UTF-8");
assertEquals("LongString not converted to String",
longStringString, messageProperties.getHeaders().get("longString"));
}
@Test
public void testToMessagePropertiesLongStringInList() {
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("list", Arrays.asList(longString));
BasicProperties source = new BasicProperties.Builder()
.headers(headers)
.build();
MessageProperties messageProperties = messagePropertiesConverter.toMessageProperties(source, envelope, "UTF-8");
assertEquals("LongString nested in List not converted to String",
longStringString, ((List<?>) messageProperties.getHeaders().get("list")).get(0));
}
@Test
@SuppressWarnings("unchecked")
public void testToMessagePropertiesLongStringDeepInList() {
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("list", Arrays.asList(Arrays.asList(longString)));
BasicProperties source = new BasicProperties.Builder()
.headers(headers)
.build();
MessageProperties messageProperties = messagePropertiesConverter.toMessageProperties(source, envelope, "UTF-8");
assertEquals("LongString deeply nested in List not converted to String",
longStringString, ((List<?>) ((List<?>) messageProperties.getHeaders().get("list")).get(0)).get(0));
}
@Test
@SuppressWarnings("unchecked")
public void testToMessagePropertiesLongStringInMap() {
Map<String, Object> mapWithLongString = new HashMap<String, Object>();
mapWithLongString.put("longString", longString);
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("map", mapWithLongString);
BasicProperties source = new BasicProperties.Builder()
.headers(headers)
.build();
MessageProperties messageProperties = messagePropertiesConverter.toMessageProperties(source, envelope, "UTF-8");
assertEquals("LongString nested in Map not converted to String",
longStringString, ((Map<String, Object>) messageProperties.getHeaders().get("map")).get("longString"));
}
@Test
public void testFromUnsupportedValue() {
MessageProperties messageProperties = new MessageProperties();
messageProperties.setHeader("unsupported", new Object());
BasicProperties basicProps = messagePropertiesConverter.fromMessageProperties(messageProperties, "UTF-8");
assertTrue("Unsupported value not converted to String",
basicProps.getHeaders().get("unsupported") instanceof String);
}
@Test
public void testFromUnsupportedValueInList() {
MessageProperties messageProperties = new MessageProperties();
List<Object> listWithUnsupportedValue = Arrays.asList(new Object());
messageProperties.setHeader("list", listWithUnsupportedValue);
BasicProperties basicProps = messagePropertiesConverter.fromMessageProperties(messageProperties, "UTF-8");
assertTrue("Unsupported value nested in List not converted to String",
((List<?>) basicProps.getHeaders().get("list")).get(0) instanceof String);
}
@Test
@SuppressWarnings("unchecked")
public void testFromUnsupportedValueDeepInList() {
MessageProperties messageProperties = new MessageProperties();
List<List<Object>> listWithUnsupportedValue = Arrays.asList(Arrays.asList(new Object()));
messageProperties.setHeader("list", listWithUnsupportedValue);
BasicProperties basicProps = messagePropertiesConverter.fromMessageProperties(messageProperties, "UTF-8");
assertTrue("Unsupported value deeply nested in List not converted to String",
((List<Object>) ((List<?>) basicProps.getHeaders().get("list")).get(0)).get(0) instanceof String);
}
@Test
@SuppressWarnings("unchecked")
public void testFromUnsupportedValueInMap() {
MessageProperties messageProperties = new MessageProperties();
Map<String, Object> mapWithUnsupportedValue = new HashMap<String, Object>();
mapWithUnsupportedValue.put("unsupported", new Object());
messageProperties.setHeader("map", mapWithUnsupportedValue);
BasicProperties basicProps = messagePropertiesConverter.fromMessageProperties(messageProperties, "UTF-8");
assertTrue("Unsupported value nested in Map not converted to String",
((Map<String, Object>) basicProps.getHeaders().get("map")).get("unsupported") instanceof String);
}
}