Merge remote-tracking branch 'upstream/master' into 4.0.0-WIP

Conflicts:
	spring-integration-core/src/main/java/org/springframework/integration/channel/registry/ChannelRegistry.java
	spring-integration-core/src/main/java/org/springframework/integration/channel/registry/LocalChannelRegistry.java
	spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java
	spring-integration-core/src/main/java/org/springframework/integration/support/json/AbstractJacksonJsonMessageParser.java
	spring-integration-core/src/test/java/org/springframework/integration/channel/registry/LocalChannelRegistryTests.java
	spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java
	spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java
	spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingOutboundGatewayTests.java

Resolved.
This commit is contained in:
Gary Russell
2013-11-01 18:19:02 -04:00
65 changed files with 2126 additions and 909 deletions

View File

@@ -1,50 +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.channel.registry;
import org.springframework.messaging.MessageChannel;
/**
* A strategy interface used to bind a {@link MessageChannel} to a logical name. The name
* is intended to identify a logical consumer or producer of messages. This may be a
* queue, a channel adapter, another message channel, a Spring bean, etc.
*
* @author Mark Fisher
* @author David Turanski
* @since 3.0
*/
public interface ChannelRegistry {
/**
* Register a message consumer
* @param name the logical identity of the message source
* @param channel the channel bound as a consumer
*/
void inbound(String name, MessageChannel channel);
/**
* Register a message producer
* @param name the logical identity of the message target
* @param channel the channel bound as a producer
*/
void outbound(String name, MessageChannel channel);
/**
* Create a tap on an already registered inbound channel
* @param name the registered name
* @param channel the channel that will receive messages from the tap
*/
void tap(String name, MessageChannel channel);
}

View File

@@ -1,168 +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.channel.registry;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.messaging.MessageChannel;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.integration.handler.BridgeHandler;
import org.springframework.util.Assert;
/**
* A simple implementation of {@link ChannelRegistry} for in-process use. For inbound and
* outbound, creates a {@link DirectChannel} and bridges the passed
* {@link MessageChannel} to the channel which is registered in the given application
* context. If that channel does not yet exist, it will be created. For tap, it adds a
* {@link WireTap} for an inbound channel whose name matches the one provided. If no such
* inbound channel exists at the time of the method invocation, it will throw an
* Exception. Otherwise the provided channel instance will receive messages from the wire
* tap on that inbound channel.
*
* @author David Turanski
* @author Mark Fisher
* @since 3.0
*/
public class LocalChannelRegistry implements ChannelRegistry, ApplicationContextAware, InitializingBean {
private volatile AbstractApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
this.applicationContext = (AbstractApplicationContext) applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(applicationContext, "The 'applicationContext' property cannot be null");
}
/**
* Looks up or creates a DirectChannel with the given name and creates a bridge from
* that channel to the provided channel instance. Also registers a wire tap if the
* channel for the given name had been created. The target of the wire tap is a
* publish-subscribe channel.
*/
@Override
public void inbound(String name, MessageChannel channel) {
Assert.hasText(name, "a valid name is required to register an inbound channel");
Assert.notNull(channel, "channel must not be null");
DirectChannel registeredChannel = lookupOrCreateSharedChannel(name, DirectChannel.class);
bridge(registeredChannel, channel);
createSharedTapChannelIfNecessary(registeredChannel);
}
/**
* Looks up or creates a DirectChannel with the given name and creates a bridge to
* that channel from the provided channel instance.
*/
@Override
public void outbound(String name, MessageChannel channel) {
Assert.hasText(name, "a valid name is required to register an outbound channel");
Assert.notNull(channel, "channel must not be null");
Assert.isTrue(channel instanceof SubscribableChannel,
"channel must be of type " + SubscribableChannel.class.getName());
DirectChannel registeredChannel = lookupOrCreateSharedChannel(name, DirectChannel.class);
bridge((SubscribableChannel) channel, registeredChannel);
}
/**
* Looks up a wiretap for the inbound channel with the given name and creates a
* bridge from that wiretap's output channel to the provided channel instance.
* Will throw an Exception if no such wiretap exists.
*/
@Override
public void tap(String name, MessageChannel channel) {
Assert.hasText(name, "a valid name is required to register a tap channel");
Assert.notNull(channel, "channel must not be null");
SubscribableChannel tapChannel = null;
String tapName = name + ".tap";
try {
tapChannel = applicationContext.getBean(tapName, SubscribableChannel.class);
}
catch (Exception e) {
throw new IllegalArgumentException("No tap channel exists for '" + name
+ "'. A tap is only valid for a registered inbound channel.");
}
bridge(tapChannel, channel);
}
protected synchronized <T extends AbstractMessageChannel> T lookupOrCreateSharedChannel(String name, Class<T> requiredType) {
T channel = null;
if (applicationContext.containsBean(name)) {
try {
channel = applicationContext.getBean(name, requiredType);
}
catch (Exception e) {
throw new IllegalArgumentException("bean '" + name
+ "' is already registered but does not match the required type");
}
}
else {
channel = createSharedChannel(name, requiredType);
}
return channel;
}
protected <T extends AbstractMessageChannel> T createSharedChannel(String name, Class<T> requiredType) {
try {
T channel = requiredType.newInstance();
channel.setComponentName(name);
channel.setBeanFactory(applicationContext);
channel.setBeanName(name);
channel.afterPropertiesSet();
applicationContext.getBeanFactory().registerSingleton(name, channel);
return channel;
}
catch (Exception e) {
throw new IllegalArgumentException("failed to create channel: " + name, e);
}
}
private synchronized void createSharedTapChannelIfNecessary(AbstractMessageChannel channel) {
String tapName = channel.getComponentName() + ".tap";
PublishSubscribeChannel tapChannel = null;
if (!applicationContext.containsBean(tapName)) {
tapChannel = createSharedChannel(tapName, PublishSubscribeChannel.class);
WireTap wireTap = new WireTap(tapChannel);
channel.addInterceptor(wireTap);
}
else {
try {
tapChannel = applicationContext.getBean(tapName, PublishSubscribeChannel.class);
}
catch (Exception e) {
throw new IllegalArgumentException("bean '" + tapName
+ "' is already registered but does not match the required type");
}
}
}
protected BridgeHandler bridge(SubscribableChannel from, MessageChannel to) {
BridgeHandler handler = new BridgeHandler();
handler.setOutputChannel(to);
handler.afterPropertiesSet();
from.subscribe(handler);
return handler;
}
}

