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