diff --git a/build.gradle b/build.gradle index 1f7abe9be8..32e4cec77d 100644 --- a/build.gradle +++ b/build.gradle @@ -70,6 +70,7 @@ subprojects { subproject -> activeMqVersion = '5.9.0' aspectjVersion = '1.8.0' apacheSshdVersion = '0.10.1' + boonVersion = '0.22' commonsDbcpVersion = '1.4' commonsIoVersion = '2.4' commonsNetVersion = '3.3' @@ -83,7 +84,6 @@ subprojects { subproject -> hibernateVersion = '4.2.11.Final' hsqldbVersion = '2.3.2' h2Version = '1.3.175' - jacksonVersion = '1.9.13' jackson2Version = '2.3.2' javaxActivationVersion = '1.1.1' javaxMailVersion = '1.4.7' @@ -246,9 +246,9 @@ project('spring-integration-core') { compile "org.springframework.retry:spring-retry:$springRetryVersion" compile "org.projectreactor:reactor-core:$reactorVersion" compile("org.projectreactor.spring:reactor-spring-context:$reactorSpringVersion", optional) - compile("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion", optional) compile("com.fasterxml.jackson.core:jackson-databind:$jackson2Version", optional) compile("com.jayway.jsonpath:json-path:$jsonpathVersion", optional) + compile("io.fastjson:boon:$boonVersion", optional) testCompile ("org.aspectj:aspectjweaver:$aspectjVersion") } diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/inbound/InboundEndpointTests.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/inbound/InboundEndpointTests.java index 41dcf1b95b..c5df36b344 100644 --- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/inbound/InboundEndpointTests.java +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/inbound/InboundEndpointTests.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import java.util.Map; +import com.rabbitmq.client.Channel; import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; @@ -41,7 +42,7 @@ import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.support.CorrelationData; -import org.springframework.amqp.support.converter.JsonMessageConverter; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.SimpleMessageConverter; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanFactory; @@ -60,8 +61,6 @@ import org.springframework.messaging.Message; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.support.GenericMessage; -import com.rabbitmq.client.Channel; - /** * @author Artem Bilan * @author Gary Russell @@ -85,7 +84,7 @@ public class InboundEndpointTests { container.setAcknowledgeMode(AcknowledgeMode.MANUAL); AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(container); - adapter.setMessageConverter(new JsonMessageConverter()); + adapter.setMessageConverter(new Jackson2JsonMessageConverter()); PollableChannel channel = new QueueChannel(); @@ -140,7 +139,8 @@ public class InboundEndpointTests { Object payload = new Foo("bar1"); MessageProperties amqpMessageProperties = new MessageProperties(); - org.springframework.amqp.core.Message amqpMessage = new JsonMessageConverter().toMessage(payload, amqpMessageProperties); + org.springframework.amqp.core.Message amqpMessage = + new Jackson2JsonMessageConverter().toMessage(payload, amqpMessageProperties); ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener(); listener.onMessage(amqpMessage, null); @@ -186,20 +186,22 @@ public class InboundEndpointTests { })); AmqpInboundGateway gateway = new AmqpInboundGateway(container); - gateway.setMessageConverter(new JsonMessageConverter()); + gateway.setMessageConverter(new Jackson2JsonMessageConverter()); gateway.setRequestChannel(channel); gateway.setBeanFactory(mock(BeanFactory.class)); gateway.afterPropertiesSet(); - RabbitTemplate rabbitTemplate = Mockito.spy(TestUtils.getPropertyValue(gateway, "amqpTemplate", RabbitTemplate.class)); + RabbitTemplate rabbitTemplate = Mockito.spy(TestUtils.getPropertyValue(gateway, "amqpTemplate", + RabbitTemplate.class)); Mockito.doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { - org.springframework.amqp.core.Message message = (org.springframework.amqp.core.Message) invocation.getArguments()[2]; - Map headers = message.getMessageProperties().getHeaders(); + org.springframework.amqp.core.Message message = + (org.springframework.amqp.core.Message) invocation.getArguments()[2]; + Map headers = message.getMessageProperties().getHeaders(); assertTrue(headers.containsKey(JsonHeaders.TYPE_ID.replaceFirst(JsonHeaders.PREFIX, ""))); assertNotEquals("foo", headers.get(JsonHeaders.TYPE_ID.replaceFirst(JsonHeaders.PREFIX, ""))); assertFalse(headers.containsKey(JsonHeaders.CONTENT_TYPE_ID.replaceFirst(JsonHeaders.PREFIX, ""))); @@ -209,9 +211,7 @@ public class InboundEndpointTests { assertFalse(headers.containsKey(JsonHeaders.CONTENT_TYPE_ID)); return null; } - } - - ).when(rabbitTemplate).send(Mockito.anyString(), Mockito.anyString(), + }).when(rabbitTemplate).send(Mockito.anyString(), Mockito.anyString(), Mockito.any(org.springframework.amqp.core.Message.class), Mockito.any(CorrelationData.class)); DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(gateway); @@ -222,7 +222,8 @@ public class InboundEndpointTests { MessageProperties amqpMessageProperties = new MessageProperties(); amqpMessageProperties.setReplyTo("test"); amqpMessageProperties.setDeliveryTag(123L); - org.springframework.amqp.core.Message amqpMessage = new JsonMessageConverter().toMessage(payload, amqpMessageProperties); + org.springframework.amqp.core.Message amqpMessage = + new Jackson2JsonMessageConverter().toMessage(payload, amqpMessageProperties); ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener(); listener.onMessage(amqpMessage, rabbitChannel); @@ -230,7 +231,6 @@ public class InboundEndpointTests { } - public static class Foo { private String bar; @@ -257,11 +257,8 @@ public class InboundEndpointTests { Foo foo = (Foo) o; - if (bar != null ? !bar.equals(foo.bar) : foo.bar != null) { - return false; - } + return !(bar != null ? !bar.equals(foo.bar) : foo.bar != null); - return true; } @Override diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapperTests.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapperTests.java index f283744d5d..7480dd7756 100644 --- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapperTests.java +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapperTests.java @@ -27,14 +27,16 @@ import java.util.Map; import java.util.Set; import org.junit.Test; + import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageDeliveryMode; import org.springframework.amqp.core.MessageProperties; -import org.springframework.amqp.support.converter.JsonMessageConverter; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.http.MediaType; import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHeaders; import org.springframework.integration.amqp.AmqpHeaders; +import org.springframework.messaging.MessageHeaders; /** * @author Mark Fisher @@ -198,7 +200,7 @@ public class DefaultAmqpHeaderMapperTests { @Test // INT-2090 public void jsonTypeIdNotOverwritten() { DefaultAmqpHeaderMapper headerMapper = new DefaultAmqpHeaderMapper(); - JsonMessageConverter converter = new JsonMessageConverter(); + MessageConverter converter = new Jackson2JsonMessageConverter(); MessageProperties amqpProperties = new MessageProperties(); converter.toMessage("123", amqpProperties); Map headerMap = new HashMap(); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonToObjectTransformer.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonToObjectTransformer.java index 5dfa3b4c8a..d95be7d9c3 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonToObjectTransformer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonToObjectTransformer.java @@ -19,14 +19,14 @@ package org.springframework.integration.json; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.integration.mapping.support.JsonHeaders; import org.springframework.integration.support.AbstractIntegrationMessageBuilder; -import org.springframework.integration.support.json.JacksonJsonObjectMapperProvider; +import org.springframework.integration.support.json.JsonObjectMapperProvider; import org.springframework.integration.support.json.JsonObjectMapper; import org.springframework.integration.transformer.AbstractTransformer; import org.springframework.messaging.Message; /** * Transformer implementation that converts a JSON string payload into an instance of the provided target Class. - * By default this transformer uses {@linkplain JacksonJsonObjectMapperProvider} factory + * By default this transformer uses {@linkplain org.springframework.integration.support.json.JsonObjectMapperProvider} factory * to get an instance of Jackson 1 or Jackson 2 JSON-processor {@linkplain JsonObjectMapper} implementation * depending on the jackson-databind or jackson-mapper-asl libs on the classpath. * Any other {@linkplain JsonObjectMapper} implementation can be provided. @@ -34,7 +34,7 @@ import org.springframework.messaging.Message; * @author Mark Fisher * @author Artem Bilan * @see JsonObjectMapper - * @see JacksonJsonObjectMapperProvider + * @see org.springframework.integration.support.json.JsonObjectMapperProvider * @since 2.0 */ public class JsonToObjectTransformer extends AbstractTransformer implements BeanClassLoaderAware { @@ -57,7 +57,7 @@ public class JsonToObjectTransformer extends AbstractTransformer implements Bean public JsonToObjectTransformer(Class targetClass, JsonObjectMapper jsonObjectMapper) { this.targetClass = targetClass; - this.jsonObjectMapper = (jsonObjectMapper != null) ? jsonObjectMapper : JacksonJsonObjectMapperProvider.newInstance(); + this.jsonObjectMapper = (jsonObjectMapper != null) ? jsonObjectMapper : JsonObjectMapperProvider.newInstance(); } @Override 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 0652365a36..ec9eedd97f 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 @@ -16,7 +16,7 @@ package org.springframework.integration.json; import org.springframework.integration.support.AbstractIntegrationMessageBuilder; -import org.springframework.integration.support.json.JacksonJsonObjectMapperProvider; +import org.springframework.integration.support.json.JsonObjectMapperProvider; import org.springframework.integration.support.json.JsonObjectMapper; import org.springframework.integration.transformer.AbstractTransformer; import org.springframework.messaging.Message; @@ -27,7 +27,7 @@ import org.springframework.util.StringUtils; /** * Transformer implementation that converts a payload instance into a JSON string representation. - * By default this transformer uses {@linkplain JacksonJsonObjectMapperProvider} factory + * By default this transformer uses {@linkplain org.springframework.integration.support.json.JsonObjectMapperProvider} factory * to get an instance of a Jackson or Jackson 2 JSON-processor {@linkplain JsonObjectMapper} implementation * depending on the jackson-databind or jackson-mapper-asl libs on the classpath. * Any other {@linkplain JsonObjectMapper} implementation can be provided. @@ -56,7 +56,7 @@ public class ObjectToJsonTransformer extends AbstractTransformer { private volatile boolean contentTypeExplicitlySet = false; public ObjectToJsonTransformer() { - this(JacksonJsonObjectMapperProvider.newInstance()); + this(JsonObjectMapperProvider.newInstance()); } public ObjectToJsonTransformer(JsonObjectMapper jsonObjectMapper) { @@ -64,7 +64,7 @@ public class ObjectToJsonTransformer extends AbstractTransformer { } public ObjectToJsonTransformer(ResultType resultType) { - this(JacksonJsonObjectMapperProvider.newInstance(), resultType); + this(JsonObjectMapperProvider.newInstance(), resultType); } public ObjectToJsonTransformer(JsonObjectMapper jsonObjectMapper, ResultType resultType) { @@ -113,7 +113,7 @@ public class ObjectToJsonTransformer extends AbstractTransformer { headers.put(MessageHeaders.CONTENT_TYPE, this.contentType); } - this.jsonObjectMapper.populateJavaTypes(headers, message.getPayload().getClass()); + this.jsonObjectMapper.populateJavaTypes(headers, message.getPayload()); messageBuilder.copyHeaders(headers); return messageBuilder.build(); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java index 9b40706a0c..b9d8a647bc 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java @@ -104,7 +104,7 @@ public class RecipientListRouter extends AbstractMessageRouter /** * Set the recipients for this router. - * @param recipientMappings, map contains channelName and expression + * @param recipientMappings map contains channelName and expression */ @Override @ManagedAttribute diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonObjectMapper.java index 35e353c691..68ef32e558 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonObjectMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonObjectMapper.java @@ -38,7 +38,8 @@ import org.springframework.util.ClassUtils; * @author Artem Bilan * @since 3.0 */ -public abstract class AbstractJacksonJsonObjectMapper implements JsonObjectMapper, BeanClassLoaderAware { +public abstract class AbstractJacksonJsonObjectMapper extends JsonObjectMapperAdapter + implements BeanClassLoaderAware { protected static final Collection> supportedJsonTypes = Arrays.> asList(String.class, byte[].class, File.class, URL.class, InputStream.class, Reader.class); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/BoonJsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/BoonJsonObjectMapper.java new file mode 100644 index 0000000000..31eae1ea39 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/BoonJsonObjectMapper.java @@ -0,0 +1,198 @@ +package org.springframework.integration.support.json; + +import java.io.File; +import java.io.FileReader; +import java.io.InputStream; +import java.io.PipedReader; +import java.io.PipedWriter; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.Executors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.boon.json.JsonFactory; +import org.boon.json.JsonParserAndMapper; +import org.boon.json.JsonParserFactory; +import org.boon.json.JsonSerializerFactory; +import org.boon.json.JsonSlurper; +import org.boon.json.ObjectMapper; + +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.integration.mapping.support.JsonHeaders; +import org.springframework.util.ClassUtils; + +/** + * The Boon (@link https://github.com/RichardHightower/boon) {@link JsonObjectMapper} implementation. + * + * @author Artem Bilan + * @since 4.1 + */ +public class BoonJsonObjectMapper extends JsonObjectMapperAdapter, Object> + implements BeanClassLoaderAware { + + private static final Log logger = LogFactory.getLog(BoonJsonObjectMapper.class); + + private static final Collection> supportedJsonTypes = + Arrays.>asList(String.class, byte[].class, byte[].class, File.class, InputStream.class, Reader.class); + + + private final ObjectMapper objectMapper; + + private final JsonSlurper slurper = new JsonSlurper(); + + private volatile ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); + + public BoonJsonObjectMapper() { + this.objectMapper = JsonFactory.create(); + } + + public BoonJsonObjectMapper(JsonParserFactory parserFactory, JsonSerializerFactory serializerFactory) { + this.objectMapper = JsonFactory.create(parserFactory, serializerFactory); + } + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public String toJson(Object value) throws Exception { + return this.objectMapper.writeValueAsString(value); + } + + @Override + public void toJson(Object value, Writer writer) { + this.objectMapper.toJson(value, writer); + } + + @Override + @SuppressWarnings("unchecked") + public Map toJsonNode(final Object value) throws Exception { + PipedReader in = new PipedReader(); + final PipedWriter out = new PipedWriter(in); + Executors.newSingleThreadExecutor() + .execute(new Runnable() { + @Override + public void run() { + toJson(value, out); + } + }); + return (Map) this.slurper.parse(in); + } + + @Override + public T fromJson(Object json, Class type) throws Exception { + if (json instanceof String) { + return this.objectMapper.readValue((String) json, type); + } + else if (json instanceof byte[]) { + return this.objectMapper.readValue((byte[]) json, type); + } + else if (json instanceof char[]) { + return this.objectMapper.readValue((char[]) json, type); + } + else if (json instanceof File) { + return this.objectMapper.readValue((File) json, type); + } + else if (json instanceof InputStream) { + return this.objectMapper.readValue((InputStream) json, type); + } + else if (json instanceof Reader) { + return this.objectMapper.readValue((Reader) json, type); + } + else { + throw new IllegalArgumentException("'json' argument must be an instance of: " + supportedJsonTypes + + " , but gotten: " + json.getClass()); + } + } + + @Override + @SuppressWarnings("unchecked") + public T fromJson(Object json, Map javaTypes) throws Exception { + JsonParserAndMapper parser = this.objectMapper.parser(); + + Class classType = this.createJavaType(javaTypes, JsonHeaders.TYPE_ID); + + Class contentClassType = this.createJavaType(javaTypes, JsonHeaders.CONTENT_TYPE_ID); + + Class keyClassType = this.createJavaType(javaTypes, JsonHeaders.KEY_TYPE_ID); + + if (keyClassType != null) { + logger.warn("Boon doesn't support the Map 'key' conversion. Will be returned raw Map"); + if (json instanceof String) { + return (T) parser.parseMap((String) json); + } + else if (json instanceof byte[]) { + return (T) parser.parseMap((byte[]) json); + } + else if (json instanceof char[]) { + return (T) parser.parseMap((char[]) json); + } + else if (json instanceof File) { + return (T) parser.parseMap(new FileReader((File) json)); + } + else if (json instanceof InputStream) { + return (T) parser.parseMap((InputStream) json); + } + else if (json instanceof Reader) { + return (T) parser.parseMap((Reader) json); + } + else { + throw new IllegalArgumentException("'json' argument must be an instance of: " + supportedJsonTypes + + " , but gotten: " + json.getClass()); + } + } + + if (contentClassType != null) { + if (json instanceof String) { + return (T) this.objectMapper.readValue((String) json, (Class) classType, contentClassType); + } + else if (json instanceof byte[]) { + return (T) this.objectMapper.readValue((byte[]) json, (Class) classType, contentClassType); + } + else if (json instanceof char[]) { + return (T) this.objectMapper.readValue((char[]) json, (Class) classType, contentClassType); + } + else if (json instanceof File) { + return (T) this.objectMapper.readValue((File) json, (Class) classType, contentClassType); + } + else if (json instanceof InputStream) { + return (T) this.objectMapper.readValue((InputStream) json, (Class) classType, + contentClassType); + } + else if (json instanceof Reader) { + return (T) this.objectMapper.readValue((Reader) json, (Class) classType, contentClassType); + } + else { + throw new IllegalArgumentException("'json' argument must be an instance of: " + supportedJsonTypes + + " , but gotten: " + json.getClass()); + } + } + + return (T) fromJson(json, classType); + } + + protected Class createJavaType(Map javaTypes, String javaTypeKey) throws Exception { + Object classValue = javaTypes.get(javaTypeKey); + if (classValue instanceof Class) { + return (Class) classValue; + } + else if (classValue != null) { + return ClassUtils.forName(classValue.toString(), this.classLoader); + } + else { + return null; + } + } + + @Override + public T fromJson(Object parser, Type valueType) throws Exception { + throw new UnsupportedOperationException("Boon doesn't support JSON reader parser abstraction"); + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java index 40aa383d97..060bf4711e 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java @@ -25,14 +25,14 @@ import java.net.URL; import java.util.Collection; import java.util.Map; -import org.springframework.integration.mapping.support.JsonHeaders; -import org.springframework.util.Assert; - import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.integration.mapping.support.JsonHeaders; +import org.springframework.util.Assert; + /** * Jackson 2 JSON-processor (@link https://github.com/FasterXML) {@linkplain JsonObjectMapper} implementation. * Delegates toJson and fromJson @@ -90,7 +90,8 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper map, Class sourceClass) { - JavaType javaType = this.objectMapper.constructType(sourceClass); - map.put(JsonHeaders.TYPE_ID, javaType.getRawClass()); - - if (javaType.isContainerType() && !javaType.isArrayType()) { - map.put(JsonHeaders.CONTENT_TYPE_ID, javaType.getContentType().getRawClass()); - } - - if (javaType.getKeyType() != null) { - map.put(JsonHeaders.KEY_TYPE_ID, javaType.getKeyType().getRawClass()); - } - } - @Override @SuppressWarnings({ "unchecked" }) protected JavaType extractJavaType(Map javaTypes) throws Exception { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonMessageParser.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonMessageParser.java deleted file mode 100644 index 55f1147e7a..0000000000 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonMessageParser.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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.json; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.codehaus.jackson.JsonFactory; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.JsonToken; - -import org.springframework.messaging.Message; -import org.springframework.util.Assert; - -/** - * {@link JsonInboundMessageMapper.JsonMessageParser} implementation that parses JSON messages - * and builds a {@link Message} with the specified payload type from provided {@link JsonInboundMessageMapper}. - * Uses Jackson JSON-processor (@link http://jackson.codehaus.org). - * - * @deprecated Please migrate to {@link Jackson2JsonMessageParser} for Jackson 2.x. - * - * @author Artem Bilan - * @since 3.0 - */ -@Deprecated -public class JacksonJsonMessageParser extends AbstractJacksonJsonMessageParser { - - public JacksonJsonMessageParser() { - super(new JacksonJsonObjectMapper()); - } - - @Override - protected JsonParser createJsonParser(String jsonMessage) throws Exception { - return new JsonFactory().createJsonParser(jsonMessage); - } - - @Override - protected Message parseWithHeaders(JsonParser parser, String jsonMessage) throws Exception { - String error = AbstractJsonInboundMessageMapper.MESSAGE_FORMAT_ERROR + jsonMessage; - Assert.isTrue(parser.nextToken() == JsonToken.START_OBJECT, error); - Map headers = null; - Object payload = null; - while (parser.nextToken() != JsonToken.END_OBJECT) { - Assert.isTrue(parser.getCurrentToken() == JsonToken.FIELD_NAME, error); - boolean isHeadersToken = "headers".equals(parser.getCurrentName()); - boolean isPayloadToken = "payload".equals(parser.getCurrentName()); - Assert.isTrue(isHeadersToken || isPayloadToken, error); - if (isHeadersToken) { - Assert.isTrue(parser.nextToken() == JsonToken.START_OBJECT, error); - headers = readHeaders(parser, jsonMessage); - } - else if (isPayloadToken) { - parser.nextToken(); - payload = this.readPayload(parser, jsonMessage); - } - } - Assert.notNull(headers, error); - return this.getMessageBuilderFactory().withPayload(payload).copyHeaders(headers).build(); - } - - private Map readHeaders(JsonParser parser, String jsonMessage) throws Exception { - Map headers = new LinkedHashMap(); - while (parser.nextToken() != JsonToken.END_OBJECT) { - String headerName = parser.getCurrentName(); - parser.nextToken(); - Object headerValue = this.readHeader(parser, headerName, jsonMessage); - headers.put(headerName, headerValue); - } - return headers; - } - -} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonObjectMapper.java deleted file mode 100644 index 1b50d68cba..0000000000 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonObjectMapper.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 - * - * 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.json; - -import java.io.File; -import java.io.InputStream; -import java.io.Reader; -import java.io.Writer; -import java.lang.reflect.Type; -import java.net.URL; -import java.util.Collection; -import java.util.Map; - -import org.codehaus.jackson.JsonNode; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.type.JavaType; - -import org.springframework.integration.mapping.support.JsonHeaders; -import org.springframework.util.Assert; - -/** - * Jackson JSON-processor (@link http://jackson.codehaus.org) {@linkplain JsonObjectMapper} implementation. - * Delegates toJson and fromJson - * to the {@linkplain org.codehaus.jackson.map.ObjectMapper} - * - * @deprecated Please migrate to {@link Jackson2JsonObjectMapper} for Jackson 2.x. - * - * @author Artem Bilan - * @since 3.0 - */ -@Deprecated -public class JacksonJsonObjectMapper extends AbstractJacksonJsonObjectMapper { - - private final ObjectMapper objectMapper; - - public JacksonJsonObjectMapper() { - this.objectMapper = new ObjectMapper(); - } - - public JacksonJsonObjectMapper(ObjectMapper objectMapper) { - Assert.notNull(objectMapper, "objectMapper must not be null"); - this.objectMapper = objectMapper; - } - - @Override - public String toJson(Object value) throws Exception { - return this.objectMapper.writeValueAsString(value); - } - - @Override - public void toJson(Object value, Writer writer) throws Exception { - this.objectMapper.writeValue(writer, value); - } - - @Override - public JsonNode toJsonNode(Object value) throws Exception { - return this.objectMapper.valueToTree(value); - } - - @Override - public T fromJson(JsonParser parser, Type valueType) throws Exception { - return this.objectMapper.readValue(parser, this.constructType(valueType)); - } - - @Override - protected T fromJson(Object json, JavaType type) throws Exception { - if (json instanceof String) { - return this.objectMapper.readValue((String) json, type); - } - else if(json instanceof byte[]) { - return this.objectMapper.readValue((byte[]) json, type); - } - else if (json instanceof File) { - return this.objectMapper.readValue((File) json, type); - } - else if (json instanceof URL) { - return this.objectMapper.readValue((URL) json, type); - } - else if (json instanceof InputStream) { - return this.objectMapper.readValue((InputStream) json, type); - } - else if (json instanceof Reader) { - return this.objectMapper.readValue((Reader) json, type); - } - else { - throw new IllegalArgumentException("'json' argument must be an instance of: " + supportedJsonTypes); - } - } - - @Override - public void populateJavaTypes(Map map, Class sourceClass) { - JavaType javaType = this.constructType(sourceClass); - map.put(JsonHeaders.TYPE_ID, javaType.getRawClass()); - - if (javaType.isContainerType() && !javaType.isArrayType()) { - map.put(JsonHeaders.CONTENT_TYPE_ID, javaType.getContentType().getRawClass()); - } - - if (javaType.getKeyType() != null) { - map.put(JsonHeaders.KEY_TYPE_ID, javaType.getKeyType().getRawClass()); - } - } - - @Override - protected JavaType constructType(Type type) { - return this.objectMapper.constructType(type); - } - - @Override - @SuppressWarnings({ "unchecked" }) - protected JavaType extractJavaType(Map javaTypes) throws Exception { - JavaType classType = this.createJavaType(javaTypes, JsonHeaders.TYPE_ID); - if (!classType.isContainerType() || classType.isArrayType()) { - return classType; - } - - JavaType contentClassType = this.createJavaType(javaTypes, JsonHeaders.CONTENT_TYPE_ID); - if (classType.getKeyType() == null) { - return this.objectMapper.getTypeFactory() - .constructCollectionType((Class>) classType.getRawClass(), contentClassType); - } - - JavaType keyClassType = this.createJavaType(javaTypes, JsonHeaders.KEY_TYPE_ID); - return this.objectMapper.getTypeFactory() - .constructMapType((Class>) classType.getRawClass(), keyClassType, contentClassType); - } - -} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java index 007fc92acb..191d4db528 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonUtils.java @@ -38,9 +38,6 @@ public final class JacksonJsonUtils { ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", classLoader) && ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", classLoader); - private static final IllegalStateException NO_JACKSON_LIB_EXCEPTION = - new IllegalStateException("Neither jackson-databind.jar, nor jackson-mapper-asl.jar aren't presented in the classpath."); - public static boolean isJackson2Present() { return jackson2Present; } @@ -49,7 +46,4 @@ public final class JacksonJsonUtils { return jacksonPresent; } - public static IllegalStateException getNoJacksonLibException() { - return NO_JACKSON_LIB_EXCEPTION; - } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapper.java index 3fea529b84..b8183f2342 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapper.java @@ -44,5 +44,5 @@ public interface JsonObjectMapper { T fromJson(P parser, Type valueType) throws Exception; - void populateJavaTypes(Map map, Class sourceClass); + void populateJavaTypes(Map map, Object object); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapperAdapter.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapperAdapter.java index eda8de1db5..6a9b564f73 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapperAdapter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapperAdapter.java @@ -18,8 +18,11 @@ package org.springframework.integration.support.json; import java.io.Writer; import java.lang.reflect.Type; +import java.util.Collection; import java.util.Map; +import org.springframework.integration.mapping.support.JsonHeaders; + /** * Simple {@linkplain JsonObjectMapper} adapter implementation, if there is no need * to provide entire operations implementation. @@ -59,7 +62,15 @@ public abstract class JsonObjectMapperAdapter implements JsonObjectMapper< } @Override - public void populateJavaTypes(Map map, Class sourceClass) { + public void populateJavaTypes(Map map, Object object) { + map.put(JsonHeaders.TYPE_ID, object.getClass()); + if (object instanceof Collection && !((Collection) object).isEmpty()) { + map.put(JsonHeaders.CONTENT_TYPE_ID, ((Collection) object).iterator().next().getClass()); + } + if (object instanceof Map && !((Map) object).isEmpty()) { + map.put(JsonHeaders.CONTENT_TYPE_ID, ((Map) object).values().iterator().next().getClass()); + map.put(JsonHeaders.KEY_TYPE_ID, ((Map) object).keySet().iterator().next().getClass()); + } } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonObjectMapperProvider.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapperProvider.java similarity index 61% rename from spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonObjectMapperProvider.java rename to spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapperProvider.java index 9cd0f6e79c..16d06f07ad 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JacksonJsonObjectMapperProvider.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonObjectMapperProvider.java @@ -17,9 +17,11 @@ package org.springframework.integration.support.json; +import org.springframework.util.ClassUtils; + /** - * Simple factory to provide {@linkplain Jackson2JsonObjectMapper} or {@linkplain JacksonJsonObjectMapper} - * instances dependently of jackson-databind or jackson-mapper-asl libs in the classpath. + * Simple factory to provide {@linkplain JsonObjectMapper} + * instances dependently of jackson-databind or boon libs in the classpath. * If there are both libs in the classpath, it prefers Jackson 2 JSON-processor implementation. * If there is not any of them, {@linkplain IllegalStateException} will be thrown. * @@ -28,18 +30,25 @@ package org.springframework.integration.support.json; * @since 3.0 * * @see Jackson2JsonObjectMapper + * @see org.springframework.integration.support.json.BoonJsonObjectMapper */ -public final class JacksonJsonObjectMapperProvider { +public final class JsonObjectMapperProvider { + + private static final ClassLoader classLoader = JsonObjectMapperProvider.class.getClassLoader(); + + private static final boolean boonPresent = + ClassUtils.isPresent("org.boon.json.ObjectMapper", classLoader); - @SuppressWarnings("deprecation") public static JsonObjectMapper newInstance() { if (JacksonJsonUtils.isJackson2Present()) { return new Jackson2JsonObjectMapper(); } - if(JacksonJsonUtils.isJacksonPresent()) { - return new JacksonJsonObjectMapper(); + else if (boonPresent) { + return new BoonJsonObjectMapper(); + } + else { + throw new IllegalStateException("Neither jackson-databind.jar, nor boon.jar is present in the classpath."); } - throw JacksonJsonUtils.getNoJacksonLibException(); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonOutboundMessageMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonOutboundMessageMapper.java index c9321a8501..daa6806a73 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonOutboundMessageMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/JsonOutboundMessageMapper.java @@ -35,7 +35,7 @@ public class JsonOutboundMessageMapper implements OutboundMessageMapper private volatile JsonObjectMapper jsonObjectMapper; public JsonOutboundMessageMapper() { - this(JacksonJsonObjectMapperProvider.newInstance()); + this(JsonObjectMapperProvider.newInstance()); } public JsonOutboundMessageMapper(JsonObjectMapper jsonObjectMapper) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToMapTransformer.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToMapTransformer.java index e203dd249d..d5123fd68d 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToMapTransformer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ObjectToMapTransformer.java @@ -20,7 +20,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import org.springframework.integration.support.json.JacksonJsonObjectMapperProvider; +import org.springframework.integration.support.json.JsonObjectMapperProvider; import org.springframework.integration.support.json.JsonObjectMapper; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -54,7 +54,7 @@ import org.springframework.util.StringUtils; */ public class ObjectToMapTransformer extends AbstractPayloadTransformer> { - private final JsonObjectMapper jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance(); + private final JsonObjectMapper jsonObjectMapper = JsonObjectMapperProvider.newInstance(); private volatile boolean shouldFlattenKeys = true; diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/AbstractJsonInboundMessageMapperTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/AbstractJsonInboundMessageMapperTests.java index 2da2dea607..cd4520b734 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/AbstractJsonInboundMessageMapperTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/AbstractJsonInboundMessageMapperTests.java @@ -33,10 +33,10 @@ import org.junit.Test; import org.springframework.core.ParameterizedTypeReference; import org.springframework.integration.message.MessageMatcher; import org.springframework.integration.support.MessageBuilder; -import org.springframework.integration.support.json.JacksonJsonObjectMapperProvider; import org.springframework.integration.support.json.JsonInboundMessageMapper; import org.springframework.integration.support.json.JsonInboundMessageMapper.JsonMessageParser; import org.springframework.integration.support.json.JsonObjectMapper; +import org.springframework.integration.support.json.JsonObjectMapperProvider; import org.springframework.messaging.Message; /** @@ -48,7 +48,7 @@ import org.springframework.messaging.Message; */ public abstract class AbstractJsonInboundMessageMapperTests { - private final JsonObjectMapper mapper = JacksonJsonObjectMapperProvider.newInstance(); + private final JsonObjectMapper mapper = JsonObjectMapperProvider.newInstance(); @Factory public static Matcher> sameExceptImmutableHeaders(Message operand) { diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/JacksonJsonInboundMessageMapperTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/JacksonJsonInboundMessageMapperTests.java deleted file mode 100644 index 4806b1dc7a..0000000000 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/JacksonJsonInboundMessageMapperTests.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.json; - -import org.springframework.integration.support.json.JsonInboundMessageMapper.JsonMessageParser; - -/** - * @author Gary Russell - * @since 3.0 - * - */ -@Deprecated -public class JacksonJsonInboundMessageMapperTests extends AbstractJsonInboundMessageMapperTests { - - @Override - protected JsonMessageParser getParser() { - return new org.springframework.integration.support.json.JacksonJsonMessageParser(); - } - -} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/JacksonJsonSymmetricalMessageMappingTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/JacksonJsonSymmetricalMessageMappingTests.java deleted file mode 100644 index 9915b2a242..0000000000 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/JacksonJsonSymmetricalMessageMappingTests.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.json; - -import org.springframework.integration.support.json.JsonInboundMessageMapper.JsonMessageParser; - -/** - * @author Gary Russell - * @since 3.0 - * - */ -@Deprecated -public class JacksonJsonSymmetricalMessageMappingTests extends AbstractJsonSymmetricalMessageMappingTests { - - @Override - protected JsonMessageParser getParser() { - return new org.springframework.integration.support.json.JacksonJsonMessageParser(); - } - -} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/JsonOutboundMessageMapperTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonOutboundMessageMapperTests.java index f6c01055aa..f6b393aaa9 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/JsonOutboundMessageMapperTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonOutboundMessageMapperTests.java @@ -16,16 +16,14 @@ package org.springframework.integration.json; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.IOException; -import org.codehaus.jackson.JsonFactory; -import org.codehaus.jackson.JsonParseException; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.JsonToken; -import org.codehaus.jackson.map.ObjectMapper; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; import org.springframework.integration.history.MessageHistory; @@ -112,8 +110,8 @@ public class JsonOutboundMessageMapperTests { assertEquals(payload, parsedPayload); } - private TestBean extractJsonPayloadToTestBean(String json) throws JsonParseException, IOException { - JsonParser parser = jsonFactory.createJsonParser(json); + private TestBean extractJsonPayloadToTestBean(String json) throws IOException { + JsonParser parser = jsonFactory.createParser(json); do { parser.nextToken(); } while(parser.getCurrentToken() != JsonToken.FIELD_NAME || !parser.getCurrentName().equals("payload")); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerTests.java index 6b89d19f75..a318983c39 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonToObjectTransformerTests.java @@ -16,16 +16,16 @@ package org.springframework.integration.json; -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import org.springframework.integration.support.json.Jackson2JsonObjectMapper; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.GenericMessage; +import static org.junit.Assert.*; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; + +import org.springframework.integration.support.json.BoonJsonObjectMapper; +import org.springframework.integration.support.json.Jackson2JsonObjectMapper; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.GenericMessage; /** * @author Mark Fisher @@ -62,4 +62,17 @@ public class JsonToObjectTransformerTests { assertEquals("123 Main Street", person.getAddress().toString()); } + + @Test + public void testBoonJsonObjectMapper() throws Exception { + JsonToObjectTransformer transformer = new JsonToObjectTransformer(TestPerson.class, new BoonJsonObjectMapper()); + String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42,\"address\":{\"number\":123,\"street\":\"Main Street\"}}"; + Message message = transformer.transform(new GenericMessage(jsonString)); + TestPerson person = (TestPerson) message.getPayload(); + assertEquals("John", person.getFirstName()); + assertEquals("Doe", person.getLastName()); + assertEquals(42, person.getAge()); + assertEquals("123 Main Street", person.getAddress().toString()); + } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/json/JsonTransformersSymmetricalTests.java b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonTransformersSymmetricalTests.java index 826d69c7b6..2b525d3fd3 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/json/JsonTransformersSymmetricalTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/json/JsonTransformersSymmetricalTests.java @@ -16,10 +16,15 @@ package org.springframework.integration.json; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; +import java.util.ArrayList; +import java.util.List; + +import org.hamcrest.Matchers; import org.junit.Test; +import org.springframework.integration.support.json.BoonJsonObjectMapper; import org.springframework.messaging.Message; import org.springframework.messaging.support.GenericMessage; @@ -35,13 +40,35 @@ public class JsonTransformersSymmetricalTests { TestPerson person = new TestPerson("John", "Doe", 42); person.setAddress(new TestAddress(123, "Main Street")); + List payload = new ArrayList(); + payload.add(person); + ObjectToJsonTransformer objectToJsonTransformer = new ObjectToJsonTransformer(); - Message jsonMessage = objectToJsonTransformer.transform(new GenericMessage(person)); + Message jsonMessage = objectToJsonTransformer.transform(new GenericMessage(payload)); JsonToObjectTransformer jsonToObjectTransformer = new JsonToObjectTransformer(); - Message result = jsonToObjectTransformer.transform(jsonMessage); - - assertEquals(person, result.getPayload()); + Object result = jsonToObjectTransformer.transform(jsonMessage).getPayload(); + assertThat(result, Matchers.instanceOf(List.class)); + assertEquals(person, ((List) result).get(0)); } + @Test + public void testBoonObjectToJson_JsonToObject() { + + TestPerson person = new TestPerson("John", "Doe", 42); + person.setAddress(new TestAddress(123, "Main Street")); + + List payload = new ArrayList(); + payload.add(person); + + ObjectToJsonTransformer objectToJsonTransformer = new ObjectToJsonTransformer(new BoonJsonObjectMapper()); + Message jsonMessage = objectToJsonTransformer.transform(new GenericMessage(payload)); + + JsonToObjectTransformer jsonToObjectTransformer = new JsonToObjectTransformer(new BoonJsonObjectMapper()); + Object result = jsonToObjectTransformer.transform(jsonMessage).getPayload(); + assertThat(result, Matchers.instanceOf(List.class)); + assertEquals(person, ((List) result).get(0)); + } + + } 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 90ae2bba17..8a67f2bbf0 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 @@ -18,6 +18,11 @@ + + + + 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 b5eac1a74a..902de88128 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 @@ -16,15 +16,13 @@ package org.springframework.integration.json; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.fasterxml.jackson.databind.JsonNode; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,6 +33,7 @@ import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.mapping.support.JsonHeaders; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.support.json.Jackson2JsonObjectMapper; import org.springframework.integration.support.json.JsonObjectMapperAdapter; @@ -45,8 +44,6 @@ import org.springframework.messaging.MessageHeaders; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.fasterxml.jackson.databind.JsonNode; - /** * @author Mark Fisher * @author Oleg Zhurakousky @@ -70,6 +67,9 @@ public class ObjectToJsonTransformerParserTests { @Autowired private volatile MessageChannel jsonNodeInput; + @Autowired + private volatile MessageChannel boonJsonNodeInput; + @Test public void testContentType(){ ObjectToJsonTransformer transformer = @@ -165,6 +165,25 @@ public class ObjectToJsonTransformerParserTests { assertTrue(expression.getValue(evaluationContext, payload, Boolean.class)); } + @Test + public void testBoonNodeResultType() { + TestPerson person = new TestPerson(); + person.setFirstName("John"); + person.setLastName("Doe"); + person.setAge(42); + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload(person).setReplyChannel(replyChannel).build(); + this.boonJsonNodeInput.send(message); + Message reply = replyChannel.receive(0); + assertNotNull(reply); + Object payload = reply.getPayload(); + assertThat(payload, Matchers.instanceOf(Map.class)); + assertEquals(TestPerson.class, reply.getHeaders().get(JsonHeaders.TYPE_ID)); + + Expression expression = new SpelExpressionParser().parseExpression("[firstName] == 'John' and [age] == 42"); + assertTrue(expression.getValue(new StandardEvaluationContext(), payload, Boolean.class)); + } + static class CustomJsonObjectMapper extends JsonObjectMapperAdapter { @Override 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 a08a852b3f..78d21d3743 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 @@ -16,16 +16,24 @@ package org.springframework.integration.json; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.Test; +import org.springframework.context.expression.MapAccessor; +import org.springframework.expression.Expression; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.support.json.BoonJsonObjectMapper; import org.springframework.integration.support.json.Jackson2JsonObjectMapper; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; @@ -142,4 +150,39 @@ public class ObjectToJsonTransformerTests { assertTrue(addressResult.contains("street:\"Main Street\"")); } + @Test + public void testBoonJsonObjectMapper() throws Exception { + ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(new BoonJsonObjectMapper()); + TestPerson person = new TestPerson("John", "Doe", 42); + person.setAddress(new TestAddress(123, "Main Street")); + String result = (String) transformer.transform(new GenericMessage(person)).getPayload(); + assertTrue(result.contains("\"firstName\":\"John\"")); + assertTrue(result.contains("\"lastName\":\"Doe\"")); + assertTrue(result.contains("\"age\":42")); + Pattern addressPattern = Pattern.compile("(\"address\":\\{.*?\\})"); + Matcher matcher = addressPattern.matcher(result); + assertTrue(matcher.find()); + String addressResult = matcher.group(1); + assertTrue(addressResult.contains("\"number\":123")); + assertTrue(addressResult.contains("\"street\":\"Main Street\"")); + } + + @Test + public void testBoonJsonObjectMapper_toNode() throws Exception { + ObjectToJsonTransformer transformer = new ObjectToJsonTransformer(new BoonJsonObjectMapper(), + ObjectToJsonTransformer.ResultType.NODE); + TestPerson person = new TestPerson("John", "Doe", 42); + person.setAddress(new TestAddress(123, "Main Street")); + Object payload = transformer.transform(new GenericMessage(person)).getPayload(); + assertThat(payload, instanceOf(Map.class)); + + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("firstName + ': ' + address.street"); + StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + evaluationContext.addPropertyAccessor(new MapAccessor()); + String value = expression.getValue(evaluationContext, payload, String.class); + + assertEquals("John: Main Street", value); + } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ObjectToMapTransformerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ObjectToMapTransformerTests.java index 6769517675..91cdfd9c0b 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ObjectToMapTransformerTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ObjectToMapTransformerTests.java @@ -15,6 +15,8 @@ */ package org.springframework.integration.transformer; +import static org.junit.Assert.*; + import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; @@ -23,22 +25,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.codehaus.jackson.JsonGenerationException; -import org.codehaus.jackson.JsonParseException; -import org.codehaus.jackson.map.JsonMappingException; import org.junit.Test; + import org.springframework.context.expression.MapAccessor; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.messaging.Message; import org.springframework.integration.support.MessageBuilder; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import static org.junit.Assert.assertNull; +import org.springframework.messaging.Message; /** * @@ -47,9 +42,10 @@ import static org.junit.Assert.assertNull; * @since 2.0 */ public class ObjectToMapTransformerTests { + @SuppressWarnings("unchecked") @Test - public void testObjectToSpelMapTransformer() throws JsonParseException, JsonMappingException, JsonGenerationException, IOException{ + public void testObjectToSpelMapTransformer() throws IOException { Employee employee = this.buildEmployee(); StandardEvaluationContext context = new StandardEvaluationContext(); context.addPropertyAccessor(new MapAccessor()); diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/serializer/MapJsonSerializer.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/serializer/MapJsonSerializer.java index 01b962edb9..2b3e2d13dd 100644 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/serializer/MapJsonSerializer.java +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/serializer/MapJsonSerializer.java @@ -26,13 +26,13 @@ import java.util.Map; import org.springframework.core.serializer.Deserializer; import org.springframework.core.serializer.Serializer; -import org.springframework.integration.support.json.JacksonJsonObjectMapperProvider; +import org.springframework.integration.support.json.JsonObjectMapperProvider; import org.springframework.integration.support.json.JsonObjectMapper; import org.springframework.util.Assert; /** * Serializes a {@link Map} as JSON. Deserializes JSON to - * a {@link Map}. The default {@link JacksonJsonObjectMapperProvider#newInstance()} can be + * a {@link Map}. The default {@link org.springframework.integration.support.json.JsonObjectMapperProvider#newInstance()} can be * overridden using {@link #setJsonObjectMapper(JsonObjectMapper)}. *

* The JSON deserializer can't delimit multiple JSON @@ -48,7 +48,7 @@ import org.springframework.util.Assert; */ public class MapJsonSerializer implements Serializer>, Deserializer> { - private volatile JsonObjectMapper jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance(); + private volatile JsonObjectMapper jsonObjectMapper = JsonObjectMapperProvider.newInstance(); private volatile Deserializer packetDeserializer = new ByteArrayLfSerializer(); diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpUnicastEndToEndTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpUnicastEndToEndTests.java index 614d457abe..229e6a9b32 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpUnicastEndToEndTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpUnicastEndToEndTests.java @@ -96,7 +96,7 @@ public class UdpUnicastEndToEndTests implements Runnable { } @Test - public void tesUudpOutboundChannelAdapterWithinChain() throws Exception { + public void tesUdpOutboundChannelAdapterWithinChain() throws Exception { UdpUnicastEndToEndTests launcher = new UdpUnicastEndToEndTests(); Thread t = new Thread(launcher); t.start(); // launch the receiver @@ -189,7 +189,7 @@ public class UdpUnicastEndToEndTests implements Runnable { public static void main(String[] args) throws Exception { hangAroundFor = 120000; new UdpUnicastEndToEndTests().runIt(); - new UdpUnicastEndToEndTests().tesUudpOutboundChannelAdapterWithinChain(); + new UdpUnicastEndToEndTests().tesUdpOutboundChannelAdapterWithinChain(); } } diff --git a/spring-integration-redis/src/test/java/org/springframework/integration/redis/outbound/RedisQueueOutboundChannelAdapterTests.java b/spring-integration-redis/src/test/java/org/springframework/integration/redis/outbound/RedisQueueOutboundChannelAdapterTests.java index e913750610..d3ba16574d 100644 --- a/spring-integration-redis/src/test/java/org/springframework/integration/redis/outbound/RedisQueueOutboundChannelAdapterTests.java +++ b/spring-integration-redis/src/test/java/org/springframework/integration/redis/outbound/RedisQueueOutboundChannelAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors + * Copyright 2013-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. @@ -31,7 +31,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.data.redis.serializer.JacksonJsonRedisSerializer; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.integration.mapping.InboundMessageMapper; @@ -69,7 +69,8 @@ public class RedisQueueOutboundChannelAdapterTests extends RedisAvailableTests { final String queueName = "si.test.testRedisQueueOutboundChannelAdapter"; - final RedisQueueOutboundChannelAdapter handler = new RedisQueueOutboundChannelAdapter(queueName, this.connectionFactory); + final RedisQueueOutboundChannelAdapter handler = new RedisQueueOutboundChannelAdapter(queueName, + this.connectionFactory); String payload = "testing"; handler.handleMessage(MessageBuilder.withPayload(payload).build()); @@ -105,7 +106,8 @@ public class RedisQueueOutboundChannelAdapterTests extends RedisAvailableTests { final String queueName = "si.test.testRedisQueueOutboundChannelAdapter2"; - final RedisQueueOutboundChannelAdapter handler = new RedisQueueOutboundChannelAdapter(queueName, this.connectionFactory); + final RedisQueueOutboundChannelAdapter handler = new RedisQueueOutboundChannelAdapter(queueName, + this.connectionFactory); handler.setExtractPayload(false); Message message = MessageBuilder.withPayload("testing").build(); @@ -131,8 +133,9 @@ public class RedisQueueOutboundChannelAdapterTests extends RedisAvailableTests { final String queueName = "si.test.testRedisQueueOutboundChannelAdapter2"; - final RedisQueueOutboundChannelAdapter handler = new RedisQueueOutboundChannelAdapter(queueName, this.connectionFactory); - handler.setSerializer(new JacksonJsonRedisSerializer(Object.class)); + final RedisQueueOutboundChannelAdapter handler = new RedisQueueOutboundChannelAdapter(queueName, + this.connectionFactory); + handler.setSerializer(new Jackson2JsonRedisSerializer(Object.class)); RedisTemplate redisTemplate = new StringRedisTemplate(); redisTemplate.setConnectionFactory(this.connectionFactory); @@ -168,7 +171,8 @@ public class RedisQueueOutboundChannelAdapterTests extends RedisAvailableTests { String result = redisTemplate.boundListOps(queueName).rightPop(5000, TimeUnit.MILLISECONDS); assertNotNull(result); - InboundMessageMapper mapper = new JsonInboundMessageMapper(String.class, new Jackson2JsonMessageParser()); + InboundMessageMapper mapper = new JsonInboundMessageMapper(String.class, + new Jackson2JsonMessageParser()); Message resultMessage = mapper.toMessage(result); assertEquals(message.getPayload(), resultMessage.getPayload()); } diff --git a/src/reference/docbook/transformer.xml b/src/reference/docbook/transformer.xml index 3a1feafb5b..c40f3cb11f 100644 --- a/src/reference/docbook/transformer.xml +++ b/src/reference/docbook/transformer.xml @@ -240,9 +240,9 @@ public class Child { type="foo.MyDomainObject"/>]]> - These use a vanilla Jackson ObjectMapper by default. If you wish to customize the ObjectMapper (for example, - to configure the 'ALLOW_COMMENTS' feature when parsing JSON), you can supply a reference to your custom ObjectMapper bean using - the object-mapper attribute. + These use a vanilla JsonObjectMapper by default based on implementation from + classpath. You can provide your own custom JsonObjectMapper implementation + with appropriate options or based on required library (e.g. GSON). object-mapper attribute references an instance of a new strategy interface JsonObjectMapper. This abstraction allows multiple implementations of json mappers to be used. Implementations that wrap - Jackson 1.x and + Boon and Jackson 2 are provided, with the version being detected on the classpath. These classes are - JacksonJsonObjectMapper and Jackson2JsonObjectMapper. + BoonJsonObjectMapper and Jackson2JsonObjectMapper. - For backward compatibility, a simple Jackson 1.x ObjectMapper can be provided - instead of a JsonObjectMapper. This will be removed in a future release. + Note, BoonJsonObjectMapper is provided since version 4.1. - If there are requirements to use both Jackson libraries in the same application, keep in mind that - before version 3.0, the JSON transformers used only Jackson 1.x and, from 3.0 on, the framework will - select Jackson 2 by default, if both are on the classpath. - So, to avoid unexpected issues with Jackson's mapping features, when using annotations, there may be - a need to apply annotations from both Jacksons on domain classes: + If there are requirements to use both Jackson libraries and/or Boon in the same application, + keep in mind that before version 3.0, the JSON transformers used only Jackson 1.x. + From 4.1 on, the framework will select Jackson 2 by default ahead of the + Boon implementation if both are on the classpath. + Jackson 1.x is no longer supported by the framework internally but, of course, you can still use it + within your code. + To avoid unexpected issues with JSON mapping features, when using annotations, there may be + a need to apply annotations from both Jacksons and/or Boon on domain classes: @@ -377,6 +381,7 @@ public class Foo { The node JSON representation provides efficiency for using the JsonPropertyAccessor, when the downstream message flow uses SpEL expressions with access to the properties of the JSON data. See . + When using Boon, the NODE representation is a Map<String, Object> diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index 0aa753ddb2..22257840b5 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -162,5 +162,13 @@ See for more information. +
+ BoonJsonObjectMapper + + The Boon JsonObjectMapper is now provided + for the JSON transformers. See for more information. + +
+