View File

@@ -1,4 +0,0 @@
/**
* Provides classes representing channel registries.
*/
package org.springframework.integration.channel.registry;

View File

@@ -73,7 +73,7 @@ public class ServiceActivatorFactoryBean extends AbstractStandardMessageHandlerF
}
/*
* Return a reply-producing message handler so that we still get 'produced no reply' messages
* and the super class will inject the advice chain to advise the handler if needed.
* and the super class will inject the advice chain to advise the handler method if needed.
*/
handler = new AbstractReplyProducingMessageHandler() {

View File

@@ -16,12 +16,12 @@
package org.springframework.integration.config.xml;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.json.JsonToObjectTransformer;
import org.springframework.util.StringUtils;
/**
* @author Mark Fisher
@@ -39,7 +39,9 @@ public class JsonToObjectTransformerParser extends AbstractTransformerParser {
protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String type = element.getAttribute("type");
String objectMapper = element.getAttribute("object-mapper");
builder.addConstructorArgValue(type);
if (StringUtils.hasText(type)) {
builder.addConstructorArgValue(type);
}
if (StringUtils.hasText(objectMapper)) {
builder.addConstructorArgReference(objectMapper);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 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.Arrays;
import java.util.Collection;
import java.util.Collections;
/**
* Pre-defined names and prefixes to be used for setting and/or retrieving JSON
* entries from/to Message Headers and other adapter, e.g. AMQP.
*
* @author Artem Bilan
* @since 3.0
*/
public class JsonHeaders {
public static final String PREFIX = "json";
public static final String TYPE_ID = PREFIX + "__TypeId__";
public static final String CONTENT_TYPE_ID = PREFIX + "__ContentTypeId__";
public static final String KEY_TYPE_ID = PREFIX + "__KeyTypeId__";
public static final Collection<String> HEADERS =
Collections.unmodifiableList(Arrays.asList(TYPE_ID, CONTENT_TYPE_ID, KEY_TYPE_ID));
}

View File

@@ -16,10 +16,13 @@
package org.springframework.integration.json;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.integration.Message;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.json.JacksonJsonObjectMapper;
import org.springframework.integration.support.json.JacksonJsonObjectMapperProvider;
import org.springframework.integration.support.json.JsonObjectMapper;
import org.springframework.integration.transformer.AbstractPayloadTransformer;
import org.springframework.integration.transformer.AbstractTransformer;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -36,13 +39,17 @@ import org.springframework.util.ClassUtils;
* @see JacksonJsonObjectMapperProvider
* @since 2.0
*/
public class JsonToObjectTransformer<T> extends AbstractPayloadTransformer<String, T> {
public class JsonToObjectTransformer extends AbstractTransformer implements BeanClassLoaderAware {
private final Class<T> targetClass;
private final Class<?> targetClass;
private final JsonObjectMapper<?> jsonObjectMapper;
public JsonToObjectTransformer(Class<T> targetClass) {
public JsonToObjectTransformer() {
this((Class<?>) null);
}
public JsonToObjectTransformer(Class<?> targetClass) {
this(targetClass, null);
}
@@ -52,8 +59,7 @@ public class JsonToObjectTransformer<T> extends AbstractPayloadTransformer<Strin
* @deprecated in favor of {@link #JsonToObjectTransformer(Class, JsonObjectMapper)}
*/
@Deprecated
public JsonToObjectTransformer(Class<T> targetClass, Object objectMapper) throws ClassNotFoundException {
Assert.notNull(targetClass, "targetClass must not be null");
public JsonToObjectTransformer(Class<?> targetClass, Object objectMapper) throws ClassNotFoundException {
this.targetClass = targetClass;
if (objectMapper != null) {
try {
@@ -70,15 +76,34 @@ public class JsonToObjectTransformer<T> extends AbstractPayloadTransformer<Strin
}
}
public JsonToObjectTransformer(Class<T> targetClass, JsonObjectMapper<?> jsonObjectMapper) {
Assert.notNull(targetClass, "targetClass must not be null");
public JsonToObjectTransformer(JsonObjectMapper<?> jsonObjectMapper) {
this(null, jsonObjectMapper);
}
public JsonToObjectTransformer(Class<?> targetClass, JsonObjectMapper<?> jsonObjectMapper) {
this.targetClass = targetClass;
this.jsonObjectMapper = (jsonObjectMapper != null) ? jsonObjectMapper : JacksonJsonObjectMapperProvider.newInstance();
}
@Override
protected T transformPayload(String payload) throws Exception {
return this.jsonObjectMapper.fromJson(payload, this.targetClass);
public void setBeanClassLoader(ClassLoader classLoader) {
if (this.jsonObjectMapper instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) this.jsonObjectMapper).setBeanClassLoader(classLoader);
}
}
@Override
protected Object doTransform(Message<?> message) throws Exception {
if (this.targetClass != null) {
return this.jsonObjectMapper.fromJson(message.getPayload(), this.targetClass);
}
else {
Object result = this.jsonObjectMapper.fromJson(message.getPayload(), message.getHeaders());
MessageBuilder<Object> messageBuilder = MessageBuilder.withPayload(result)
.copyHeaders(message.getHeaders())
.removeHeaders(JsonHeaders.HEADERS.toArray(new String[3]));
return messageBuilder.build();
}
}
}

View File

@@ -109,6 +109,9 @@ public class ObjectToJsonTransformer extends AbstractTransformer {
else if (StringUtils.hasLength(this.contentType)) {
headers.put(MessageHeaders.CONTENT_TYPE, this.contentType);
}
this.jsonObjectMapper.populateJavaTypes(headers, message.getPayload().getClass());
messageBuilder.copyHeaders(headers);
return messageBuilder.build();
}

View File

@@ -26,6 +26,8 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.json.JsonHeaders;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -248,7 +250,7 @@ public abstract class AbstractHeaderMapper<T> implements RequestReplyHeaderMappe
if (!type.isAssignableFrom(value.getClass())) {
if (logger.isWarnEnabled()) {
logger.warn("skipping header '" + name + "' since it is not of expected type [" + type + "], it is [" +
value.getClass() + "]");
value.getClass() + "]");
}
return null;
}
@@ -271,9 +273,14 @@ public abstract class AbstractHeaderMapper<T> implements RequestReplyHeaderMappe
*/
private String addPrefixIfNecessary(String prefix, String propertyName) {
String headerName = propertyName;
if (StringUtils.hasText(prefix) && !headerName.startsWith(prefix) && !headerName.equals(MessageHeaders.CONTENT_TYPE)) {
if (StringUtils.hasText(prefix) && !headerName.startsWith(prefix) &&
!headerName.equals(MessageHeaders.CONTENT_TYPE) &&
(!JsonHeaders.HEADERS.contains(headerName) || !JsonHeaders.HEADERS.contains(JsonHeaders.PREFIX + headerName))) {
headerName = prefix + propertyName;
}
if (JsonHeaders.HEADERS.contains(JsonHeaders.PREFIX + headerName)) {
headerName = JsonHeaders.PREFIX + headerName;
}
return headerName;
}

View File

@@ -16,10 +16,14 @@
package org.springframework.integration.store.metadata;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import org.apache.commons.logging.Log;
@@ -37,6 +41,7 @@ import org.springframework.util.DefaultPropertiesPersister;
*
* @author Oleg Zhurakousky
* @author Mark Fisher
* @author Gary Russell
* @since 2.0
*/
public class PropertiesPersistingMetadataStore implements MetadataStore, InitializingBean, DisposableBean {
@@ -86,9 +91,9 @@ public class PropertiesPersistingMetadataStore implements MetadataStore, Initial
}
private void saveMetadata() {
FileOutputStream outputStream = null;
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(this.file);
outputStream = new BufferedOutputStream(new FileOutputStream(this.file));
this.persister.store(this.metadata, outputStream, "Last feed entry");
}
catch (IOException e) {
@@ -104,15 +109,15 @@ public class PropertiesPersistingMetadataStore implements MetadataStore, Initial
}
catch (IOException e) {
// not fatal for the functionality of the component
logger.warn("Failed to close FileOutputStream to " + this.file.getAbsolutePath(), e);
logger.warn("Failed to close OutputStream to " + this.file.getAbsolutePath(), e);
}
}
}
private void loadMetadata() {
FileInputStream inputStream = null;
InputStream inputStream = null;
try {
inputStream = new FileInputStream(this.file);
inputStream = new BufferedInputStream(new FileInputStream(this.file));
this.persister.load(this.metadata, inputStream);
}
catch (Exception e) {
@@ -128,7 +133,7 @@ public class PropertiesPersistingMetadataStore implements MetadataStore, Initial
}
catch (Exception e2) {
// non fatal
logger.warn("Failed to close FileInputStream for: " + this.file.getAbsolutePath());
logger.warn("Failed to close InputStream for: " + this.file.getAbsolutePath());
}
}
}

View File

@@ -16,8 +16,10 @@
package org.springframework.integration.support.json;
import org.springframework.messaging.Message;
import java.lang.reflect.Type;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
/**
* Base {@link JsonInboundMessageMapper.JsonMessageParser} implementation for Jackson processors.
@@ -68,7 +70,7 @@ abstract class AbstractJacksonJsonMessageParser<P> implements JsonInboundMessage
Class<?> headerType = this.messageMapper.getHeaderTypes().containsKey(headerName) ?
this.messageMapper.getHeaderTypes().get(headerName) : Object.class;
try {
return this.objectMapper.fromJson(parser, headerType);
return this.objectMapper.fromJson(parser, (Type) headerType);
}
catch (Exception e) {
throw new IllegalArgumentException("Mapping header '" + headerName + "' of JSON message '" +

View File

@@ -0,0 +1,84 @@
/*
* Copyright 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.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.util.ClassUtils;
/**
* Base class for Jackson {@link JsonObjectMapper} implementations.
*
* @author Artem Bilan
* @since 3.0
*/
public abstract class AbstractJacksonJsonObjectMapper<P, J> implements JsonObjectMapper<P>, BeanClassLoaderAware {
protected static final Collection<Class<?>> supportedJsonTypes =
Arrays.<Class<?>> asList(String.class, byte[].class, File.class, URL.class, InputStream.class, Reader.class);
private volatile ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public <T> T fromJson(Object json, Class<T> valueType) throws Exception {
return this.fromJson(json, this.constructType(valueType));
}
@Override
public <T> T fromJson(Object json, Map<String, Object> javaTypes) throws Exception {
J javaType = this.extractJavaType(javaTypes);
return this.fromJson(json, javaType);
}
protected J createJavaType(Map<String, Object> javaTypes, String javaTypeKey) throws Exception {
Object classValue = javaTypes.get(javaTypeKey);
if (classValue == null) {
throw new IllegalArgumentException("Could not resolve '" + javaTypeKey + "' in 'javaTypes'.");
}
else {
Class<?> aClass = null;
if (classValue instanceof Class<?>) {
aClass = (Class<?>) classValue;
}
else {
aClass = ClassUtils.forName(classValue.toString(), this.classLoader);
}
return this.constructType(aClass);
}
}
protected abstract <T> T fromJson(Object json, J type) throws Exception;
protected abstract J extractJavaType(Map<String, Object> javaTypes) throws Exception;
protected abstract J constructType(Type type);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 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,15 +16,22 @@
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.springframework.integration.json.JsonHeaders;
import org.springframework.util.Assert;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.util.Assert;
/**
* Jackson 2 JSON-processor (@link https://github.com/FasterXML) {@linkplain JsonObjectMapper} implementation.
* Delegates <code>toJson</code> and <code>fromJson</code>
@@ -33,7 +40,7 @@ import org.springframework.util.Assert;
* @author Artem Bilan
* @since 3.0
*/
public class Jackson2JsonObjectMapper implements JsonObjectMapper<JsonParser> {
public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper<JsonParser, JavaType> {
private final ObjectMapper objectMapper;
@@ -46,6 +53,7 @@ public class Jackson2JsonObjectMapper implements JsonObjectMapper<JsonParser> {
this.objectMapper = objectMapper;
}
@Override
public String toJson(Object value) throws Exception {
return this.objectMapper.writeValueAsString(value);
}
@@ -55,18 +63,72 @@ public class Jackson2JsonObjectMapper implements JsonObjectMapper<JsonParser> {
this.objectMapper.writeValue(writer, value);
}
public <T> T fromJson(String json, Class<T> valueType) throws Exception {
return this.objectMapper.readValue(json, valueType);
}
@Override
public <T> T fromJson(Reader json, Class<T> valueType) throws Exception {
return this.objectMapper.readValue(json, valueType);
protected <T> 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 <T> T fromJson(JsonParser parser, Type valueType) throws Exception {
return this.objectMapper.readValue(parser, this.objectMapper.constructType(valueType));
return this.objectMapper.readValue(parser, this.constructType(valueType));
}
@Override
public void populateJavaTypes(Map<String, Object> 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<String, Object> 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<? extends Collection<?>>) classType.getRawClass(), contentClassType);
}
JavaType keyClassType = this.createJavaType(javaTypes, JsonHeaders.KEY_TYPE_ID);
return this.objectMapper.getTypeFactory()
.constructMapType((Class<? extends Map<?, ?>>) classType.getRawClass(), keyClassType, contentClassType);
}
@Override
protected JavaType constructType(Type type) {
return this.objectMapper.constructType(type);
}
}

View File

@@ -16,13 +16,20 @@
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.JsonParser;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.JavaType;
import org.springframework.integration.json.JsonHeaders;
import org.springframework.util.Assert;
/**
@@ -33,7 +40,7 @@ import org.springframework.util.Assert;
* @author Artem Bilan
* @since 3.0
*/
public class JacksonJsonObjectMapper implements JsonObjectMapper<JsonParser> {
public class JacksonJsonObjectMapper extends AbstractJacksonJsonObjectMapper<JsonParser, JavaType> {
private final ObjectMapper objectMapper;
@@ -46,6 +53,7 @@ public class JacksonJsonObjectMapper implements JsonObjectMapper<JsonParser> {
this.objectMapper = objectMapper;
}
@Override
public String toJson(Object value) throws Exception {
return this.objectMapper.writeValueAsString(value);
}
@@ -55,18 +63,72 @@ public class JacksonJsonObjectMapper implements JsonObjectMapper<JsonParser> {
this.objectMapper.writeValue(writer, value);
}
public <T> T fromJson(String json, Class<T> valueType) throws Exception {
return this.objectMapper.readValue(json, valueType);
}
@Override
public <T> T fromJson(Reader json, Class<T> valueType) throws Exception {
return this.objectMapper.readValue(json, valueType);
}
@Override
public <T> T fromJson(JsonParser parser, Type valueType) throws Exception {
return this.objectMapper.readValue(parser, this.objectMapper.constructType(valueType));
return this.objectMapper.readValue(parser, this.constructType(valueType));
}
@Override
protected <T> 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<String, Object> 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<String, Object> 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<? extends Collection<?>>) classType.getRawClass(), contentClassType);
}
JavaType keyClassType = this.createJavaType(javaTypes, JsonHeaders.KEY_TYPE_ID);
return this.objectMapper.getTypeFactory()
.constructMapType((Class<? extends Map<?, ?>>) classType.getRawClass(), keyClassType, contentClassType);
}
}

