objectMapper) {
+ this.objectMapper = objectMapper;
+ }
+
+ @Override
+ public Message> doInParser(JsonInboundMessageMapper messageMapper, String jsonMessage) throws Exception {
+ if (this.messageMapper == null) {
+ this.messageMapper = messageMapper;
+ }
+ P parser = this.createJsonParser(jsonMessage);
+ if (messageMapper.isMapToPayload()) {
+ try {
+ return MessageBuilder.withPayload(this.readPayload(parser)).build();
+ }
+ catch (Exception ex) {
+ throw new IllegalArgumentException("Mapping of JSON message " + jsonMessage +
+ " directly to payload of type " + messageMapper.getPayloadType() + " failed.", ex);
+ }
+ }
+ else {
+ return this.parseWithHeaders(parser, jsonMessage);
+ }
+ }
+
+ protected Object readPayload(P parser) throws Exception {
+ return objectMapper.fromJson(parser, this.messageMapper.getPayloadType());
+ }
+
+ protected abstract Message> parseWithHeaders(P parser, String jsonMessage) throws Exception;
+
+ protected abstract P createJsonParser(String jsonMessage) throws Exception;
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/AbstractJsonInboundMessageMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/json/AbstractJsonInboundMessageMapper.java
new file mode 100644
index 0000000000..c5d2102135
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/AbstractJsonInboundMessageMapper.java
@@ -0,0 +1,73 @@
+/*
+ * 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 java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.integration.MessageHeaders;
+import org.springframework.integration.mapping.InboundMessageMapper;
+import org.springframework.util.Assert;
+
+/**
+ * Abstract {@link InboundMessageMapper} implementation that maps incoming JSON messages
+ * to a {@link org.springframework.integration.Message} with the specified payload type.
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ *
+ * @see JsonInboundMessageMapper
+ */
+public abstract class AbstractJsonInboundMessageMapper
implements InboundMessageMapper {
+
+ protected static final String MESSAGE_FORMAT_ERROR = "JSON message is invalid. Expected a message in the format of either " +
+ "{\"headers\":{...},\"payload\":{...}} or {\"payload\":{...}.\"headers\":{...}} but was ";
+
+ protected static final Map> DEFAULT_HEADER_TYPES = new HashMap>();
+
+ static {
+ DEFAULT_HEADER_TYPES.put(MessageHeaders.PRIORITY, Integer.class);
+ DEFAULT_HEADER_TYPES.put(MessageHeaders.EXPIRATION_DATE, Long.class);
+ DEFAULT_HEADER_TYPES.put(MessageHeaders.SEQUENCE_SIZE, Integer.class);
+ DEFAULT_HEADER_TYPES.put(MessageHeaders.SEQUENCE_NUMBER, Integer.class);
+ }
+
+ protected final Type payloadType;
+
+ protected final Map> headerTypes = DEFAULT_HEADER_TYPES;
+
+ protected volatile boolean mapToPayload = false;
+
+ public AbstractJsonInboundMessageMapper(Type payloadType) {
+ Assert.notNull(payloadType, "payloadType must not be null");
+ this.payloadType = payloadType;
+ }
+
+ public void setHeaderTypes(Map> headerTypes) {
+ this.headerTypes.putAll(headerTypes);
+ }
+
+ public void setMapToPayload(boolean mapToPayload) {
+ this.mapToPayload = mapToPayload;
+ }
+
+ protected abstract Object readPayload(P parser, String jsonMessage) throws Exception;
+
+ protected abstract Map readHeaders(P parser, String jsonMessage) throws Exception;
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/Jackson2JsonMessageParser.java b/spring-integration-core/src/main/java/org/springframework/integration/json/Jackson2JsonMessageParser.java
new file mode 100644
index 0000000000..90b1748bda
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/Jackson2JsonMessageParser.java
@@ -0,0 +1,99 @@
+/*
+ * 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 java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+import org.springframework.integration.Message;
+import org.springframework.integration.support.MessageBuilder;
+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 2 JSON-processor (@link https://github.com/FasterXML).
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ */
+class Jackson2JsonMessageParser extends AbstractJacksonJsonMessageParser {
+
+ public Jackson2JsonMessageParser() {
+ super(new Jackson2JsonObjectMapper());
+ }
+
+ @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();
+ try {
+ payload = this.readPayload(parser);
+ }
+ catch (JsonMappingException ex) {
+ throw new IllegalArgumentException("Mapping payload of JSON message " + jsonMessage +
+ " to payload type " + messageMapper.getPayloadType() + " failed.", ex);
+ }
+ }
+ }
+ Assert.notNull(headers, error);
+ return MessageBuilder.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();
+ Class> headerType = this.messageMapper.getHeaderTypes().containsKey(headerName) ?
+ this.messageMapper.getHeaderTypes().get(headerName) : Object.class;
+ try {
+ headers.put(headerName, this.objectMapper.fromJson(parser, headerType));
+ }
+ catch (JsonMappingException ex) {
+ throw new IllegalArgumentException("Mapping header \"" + headerName + "\" of JSON message " +
+ jsonMessage + " to header type " + messageMapper.getPayloadType() + " failed.", ex);
+ }
+ }
+ return headers;
+ }
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/Jackson2JsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/json/Jackson2JsonObjectMapper.java
new file mode 100644
index 0000000000..a543f7feeb
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/Jackson2JsonObjectMapper.java
@@ -0,0 +1,72 @@
+/*
+ * 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 java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.springframework.util.Assert;
+
+/**
+ * Jackson 2 JSON-processor (@link https://github.com/FasterXML) {@linkplain JsonObjectMapper} implementation.
+ * Delegates toJson and fromJson
+ * to the {@linkplain com.fasterxml.jackson.databind.ObjectMapper}
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ */
+public class Jackson2JsonObjectMapper implements JsonObjectMapper {
+
+ private final ObjectMapper objectMapper;
+
+ public Jackson2JsonObjectMapper() {
+ this.objectMapper = new ObjectMapper();
+ }
+
+ public Jackson2JsonObjectMapper(ObjectMapper objectMapper) {
+ Assert.notNull(objectMapper, "objectMapper must not be null");
+ this.objectMapper = objectMapper;
+ }
+
+ 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);
+ }
+
+ public T fromJson(String json, Class valueType) throws Exception {
+ return this.objectMapper.readValue(json, valueType);
+ }
+
+ @Override
+ public T fromJson(Reader json, Class valueType) throws Exception {
+ return this.objectMapper.readValue(json, valueType);
+ }
+
+ @Override
+ public T fromJson(JsonParser parser, Type valueType) throws Exception {
+ return this.objectMapper.readValue(parser, this.objectMapper.constructType(valueType));
+ }
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonMessageParser.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonMessageParser.java
new file mode 100644
index 0000000000..b081cb48d3
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonMessageParser.java
@@ -0,0 +1,97 @@
+/*
+ * 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 java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.springframework.integration.Message;
+import org.springframework.integration.support.MessageBuilder;
+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).
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ */
+class JacksonJsonMessageParser extends AbstractJacksonJsonMessageParser {
+
+ 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();
+ try {
+ payload = this.readPayload(parser);
+ }
+ catch (JsonMappingException ex) {
+ throw new IllegalArgumentException("Mapping payload of JSON message " + jsonMessage +
+ " to payload type " + messageMapper.getPayloadType() + " failed.", ex);
+ }
+ }
+ }
+ Assert.notNull(headers, error);
+ return MessageBuilder.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();
+ Class> headerType = this.messageMapper.getHeaderTypes().containsKey(headerName) ?
+ this.messageMapper.getHeaderTypes().get(headerName) : Object.class;
+ try {
+ headers.put(headerName, this.objectMapper.fromJson(parser, headerType));
+ }
+ catch (JsonMappingException ex) {
+ throw new IllegalArgumentException("Mapping header \"" + headerName + "\" of JSON message " +
+ jsonMessage + " to header type " + messageMapper.getPayloadType() + " failed.", ex);
+ }
+ }
+ return headers;
+ }
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonObjectMapper.java
new file mode 100644
index 0000000000..f769e66052
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonObjectMapper.java
@@ -0,0 +1,72 @@
+/*
+ * 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 java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.map.ObjectMapper;
+
+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}
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ */
+public class JacksonJsonObjectMapper implements JsonObjectMapper {
+
+ 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;
+ }
+
+ 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);
+ }
+
+ public T fromJson(String json, Class valueType) throws Exception {
+ return this.objectMapper.readValue(json, valueType);
+ }
+
+ @Override
+ public T fromJson(Reader json, Class valueType) throws Exception {
+ return this.objectMapper.readValue(json, valueType);
+ }
+
+ @Override
+ public T fromJson(JsonParser parser, Type valueType) throws Exception {
+ return this.objectMapper.readValue(parser, this.objectMapper.constructType(valueType));
+ }
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonObjectMapperProvider.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonObjectMapperProvider.java
new file mode 100644
index 0000000000..9a8818d0e8
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JacksonJsonObjectMapperProvider.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util.ClassUtils;
+
+/**
+ * Simple factory to provide {@linkplain Jackson2JsonObjectMapper} or {@linkplain JacksonJsonObjectMapper}
+ * instances dependently of jackson-databind or jackson-mapper-asl libs in the classpath.
+ * If there are both libs in the classpath, it prefers Jackson 2 JSON-processor implementation.
+ * If there is no any of them, {@linkplain IllegalStateException} will be thrown.
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ *
+ * @see Jackson2JsonObjectMapper
+ * @see JacksonJsonObjectMapper
+ * @see JsonToObjectTransformer
+ * @see ObjectToJsonTransformer
+ */
+public final class JacksonJsonObjectMapperProvider {
+
+ private static final ClassLoader classLoader = JacksonJsonObjectMapperProvider.class.getClassLoader();
+
+ private static final boolean jackson2Present =
+ ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
+ ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
+
+ private static final boolean jacksonPresent =
+ 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 JsonObjectMapper> newInstance() {
+ if (jackson2Present) {
+ return new Jackson2JsonObjectMapper();
+ }
+ if(jacksonPresent) {
+ return new JacksonJsonObjectMapper();
+ }
+ throw NO_JACKSON_LIB_EXCEPTION;
+ }
+
+ public static JsonInboundMessageMapper.JsonMessageParser> newJsonMessageParser() {
+ if (jackson2Present) {
+ return new Jackson2JsonMessageParser();
+ }
+ if(jacksonPresent) {
+ return new Jackson2JsonMessageParser();
+ }
+ throw NO_JACKSON_LIB_EXCEPTION;
+ }
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonInboundMessageMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonInboundMessageMapper.java
index 582ca852a7..5126e629d5 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonInboundMessageMapper.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonInboundMessageMapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * 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.
@@ -16,142 +16,78 @@
package org.springframework.integration.json;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.lang.reflect.Type;
import java.util.Map;
-import org.codehaus.jackson.JsonFactory;
-import org.codehaus.jackson.JsonParser;
-import org.codehaus.jackson.JsonToken;
-import org.codehaus.jackson.map.JsonMappingException;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.codehaus.jackson.map.type.TypeFactory;
-import org.codehaus.jackson.type.JavaType;
-import org.codehaus.jackson.type.TypeReference;
-
import org.springframework.integration.Message;
-import org.springframework.integration.MessageHeaders;
-import org.springframework.integration.mapping.InboundMessageMapper;
-import org.springframework.integration.support.MessageBuilder;
-import org.springframework.util.Assert;
/**
- * {@link InboundMessageMapper} implementation that maps incoming JSON messages to a {@link Message} with the specified payload type.
- *
+ * {@link org.springframework.integration.mapping.InboundMessageMapper} implementation that maps incoming JSON messages
+ * to a {@link Message} with the specified payload type.
+ * By default it uses {@link JacksonJsonObjectMapperProvider} to get Jackson or Jackson 2 {@link JsonMessageParser}
+ * implementation dependently from classpath.
+ * Any other {@link JsonMessageParser} implementation may be provided through the appropriate constructor.
+ *
* @author Jeremy Grelle
* @author Oleg Zhurakousky
* @author Mark Fisher
+ * @author Artem Bilan
* @since 2.0
*/
-public class JsonInboundMessageMapper implements InboundMessageMapper {
-
- private static final String MESSAGE_FORMAT_ERROR = "JSON message is invalid. Expected a message in the format of either " +
- "{\"headers\":{...},\"payload\":{...}} or {\"payload\":{...}.\"headers\":{...}} but was ";
-
- private static final Map> DEFAULT_HEADER_TYPES = new HashMap>();
-
- static {
- DEFAULT_HEADER_TYPES.put(MessageHeaders.PRIORITY, Integer.class);
- DEFAULT_HEADER_TYPES.put(MessageHeaders.EXPIRATION_DATE, Long.class);
- DEFAULT_HEADER_TYPES.put(MessageHeaders.SEQUENCE_SIZE, Integer.class);
- DEFAULT_HEADER_TYPES.put(MessageHeaders.SEQUENCE_NUMBER, Integer.class);
- }
-
-
- private final JavaType payloadType;
-
- private final Map> headerTypes = DEFAULT_HEADER_TYPES;
-
- private volatile ObjectMapper objectMapper = new ObjectMapper();
-
- private volatile boolean mapToPayload = false;
+public class JsonInboundMessageMapper extends AbstractJsonInboundMessageMapper> {
+ private volatile JsonMessageParser> messageParser;
public JsonInboundMessageMapper(Class> payloadType) {
- Assert.notNull(payloadType, "payloadType must not be null");
- this.payloadType = TypeFactory.defaultInstance().constructType(payloadType);
+ this((Type) payloadType);
}
- public JsonInboundMessageMapper(TypeReference> typeReference) {
- Assert.notNull(typeReference, "typeReference must not be null");
- this.payloadType = TypeFactory.defaultInstance().constructType(typeReference);
+ public JsonInboundMessageMapper(Type payloadType) {
+ this(payloadType, null);
}
-
- public void setObjectMapper(ObjectMapper objectMapper) {
- Assert.notNull(objectMapper, "objectMapper must not be null");
- this.objectMapper = objectMapper;
+ public JsonInboundMessageMapper(Class> payloadType, JsonMessageParser> messageParser) {
+ this((Type) payloadType, messageParser);
}
- public void setHeaderTypes(Map> headerTypes) {
- this.headerTypes.putAll(headerTypes);
+ public JsonInboundMessageMapper(Type payloadType, JsonMessageParser> messageParser) {
+ super(payloadType);
+ this.messageParser = messageParser != null ? messageParser : JacksonJsonObjectMapperProvider.newJsonMessageParser();
}
- public void setMapToPayload(boolean mapToPayload) {
- this.mapToPayload = mapToPayload;
+ public boolean isMapToPayload() {
+ return mapToPayload;
+ }
+
+ public Type getPayloadType() {
+ return payloadType;
+ }
+
+ public Map> getHeaderTypes() {
+ return headerTypes;
}
public Message> toMessage(String jsonMessage) throws Exception {
- JsonParser parser = new JsonFactory().createJsonParser(jsonMessage);
- if (this.mapToPayload) {
- try {
- return MessageBuilder.withPayload(readPayload(parser, jsonMessage)).build();
- }
- catch (JsonMappingException ex) {
- throw new IllegalArgumentException("Mapping of JSON message " + jsonMessage +
- " directly to payload of type " + this.payloadType.getRawClass().getName() + " failed.", ex);
- }
- }
- else {
- String error = 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();
- try {
- payload = readPayload(parser, jsonMessage);
- }
- catch (JsonMappingException ex) {
- throw new IllegalArgumentException("Mapping payload of JSON message " + jsonMessage +
- " to payload type " + this.payloadType.getRawClass().getName() + " failed.", ex);
- }
- }
- }
- Assert.notNull(headers, error);
- return MessageBuilder.withPayload(payload).copyHeaders(headers).build();
- }
+ return this.messageParser.doInParser(this, jsonMessage);
}
- protected Map readHeaders(JsonParser parser, String jsonMessage) throws Exception{
- Map headers = new LinkedHashMap();
- while (parser.nextToken() != JsonToken.END_OBJECT) {
- String headerName = parser.getCurrentName();
- parser.nextToken();
- Class> headerType = this.headerTypes.containsKey(headerName) ?
- this.headerTypes.get(headerName) : Object.class;
- try {
- headers.put(headerName, this.objectMapper.readValue(parser, headerType));
- }
- catch (JsonMappingException ex) {
- throw new IllegalArgumentException("Mapping header \"" + headerName + "\" of JSON message " +
- jsonMessage + " to header type " + this.payloadType.getRawClass().getName() + " failed.", ex);
- }
- }
- return headers;
+ @Override
+ protected Map readHeaders(JsonMessageParser> parser, String jsonMessage) throws Exception {
+ //No-op
+ return null;
}
- protected Object readPayload(JsonParser parser, String jsonMessage) throws Exception {
- return this.objectMapper.readValue(parser, this.payloadType);
+ @Override
+ protected Object readPayload(JsonMessageParser> parser, String jsonMessage) throws Exception {
+ //No-op
+ return null;
+ }
+
+
+ public static interface JsonMessageParser
{
+
+ Message> doInParser(JsonInboundMessageMapper messageMapper, String jsonMessage) throws Exception;
+
}
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonObjectMapper.java
new file mode 100644
index 0000000000..63df16a0d1
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonObjectMapper.java
@@ -0,0 +1,46 @@
+/*
+ * 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 java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+
+/**
+ * Strategy interface to convert an Object to/from the JSON representation.
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ *
+ * @see JsonToObjectTransformer
+ * @see ObjectToJsonTransformer
+ * @see JacksonJsonObjectMapper
+ * @see Jackson2JsonObjectMapper
+ */
+public interface JsonObjectMapper
{
+
+ String toJson(Object value) throws Exception;
+
+ void toJson(Object value, Writer writer) throws Exception;
+
+ T fromJson(String json, Class valueType) throws Exception;
+
+ T fromJson(Reader json, Class valueType) throws Exception;
+
+ T fromJson(P parser, Type valueType) throws Exception;
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonObjectMapperAdapter.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonObjectMapperAdapter.java
new file mode 100644
index 0000000000..5d4728cd39
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonObjectMapperAdapter.java
@@ -0,0 +1,56 @@
+/*
+ * 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 java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+
+/**
+ * Simple {@linkplain JsonObjectMapper} adapter implementation, if there is no need
+ * to provide entire operations implementation.
+ *
+ * @author Artem Bilan
+ * @since 3.0
+ */
+public abstract class JsonObjectMapperAdapter
implements JsonObjectMapper
{
+
+ @Override
+ public String toJson(Object value) throws Exception {
+ return null;
+ }
+
+ @Override
+ public void toJson(Object value, Writer writer) throws Exception {
+ }
+
+ @Override
+ public T fromJson(String json, Class valueType) throws Exception {
+ return null;
+ }
+
+ @Override
+ public T fromJson(Reader json, Class valueType) throws Exception {
+ return null;
+ }
+
+ @Override
+ public T fromJson(P parser, Type valueType) throws Exception {
+ return null;
+ }
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonOutboundMessageMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonOutboundMessageMapper.java
index 89e729048b..ab2f233145 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonOutboundMessageMapper.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonOutboundMessageMapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -15,32 +15,31 @@
*/
package org.springframework.integration.json;
-
-import java.io.StringWriter;
-
-import org.codehaus.jackson.map.ObjectMapper;
-
import org.springframework.integration.Message;
import org.springframework.integration.mapping.OutboundMessageMapper;
import org.springframework.util.Assert;
/**
* {@link OutboundMessageMapper} implementation the converts a {@link Message} to a JSON string representation.
- *
+ *
* @author Jeremy Grelle
* @author Mark Fisher
+ * @author Artem Bilan
* @since 2.0
*/
public class JsonOutboundMessageMapper implements OutboundMessageMapper {
private volatile boolean shouldExtractPayload = false;
- private volatile ObjectMapper objectMapper = new ObjectMapper();
+ private volatile JsonObjectMapper> jsonObjectMapper;
+ public JsonOutboundMessageMapper() {
+ this(JacksonJsonObjectMapperProvider.newInstance());
+ }
- public void setObjectMapper(ObjectMapper objectMapper) {
- Assert.notNull(objectMapper, "objectMapper must not be null");
- this.objectMapper = objectMapper;
+ public JsonOutboundMessageMapper(JsonObjectMapper> jsonObjectMapper) {
+ Assert.notNull(jsonObjectMapper, "jsonObjectMapper must not be null");
+ this.jsonObjectMapper = jsonObjectMapper;
}
public void setShouldExtractPayload(boolean shouldExtractPayload) {
@@ -48,14 +47,7 @@ public class JsonOutboundMessageMapper implements OutboundMessageMapper
}
public String fromMessage(Message> message) throws Exception {
- StringWriter writer = new StringWriter();
- if (this.shouldExtractPayload) {
- this.objectMapper.writeValue(writer, message.getPayload());
- }
- else {
- this.objectMapper.writeValue(writer, message);
- }
- return writer.toString();
+ return this.jsonObjectMapper.toJson(this.shouldExtractPayload ? message.getPayload() : message);
}
}
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 641146c2af..30d8c5d828 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * 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.
@@ -16,37 +16,66 @@
package org.springframework.integration.json;
-import org.codehaus.jackson.map.ObjectMapper;
-
import org.springframework.integration.transformer.AbstractPayloadTransformer;
import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
/**
* Transformer implementation that converts a JSON string payload into an instance of the provided target Class.
+ * By default this transformer uses {@linkplain JacksonJsonObjectMapperProvider} 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.
*
* @author Mark Fisher
+ * @author Artem Bilan
+ * @see JsonObjectMapper
+ * @see JacksonJsonObjectMapperProvider
* @since 2.0
*/
public class JsonToObjectTransformer extends AbstractPayloadTransformer {
private final Class targetClass;
- private final ObjectMapper objectMapper;
-
+ private final JsonObjectMapper> jsonObjectMapper;
public JsonToObjectTransformer(Class targetClass) {
this(targetClass, null);
}
- public JsonToObjectTransformer(Class targetClass, ObjectMapper objectMapper) {
+ /**
+ * Backward compatibility - allows existing configurations using Jackson 1.x to inject
+ * an ObjectMapper directly.
+ * @deprecated in favor of {@link #JsonToObjectTransformer(Class, JsonObjectMapper)}
+ */
+ @Deprecated
+ public JsonToObjectTransformer(Class targetClass, Object objectMapper) throws ClassNotFoundException {
Assert.notNull(targetClass, "targetClass must not be null");
this.targetClass = targetClass;
- this.objectMapper = (objectMapper != null) ? objectMapper : new ObjectMapper();
+ if (objectMapper != null) {
+ try {
+ Class> objectMapperClass = ClassUtils.forName("org.codehaus.jackson.map.ObjectMapper", ClassUtils.getDefaultClassLoader());
+ Assert.isTrue(objectMapperClass.isAssignableFrom(objectMapper.getClass()));
+ this.jsonObjectMapper = new JacksonJsonObjectMapper((org.codehaus.jackson.map.ObjectMapper) objectMapper);
+ }
+ catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ else {
+ this.jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
+ }
}
+ public JsonToObjectTransformer(Class targetClass, JsonObjectMapper> jsonObjectMapper) {
+ Assert.notNull(targetClass, "targetClass must not be null");
+ this.targetClass = targetClass;
+ this.jsonObjectMapper = (jsonObjectMapper != null) ? jsonObjectMapper : JacksonJsonObjectMapperProvider.newInstance();
+ }
+ @Override
protected T transformPayload(String payload) throws Exception {
- return this.objectMapper.readValue(payload, this.targetClass);
+ return this.jsonObjectMapper.fromJson(payload, this.targetClass);
}
}
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 c0d5dc9c92..8a212a7b03 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
@@ -15,42 +15,64 @@
*/
package org.springframework.integration.json;
-import java.io.StringWriter;
-
-import org.codehaus.jackson.map.ObjectMapper;
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.ClassUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
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
+ * 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.
*
* @author Mark Fisher
* @author James Carr
* @author Oleg Zhurakousky
* @author Gary Russell
+ * @author Artem Bilan
* @since 2.0
*/
public class ObjectToJsonTransformer extends AbstractTransformer {
public static final String JSON_CONTENT_TYPE = "application/json";
- private final ObjectMapper objectMapper;
+ private final JsonObjectMapper> jsonObjectMapper;
private volatile String contentType = JSON_CONTENT_TYPE;
+
private volatile boolean contentTypeExplicitlySet = false;
- public ObjectToJsonTransformer(ObjectMapper objectMapper) {
+ /**
+ * Backward compatibility - allows existing configurations using Jackson 1.x to inject
+ * an ObjectMapper directly.
+ * @deprecated in favor of {@link #ObjectToJsonTransformer(JsonObjectMapper)}
+ */
+ @Deprecated
+ public ObjectToJsonTransformer(Object objectMapper) {
Assert.notNull(objectMapper, "objectMapper must not be null");
- this.objectMapper = objectMapper;
+ try {
+ Class> objectMapperClass = ClassUtils.forName("org.codehaus.jackson.map.ObjectMapper", ClassUtils.getDefaultClassLoader());
+ Assert.isTrue(objectMapperClass.isAssignableFrom(objectMapper.getClass()));
+ this.jsonObjectMapper = new JacksonJsonObjectMapper((org.codehaus.jackson.map.ObjectMapper) objectMapper);
+ }
+ catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public ObjectToJsonTransformer(JsonObjectMapper> jsonObjectMapper) {
+ Assert.notNull(jsonObjectMapper, "jsonObjectMapper must not be null");
+ this.jsonObjectMapper = jsonObjectMapper;
}
public ObjectToJsonTransformer() {
- this.objectMapper = new ObjectMapper();
+ this.jsonObjectMapper = JacksonJsonObjectMapperProvider.newInstance();
}
/**
@@ -58,29 +80,23 @@ public class ObjectToJsonTransformer extends AbstractTransformer {
*
* @param contentType
*/
- public void setContentType(String contentType){
+ public void setContentType(String contentType) {
// only null assertion is needed since "" is a valid value
Assert.notNull(contentType, "'contentType' must not be null");
this.contentTypeExplicitlySet = true;
this.contentType = contentType.trim();
}
- private String transformPayload(Object payload) throws Exception {
- StringWriter writer = new StringWriter();
- this.objectMapper.writeValue(writer, payload);
- return writer.toString();
- }
-
@Override
protected Object doTransform(Message> message) throws Exception {
- String payload = this.transformPayload(message.getPayload());
+ String payload = this.jsonObjectMapper.toJson(message.getPayload());
MessageBuilder messageBuilder = MessageBuilder.withPayload(payload);
LinkedCaseInsensitiveMap