From 33e356da2872dc27819c7889ad9e3fab23b46a62 Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Mon, 7 Nov 2011 16:01:26 -0500 Subject: [PATCH 1/3] INT-2228 - Content Enricher - request-channel should be optional see also: https://jira.springsource.org/browse/INT-2228 --- .../config/xml/EnricherParser.java | 7 +- .../transformer/ContentEnricher.java | 56 +++++- .../config/xml/spring-integration-2.1.xsd | 18 +- ...rserTestsWithoutRequestChannel-context.xml | 22 +++ ...icherParserTestsWithoutRequestChannel.java | 131 ++++++++++++++ .../transformer/ContentEnricherTests.java | 164 ++++++++++++++++++ 6 files changed, 385 insertions(+), 13 deletions(-) create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTestsWithoutRequestChannel-context.xml create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTestsWithoutRequestChannel.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java index c56e999fab..cc18017a47 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java @@ -43,10 +43,15 @@ public class EnricherParser extends AbstractConsumerEndpointParser { final BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ContentEnricher.class); String requestChannel = element.getAttribute("request-channel"); String replyChannel = element.getAttribute("reply-channel"); - builder.addConstructorArgReference(requestChannel); + + if (StringUtils.hasText(requestChannel)) { + builder.addConstructorArgReference(requestChannel); + } + if (StringUtils.hasText(replyChannel)) { builder.addConstructorArgReference(replyChannel); } + List propertyElements = DomUtils.getChildElementsByTagName(element, "property"); if (!CollectionUtils.isEmpty(propertyElements)) { ManagedMap propertyExpressions = new ManagedMap(); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java index fe693d3bd6..1c95d20c1c 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java @@ -46,7 +46,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem private final Map propertyExpressions = new HashMap(); - private final Gateway gateway = new Gateway(); + private final Gateway gateway; private final SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); @@ -56,6 +56,15 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem private Expression requestPayloadExpression; + /** + * Create a Content Enricher without providing a request channel. This is + * useful when only static values shall be enriched. + */ + public ContentEnricher() { + this.evaluationContext.addPropertyAccessor(new MapAccessor()); + this.gateway = null; + } + /** * Create a Content Enricher with the given request channel. An anonymous reply channel * will be created for each request. @@ -69,6 +78,9 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem */ public ContentEnricher(MessageChannel requestChannel, MessageChannel replyChannel) { Assert.notNull(requestChannel, "requestChannel must not be null"); + + this.gateway = new Gateway(); + this.gateway.setRequestChannel(requestChannel); if (replyChannel != null) { this.gateway.setReplyChannel(replyChannel); @@ -135,7 +147,11 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem @Override public void onInit() { super.onInit(); - this.gateway.afterPropertiesSet(); + + if (this.gateway != null) { + this.gateway.afterPropertiesSet(); + } + } @Override @@ -171,7 +187,13 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem .build(); } - final Message replyMessage = this.gateway.sendAndReceiveMessage(actualRequestMessage); + final Message replyMessage; + + if (this.gateway == null) { + replyMessage = actualRequestMessage; + } else { + replyMessage = this.gateway.sendAndReceiveMessage(actualRequestMessage); + } for (Map.Entry entry : this.propertyExpressions.entrySet()) { Expression propertyExpression = entry.getKey(); @@ -184,20 +206,36 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem } - /* - * Lifecycle implementation + /** + * Lifecycle implementation. If no requestChannel is defined, this method + * has no effect as in that case no Gateway is initialized. */ - public void start() { - this.gateway.start(); + if (this.gateway != null) { + this.gateway.start(); + } } + /** + * Lifecycle implementation. If no requestChannel is defined, this method + * has no effect as in that case no Gateway is initialized. + */ public void stop() { - this.gateway.stop(); + if (this.gateway != null) { + this.gateway.stop(); + } } + /** + * Lifecycle implementation. If no requestChannel is defined, this method + * will return always return true as no Gateway is initialized. + */ public boolean isRunning() { - return this.gateway.isRunning(); + if (this.gateway != null) { + return this.gateway.isRunning(); + } else { + return true; + } } diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.1.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.1.xsd index 07ec3d65a5..eaf4bbae3b 100644 --- a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.1.xsd +++ b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.1.xsd @@ -1014,10 +1014,14 @@ endpoint itself is a Polling Consumer for a channel with a queue. - + - Channel to which a Message will be sent to get the data to use for enrichment. + Channel to which a Message will be sent to get the data to use + for enrichment. This attribute is optional. Not specifying a + 'request-channel' is useful in situations, where only static + values shall be used for enrichment using the 'property' + sub-element. @@ -1045,6 +1049,10 @@ endpoint itself is a Polling Consumer for a channel with a queue. Boolean value indicating whether any payload that implements Cloneable should be cloned prior to sending the Message to the request chanenl for acquiring the enriching data. The cloned version would be used as the target payload for the ultimate reply. + + If the payload does NOT implement 'Cloneable', then setting this + attribute to 'true' has NO effect. + Default is false. @@ -1089,7 +1097,11 @@ endpoint itself is a Polling Consumer for a channel with a queue. diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTestsWithoutRequestChannel-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTestsWithoutRequestChannel-context.xml new file mode 100644 index 0000000000..1fd7e54d8f --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTestsWithoutRequestChannel-context.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTestsWithoutRequestChannel.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTestsWithoutRequestChannel.java new file mode 100644 index 0000000000..380fe4dce9 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTestsWithoutRequestChannel.java @@ -0,0 +1,131 @@ +/* + * Copyright 2002-2011 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 org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.expression.Expression; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.integration.transformer.ContentEnricher; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Mark Fisher + * @since 2.1 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class EnricherParserTestsWithoutRequestChannel { + + @Autowired + private ApplicationContext context; + + + @Test + @SuppressWarnings("unchecked") + public void configurationCheck() { + Object endpoint = context.getBean("enricher"); + assertEquals(EventDrivenConsumer.class, endpoint.getClass()); + Object handler = TestUtils.getPropertyValue(endpoint, "handler"); + assertEquals(ContentEnricher.class, handler.getClass()); + ContentEnricher enricher = (ContentEnricher) handler; + assertEquals(99, enricher.getOrder()); + + DirectFieldAccessor accessor = new DirectFieldAccessor(enricher); + + assertNull(accessor.getPropertyValue("gateway")); + assertEquals(context.getBean("output"), accessor.getPropertyValue("outputChannel")); + assertEquals(false, accessor.getPropertyValue("shouldClonePayload")); + assertNull(accessor.getPropertyValue("requestPayloadExpression")); + + Map propertyExpressions = (Map) accessor.getPropertyValue("propertyExpressions"); + for (Map.Entry e : propertyExpressions.entrySet()) { + if ("name".equals(e.getKey().getExpressionString())) { + assertEquals("payload.name", e.getValue().getExpressionString()); + } + else if ("age".equals(e.getKey().getExpressionString())) { + assertEquals("42", e.getValue().getExpressionString()); + } + else { + throw new IllegalStateException("expected 'name' and 'age' only, not: " + e.getKey().getExpressionString()); + } + } + } + + @Test + public void integrationTest() { + + Target original = new Target(); + + original.setAge(100); + original.setName("original name"); + + Message request = MessageBuilder.withPayload(original).build(); + context.getBean("input", MessageChannel.class).send(request); + Message reply = context.getBean("output", PollableChannel.class).receive(0); + Target enriched = (Target) reply.getPayload(); + assertEquals("original name", enriched.getName()); + assertEquals(42, enriched.getAge()); + assertSame(original, enriched); + } + + public static class Target implements Cloneable { + + private volatile String name; + + private volatile int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Object clone() { + Target copy = new Target(); + copy.setName(this.name); + copy.setAge(this.age); + return copy; + } + } + +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java index 99f319a767..8ead98e0b5 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java @@ -18,6 +18,10 @@ package org.springframework.integration.transformer; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import java.util.HashMap; import java.util.Map; @@ -26,6 +30,7 @@ import org.junit.Test; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.integration.Message; +import org.springframework.integration.MessageHandlingException; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; @@ -59,6 +64,34 @@ public class ContentEnricherTests { assertEquals("Doe, John", ((Target) reply.getPayload()).getName()); } + @Test + public void testSimplePropertyWithoutUsingRequestChannel() { + QueueChannel replyChannel = new QueueChannel(); + ContentEnricher enricher = new ContentEnricher(); + SpelExpressionParser parser = new SpelExpressionParser(); + Map propertyExpressions = new HashMap(); + propertyExpressions.put("name", parser.parseExpression("'just a static string'")); + enricher.setPropertyExpressions(propertyExpressions); + Target target = new Target("replace me"); + Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); + enricher.handleMessage(requestMessage); + Message reply = replyChannel.receive(0); + assertEquals("just a static string", ((Target) reply.getPayload()).getName()); + } + + @Test + public void testContentEnricherWithNullRequestChannel() { + + try { + new ContentEnricher(null); + } catch (IllegalArgumentException e) { + assertEquals("requestChannel must not be null", e.getMessage()); + return; + } + + fail("Expected an IllegalArgumentException to be thrown."); + } + @Test public void nestedProperty() { QueueChannel replyChannel = new QueueChannel(); @@ -108,6 +141,98 @@ public class ContentEnricherTests { assertNotSame(target, result); } + @Test + public void clonePayloadIgnored() { + QueueChannel replyChannel = new QueueChannel(); + DirectChannel requestChannel = new DirectChannel(); + requestChannel.subscribe(new AbstractReplyProducingMessageHandler() { + @Override + protected Object handleRequestMessage(Message requestMessage) { + return new Source("John", "Doe"); + } + }); + ContentEnricher enricher = new ContentEnricher(requestChannel); + enricher.setShouldClonePayload(true); + SpelExpressionParser parser = new SpelExpressionParser(); + Map propertyExpressions = new HashMap(); + propertyExpressions.put("name", parser.parseExpression("payload.lastName + ', ' + payload.firstName")); + enricher.setPropertyExpressions(propertyExpressions); + TargetUser target = new TargetUser(); + target.setName("replace me"); + + Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); + enricher.handleMessage(requestMessage); + Message reply = replyChannel.receive(0); + TargetUser result = (TargetUser) reply.getPayload(); + assertEquals("Doe, John", result.getName()); + + assertSame(target, result); + } + + @Test + public void clonePayloadWithFailure() { + QueueChannel replyChannel = new QueueChannel(); + DirectChannel requestChannel = new DirectChannel(); + requestChannel.subscribe(new AbstractReplyProducingMessageHandler() { + @Override + protected Object handleRequestMessage(Message requestMessage) { + return new Source("John", "Doe"); + } + }); + ContentEnricher enricher = new ContentEnricher(requestChannel); + enricher.setShouldClonePayload(true); + SpelExpressionParser parser = new SpelExpressionParser(); + Map propertyExpressions = new HashMap(); + propertyExpressions.put("name", parser.parseExpression("payload.lastName + ', ' + payload.firstName")); + enricher.setPropertyExpressions(propertyExpressions); + UncloneableTargetUser target = new UncloneableTargetUser(); + target.setName("replace me"); + + Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); + + try { + enricher.handleMessage(requestMessage); + } catch (MessageHandlingException e) { + assertEquals("Failed to clone payload object", e.getMessage()); + return; + } + + fail("Expected a MessageHandlingException to be thrown."); + + } + + @Test + public void testLifeCycleMethodsWithoutRequestChannel() { + ContentEnricher enricher = new ContentEnricher(); + + enricher.afterPropertiesSet(); + + assertTrue(enricher.isRunning()); + enricher.stop(); + assertTrue(enricher.isRunning()); + } + + @Test + public void testLifeCycleMethodsWithRequestChannel() { + + DirectChannel requestChannel = new DirectChannel(); + requestChannel.subscribe(new AbstractReplyProducingMessageHandler() { + @Override + protected Object handleRequestMessage(Message requestMessage) { + return new Source("John", "Doe"); + } + }); + ContentEnricher enricher = new ContentEnricher(requestChannel); + + enricher.afterPropertiesSet(); + + enricher.start(); + assertTrue(enricher.isRunning()); + enricher.stop(); + assertFalse(enricher.isRunning()); + enricher.start(); + assertTrue(enricher.isRunning()); + } @SuppressWarnings("unused") private static final class Source { @@ -166,4 +291,43 @@ public class ContentEnricherTests { } } + public static final class TargetUser { + + private volatile String name; + + public TargetUser() { + this.name = "default"; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + } + + public static final class UncloneableTargetUser implements Cloneable { + + private volatile String name; + + public UncloneableTargetUser() { + this.name = "default"; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public Object clone() { + throw new IllegalStateException("Cloning not possible"); + } + } + } \ No newline at end of file From a22ec2f8b0c85038ba05381ac91a7d4fcdb32b6f Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Tue, 8 Nov 2011 17:50:46 -0500 Subject: [PATCH 2/3] INT-2228 - Enricher fixes based on code review --- .../config/xml/EnricherParser.java | 11 +-- .../transformer/ContentEnricher.java | 83 ++++++++++--------- .../transformer/ContentEnricherTests.java | 42 ++++++++-- 3 files changed, 82 insertions(+), 54 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java index cc18017a47..55f084912c 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java @@ -41,16 +41,9 @@ public class EnricherParser extends AbstractConsumerEndpointParser { @Override protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { final BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ContentEnricher.class); - String requestChannel = element.getAttribute("request-channel"); - String replyChannel = element.getAttribute("reply-channel"); - if (StringUtils.hasText(requestChannel)) { - builder.addConstructorArgReference(requestChannel); - } - - if (StringUtils.hasText(replyChannel)) { - builder.addConstructorArgReference(replyChannel); - } + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-channel"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel"); List propertyElements = DomUtils.getChildElementsByTagName(element, "property"); if (!CollectionUtils.isEmpty(propertyElements)) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java index 1c95d20c1c..99e586c10d 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java @@ -36,8 +36,10 @@ import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; /** - * Content Enricher is a Message Transformer that invokes any downstream message flow via - * its request channel and then applies values from the reply Message to the original payload. + * Content Enricher is a Message Transformer that can augment a message's payload + * with either static values or by optionally invoking a downstream message flow + * via its request channel and then applying values from the reply Message to the + * original payload. * * @author Mark Fisher * @since 2.1 @@ -46,7 +48,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem private final Map propertyExpressions = new HashMap(); - private final Gateway gateway; + private Gateway gateway = null; private final SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); @@ -56,38 +58,8 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem private Expression requestPayloadExpression; - /** - * Create a Content Enricher without providing a request channel. This is - * useful when only static values shall be enriched. - */ - public ContentEnricher() { - this.evaluationContext.addPropertyAccessor(new MapAccessor()); - this.gateway = null; - } - - /** - * Create a Content Enricher with the given request channel. An anonymous reply channel - * will be created for each request. - */ - public ContentEnricher(MessageChannel requestChannel) { - this(requestChannel, null); - } - - /** - * Create a Content Enricher with the given request and reply channels. - */ - public ContentEnricher(MessageChannel requestChannel, MessageChannel replyChannel) { - Assert.notNull(requestChannel, "requestChannel must not be null"); - - this.gateway = new Gateway(); - - this.gateway.setRequestChannel(requestChannel); - if (replyChannel != null) { - this.gateway.setReplyChannel(replyChannel); - } - this.evaluationContext.addPropertyAccessor(new MapAccessor()); - } - + private MessageChannel requestChannel; + private MessageChannel replyChannel; /** * Provide the map of expressions to evaluate when enriching the target payload. @@ -108,6 +80,25 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem } } + /** + * Sets the content enricher's request channel. If specified, then an internal + * {@link Gateway} will be initialized. Setting a request channel is optional. + * Not setting a request channel is useful in situations where + * message payloads shall be enriched with static values only. + */ + public void setRequestChannel(MessageChannel requestChannel) { + this.requestChannel = requestChannel; + } + + /** + * Sets the content enricher's reply channel. If not specified, yet the request + * channel is set, an anonymous reply channel will automatically created + * for each request. + */ + public void setReplyChannel(MessageChannel replyChannel) { + this.replyChannel = replyChannel; + } + /** * By default the original message's payload will be used as the actual payload * that will be send to the request-channel. @@ -144,14 +135,32 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem this.shouldClonePayload = shouldClonePayload; } + + /** + * Initializes the Content Enricher. Will instantiate an internal Gateway if + * the requestChannel is set. + */ @Override public void onInit() { super.onInit(); - if (this.gateway != null) { - this.gateway.afterPropertiesSet(); + if (this.replyChannel != null) { + Assert.notNull(this.requestChannel, "If the replyChannel is set, then the requestChannel must not be null"); } + if (this.requestChannel != null) { + this.gateway = new Gateway(); + this.gateway.setRequestChannel(requestChannel); + + if (replyChannel != null) { + this.gateway.setReplyChannel(replyChannel); + } + + this.gateway.afterPropertiesSet(); + } + + this.evaluationContext.addPropertyAccessor(new MapAccessor()); + } @Override diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java index 8ead98e0b5..159ce46d82 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java @@ -52,11 +52,16 @@ public class ContentEnricherTests { return new Source("John", "Doe"); } }); - ContentEnricher enricher = new ContentEnricher(requestChannel); + + ContentEnricher enricher = new ContentEnricher(); + enricher.setRequestChannel(requestChannel); + SpelExpressionParser parser = new SpelExpressionParser(); Map propertyExpressions = new HashMap(); propertyExpressions.put("name", parser.parseExpression("payload.lastName + ', ' + payload.firstName")); enricher.setPropertyExpressions(propertyExpressions); + enricher.afterPropertiesSet(); + Target target = new Target("replace me"); Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); enricher.handleMessage(requestMessage); @@ -82,10 +87,13 @@ public class ContentEnricherTests { @Test public void testContentEnricherWithNullRequestChannel() { + ContentEnricher enricher = new ContentEnricher(); + enricher.setReplyChannel(new QueueChannel()); + try { - new ContentEnricher(null); + enricher.afterPropertiesSet(); } catch (IllegalArgumentException e) { - assertEquals("requestChannel must not be null", e.getMessage()); + assertEquals("If the replyChannel is set, then the requestChannel must not be null", e.getMessage()); return; } @@ -102,11 +110,15 @@ public class ContentEnricherTests { return new Source("John", "Doe"); } }); - ContentEnricher enricher = new ContentEnricher(requestChannel); + ContentEnricher enricher = new ContentEnricher(); + enricher.setRequestChannel(requestChannel); + SpelExpressionParser parser = new SpelExpressionParser(); Map propertyExpressions = new HashMap(); propertyExpressions.put("child.name", parser.parseExpression("payload.lastName + ', ' + payload.firstName")); enricher.setPropertyExpressions(propertyExpressions); + enricher.afterPropertiesSet(); + Target target = new Target("test"); Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); enricher.handleMessage(requestMessage); @@ -126,12 +138,16 @@ public class ContentEnricherTests { return new Source("John", "Doe"); } }); - ContentEnricher enricher = new ContentEnricher(requestChannel); + ContentEnricher enricher = new ContentEnricher(); + enricher.setRequestChannel(requestChannel); + enricher.setShouldClonePayload(true); SpelExpressionParser parser = new SpelExpressionParser(); Map propertyExpressions = new HashMap(); propertyExpressions.put("name", parser.parseExpression("payload.lastName + ', ' + payload.firstName")); enricher.setPropertyExpressions(propertyExpressions); + enricher.afterPropertiesSet(); + Target target = new Target("replace me"); Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); enricher.handleMessage(requestMessage); @@ -151,12 +167,16 @@ public class ContentEnricherTests { return new Source("John", "Doe"); } }); - ContentEnricher enricher = new ContentEnricher(requestChannel); + ContentEnricher enricher = new ContentEnricher(); + enricher.setRequestChannel(requestChannel); + enricher.setShouldClonePayload(true); SpelExpressionParser parser = new SpelExpressionParser(); Map propertyExpressions = new HashMap(); propertyExpressions.put("name", parser.parseExpression("payload.lastName + ', ' + payload.firstName")); enricher.setPropertyExpressions(propertyExpressions); + enricher.afterPropertiesSet(); + TargetUser target = new TargetUser(); target.setName("replace me"); @@ -179,12 +199,16 @@ public class ContentEnricherTests { return new Source("John", "Doe"); } }); - ContentEnricher enricher = new ContentEnricher(requestChannel); + ContentEnricher enricher = new ContentEnricher(); + enricher.setRequestChannel(requestChannel); + enricher.setShouldClonePayload(true); SpelExpressionParser parser = new SpelExpressionParser(); Map propertyExpressions = new HashMap(); propertyExpressions.put("name", parser.parseExpression("payload.lastName + ', ' + payload.firstName")); enricher.setPropertyExpressions(propertyExpressions); + enricher.afterPropertiesSet(); + UncloneableTargetUser target = new UncloneableTargetUser(); target.setName("replace me"); @@ -222,7 +246,9 @@ public class ContentEnricherTests { return new Source("John", "Doe"); } }); - ContentEnricher enricher = new ContentEnricher(requestChannel); + + ContentEnricher enricher = new ContentEnricher(); + enricher.setRequestChannel(requestChannel); enricher.afterPropertiesSet(); From 1433f2fd41ad60c314518c9dedf870e3fd36e4f5 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Thu, 10 Nov 2011 14:50:48 -0500 Subject: [PATCH 3/3] polishing --- .../transformer/ContentEnricher.java | 58 ++++++++----------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java index 99e586c10d..02b0aba300 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java @@ -42,14 +42,13 @@ import org.springframework.util.ReflectionUtils; * original payload. * * @author Mark Fisher + * @author Gunnar Hillert * @since 2.1 */ public class ContentEnricher extends AbstractReplyProducingMessageHandler implements Lifecycle { private final Map propertyExpressions = new HashMap(); - private Gateway gateway = null; - private final SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); @@ -58,8 +57,12 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem private Expression requestPayloadExpression; - private MessageChannel requestChannel; - private MessageChannel replyChannel; + private volatile MessageChannel requestChannel; + + private volatile MessageChannel replyChannel; + + private volatile Gateway gateway = null; + /** * Provide the map of expressions to evaluate when enriching the target payload. @@ -143,33 +146,24 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem @Override public void onInit() { super.onInit(); - if (this.replyChannel != null) { Assert.notNull(this.requestChannel, "If the replyChannel is set, then the requestChannel must not be null"); } - if (this.requestChannel != null) { this.gateway = new Gateway(); this.gateway.setRequestChannel(requestChannel); - if (replyChannel != null) { this.gateway.setReplyChannel(replyChannel); } - this.gateway.afterPropertiesSet(); } - this.evaluationContext.addPropertyAccessor(new MapAccessor()); - } @Override protected Object handleRequestMessage(Message requestMessage) { - final Object requestPayload = requestMessage.getPayload(); - final Object targetPayload; - if (requestPayload instanceof Cloneable && this.shouldClonePayload) { try { Method cloneMethod = requestPayload.getClass().getMethod("clone", new Class[0]); @@ -178,39 +172,32 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem catch (Exception e) { throw new MessageHandlingException(requestMessage, "Failed to clone payload object", e); } - } else { + } + else { targetPayload = requestPayload; } - final Message actualRequestMessage; - - if (this.requestPayloadExpression==null) { - - actualRequestMessage = requestMessage; - - } else { - + if (this.requestPayloadExpression == null) { + actualRequestMessage = requestMessage; + } + else { final Object requestMessagePayload = this.requestPayloadExpression.getValue(this.evaluationContext, requestMessage); actualRequestMessage = MessageBuilder.withPayload(requestMessagePayload) - .copyHeaders(requestMessage.getHeaders()) - .build(); + .copyHeaders(requestMessage.getHeaders()).build(); + } + final Message replyMessage; + if (this.gateway == null) { + replyMessage = actualRequestMessage; + } + else { + replyMessage = this.gateway.sendAndReceiveMessage(actualRequestMessage); } - - final Message replyMessage; - - if (this.gateway == null) { - replyMessage = actualRequestMessage; - } else { - replyMessage = this.gateway.sendAndReceiveMessage(actualRequestMessage); - } - for (Map.Entry entry : this.propertyExpressions.entrySet()) { Expression propertyExpression = entry.getKey(); Expression valueExpression = entry.getValue(); Object value = valueExpression.getValue(this.evaluationContext, replyMessage); propertyExpression.setValue(this.evaluationContext, targetPayload, value); } - return targetPayload; } @@ -242,7 +229,8 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem public boolean isRunning() { if (this.gateway != null) { return this.gateway.isRunning(); - } else { + } + else { return true; } }