diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java index 4cf8e4ec2a..0ca91d1b1b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java @@ -41,6 +41,8 @@ public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHan registerBeanDefinitionParser("header-enricher", new StandardHeaderEnricherParser()); registerBeanDefinitionParser("header-filter", new HeaderFilterParser()); registerBeanDefinitionParser("object-to-string-transformer", new ObjectToStringTransformerParser()); + registerBeanDefinitionParser("object-to-map-transformer", new ObjectToMapTransformerParser()); + registerBeanDefinitionParser("map-to-object-transformer", new MapToObjectTransformerParser()); registerBeanDefinitionParser("payload-serializing-transformer", new PayloadSerializingTransformerParser()); registerBeanDefinitionParser("payload-deserializing-transformer", new PayloadDeserializingTransformerParser()); registerBeanDefinitionParser("claim-check-in", new ClaimCheckInParser()); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/MapToObjectTransformerParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/MapToObjectTransformerParser.java new file mode 100644 index 0000000000..a5f908ca58 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/MapToObjectTransformerParser.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2010 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.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 + * @since 2.0 + */ +public class MapToObjectTransformerParser extends AbstractTransformerParser { + + @Override + protected String getTransformerClassName() { + return MapToObjectTransformer.class.getName(); + } + + @Override + 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)){ + 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"); + } + } +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToMapTransformerParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToMapTransformerParser.java new file mode 100644 index 0000000000..6226f63cfa --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ObjectToMapTransformerParser.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.transformer.ObjectToMapTransformer; +import org.w3c.dom.Element; + +/** + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class ObjectToMapTransformerParser extends AbstractTransformerParser { + + @Override + protected String getTransformerClassName() { + return ObjectToMapTransformer.class.getName(); + } + + @Override + protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + } +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/MapToObjectTransformer.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/MapToObjectTransformer.java index 76fe5bd649..bc57e087bb 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/MapToObjectTransformer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/MapToObjectTransformer.java @@ -58,7 +58,6 @@ public class MapToObjectTransformer extends AbstractPayloadTransformer, this.targetClass = targetClass; this.targetBeanName = null; } - /** * @param beanName */ @@ -67,8 +66,6 @@ public class MapToObjectTransformer extends AbstractPayloadTransformer, this.targetBeanName = beanName; this.targetClass = null; } - - /* * (non-Javadoc) * @see org.springframework.integration.transformer.AbstractPayloadTransformer#transformPayload(java.lang.Object) diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd index 93fc7beb04..fa1566b20c 100644 --- a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd +++ b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd @@ -1031,6 +1031,8 @@ + + @@ -1498,6 +1500,60 @@ + + + + Defines a Transformer that converts any Object payload to a SpEL Map. + + + + + + + + + + + + + + + + Defines a Transformer that converts SpEL-based Map to an object. + + + + + + + + + + + + + + + + + The name of the bean to be produced by this transformer. The bean MUST BE of scope 'prototype'. + NOTE: This attribute is mutually-exclusive with 'type'. + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context-fail.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context-fail.xml new file mode 100644 index 0000000000..c217366c1e --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context-fail.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context.xml new file mode 100644 index 0000000000..668db0363b --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests-context.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests.java new file mode 100644 index 0000000000..a4da5b4175 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MapToObjectTransformerParserTests.java @@ -0,0 +1,184 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; + +import java.util.HashMap; +import java.util.Map; + +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.beans.factory.annotation.Qualifier; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.convert.converter.Converter; +import org.springframework.integration.channel.PollableChannel; +import org.springframework.integration.core.Message; +import org.springframework.integration.core.MessageChannel; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + + +/** + * @author Oleg Zhurakousky + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class MapToObjectTransformerParserTests { + @Autowired + @Qualifier("input") + private MessageChannel input; + @Autowired + @Qualifier("output") + private PollableChannel output; + + @Autowired + @Qualifier("inputA") + private MessageChannel inputA; + @Autowired + @Qualifier("outputA") + private PollableChannel outputA; + + @SuppressWarnings("unchecked") + @Test + public void testMapToObjectTransformationWithType(){ + Map map = new HashMap(); + 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(); + input.send(message); + + Message outMessage = output.receive(); + + Person person = (Person) outMessage.getPayload(); + assertNotNull(person); + assertEquals("Justin", person.getFname()); + assertEquals("Case", person.getLname()); + assertNull(person.getSsn()); + assertNotNull(person.getAddress()); + assertEquals("1123 Main st", person.getAddress().getStreet()); + } + + @SuppressWarnings("unchecked") + @Test + public void testMapToObjectTransformationWithRef(){ + Map map = new HashMap(); + 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(); + inputA.send(message); + Message newMessage = outputA.receive(); + Person person = (Person) newMessage.getPayload(); + assertNotNull(person); + assertEquals("Justin", person.getFname()); + assertEquals("Case", person.getLname()); + assertNull(person.getSsn()); + assertNotNull(person.getAddress()); + assertEquals("1123 Main st", person.getAddress().getStreet()); + } + + + @SuppressWarnings("unchecked") + @Test + public void testMapToObjectTransformationWithConversionService(){ + Map map = new HashMap(); + map.put("fname", "Justin"); + map.put("lname", "Case"); + map.put("address", "1123 Main st"); + + Message message = MessageBuilder.withPayload(map).build(); + inputA.send(message); + + Message newMessage = outputA.receive(); + Person person = (Person) newMessage.getPayload(); + assertNotNull(person); + assertEquals("Justin", person.getFname()); + assertEquals("Case", person.getLname()); + assertNotNull(person.getAddress()); + assertEquals("1123 Main st", person.getAddress().getStreet()); + } + @Test(expected=BeanCreationException.class) + public void testNonPrototypeFailure(){ + new ClassPathXmlApplicationContext("MapToObjectTransformerParserTests-context-fail.xml", MapToObjectTransformerParserTests.class); + } + + 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() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + } + + public static class StringToAddressConverter implements Converter{ + public StringToAddressConverter(){} + public Address convert(String source) { + Address address = new Address(); + address.setStreet(source); + return address; + } + } +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToMapTransformerParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToMapTransformerParserTests-context.xml new file mode 100644 index 0000000000..a319c327e9 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToMapTransformerParserTests-context.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToMapTransformerParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToMapTransformerParserTests.java new file mode 100644 index 0000000000..7746a8fe99 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ObjectToMapTransformerParserTests.java @@ -0,0 +1,289 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.integration.channel.PollableChannel; +import org.springframework.integration.core.Message; +import org.springframework.integration.core.MessageChannel; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.transformer.MessageTransformationException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Oleg Zhurakousky + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class ObjectToMapTransformerParserTests { + + @Autowired + @Qualifier("directInput") + private MessageChannel directInput; + + @Autowired + @Qualifier("output") + private PollableChannel output; + + + @SuppressWarnings("unchecked") + @Test + public void testObjectToSpelMapTransformer(){ + Employee employee = this.buildEmployee(); + EvaluationContext context = new StandardEvaluationContext(employee); + ExpressionParser parser = new SpelExpressionParser(); + + Message message = MessageBuilder.withPayload(employee).build(); + directInput.send(message); + + Message> outputMessage = (Message>) output.receive(); + Map transformedMap = outputMessage.getPayload(); + assertNotNull(outputMessage.getPayload()); + for (String key : transformedMap.keySet()) { + Expression expression = parser.parseExpression(key); + Object valueFromTheMap = transformedMap.get(key); + Object valueFromExpression = expression.getValue(context); + String packageNameOfValueType = valueFromTheMap.getClass().getPackage().getName(); + assertTrue(packageNameOfValueType.startsWith("java.lang")); + assertEquals(valueFromTheMap, valueFromExpression); + } + } + @Test(expected=MessageTransformationException.class) + public void testObjectToSpelMapTransformerWithCycle(){ + Employee employee = this.buildEmployee(); + Child child = new Child(); + Person parent = employee.getPerson(); + parent.setChild(child); + child.setParent(parent); + Message message = MessageBuilder.withPayload(employee).build(); + directInput.send(message); + } + + @SuppressWarnings("unchecked") + public Employee buildEmployee(){ + Address companyAddress = new Address(); + companyAddress.setCity("Philadelphia"); + companyAddress.setStreet("1123 Main"); + companyAddress.setZip("12345"); + + Map coordinates = new HashMap(); + coordinates.put("latitude", new Long[]{(long)1, (long)5, (long)13}); + coordinates.put("longitude", new Long[]{(long)156}); + companyAddress.setCoordinates(coordinates); + + Employee employee = new Employee(); + employee.setCompanyName("ABC Inc."); + employee.setCompanyAddress(companyAddress); + ArrayList departments = new ArrayList(); + departments.add("HR"); + departments.add("IT"); + employee.setDepartments(departments); + + Person person = new Person(); + person.setFname("Justin"); + person.setLname("Case"); + person.setAkaNames("Hard", "Use", "Beer"); + Address personAddress = new Address(); + personAddress.setCity("Philly"); + personAddress.setStreet("123 Main"); + List listTestData = new ArrayList(); + listTestData.add("hello"); + listTestData.add("blah"); + Map> mapWithListTestData = new HashMap>(); + mapWithListTestData.put("mapWithListTestData", listTestData); + personAddress.setMapWithListData(mapWithListTestData); + person.setAddress(personAddress); + + Map remarksA = new HashMap(); + Map remarksB = new HashMap(); + remarksA.put("foo", "foo"); + remarksA.put("bar", "bar"); + remarksB.put("baz", "baz"); + List> remarks = new ArrayList>(); + remarks.add(remarksA); + remarks.add(remarksB); + person.setRemarks(remarks); + employee.setPerson(person); + + Map> testMapData = new HashMap>(); + + Map internalMapA = new HashMap(); + internalMapA.put("foo", "foo"); + internalMapA.put("bar", "bar"); + Map internalMapB = new HashMap(); + internalMapB.put("baz", "baz"); + + testMapData.put("internalMapA", internalMapA); + testMapData.put("internalMapB", internalMapB); + + employee.setTestMapInMapData(testMapData); + return employee; + } + + public static class Employee{ + private List departments; + private String companyName; + private Person person; + private Address companyAddress; + private Map> testMapInMapData; + public Map> getTestMapInMapData() { + return testMapInMapData; + } + public void setTestMapInMapData( + Map> testMapInMapData) { + this.testMapInMapData = testMapInMapData; + } + public String getCompanyName() { + return companyName; + } + public void setCompanyName(String companyName) { + this.companyName = companyName; + } + public Person getPerson() { + return person; + } + public void setPerson(Person person) { + this.person = person; + } + public Address getCompanyAddress() { + return companyAddress; + } + public void setCompanyAddress(Address companyAddress) { + this.companyAddress = companyAddress; + } + public List getDepartments() { + return departments; + } + public void setDepartments(List departments) { + this.departments = departments; + } + } + + public static class Person{ + private String fname; + private String lname; + private String[] akaNames; + private List> remarks; + private Child child; + public Child getChild() { + return child; + } + public void setChild(Child child) { + this.child = child; + } + public List> getRemarks() { + return remarks; + } + public void setRemarks(List> remarks) { + this.remarks = remarks; + } + private Address address; + public String[] getAkaNames() { + return akaNames; + } + public void setAkaNames(String... akaNames) { + this.akaNames = akaNames; + } + 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; + private String city; + private String zip; + private Map> mapWithListData; + private Map coordinates; + public Map> getMapWithListData() { + return mapWithListData; + } + public void setMapWithListData(Map> mapWithListData) { + this.mapWithListData = mapWithListData; + } + public String getStreet() { + return street; + } + public void setStreet(String street) { + this.street = street; + } + public String getCity() { + return city; + } + public void setCity(String city) { + this.city = city; + } + public String getZip() { + return zip; + } + public void setZip(String zip) { + this.zip = zip; + } + public Map getCoordinates() { + return coordinates; + } + public void setCoordinates(Map coordinates) { + this.coordinates = coordinates; + } + } + + public static class Child { + private Person parent; + + public Person getParent() { + return parent; + } + + public void setParent(Person parent) { + this.parent = parent; + } + } + +}