From be5bcb3e950ee7c6ec6f41e59fcbe8905f993c7d Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 15 Dec 2011 16:32:08 -0500 Subject: [PATCH] INTSAMPLES-39 Dynamic FTP Router Demonstrates the use of Spring 3.1 to instantiate multiple application contexts, each with a differently configured ftp outbound adapter, together with a POJO Channel resolver used to send messages to the appropriate context. --- advanced/dynamic-ftp/pom.xml | 69 ++++++++++++++++ advanced/dynamic-ftp/readme.txt | 28 +++++++ .../ftp/DynamicFtpChannelResolver.java | 82 +++++++++++++++++++ .../dynamic-ftp-outbound-adapter-context.xml | 27 ++++++ .../ftp/DynamicFtpChannelResolverTests.java | 45 ++++++++++ .../ftp/FtpOutboundChannelAdapterSample.java | 70 ++++++++++++++++ ...tpOutboundChannelAdapterSample-context.xml | 17 ++++ .../src/test/resources/log4j.properties | 8 ++ 8 files changed, 346 insertions(+) create mode 100644 advanced/dynamic-ftp/pom.xml create mode 100644 advanced/dynamic-ftp/readme.txt create mode 100644 advanced/dynamic-ftp/src/main/java/org/springframework/integration/samples/ftp/DynamicFtpChannelResolver.java create mode 100644 advanced/dynamic-ftp/src/main/resources/META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml create mode 100644 advanced/dynamic-ftp/src/test/java/org/springframework/integration/samples/ftp/DynamicFtpChannelResolverTests.java create mode 100644 advanced/dynamic-ftp/src/test/java/org/springframework/integration/samples/ftp/FtpOutboundChannelAdapterSample.java create mode 100644 advanced/dynamic-ftp/src/test/resources/META-INF/spring/integration/DynamicFtpOutboundChannelAdapterSample-context.xml create mode 100644 advanced/dynamic-ftp/src/test/resources/log4j.properties diff --git a/advanced/dynamic-ftp/pom.xml b/advanced/dynamic-ftp/pom.xml new file mode 100644 index 00000000..3afb14e3 --- /dev/null +++ b/advanced/dynamic-ftp/pom.xml @@ -0,0 +1,69 @@ + + 4.0.0 + org.springframework.integration.samples + dynamic-ftp + 2.1.0 + Dynamic FTP Demo + jar + + 3.1.0.RELEASE + 2.1.0.RC1 + 1.2.16 + 4.7 + + + + org.springframework + spring-context + ${spring.framework.version} + + + org.springframework.integration + spring-integration-ftp + ${spring.integration.version} + + + log4j + log4j + ${log4j.version} + + + + junit + junit + ${junit.version} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + -Xlint:all + true + false + + + + + + + repository.springframework.maven.release + Spring Framework Maven Release Repository + http://maven.springframework.org/release + + + repository.springframework.maven.milestone + Spring Framework Maven Milestone Repository + http://maven.springframework.org/milestone + + + repository.springframework.maven.snapshot + Spring Framework Maven Snapshot Repository + http://maven.springframework.org/snapshot + + + diff --git a/advanced/dynamic-ftp/readme.txt b/advanced/dynamic-ftp/readme.txt new file mode 100644 index 00000000..4f37adfc --- /dev/null +++ b/advanced/dynamic-ftp/readme.txt @@ -0,0 +1,28 @@ +This example demonstrates one technique for sending files to dynamic destinations. + +See DynamicFtpChannelResolver. The resolve() method maps a customer to a +channel, where the channel is defined in a separate application context +for each customer. The application context property placeholders are configured +using the Spring 3.1 Environment management by supplying a custom property +source. + +A real implementation would provide these properties based on the customer; this +sample simply uses the customer as part of the host name. + +The application context is loaded from the file in src/main/resources/... + +DynamicFtpChannelResolverTests is a simple JUnit test case that verifies +the same channel is used for the same customer each time, and that a +different channel is used for a different customer. + +FtpOutboundChannelAdapterSample shows how messages sent with a different customer +header are routed to a different ftp adapter. It simply verifies that the +correct UnknownHostException is returned. + +Notice in the config file how an expression-based router is used to invoke +the DynamicFtpChannelResolver to obtain a reference to the appropriate +channel for the customer. Notice that the channel resolver is a simple +POJO, invoked using SpEL... + + diff --git a/advanced/dynamic-ftp/src/main/java/org/springframework/integration/samples/ftp/DynamicFtpChannelResolver.java b/advanced/dynamic-ftp/src/main/java/org/springframework/integration/samples/ftp/DynamicFtpChannelResolver.java new file mode 100644 index 00000000..c14e82b7 --- /dev/null +++ b/advanced/dynamic-ftp/src/main/java/org/springframework/integration/samples/ftp/DynamicFtpChannelResolver.java @@ -0,0 +1,82 @@ +/* + * 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.samples.ftp; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.integration.MessageChannel; + +/** + * Demonstrates how a dynamic Spring Integration flow snippet can be used + * to send files to dynamic destinations. + * + * @author Gary Russell + * @since 2.1 + * + */ +public class DynamicFtpChannelResolver { + + private final Map channels = new HashMap(); + + /** + * Resolve a customer to a channel, where each customer gets a private + * application context and the channel is the inbound channel to that + * application context. + * + * @param customer + * @return a channel + */ + public MessageChannel resolve(String customer) { + MessageChannel channel = this.channels.get(customer); + if (channel == null) { + ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext( + new String[] { "/META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml" }, + false); + this.setEnvironmentForCustomer(ctx, customer); + ctx.refresh(); + channel = ctx.getBean("toFtpChannel", MessageChannel.class); + this.channels.put(customer, channel); + } + return channel; + } + + /** + * Use Spring 3.1. environment support to set properties for the + * customer-specific application context. + * + * @param ctx + * @param customer + */ + private void setEnvironmentForCustomer(ConfigurableApplicationContext ctx, + String customer) { + StandardEnvironment env = new StandardEnvironment(); + Properties props = new Properties(); + // populate properties for customer + props.setProperty("host", "host.for." + customer); + props.setProperty("user", "user"); + props.setProperty("password", "password"); + props.setProperty("remote.directory", "/tmp"); + PropertiesPropertySource pps = new PropertiesPropertySource("ftpprops", props); + env.getPropertySources().addLast(pps); + ctx.setEnvironment(env); + } +} diff --git a/advanced/dynamic-ftp/src/main/resources/META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml b/advanced/dynamic-ftp/src/main/resources/META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml new file mode 100644 index 00000000..a5656816 --- /dev/null +++ b/advanced/dynamic-ftp/src/main/resources/META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + diff --git a/advanced/dynamic-ftp/src/test/java/org/springframework/integration/samples/ftp/DynamicFtpChannelResolverTests.java b/advanced/dynamic-ftp/src/test/java/org/springframework/integration/samples/ftp/DynamicFtpChannelResolverTests.java new file mode 100644 index 00000000..bb77a4b3 --- /dev/null +++ b/advanced/dynamic-ftp/src/test/java/org/springframework/integration/samples/ftp/DynamicFtpChannelResolverTests.java @@ -0,0 +1,45 @@ +/* + * 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.samples.ftp; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.integration.MessageChannel; + +/** + * @author Gary Russell + * @since 2.1 + * + */ +public class DynamicFtpChannelResolverTests { + + /** + * Test method for {@link org.springframework.integration.samples.ftp.DynamicFtpChannelResolver#resolve(java.lang.String)}. + */ + @Test + public void testResolve() { + DynamicFtpChannelResolver dynamicFtpChannelResolver = new DynamicFtpChannelResolver(); + MessageChannel channel1 = dynamicFtpChannelResolver.resolve("customer1"); + assertNotNull(channel1); + MessageChannel channel2 = dynamicFtpChannelResolver.resolve("customer2"); + assertNotNull(channel2); + assertNotSame(channel1, channel2); + MessageChannel channel1a = dynamicFtpChannelResolver.resolve("customer1"); + assertSame(channel1, channel1a); + } + +} diff --git a/advanced/dynamic-ftp/src/test/java/org/springframework/integration/samples/ftp/FtpOutboundChannelAdapterSample.java b/advanced/dynamic-ftp/src/test/java/org/springframework/integration/samples/ftp/FtpOutboundChannelAdapterSample.java new file mode 100644 index 00000000..c856686b --- /dev/null +++ b/advanced/dynamic-ftp/src/test/java/org/springframework/integration/samples/ftp/FtpOutboundChannelAdapterSample.java @@ -0,0 +1,70 @@ +/* + * 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.samples.ftp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.net.UnknownHostException; + +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.support.MessageBuilder; + +/** + * @author Gary Russell + * + */ +public class FtpOutboundChannelAdapterSample { + + @Test + public void runDemo() throws Exception{ + ApplicationContext ctx = + new ClassPathXmlApplicationContext("META-INF/spring/integration/DynamicFtpOutboundChannelAdapterSample-context.xml"); + MessageChannel channel = ctx.getBean("toDynRouter", MessageChannel.class); + File file = File.createTempFile("temp", "txt"); + Message message = MessageBuilder.withPayload(file) + .setHeader("customer", "cust1") + .build(); + try { + channel.send(message); + } catch (MessageHandlingException e) { + assertTrue(e.getCause().getCause() instanceof UnknownHostException); + assertEquals("host.for.cust1", e.getCause().getCause().getMessage()); + } + // send another so we can see in the log we don't create the ac again. + try { + channel.send(message); + } catch (MessageHandlingException e) { + assertTrue(e.getCause().getCause() instanceof UnknownHostException); + assertEquals("host.for.cust1", e.getCause().getCause().getMessage()); + } + // send to a different customer; again, check the log to see a new ac is built + message = MessageBuilder.withPayload(file) + .setHeader("customer", "cust2").build(); + try { + channel.send(message); + } catch (MessageHandlingException e) { + assertTrue(e.getCause().getCause() instanceof UnknownHostException); + assertEquals("host.for.cust2", e.getCause().getCause().getMessage()); + } + } +} diff --git a/advanced/dynamic-ftp/src/test/resources/META-INF/spring/integration/DynamicFtpOutboundChannelAdapterSample-context.xml b/advanced/dynamic-ftp/src/test/resources/META-INF/spring/integration/DynamicFtpOutboundChannelAdapterSample-context.xml new file mode 100644 index 00000000..c83c9941 --- /dev/null +++ b/advanced/dynamic-ftp/src/test/resources/META-INF/spring/integration/DynamicFtpOutboundChannelAdapterSample-context.xml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/advanced/dynamic-ftp/src/test/resources/log4j.properties b/advanced/dynamic-ftp/src/test/resources/log4j.properties new file mode 100644 index 00000000..c4672eb7 --- /dev/null +++ b/advanced/dynamic-ftp/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n + +log4j.category.org.springframework=WARN +log4j.category.org.springframework.integration=DEBUG