From 9656ee70f4c28a26dd28bd7444350c5920f8bb67 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Tue, 15 May 2012 12:50:52 -0400 Subject: [PATCH] INT-2453 content-type and ObjectToJsonTransformer Added 'content-type' functionality to ObjectToJsonTransformer Added 'content-type' attribute to object-to-json-transformer element Added parser and usage tests to ensure propper overrides (see Mark's last comment here https://github.com/SpringSource/spring-integration/pull/409) Applied required changes to Amqp module to work with standard 'content-type' header INT-2453 polish based on PR comments INT-2453 polished test INT-2453 polished parser --- .../integration/amqp/AmqpHeaders.java | 3 +- .../integration/MessageHeaders.java | 4 +- .../xml/ObjectToJsonTransformerParser.java | 10 ++- .../json/ObjectToJsonTransformer.java | 52 ++++++++++++++-- .../mapping/AbstractHeaderMapper.java | 2 +- .../config/xml/spring-integration-2.2.xsd | 9 +++ ...ctToJsonTransformerParserTests-context.xml | 8 ++- .../ObjectToJsonTransformerParserTests.java | 26 +++++++- .../json/ObjectToJsonTransformerTests.java | 61 ++++++++++++++++--- 9 files changed, 152 insertions(+), 23 deletions(-) diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/AmqpHeaders.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/AmqpHeaders.java index fefd1a1912..2b09b410a4 100644 --- a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/AmqpHeaders.java +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/AmqpHeaders.java @@ -16,6 +16,7 @@ package org.springframework.integration.amqp; +import org.springframework.integration.MessageHeaders; import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper; /** @@ -44,7 +45,7 @@ public abstract class AmqpHeaders { public static final String CONTENT_LENGTH = PREFIX + "contentLength"; - public static final String CONTENT_TYPE = PREFIX + "contentType"; + public static final String CONTENT_TYPE = MessageHeaders.CONTENT_TYPE; public static final String CORRELATION_ID = PREFIX + "correlationId"; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java b/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java index 0b062bd1c0..e75bffff7b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -86,6 +86,8 @@ public final class MessageHeaders implements Map, Serializable { public static final String SEQUENCE_DETAILS = "sequenceDetails"; + public static final String CONTENT_TYPE = "content-type"; + private final Map headers; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToJsonTransformerParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToJsonTransformerParser.java index fb06747da0..31f8be64f6 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToJsonTransformerParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToJsonTransformerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -18,11 +18,13 @@ package org.springframework.integration.config.xml; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.MessageHeaders; import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** * @author Mark Fisher + * @author Oleg Zhurakousky * @since 2.0 */ public class ObjectToJsonTransformerParser extends AbstractTransformerParser { @@ -38,6 +40,8 @@ public class ObjectToJsonTransformerParser extends AbstractTransformerParser { if (StringUtils.hasText(objectMapper)) { builder.addConstructorArgReference(objectMapper); } + if (element.hasAttribute(MessageHeaders.CONTENT_TYPE)){ + builder.addPropertyValue("contentType", element.getAttribute("content-type")); + } } - -} +} \ No newline at end of file diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/ObjectToJsonTransformer.java b/spring-integration-core/src/main/java/org/springframework/integration/json/ObjectToJsonTransformer.java index 188f8923e2..c1e85e35ad 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/json/ObjectToJsonTransformer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/json/ObjectToJsonTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -13,26 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.integration.json; import java.io.StringWriter; import org.codehaus.jackson.map.ObjectMapper; -import org.springframework.integration.transformer.AbstractPayloadTransformer; +import org.springframework.integration.Message; +import org.springframework.integration.MessageHeaders; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.transformer.AbstractTransformer; import org.springframework.util.Assert; +import org.springframework.util.LinkedCaseInsensitiveMap; /** * Transformer implementation that converts a payload instance into a JSON string representation. * * @author Mark Fisher + * @author James Carr + * @author Oleg Zhurakousky * @since 2.0 */ -public class ObjectToJsonTransformer extends AbstractPayloadTransformer { +public class ObjectToJsonTransformer extends AbstractTransformer { + + public static final String JSON_CONTENT_TYPE = "application/json"; private final ObjectMapper objectMapper; + private volatile String contentType = JSON_CONTENT_TYPE; + private volatile boolean contentTypeExplicitlySet = false; public ObjectToJsonTransformer(ObjectMapper objectMapper) { Assert.notNull(objectMapper, "objectMapper must not be null"); @@ -43,11 +52,42 @@ public class ObjectToJsonTransformer extends AbstractPayloadTransformer message) throws Exception { + String payload = this.transformPayload(message.getPayload()); + MessageBuilder messageBuilder = MessageBuilder.withPayload(payload); + + LinkedCaseInsensitiveMap headers = new LinkedCaseInsensitiveMap(); + headers.putAll(message.getHeaders()); + + if (headers.containsKey(MessageHeaders.CONTENT_TYPE)) { + if (this.contentTypeExplicitlySet){ + // override + headers.put(MessageHeaders.CONTENT_TYPE, this.contentType); + } + } + else { + headers.put(MessageHeaders.CONTENT_TYPE, this.contentType); + } + messageBuilder.copyHeaders(headers); + return messageBuilder.build(); + } +} \ No newline at end of file diff --git a/spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java index 6fc669f851..c376e3e68f 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java @@ -268,7 +268,7 @@ public abstract class AbstractHeaderMapper implements RequestReplyHeaderMappe */ private String addPrefixIfNecessary(String prefix, String propertyName) { String headerName = propertyName; - if (StringUtils.hasText(prefix) && !headerName.startsWith(prefix)) { + if (StringUtils.hasText(prefix) && !headerName.startsWith(prefix) && !headerName.equals(MessageHeaders.CONTENT_TYPE)) { headerName = prefix + propertyName; } return headerName; diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.2.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.2.xsd index 39574a0fcd..c25810f846 100644 --- a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.2.xsd +++ b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.2.xsd @@ -2017,6 +2017,15 @@ + + + + Allows you to set 'content-type' Message header. In the case that a content-type header is already present, + we will only override that value IF this attribute is explicitely set with + content-type value (e.g., content-type="text/xml") + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests-context.xml index 209b09a2b4..36e0fed0af 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests-context.xml @@ -7,9 +7,13 @@ http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd"> - + - + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests.java index 6dea3683a1..6462889ab8 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerParserTests.java @@ -27,22 +27,27 @@ import org.codehaus.jackson.JsonGenerator.Feature; import org.codehaus.jackson.map.ObjectMapper; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Mark Fisher + * @author Oleg Zhurakousky * @since 2.0 */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) public class ObjectToJsonTransformerParserTests { + + @Autowired + private volatile ApplicationContext context; @Autowired private volatile MessageChannel defaultObjectMapperInput; @@ -50,6 +55,25 @@ public class ObjectToJsonTransformerParserTests { @Autowired private volatile MessageChannel customObjectMapperInput; + @Test + public void testContextType(){ + ObjectToJsonTransformer transformer = + TestUtils.getPropertyValue(context.getBean("defaultTransformer"), "handler.transformer", ObjectToJsonTransformer.class); + assertEquals("application/json", TestUtils.getPropertyValue(transformer, "contentType")); + + transformer = + TestUtils.getPropertyValue(context.getBean("customTransformer"), "handler.transformer", ObjectToJsonTransformer.class); + assertEquals("application/json", TestUtils.getPropertyValue(transformer, "contentType")); + + transformer = + TestUtils.getPropertyValue(context.getBean("emptyContentTypeTransformer"), "handler.transformer", ObjectToJsonTransformer.class); + assertEquals("", TestUtils.getPropertyValue(transformer, "contentType")); + + transformer = + TestUtils.getPropertyValue(context.getBean("overridenContentTypeTransformer"), "handler.transformer", ObjectToJsonTransformer.class); + assertEquals("text/xml", TestUtils.getPropertyValue(transformer, "contentType")); + } + @Test public void defaultObjectMapper() { diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerTests.java index d36728a882..d0a95048e4 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/ObjectToJsonTransformerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -16,9 +16,6 @@ package org.springframework.integration.json; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -26,8 +23,17 @@ import org.codehaus.jackson.JsonGenerator.Feature; import org.codehaus.jackson.map.ObjectMapper; import org.junit.Test; +import org.springframework.integration.Message; +import org.springframework.integration.MessageHeaders; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.support.MessageBuilder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + /** * @author Mark Fisher + * @author Oleg Zhurakousky * @since 2.0 */ public class ObjectToJsonTransformerTests { @@ -35,14 +41,53 @@ public class ObjectToJsonTransformerTests { @Test public void simpleStringPayload() throws Exception { ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); - String result = transformer.transformPayload("foo"); + String result = (String) transformer.transform(new GenericMessage("foo")).getPayload(); assertEquals("\"foo\"", result); } + @Test + public void withDefaultContentType() throws Exception { + ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); + Message result = transformer.transform(new GenericMessage("foo")); + assertEquals(ObjectToJsonTransformer.JSON_CONTENT_TYPE, result.getHeaders().get(MessageHeaders.CONTENT_TYPE)); + } + + @Test + public void withProvidedContentType() throws Exception { + ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); + Message message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE, "text/xml").build(); + Message result = transformer.transform(message); + assertEquals("text/xml", result.getHeaders().get(MessageHeaders.CONTENT_TYPE)); + } + + @Test + public void withProvidedContentTypeWithOverride() throws Exception { + ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); + transformer.setContentType(ObjectToJsonTransformer.JSON_CONTENT_TYPE); + Message message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE, "text/xml").build(); + Message result = transformer.transform(message); + assertEquals(ObjectToJsonTransformer.JSON_CONTENT_TYPE, result.getHeaders().get(MessageHeaders.CONTENT_TYPE)); + } + + @Test + public void withProvidedContentTypeAsEmptyString() throws Exception { + ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); + transformer.setContentType(""); + Message message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE, "text/xml").build(); + Message result = transformer.transform(message); + assertEquals("", result.getHeaders().get(MessageHeaders.CONTENT_TYPE)); + } + + @Test(expected=IllegalArgumentException.class) + public void withProvidedContentTypeAsNull() throws Exception { + ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); + transformer.setContentType(null); + } + @Test public void simpleIntegerPayload() throws Exception { ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(); - String result = transformer.transformPayload(123); + String result = (String) transformer.transform(new GenericMessage(123)).getPayload(); assertEquals("123", result); } @@ -52,7 +97,7 @@ public class ObjectToJsonTransformerTests { TestAddress address = new TestAddress(123, "Main Street"); TestPerson person = new TestPerson("John", "Doe", 42); person.setAddress(address); - String result = transformer.transformPayload(person); + String result = (String) transformer.transform(new GenericMessage(person)).getPayload(); assertTrue(result.contains("\"firstName\":\"John\"")); assertTrue(result.contains("\"lastName\":\"Doe\"")); assertTrue(result.contains("\"age\":42")); @@ -71,7 +116,7 @@ public class ObjectToJsonTransformerTests { ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(customMapper); TestPerson person = new TestPerson("John", "Doe", 42); person.setAddress(new TestAddress(123, "Main Street")); - String result = transformer.transformPayload(person); + String result = (String) transformer.transform(new GenericMessage(person)).getPayload(); assertTrue(result.contains("firstName:\"John\"")); assertTrue(result.contains("lastName:\"Doe\"")); assertTrue(result.contains("age:42"));