View File

@@ -16,9 +16,9 @@
package org.springframework.integration.support.json;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.Map;
/**
* Strategy interface to convert an Object to/from the JSON representation.
@@ -33,10 +33,11 @@ public interface JsonObjectMapper<P> {
void toJson(Object value, Writer writer) throws Exception;
<T> T fromJson(String json, Class<T> valueType) throws Exception;
<T> T fromJson(Object json, Class<T> valueType) throws Exception;
<T> T fromJson(Reader json, Class<T> valueType) throws Exception;
<T> T fromJson(Object json, Map<String, Object> javaTypes) throws Exception;
<T> T fromJson(P parser, Type valueType) throws Exception;
void populateJavaTypes(Map<String, Object> map, Class<?> sourceClass);
}

View File

@@ -16,9 +16,9 @@
package org.springframework.integration.support.json;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.Map;
/**
* Simple {@linkplain JsonObjectMapper} adapter implementation, if there is no need
@@ -39,12 +39,7 @@ public abstract class JsonObjectMapperAdapter<P> implements JsonObjectMapper<P>
}
@Override
public <T> T fromJson(String json, Class<T> valueType) throws Exception {
return null;
}
@Override
public <T> T fromJson(Reader json, Class<T> valueType) throws Exception {
public <T> T fromJson(Object json, Class<T> valueType) throws Exception {
return null;
}
@@ -53,4 +48,13 @@ public abstract class JsonObjectMapperAdapter<P> implements JsonObjectMapper<P>
return null;
}
@Override
public <T> T fromJson(Object json, Map<String, Object> javaTypes) throws Exception {
return null;
}
@Override
public void populateJavaTypes(Map<String, Object> map, Class<?> sourceClass) {
}
}

View File

@@ -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,6 +16,8 @@
package org.springframework.integration.util;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
@@ -24,6 +26,24 @@ import java.util.Set;
*/
public abstract class ClassUtils {
/**
* Map with primitive wrapper type as key and corresponding primitive
* type as value, for example: Integer.class -> int.class.
*/
private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);
static {
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
primitiveWrapperTypeMap.put(Byte.class, byte.class);
primitiveWrapperTypeMap.put(Character.class, char.class);
primitiveWrapperTypeMap.put(Double.class, double.class);
primitiveWrapperTypeMap.put(Float.class, float.class);
primitiveWrapperTypeMap.put(Integer.class, int.class);
primitiveWrapperTypeMap.put(Long.class, long.class);
primitiveWrapperTypeMap.put(Short.class, short.class);
}
public static Class<?> findClosestMatch(Class<?> type, Set<Class<?>> candidates, boolean failOnTie) {
int minTypeDiffWeight = Integer.MAX_VALUE;
Class<?> closestMatch = null;
@@ -35,7 +55,7 @@ public abstract class ClassUtils {
}
else if (failOnTie && typeDiffWeight < Integer.MAX_VALUE && (typeDiffWeight == minTypeDiffWeight)) {
throw new IllegalStateException("Unresolvable ambiguity while attempting to find closest match for [" +
type.getName() + "]. Candidate types [" + closestMatch.getName() + "] and [" + candidate.getName() +
type.getName() + "]. Candidate types [" + closestMatch.getName() + "] and [" + candidate.getName() +
"] have equal weight.");
}
}
@@ -67,4 +87,14 @@ public abstract class ClassUtils {
return result;
}
/**
* Resolve the given class if it is a primitive wrapper class,
* returning the corresponding primitive type instead.
* @param clazz the wrapper class to check
* @return the corresponding primitive if the clazz is a wrapper, otherwise null
*/
public static Class<?> resolvePrimitiveType(Class<?> clazz) {
return primitiveWrapperTypeMap.get(clazz);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 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.util;
/**
* Utility methods for analyzing stack traces.
*
* @author Gary Russell
* @since 3.0
*
*/
public class StackTraceUtils {
private StackTraceUtils() {}
/**
* Traverses the stack trace element array looking for instances that contain the first or second
* Strings in the className property.
* @param firstClass The first class to look for.
* @param secondClass The second class to look for.
* @param stackTrace The stack trace.
* @return true if the first class appears first, false if the second appears first
* @throws IllegalArgumentException if neither class is found.
*/
public static boolean isFrameContainingXBeforeFrameContainingY(String firstClass, String secondClass, StackTraceElement[] stackTrace) {
for (StackTraceElement element : stackTrace) {
if (element.getClassName().contains(firstClass)) {
return true;
}
else if (element.getClassName().contains(secondClass)) {
return false;
}
}
throw new IllegalArgumentException("Neither " + firstClass + " nor " + secondClass + " class found");
}
}

View File

@@ -1,171 +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.channel.registry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessagingException;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.GenericMessage;
/**
* @author David Turanski
* @author Mark Fisher
* @since 3.0
*/
public class LocalChannelRegistryTests {
private LocalChannelRegistry registry = new LocalChannelRegistry();
private GenericApplicationContext context = new GenericApplicationContext();
@Before
public void setUp() {
registry.setApplicationContext(context);
context.refresh();
}
@Test
public void testInbound() {
DirectChannel channel = new DirectChannel();
registry.inbound("inbound", channel);
assertTrue(context.containsBean("inbound"));
final AtomicBoolean messageReceived = new AtomicBoolean();
channel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
messageReceived.set(true);
assertEquals("hello", message.getPayload());
}
});
SubscribableChannel registeredChannel = context.getBean("inbound", SubscribableChannel.class);
registeredChannel.send(new GenericMessage<String>("hello"));
assertTrue(messageReceived.get());
}
@Test
public void testOutbound() {
DirectChannel channel = new DirectChannel();
registry.outbound("outbound", channel);
assertTrue(context.containsBean("outbound"));
final AtomicBoolean messageReceived = new AtomicBoolean();
SubscribableChannel registeredChannel = context.getBean("outbound", SubscribableChannel.class);
registeredChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
messageReceived.set(true);
assertEquals("hello", message.getPayload());
}
});
channel.send(new GenericMessage<String>("hello"));
assertTrue(messageReceived.get());
}
@Test(expected = IllegalArgumentException.class)
public void testOutboundTapShouldFail() {
DirectChannel channel = new DirectChannel();
registry.outbound("outbound", channel);
DirectChannel tapChannel = new DirectChannel();
registry.tap("outbound", tapChannel);
}
@Test
public void testInboundTap() {
DirectChannel channel = new DirectChannel();
registry.inbound("inbound", channel);
DirectChannel tapChannel = new DirectChannel();
registry.tap("inbound", tapChannel);
final AtomicBoolean originalMessageReceived = new AtomicBoolean();
final AtomicBoolean tapMessageReceived = new AtomicBoolean();
tapChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
tapMessageReceived.set(true);
assertEquals("hello", message.getPayload());
}
});
channel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
originalMessageReceived.set(true);
assertEquals("hello", message.getPayload());
}
});
MessageChannel registeredChannel = context.getBean("inbound", MessageChannel.class);
registeredChannel.send(new GenericMessage<String>("hello"));
assertTrue(originalMessageReceived.get());
assertTrue(tapMessageReceived.get());
}
@Test
public void testFlowThroughRegisteredChannelFromOutboundToInbound() {
DirectChannel outbound = new DirectChannel();
DirectChannel inbound = new DirectChannel();
registry.outbound("foo", outbound);
registry.inbound("foo", inbound);
final AtomicBoolean messageReceived = new AtomicBoolean();
inbound.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
messageReceived.set(true);
assertEquals("hello", message.getPayload());
}
});
outbound.send(new GenericMessage<String>("hello"));
assertTrue(messageReceived.get());
}
@Test
public void testFlowThroughRegisteredChannelFromOutboundToInboundWithTap() {
DirectChannel outbound = new DirectChannel();
DirectChannel inbound = new DirectChannel();
DirectChannel tap = new DirectChannel();
registry.outbound("foo", outbound);
registry.inbound("foo", inbound);
registry.tap("foo", tap);
final AtomicBoolean originalMessageReceived = new AtomicBoolean();
final AtomicBoolean tapMessageReceived = new AtomicBoolean();
inbound.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
originalMessageReceived.set(true);
assertEquals("hello", message.getPayload());
}
});
tap.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
tapMessageReceived.set(true);
assertEquals("hello", message.getPayload());
}
});
outbound.send(new GenericMessage<String>("hello"));
assertTrue(originalMessageReceived.get());
assertTrue(tapMessageReceived.get());
}
}

