INT-2888 Fix MapToObjectTransformerParser CL Issue
In the parent-child environment configuration that relies on class names may produce a `ClassNotFoundException`. * remove usage of `ClassLoader` in the `MapToObjectTransformerParser` and allow to use the `ConversionService` from application context * remove deprecation from `MapToObjectTransformer` * refactor `MapToObjectTransformer` to use `IntegrationObjectSupport#getConversionService()` * remove fallback to the `beanFactory#getConversionService()` in the `ExpressionUtils` JIRA: https://jira.springsource.org/browse/INT-2888 INT-2928: Do Not Fallback to BF's ConversionService * Polishing according PR's comments * Important note about `conversionService` & `integrationConversionService` beans * Link a JIRA about elimination of `BeanFactory`'s `ConversionService` usage * Add a note to the 2.2-3.0 Migration Guide JIRA: https://jira.springsource.org/browse/INT-2928 INT-2888 Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
e994c4dab3
commit
77fae8844a
@@ -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.
|
||||
@@ -18,13 +18,12 @@ package org.springframework.integration.config.xml;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.transformer.MapToObjectTransformer;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapToObjectTransformerParser extends AbstractTransformerParser {
|
||||
@@ -38,17 +37,17 @@ public class MapToObjectTransformerParser extends AbstractTransformerParser {
|
||||
protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
String ref = element.getAttribute("ref");
|
||||
String type = element.getAttribute("type");
|
||||
Assert.isTrue(!(StringUtils.hasText(ref) && StringUtils.hasText(type)),
|
||||
"'type' and 'ref' attributes are mutually-exclusive, but both have valid values; type: " + type + "; ref:");
|
||||
if (StringUtils.hasText(ref)){
|
||||
if (StringUtils.hasText(ref) && StringUtils.hasText(type)) {
|
||||
parserContext.getReaderContext().error("'type' and 'ref' attributes are mutually-exclusive, " +
|
||||
"but both have valid values; type: " + type + "; ref: " + ref,
|
||||
IntegrationNamespaceUtils.createElementDescription(element));
|
||||
}
|
||||
if (StringUtils.hasText(ref)) {
|
||||
builder.getBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(ref, "java.lang.String");
|
||||
} else if (StringUtils.hasText(type)){
|
||||
ClassLoader classLoader = parserContext.getReaderContext().getBeanClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = this.getClass().getClassLoader();
|
||||
}
|
||||
Class<?> clazz = ClassUtils.resolveClassName(type, classLoader);
|
||||
builder.getBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(clazz, "java.lang.Class");
|
||||
}
|
||||
else if (StringUtils.hasText(type)) {
|
||||
builder.getBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(type, "java.lang.Class");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.integration.expression;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
@@ -32,6 +31,7 @@ import org.springframework.integration.context.IntegrationContextUtils;
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @since 2.2
|
||||
*/
|
||||
public abstract class ExpressionUtils {
|
||||
@@ -97,10 +97,7 @@ public abstract class ExpressionUtils {
|
||||
* @return the evaluation context.
|
||||
*/
|
||||
public static StandardEvaluationContext createStandardEvaluationContext(BeanFactory beanFactory) {
|
||||
ConversionService conversionService = IntegrationContextUtils.getConversionService(beanFactory);
|
||||
if (conversionService == null && beanFactory instanceof ConfigurableListableBeanFactory){
|
||||
conversionService = ((ConfigurableListableBeanFactory)beanFactory).getConversionService();
|
||||
}
|
||||
return createStandardEvaluationContext(new BeanFactoryResolver(beanFactory), conversionService);
|
||||
return createStandardEvaluationContext(new BeanFactoryResolver(beanFactory),
|
||||
IntegrationContextUtils.getConversionService(beanFactory));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -36,14 +35,15 @@ import org.springframework.validation.DataBinder;
|
||||
* to types that represent the properties of the Object.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapToObjectTransformer extends AbstractPayloadTransformer<Map<?,?>, Object>{
|
||||
public class MapToObjectTransformer extends AbstractPayloadTransformer<Map<?, ?>, Object> {
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
private final String targetBeanName;
|
||||
|
||||
/**
|
||||
* @param targetClass
|
||||
*/
|
||||
@@ -52,6 +52,7 @@ public class MapToObjectTransformer extends AbstractPayloadTransformer<Map<?,?>,
|
||||
this.targetClass = targetClass;
|
||||
this.targetBeanName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beanName
|
||||
*/
|
||||
@@ -60,31 +61,30 @@ public class MapToObjectTransformer extends AbstractPayloadTransformer<Map<?,?>,
|
||||
this.targetBeanName = beanName;
|
||||
this.targetClass = null;
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.integration.transformer.AbstractPayloadTransformer#transformPayload(java.lang.Object)
|
||||
*/
|
||||
protected Object transformPayload(Map<?,?> payload) throws Exception {
|
||||
|
||||
@Override
|
||||
protected Object transformPayload(Map<?, ?> payload) throws Exception {
|
||||
Object target = (this.targetClass != null)
|
||||
? BeanUtils.instantiate(this.targetClass)
|
||||
: this.getBeanFactory().getBean(this.targetBeanName);
|
||||
|
||||
DataBinder binder = new DataBinder(target);
|
||||
ConversionService conversionService = null;
|
||||
if (this.getBeanFactory() instanceof ConfigurableBeanFactory){
|
||||
conversionService = ((ConfigurableBeanFactory)this.getBeanFactory()).getConversionService();
|
||||
}
|
||||
if (conversionService == null){
|
||||
ConversionService conversionService = this.getConversionService();
|
||||
if (conversionService == null) {
|
||||
conversionService = new DefaultConversionService();
|
||||
}
|
||||
binder.setConversionService(conversionService);
|
||||
binder.bind(new MutablePropertyValues(payload));
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
protected void onInit(){
|
||||
@Override
|
||||
protected void onInit() {
|
||||
if (StringUtils.hasText(this.targetBeanName)) {
|
||||
Assert.isTrue(this.getBeanFactory().isPrototype(this.targetBeanName),
|
||||
"target bean [" + targetBeanName + "] must have 'prototype' scope");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,27 +9,27 @@
|
||||
<int:channel id="output">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int:map-to-object-transformer input-channel="input"
|
||||
output-channel="output"
|
||||
|
||||
<int:map-to-object-transformer input-channel="input"
|
||||
output-channel="output"
|
||||
type="org.springframework.integration.config.xml.MapToObjectTransformerParserTests$Person"/>
|
||||
|
||||
|
||||
<int:channel id="inputA"/>
|
||||
<int:channel id="outputA">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<int:map-to-object-transformer input-channel="inputA"
|
||||
output-channel="outputA"
|
||||
|
||||
<int:map-to-object-transformer input-channel="inputA"
|
||||
output-channel="outputA"
|
||||
ref="person"/>
|
||||
|
||||
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
|
||||
|
||||
<bean id="conversionService" name="integrationConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
|
||||
<property name="converters">
|
||||
<list>
|
||||
<bean class="org.springframework.integration.config.xml.MapToObjectTransformerParserTests$StringToAddressConverter" />
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="person" class="org.springframework.integration.config.xml.MapToObjectTransformerParserTests$Person" scope="prototype"/>
|
||||
</beans>
|
||||
|
||||
@@ -20,43 +20,49 @@ import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public class MapToObjectTransformerTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void testMapToObjectTransformation(){
|
||||
Map map = new HashMap();
|
||||
public void testMapToObjectTransformation() {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("fname", "Justin");
|
||||
map.put("lname", "Case");
|
||||
Address address = new Address();
|
||||
address.setStreet("1123 Main st");
|
||||
map.put("address", address);
|
||||
|
||||
Message message = MessageBuilder.withPayload(map).build();
|
||||
Message<?> message = MessageBuilder.withPayload(map).build();
|
||||
|
||||
MapToObjectTransformer transformer = new MapToObjectTransformer(Person.class);
|
||||
transformer.setBeanFactory(this.getBeanFactory());
|
||||
Message newMessage = transformer.transform(message);
|
||||
Message<?> newMessage = transformer.transform(message);
|
||||
Person person = (Person) newMessage.getPayload();
|
||||
assertNotNull(person);
|
||||
assertEquals("Justin", person.getFname());
|
||||
@@ -67,20 +73,20 @@ public class MapToObjectTransformerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapToObjectTransformationWithPrototype(){
|
||||
Map map = new HashMap();
|
||||
public void testMapToObjectTransformationWithPrototype() {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("fname", "Justin");
|
||||
map.put("lname", "Case");
|
||||
Address address = new Address();
|
||||
address.setStreet("1123 Main st");
|
||||
map.put("address", address);
|
||||
|
||||
Message message = MessageBuilder.withPayload(map).build();
|
||||
Message<?> message = MessageBuilder.withPayload(map).build();
|
||||
StaticApplicationContext ac = new StaticApplicationContext();
|
||||
ac.registerPrototype("person", Person.class);
|
||||
MapToObjectTransformer transformer = new MapToObjectTransformer("person");
|
||||
transformer.setBeanFactory(ac.getBeanFactory());
|
||||
Message newMessage = transformer.transform(message);
|
||||
Message<?> newMessage = transformer.transform(message);
|
||||
Person person = (Person) newMessage.getPayload();
|
||||
assertNotNull(person);
|
||||
assertEquals("Justin", person.getFname());
|
||||
@@ -91,20 +97,22 @@ public class MapToObjectTransformerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapToObjectTransformationWithConversionService(){
|
||||
Map map = new HashMap();
|
||||
public void testMapToObjectTransformationWithConversionService() {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("fname", "Justin");
|
||||
map.put("lname", "Case");
|
||||
map.put("address", "1123 Main st");
|
||||
|
||||
Message message = MessageBuilder.withPayload(map).build();
|
||||
Message<?> message = MessageBuilder.withPayload(map).build();
|
||||
|
||||
MapToObjectTransformer transformer = new MapToObjectTransformer(Person.class);
|
||||
ConfigurableBeanFactory beanFactory = this.getBeanFactory();
|
||||
((GenericConversionService)beanFactory.getConversionService()).addConverter(new StringToAddressConverter());
|
||||
BeanFactory beanFactory = this.getBeanFactory();
|
||||
ConverterRegistry conversionService =
|
||||
beanFactory.getBean(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME, ConverterRegistry.class);
|
||||
conversionService.addConverter(new StringToAddressConverter());
|
||||
transformer.setBeanFactory(beanFactory);
|
||||
|
||||
Message newMessage = transformer.transform(message);
|
||||
Message<?> newMessage = transformer.transform(message);
|
||||
Person person = (Person) newMessage.getPayload();
|
||||
assertNotNull(person);
|
||||
assertEquals("Justin", person.getFname());
|
||||
@@ -113,45 +121,73 @@ public class MapToObjectTransformerTests {
|
||||
assertEquals("1123 Main st", person.getAddress().getStreet());
|
||||
}
|
||||
|
||||
private ConfigurableBeanFactory getBeanFactory(){
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
GenericConversionService conversionService = new DefaultConversionService();
|
||||
beanFactory.setConversionService(conversionService);
|
||||
return beanFactory;
|
||||
private BeanFactory getBeanFactory() {
|
||||
GenericApplicationContext ctx = TestUtils.createTestApplicationContext();
|
||||
Constructor<?> constructorToUse = null;
|
||||
try {
|
||||
// Add the integrationConversionService (reflection needed because of package protection)
|
||||
final Class<?> conversionServiceCreatorClass = ClassUtils.forName("org.springframework.integration.context.ConversionServiceCreator",
|
||||
ClassUtils.getDefaultClassLoader());
|
||||
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
|
||||
public Constructor<?> run() throws Exception {
|
||||
return conversionServiceCreatorClass.getDeclaredConstructor((Class[]) null);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Unexpected Privilege Exception: ", e);
|
||||
}
|
||||
|
||||
ctx.addBeanFactoryPostProcessor((BeanFactoryPostProcessor) BeanUtils.instantiateClass(constructorToUse));
|
||||
ctx.refresh();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public static class Person{
|
||||
public static class Person {
|
||||
|
||||
private String fname;
|
||||
|
||||
private String lname;
|
||||
|
||||
private String ssn;
|
||||
|
||||
private Address address;
|
||||
|
||||
public String getSsn() {
|
||||
return ssn;
|
||||
}
|
||||
|
||||
public void setSsn(String ssn) {
|
||||
this.ssn = ssn;
|
||||
}
|
||||
|
||||
public String getFname() {
|
||||
return fname;
|
||||
}
|
||||
|
||||
public void setFname(String fname) {
|
||||
this.fname = fname;
|
||||
}
|
||||
|
||||
public String getLname() {
|
||||
return lname;
|
||||
}
|
||||
|
||||
public void setLname(String lname) {
|
||||
this.lname = lname;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Address {
|
||||
|
||||
private String street;
|
||||
|
||||
public String getStreet() {
|
||||
@@ -163,7 +199,8 @@ public class MapToObjectTransformerTests {
|
||||
}
|
||||
}
|
||||
|
||||
public class StringToAddressConverter implements Converter<String, Address>{
|
||||
public class StringToAddressConverter implements Converter<String, Address> {
|
||||
|
||||
public Address convert(String source) {
|
||||
Address address = new Address();
|
||||
address.setStreet(source);
|
||||
|
||||
Reference in New Issue
Block a user