View File

@@ -22,19 +22,19 @@
<service-activator id="replyingHandlerWithStandardMethodTestService"
input-channel="replyingHandlerWithStandardMethodTestInputChannel"
method="handleMessage">
<beans:bean
<beans:bean id="innerReplyingHandler"
class="org.springframework.integration.handler.ServiceActivatorDefaultFrameworkMethodTests$TestReplyingMessageHandler"/>
</service-activator>
<service-activator id="replyingHandlerWithOtherMethodTestService"
input-channel="replyingHandlerWithOtherMethodTestInputChannel"
method="foo">
<beans:bean
<beans:bean id="innerReplyingHandlerFoo"
class="org.springframework.integration.handler.ServiceActivatorDefaultFrameworkMethodTests$TestReplyingMessageHandler"/>
</service-activator>
<service-activator id="handlerTestService" input-channel="handlerTestInputChannel">
<beans:bean
<beans:bean id="innerHandler"
class="org.springframework.integration.handler.ServiceActivatorDefaultFrameworkMethodTests$TestMessageHandler"/>
</service-activator>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<service-activator id="optimizedRefReplyingHandlerTestService1"
input-channel="optimizedRefReplyingHandlerTestInputChannel" ref="testReplyingMessageHandler"/>
<service-activator id="optimizedRefReplyingHandlerTestService2"
input-channel="optimizedRefReplyingHandlerTestInputChannel" ref="testReplyingMessageHandler"/>
<beans:bean id="testReplyingMessageHandler"
class="org.springframework.integration.handler.ServiceActivatorDefaultFrameworkMethodTests$TestReplyingMessageHandler"/>
</beans:beans>

View File

@@ -17,16 +17,24 @@
package org.springframework.integration.handler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.util.StackTraceUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
@@ -90,7 +98,7 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
assertEquals("TEST", reply.getPayload());
assertEquals("replyingHandlerTestInputChannel,replyingHandlerTestService", reply.getHeaders().get("history").toString());
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
assertEquals("doDispatch", st[3].getMethodName()); // close to the metal
assertTrue(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("Dispatcher", "MethodInvokerHelper", st)); // close to the metal
}
@Test
@@ -103,7 +111,7 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
assertEquals("optimizedRefReplyingHandlerTestInputChannel,optimizedRefReplyingHandlerTestService",
reply.getHeaders().get("history").toString());
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
assertEquals("doDispatch", st[3].getMethodName());
assertTrue(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("Dispatcher", "MethodInvokerHelper", st)); // close to the metal
}
@Test
@@ -115,7 +123,7 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
assertEquals("TEST", reply.getPayload());
assertEquals("replyingHandlerWithStandardMethodTestInputChannel,replyingHandlerWithStandardMethodTestService", reply.getHeaders().get("history").toString());
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
assertEquals("doDispatch", st[3].getMethodName()); // close to the metal
assertTrue(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("Dispatcher", "MethodInvokerHelper", st)); // close to the metal
}
@Test
@@ -149,6 +157,22 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
assertEquals("processorTestInputChannel,processorTestService", reply.getHeaders().get("history").toString());
}
@Test
public void testFailOnDoubleReference() {
try {
new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-fail-context.xml",
this.getClass());
fail("Expected exception due to 2 endpoints referencing the same bean");
}
catch (Exception e) {
assertThat(e, Matchers.instanceOf(BeanCreationException.class));
assertThat(e.getCause(), Matchers.instanceOf(BeanCreationException.class));
assertThat(e.getCause().getCause(), Matchers.instanceOf(IllegalArgumentException.class));
assertThat(e.getCause().getCause().getMessage(),
Matchers.containsString("An AbstractReplyProducingMessageHandler may only be referenced once"));
}
}
@SuppressWarnings("unused")
private static class TestReplyingMessageHandler extends AbstractReplyProducingMessageHandler {
@@ -162,6 +186,10 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
}
public String foo(String in) {
Exception e = new RuntimeException();
StackTraceElement[] st = e.getStackTrace();
// use this to test that StackTraceUtils works as expected and returns false
assertFalse(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("Dispatcher", "MethodInvokerHelper", st));
return "bar";
}
@@ -174,7 +202,7 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
public void handleMessage(Message<?> requestMessage) {
Exception e = new RuntimeException();
StackTraceElement[] st = e.getStackTrace();
assertEquals("doDispatch", st[4].getMethodName());
assertTrue(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("Dispatcher", "MethodInvokerHelper", st)); // close to the metal
}
}

View File

@@ -8,16 +8,16 @@
http://www.springframework.org/schema/integration/spring-integration.xsd">
<json-to-object-transformer id="defaultJacksonMapperTransformer" input-channel="defaultObjectMapperInput"
type="org.springframework.integration.json.JsonToObjectTransformerParserTests$TestPerson"/>
type="org.springframework.integration.json.TestPerson"/>
<json-to-object-transformer id="customJacksonMapperTransformer" input-channel="customObjectMapperInput"
type="org.springframework.integration.json.JsonToObjectTransformerParserTests$TestPerson"
type="org.springframework.integration.json.TestPerson"
object-mapper="customObjectMapper"/>
<beans:bean id="customObjectMapper" class="org.springframework.integration.json.JsonToObjectTransformerParserTests$CustomObjectMapper"/>
<json-to-object-transformer id="customJsonMapperTransformer" input-channel="customJsonObjectMapperInput"
type="org.springframework.integration.json.JsonToObjectTransformerParserTests$TestPerson"
type="org.springframework.integration.json.TestPerson"
object-mapper="customJsonObjectMapper"/>
<beans:bean id="customJsonObjectMapper" class="org.springframework.integration.json.JsonToObjectTransformerParserTests$CustomJsonObjectMapper"/>

View File

@@ -134,79 +134,6 @@ public class JsonToObjectTransformerParserTests {
}
static class TestPerson {
private String firstName;
private String lastName;
private int age;
private TestAddress address;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastName() {
return this.lastName;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setAddress(TestAddress address) {
this.address = address;
}
public TestAddress getAddress() {
return this.address;
}
@Override
public String toString() {
return "name=" + this.firstName + " " + this.lastName
+ ", age=" + this.age + ", address=" + this.address;
}
}
static class TestAddress {
private int number;
private String street;
public void setNumber(int number) {
this.number = number;
}
public void setStreet(String street) {
this.street = street;
}
@Override
public String toString() {
return this.number + " " + this.street;
}
}
static class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
@@ -219,8 +146,8 @@ public class JsonToObjectTransformerParserTests {
static class CustomJsonObjectMapper extends JsonObjectMapperAdapter {
@Override
public Object fromJson(String json, Class valueType) throws Exception {
return new TestJsonContainer(json);
public Object fromJson(Object json, Class valueType) throws Exception {
return new TestJsonContainer((String) json);
}
}

View File

@@ -22,6 +22,8 @@ import org.codehaus.jackson.JsonParser.Feature;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Test;
import org.springframework.integration.Message;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.json.JacksonJsonObjectMapper;
/**
@@ -33,9 +35,11 @@ public class JsonToObjectTransformerTests {
@Test
public void objectPayload() throws Exception {
JsonToObjectTransformer<TestPerson> transformer = new JsonToObjectTransformer<TestPerson>(TestPerson.class);
JsonToObjectTransformer transformer = new JsonToObjectTransformer(TestPerson.class);
String jsonString = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":42,\"address\":{\"number\":123,\"street\":\"Main Street\"}}";
TestPerson person = transformer.transformPayload(jsonString);
Message<?> message = transformer.transform(new GenericMessage<String>(jsonString));
@SuppressWarnings("unchecked")
TestPerson person = (TestPerson) message.getPayload();
assertEquals("John", person.getFirstName());
assertEquals("Doe", person.getLastName());
assertEquals(42, person.getAge());
@@ -47,10 +51,12 @@ public class JsonToObjectTransformerTests {
ObjectMapper customMapper = new ObjectMapper();
customMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, Boolean.TRUE);
customMapper.configure(Feature.ALLOW_SINGLE_QUOTES, Boolean.TRUE);
JsonToObjectTransformer<TestPerson> transformer =
new JsonToObjectTransformer<TestPerson>(TestPerson.class, new JacksonJsonObjectMapper(customMapper));
JsonToObjectTransformer transformer =
new JsonToObjectTransformer(TestPerson.class, new JacksonJsonObjectMapper(customMapper));
String jsonString = "{firstName:'John', lastName:'Doe', age:42, address:{number:123, street:'Main Street'}}";
TestPerson person = transformer.transformPayload(jsonString);
Message<?> message = transformer.transform(new GenericMessage<String>(jsonString));
@SuppressWarnings("unchecked")
TestPerson person = (TestPerson) message.getPayload();
assertEquals("John", person.getFirstName());
assertEquals("Doe", person.getLastName());
assertEquals(42, person.getAge());
@@ -60,82 +66,8 @@ public class JsonToObjectTransformerTests {
@SuppressWarnings("deprecation")
@Test(expected = IllegalArgumentException.class)
public void testInt2831IllegalArgument() throws Exception {
new JsonToObjectTransformer<String>(String.class, new Object());
new JsonToObjectTransformer(String.class, new Object());
}
@SuppressWarnings("unused")
private static class TestPerson {
private String firstName;
private String lastName;
private int age;
private TestAddress address;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastName() {
return this.lastName;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setAddress(TestAddress address) {
this.address = address;
}
public TestAddress getAddress() {
return this.address;
}
@Override
public String toString() {
return "name=" + this.firstName + " " + this.lastName
+ ", age=" + this.age + ", address=" + this.address;
}
}
@SuppressWarnings("unused")
private static class TestAddress {
private int number;
private String street;
public void setNumber(int number) {
this.number = number;
}
public void setStreet(String street) {
this.street = street;
}
@Override
public String toString() {
return this.number + " " + this.street;
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 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 static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.integration.Message;
import org.springframework.integration.message.GenericMessage;
/**
* @author Artem Bilan
* @since 3.0
*/
public class JsonTransformersSymmetricalTests {
@Test
public void testInt2809ObjectToJson_JsonToObject() {
TestPerson person = new TestPerson("John", "Doe", 42);
person.setAddress(new TestAddress(123, "Main Street"));
ObjectToJsonTransformer objectToJsonTransformer = new ObjectToJsonTransformer();
Message<?> jsonMessage = objectToJsonTransformer.transform(new GenericMessage<Object>(person));
JsonToObjectTransformer jsonToObjectTransformer = new JsonToObjectTransformer();
Message<?> result = jsonToObjectTransformer.transform(jsonMessage);
assertEquals(person, result.getPayload());
}
}

View File

@@ -174,87 +174,6 @@ public class ObjectToJsonTransformerParserTests {
}
static class TestPerson {
private String firstName;
private String lastName;
private int age;
private TestAddress address;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastName() {
return this.lastName;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setAddress(TestAddress address) {
this.address = address;
}
public TestAddress getAddress() {
return this.address;
}
@Override
public String toString() {
return "\"name\":\"" + this.firstName + " " + this.lastName
+ "\", \"age\":" + this.age + ", \"address\":\"" + this.address + "\"";
}
}
static class TestAddress {
private int number;
private String street;
public int getNumber() {
return this.number;
}
public void setNumber(int number) {
this.number = number;
}
public String getStreet() {
return this.street;
}
public void setStreet(String street) {
this.street = street;
}
@Override
public String toString() {
return this.number + " " + this.street;
}
}
static class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {

View File

@@ -147,66 +147,4 @@ public class ObjectToJsonTransformerTests {
new ObjectToJsonTransformer(new Object());
}
@SuppressWarnings("unused")
private static class TestPerson {
private final String firstName;
private final String lastName;
private final int age;
private TestAddress address;
public TestPerson(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public TestAddress getAddress() {
return address;
}
public void setAddress(TestAddress address) {
this.address = address;
}
}
@SuppressWarnings("unused")
private static class TestAddress {
private final int number;
private final String street;
public TestAddress(int number, String street) {
this.number = number;
this.street = street;
}
public int getNumber() {
return number;
}
public String getStreet() {
return street;
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 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;
/**
* @author Mark Fisher
* @since 2.0
*/
@SuppressWarnings("unused")
class TestAddress {
private volatile int number;
private volatile String street;
TestAddress() {
}
public TestAddress(int number, String street) {
this.number = number;
this.street = street;
}
public int getNumber() {
return this.number;
}
public void setNumber(int number) {
this.number = number;
}
public String getStreet() {
return this.street;
}
public void setStreet(String street) {
this.street = street;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestAddress that = (TestAddress) o;
if (number != that.number) return false;
if (street != null ? !street.equals(that.street) : that.street != null) return false;
return true;
}
@Override
public int hashCode() {
int result = number;
result = 31 * result + (street != null ? street.hashCode() : 0);
return result;
}
@Override
public String toString() {
return this.number + " " + this.street;
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 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;
/**
* @author Mark Fisher
* @since 2.0
*/
@SuppressWarnings("unused")
class TestPerson {
private volatile String firstName;
private volatile String lastName;
private volatile int age;
private volatile TestAddress address;
TestPerson() {
}
public TestPerson(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastName() {
return this.lastName;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setAddress(TestAddress address) {
this.address = address;
}
public TestAddress getAddress() {
return this.address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestPerson that = (TestPerson) o;
if (age != that.age) return false;
if (address != null ? !address.equals(that.address) : that.address != null) return false;
if (firstName != null ? !firstName.equals(that.firstName) : that.firstName != null) return false;
if (lastName != null ? !lastName.equals(that.lastName) : that.lastName != null) return false;
return true;
}
@Override
public int hashCode() {
int result = firstName != null ? firstName.hashCode() : 0;
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
result = 31 * result + age;
result = 31 * result + (address != null ? address.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "name=" + this.firstName + " " + this.lastName
+ ", age=" + this.age + ", address=" + this.address;
}
}