From f581cfc2d4afc83ac461e0085cd299708abdc08d Mon Sep 17 00:00:00 2001 From: David Turanski Date: Tue, 9 Nov 2010 16:42:57 -0500 Subject: [PATCH 01/42] added SmartLifecycle test --- .../store/MessageStoreReaperTests.java | 55 ++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests.java b/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests.java index 8bc0ada72a..e6f9cf972d 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests.java @@ -14,33 +14,46 @@ package org.springframework.integration.store; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.List; -import org.junit.Before; 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.integration.message.GenericMessage; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Dave Syer + * @author Dave Turanski */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) public class MessageStoreReaperTests { @Autowired + @Qualifier("messageStore") private MessageGroupStore messageStore; - @Before - public void init() { - ExpiryCallback.groups.clear(); - } + @Autowired + @Qualifier("messageStore2") + private MessageGroupStore messageStore2; + + @Autowired + @Qualifier("reaper2") + private MessageGroupStoreReaper reaper2; + + @Autowired + @Qualifier("expiryCallback") + private ExpiryCallback expiryCallback; + + @Autowired + @Qualifier("expiryCallback2") + private ExpiryCallback expiryCallback2; @Test public void testExpiry() throws Exception { @@ -49,12 +62,38 @@ public class MessageStoreReaperTests { // wait for expiry... Thread.sleep(200L); assertEquals(0, messageStore.getMessageGroup("FOO").size()); - assertEquals(1, ExpiryCallback.groups.size()); + assertEquals(1, expiryCallback.groups.size()); + } + + @Test + public void testSmartLifecycle() throws Exception{ + + messageStore2.addMessageToGroup("FOO", new GenericMessage("foo")); + assertEquals(1, messageStore2.getMessageGroup("FOO").size()); + + reaper2.setExpireOnDestroy(true); + reaper2.setTimeout(0); + + + if (!reaper2.isAutoStartup()){ + reaper2.start(); + } + + assertTrue(reaper2.isRunning()); + + //reaper timeout is set to 0, but need to ensure positive elapsed time + Thread.sleep(1L); + + reaper2.stop(); + assertTrue(!reaper2.isRunning()); + + assertEquals(0, messageStore2.getMessageGroup("FOO").size()); + assertEquals(1, expiryCallback2.groups.size()); } public static class ExpiryCallback implements MessageGroupCallback { - private static final List groups = new ArrayList(); + public final List groups = new ArrayList(); public void execute(MessageGroupStore messageGroupStore, MessageGroup group) { groups.add(group); From 1faa9f08b00d7464bc5b39abed1e1ff3b400162c Mon Sep 17 00:00:00 2001 From: David Turanski Date: Tue, 9 Nov 2010 16:43:57 -0500 Subject: [PATCH 02/42] added SmartLifecycle test --- .../store/MessageStoreReaperTests-context.xml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml index 8f1299d6d0..6e525e2c74 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml @@ -7,11 +7,11 @@ - - - + + + @@ -22,5 +22,15 @@ + + + + + + + + + + From 304abda2d3f7b582af8b0c29781c5f2b2203f093 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 9 Nov 2010 17:22:38 -0500 Subject: [PATCH 03/42] removing warnings --- .../integration/file/FileReadingMessageSource.java | 3 +-- .../AbstractInboundRemoteFileSystemSychronizer.java | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java index 6ef2147ea6..8cc28bf22c 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileReadingMessageSource.java @@ -45,8 +45,7 @@ import org.springframework.util.Assert; * is used. It ensures files are picked up only once from the directory. *

* A common problem with reading files is that a file may be detected before it - * is ready. The default - * {@link AcceptOnceEntryFileListFilter} + * is ready. The default {@link AcceptOnceFileListFilter} * does not prevent this. In most cases, this can be prevented if the * file-writing process renames each file as soon as it is ready for reading. A * pattern-matching filter that accepts only files that are ready (e.g. based on diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSychronizer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSychronizer.java index 40a761fd42..e8b28e2923 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSychronizer.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/synchronization/AbstractInboundRemoteFileSystemSychronizer.java @@ -55,8 +55,7 @@ public abstract class AbstractInboundRemoteFileSystemSychronizer extends Abst protected volatile FileListFilter filter = new AcceptAllFileListFilter(); /** - * The {@link ScheduledFuture} instance we get when we - * schedule our {@link SynchronizeTask} + * The {@link ScheduledFuture} instance we get when we schedule our SynchronizeTask. */ protected ScheduledFuture scheduledFuture; @@ -88,7 +87,7 @@ public abstract class AbstractInboundRemoteFileSystemSychronizer extends Abst * {@link EntryAcknowledgmentStrategy#acknowledge(Object, Object)} will be called in line with the * {@link org.springframework.integration.core.MessageSource#receive()} call so this could conceivably * be a 'live' stateful client (a connection?) that is inappropriate to cache as it has per-request state. - * @param t + * @param file * leverages strategy implementations to enable different * behavior. It's a hook to the file entry after it's been * successfully downloaded. Conceptually, you might delete the From b2f3235d7ec5d772358cc3dca85f063911cc6094 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 9 Nov 2010 17:35:08 -0500 Subject: [PATCH 04/42] removing warnings --- .../ip/tcp/TcpOutboundGateway.java | 67 +++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/TcpOutboundGateway.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/TcpOutboundGateway.java index e5c7c3546a..0f8f978856 100644 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/TcpOutboundGateway.java +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/TcpOutboundGateway.java @@ -35,7 +35,6 @@ import org.springframework.integration.ip.tcp.connection.TcpListener; import org.springframework.integration.ip.tcp.connection.TcpSender; import org.springframework.util.Assert; - /** * TCP outbound gateway that uses a client connection factory. If the factory is configured * for single-use connections, each request is sent on a new connection; if the factory does not use @@ -45,10 +44,8 @@ import org.springframework.util.Assert; * * @author Gary Russell * @since 2.0 - * */ -public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler - implements TcpSender, TcpListener { +public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler implements TcpSender, TcpListener { protected AbstractConnectionFactory connectionFactory; @@ -59,7 +56,22 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler private long replyTimeout = 10000; private long requestTimeout = 10000; - + + + /** + * @param requestTimeout the requestTimeout to set + */ + public void setRequestTimeout(long requestTimeout) { + this.requestTimeout = requestTimeout; + } + + /** + * @param replyTimeout the replyTimeout to set + */ + public void setReplyTimeout(long replyTimeout) { + this.replyTimeout = replyTimeout; + } + @Override protected Object handleRequestMessage(Message requestMessage) { Assert.notNull(connectionFactory, this.getClass().getName() + @@ -73,7 +85,9 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler throw new MessageTimeoutException(requestMessage, "Timed out waiting for connection"); } haveSemaphore = true; - logger.debug("got semaphore"); + if (logger.isDebugEnabled()) { + logger.debug("got semaphore"); + } } TcpConnection connection = this.connectionFactory.getConnection(); AsyncReply reply = new AsyncReply(); @@ -90,16 +104,20 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler logger.debug("Respose " + replyMessage); } return replyMessage; - } catch (Exception e) { + } + catch (Exception e) { if (e instanceof MessagingException) { throw (MessagingException) e; } logger.error("Tcp Gateway exception", e); throw new MessagingException("Failed to send or receive", e); - } finally { + } + finally { if (haveSemaphore) { this.semaphore.release(); - logger.debug("released semaphore"); + if (logger.isDebugEnabled()) { + logger.debug("released semaphore"); + } } } } @@ -139,32 +157,23 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler // do nothing - no asynchronous multiplexing supported } - /** - * @param replyTimeout the replyTimeout to set - */ - public void setReplyTimeout(long timeout) { - this.replyTimeout = timeout; - } - - /** - * @param requestTimeout the requestTimeout to set - */ - public void setRequestTimeout(long requestTimeout) { - this.requestTimeout = requestTimeout; - } /** * Class used to coordinate the asynchronous reply to its request. + * * @author Gary Russell * @since 2.0 - * */ - class AsyncReply { - private CountDownLatch latch; - private Message reply; + private class AsyncReply { + + private final CountDownLatch latch; + + private volatile Message reply; + public AsyncReply() { this.latch = new CountDownLatch(1); } + /** * Sender blocks here until the reply is received, or we time out * @return The return message or null if we time out @@ -175,14 +184,16 @@ public class TcpOutboundGateway extends AbstractReplyProducingMessageHandler if (!this.latch.await(replyTimeout, TimeUnit.MILLISECONDS)) { return null; } - } catch (InterruptedException e) { + } + catch (InterruptedException e) { Thread.currentThread().interrupt(); } return this.reply; } + public void setReply(Message reply) { this.reply = reply; - latch.countDown(); + this.latch.countDown(); } } From 7b1185f2f086f66280b965ade7786e1ee580056e Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 9 Nov 2010 17:58:27 -0500 Subject: [PATCH 05/42] removing warnings --- .../GlobalChannelInterceptorTests.java | 152 +++++++++--------- 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java index fc5470dd80..b0ad8a1123 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.channel.interceptor; import java.util.ArrayList; @@ -21,7 +22,7 @@ import java.util.Map; import org.junit.Assert; import org.junit.Test; -import org.springframework.beans.DirectFieldAccessor; + import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.Ordered; @@ -29,132 +30,129 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.AbstractMessageChannel; import org.springframework.integration.channel.ChannelInterceptor; +import org.springframework.integration.test.util.TestUtils; /** * @author Oleg Zhurakousky * @author Dave Turanski * @since 2.0 */ -@SuppressWarnings("all") public class GlobalChannelInterceptorTests { - + @Test - public void validateGlobalInterceptor(){ - ApplicationContext applicationContext = - new ClassPathXmlApplicationContext("GlobalChannelInterceptorTests-context.xml", GlobalChannelInterceptorTests.class); + public void validateGlobalInterceptor() { + ApplicationContext applicationContext = new ClassPathXmlApplicationContext( + "GlobalChannelInterceptorTests-context.xml", GlobalChannelInterceptorTests.class); Map channels = applicationContext.getBeansOfType(AbstractMessageChannel.class); for (String channelName : channels.keySet()) { AbstractMessageChannel channel = channels.get(channelName); - DirectFieldAccessor cAccessor = new DirectFieldAccessor(channel); - Object iList = cAccessor.getPropertyValue("interceptors"); - DirectFieldAccessor iAccessor = new DirectFieldAccessor(iList); - List interceptorList = (List) iAccessor.getPropertyValue("interceptors"); + List interceptorList = TestUtils.getPropertyValue(channel, "interceptors.interceptors", List.class); + ChannelInterceptor[] interceptors = interceptorList.toArray(new ChannelInterceptor[] {}); if (channelName.equals("inputA")){ // 328741 - ChannelInterceptor[] inter = interceptorList.toArray(new ChannelInterceptor[]{}); - Assert.assertTrue(inter.length ==10); - Assert.assertEquals("interceptor-three", inter[0].toString()); - Assert.assertEquals("interceptor-two", inter[1].toString()); - Assert.assertEquals("interceptor-eight", inter[2].toString()); - Assert.assertEquals("interceptor-seven", inter[3].toString()); - Assert.assertEquals("interceptor-five", inter[4].toString()); - Assert.assertEquals("interceptor-six", inter[5].toString()); - Assert.assertEquals("interceptor-ten", inter[6].toString()); - Assert.assertEquals("interceptor-eleven", inter[7].toString()); - Assert.assertEquals("interceptor-four", inter[8].toString()); - Assert.assertEquals("interceptor-one", inter[9].toString()); + Assert.assertTrue(interceptors.length ==10); + Assert.assertEquals("interceptor-three", interceptors[0].toString()); + Assert.assertEquals("interceptor-two", interceptors[1].toString()); + Assert.assertEquals("interceptor-eight", interceptors[2].toString()); + Assert.assertEquals("interceptor-seven", interceptors[3].toString()); + Assert.assertEquals("interceptor-five", interceptors[4].toString()); + Assert.assertEquals("interceptor-six", interceptors[5].toString()); + Assert.assertEquals("interceptor-ten", interceptors[6].toString()); + Assert.assertEquals("interceptor-eleven", interceptors[7].toString()); + Assert.assertEquals("interceptor-four", interceptors[8].toString()); + Assert.assertEquals("interceptor-one", interceptors[9].toString()); } - else - if (channelName.equals("inputB")){ - ChannelInterceptor[] inter = interceptorList.toArray(new ChannelInterceptor[]{}); - Assert.assertTrue(inter.length == 6); - Assert.assertEquals("interceptor-three", inter[0].toString()); - Assert.assertEquals("interceptor-two", inter[1].toString()); - Assert.assertEquals("interceptor-ten", inter[2].toString()); - Assert.assertEquals("interceptor-eleven", inter[3].toString()); - Assert.assertEquals("interceptor-four", inter[4].toString()); - Assert.assertEquals("interceptor-one", inter[5].toString()); + else if (channelName.equals("inputB")) { + Assert.assertTrue(interceptors.length == 6); + Assert.assertEquals("interceptor-three", interceptors[0].toString()); + Assert.assertEquals("interceptor-two", interceptors[1].toString()); + Assert.assertEquals("interceptor-ten", interceptors[2].toString()); + Assert.assertEquals("interceptor-eleven", interceptors[3].toString()); + Assert.assertEquals("interceptor-four", interceptors[4].toString()); + Assert.assertEquals("interceptor-one", interceptors[5].toString()); } - else - if (channelName.equals("foo")){ - ChannelInterceptor[] inter = interceptorList.toArray(new ChannelInterceptor[]{}); - Assert.assertTrue(inter.length == 6); - Assert.assertEquals("interceptor-two", inter[0].toString()); - Assert.assertEquals("interceptor-five", inter[1].toString()); - Assert.assertEquals("interceptor-ten", inter[2].toString()); - Assert.assertEquals("interceptor-eleven", inter[3].toString()); - Assert.assertEquals("interceptor-four", inter[4].toString()); - Assert.assertEquals("interceptor-one", inter[5].toString()); + else if (channelName.equals("foo")) { + Assert.assertTrue(interceptors.length == 6); + Assert.assertEquals("interceptor-two", interceptors[0].toString()); + Assert.assertEquals("interceptor-five", interceptors[1].toString()); + Assert.assertEquals("interceptor-ten", interceptors[2].toString()); + Assert.assertEquals("interceptor-eleven", interceptors[3].toString()); + Assert.assertEquals("interceptor-four", interceptors[4].toString()); + Assert.assertEquals("interceptor-one", interceptors[5].toString()); } - else - if (channelName.equals("bar")){ - ChannelInterceptor[] inter = interceptorList.toArray(new ChannelInterceptor[]{}); - Assert.assertTrue(inter.length == 4); - Assert.assertEquals("interceptor-eight", inter[0].toString()); - Assert.assertEquals("interceptor-seven", inter[1].toString()); - Assert.assertEquals("interceptor-ten", inter[2].toString()); - Assert.assertEquals("interceptor-eleven", inter[3].toString()); + else if (channelName.equals("bar")) { + Assert.assertTrue(interceptors.length == 4); + Assert.assertEquals("interceptor-eight", interceptors[0].toString()); + Assert.assertEquals("interceptor-seven", interceptors[1].toString()); + Assert.assertEquals("interceptor-ten", interceptors[2].toString()); + Assert.assertEquals("interceptor-eleven", interceptors[3].toString()); } - else - if (channelName.equals("baz")){ - ChannelInterceptor[] inter = interceptorList.toArray(new ChannelInterceptor[]{}); - Assert.assertTrue(inter.length == 2); - Assert.assertEquals("interceptor-ten", inter[0].toString()); - Assert.assertEquals("interceptor-eleven", inter[1].toString()); + else if (channelName.equals("baz")) { + Assert.assertTrue(interceptors.length == 2); + Assert.assertEquals("interceptor-ten", interceptors[0].toString()); + Assert.assertEquals("interceptor-eleven", interceptors[1].toString()); } } } - + @Test - public void testWildCardPatternMatch(){ - ApplicationContext applicationContext = - new ClassPathXmlApplicationContext("GlobalChannelInterceptorTests-context.xml", GlobalChannelInterceptorTests.class); - AbstractMessageChannel channel = applicationContext.getBean("inpuC",AbstractMessageChannel.class); - DirectFieldAccessor cAccessor = new DirectFieldAccessor(channel); - Object iList = cAccessor.getPropertyValue("interceptors"); - DirectFieldAccessor iAccessor = new DirectFieldAccessor(iList); - List interceptorList = (List) iAccessor.getPropertyValue("interceptors"); - List interceptorNames = new ArrayList(); - for (GlobalChannelInterceptorWrapper interceptor : interceptorList) { - interceptorNames.add(interceptor.toString()); - } - - Assert.assertTrue(interceptorNames.contains("interceptor-ten")); - Assert.assertTrue(interceptorNames.contains("interceptor-eleven")); + public void testWildCardPatternMatch() { + ApplicationContext applicationContext = new ClassPathXmlApplicationContext( + "GlobalChannelInterceptorTests-context.xml", GlobalChannelInterceptorTests.class); + AbstractMessageChannel channel = applicationContext.getBean("inpuC", AbstractMessageChannel.class); + List interceptorList = TestUtils.getPropertyValue(channel, "interceptors.interceptors", List.class); + List interceptorNames = new ArrayList(); + for (Object interceptor : interceptorList) { + interceptorNames.add(interceptor.toString()); + } + Assert.assertTrue(interceptorNames.contains("interceptor-ten")); + Assert.assertTrue(interceptorNames.contains("interceptor-eleven")); } - + public static class SampleInterceptor implements ChannelInterceptor { + private String testIdentifier; + public String getTestIdentifier() { return testIdentifier; } + public void setTestIdentifier(String testIdentifier) { this.testIdentifier = testIdentifier; } + public Message postReceive(Message message, MessageChannel channel) { return null; } - public void postSend(Message message, MessageChannel channel, - boolean sent) { + + public void postSend(Message message, MessageChannel channel, boolean sent) { } + public boolean preReceive(MessageChannel channel) { return false; } + public Message preSend(Message message, MessageChannel channel) { return null; } - public String toString(){ + + public String toString() { return "interceptor-" + testIdentifier; } - } + + public static class SampleOrderedInterceptor extends SampleInterceptor implements Ordered { + private int order; + public int getOrder() { return order; } + public void setOrder(int order) { this.order = order; } } + } From 026b9780b174dc71121cb57831703393eefee518 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 9 Nov 2010 18:37:37 -0500 Subject: [PATCH 06/42] formatting --- ...alChannelInterceptorBeanPostProcessor.java | 128 ++++++++---------- 1 file changed, 60 insertions(+), 68 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java index 8686d796f8..d124715a53 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.channel.interceptor; import java.util.ArrayList; @@ -23,6 +24,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.BeansException; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.InitializingBean; @@ -36,109 +38,99 @@ import org.springframework.util.StringUtils; /** * Will apply global interceptors to channels (<channel-interceptor>). * - * * @author Oleg Zhurakousky + * @author Mark Fisher * @since 2.0 */ -final class GlobalChannelInterceptorBeanPostProcessor implements BeanPostProcessor, InitializingBean{ - private final static Log logger = LogFactory.getLog(GlobalChannelInterceptorBeanPostProcessor.class); +final class GlobalChannelInterceptorBeanPostProcessor implements BeanPostProcessor, InitializingBean { + + private static final Log logger = LogFactory.getLog(GlobalChannelInterceptorBeanPostProcessor.class); + + private final OrderComparator comparator = new OrderComparator(); - private List channelInterceptors; - + + private volatile List channelInterceptors; + private final Set positiveOrderInterceptors = new LinkedHashSet(); + private final Set negativeOrderInterceptors = new LinkedHashSet(); - GlobalChannelInterceptorBeanPostProcessor(List channelInterceptors){ + + GlobalChannelInterceptorBeanPostProcessor(List channelInterceptors) { this.channelInterceptors = channelInterceptors; } - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String) - */ - public Object postProcessAfterInitialization(Object bean, String beanName) - throws BeansException { - return bean; + + + public void afterPropertiesSet() throws Exception { + for (GlobalChannelInterceptorWrapper channelInterceptor : this.channelInterceptors) { + if (channelInterceptor.getOrder() >= 0) { + this.positiveOrderInterceptors.add(channelInterceptor); + } + else { + this.negativeOrderInterceptors.add(channelInterceptor); + } + } } - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String) - */ - public Object postProcessBeforeInitialization(Object bean, String beanName) - throws BeansException { - - if (bean instanceof MessageChannel){ - - logger.debug("Applying global interceptors on channel '" + beanName + "'"); - this.addInterceptorsIfExist((MessageChannel) bean, beanName); + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof MessageChannel) { + if (logger.isDebugEnabled()) { + logger.debug("Applying global interceptors on channel '" + beanName + "'"); + } + this.addMatchingInterceptors((MessageChannel) bean, beanName); } - return bean; } - /* - * - */ - @SuppressWarnings("unchecked") - private List getExistingInterceptors(MessageChannel channel){ - DirectFieldAccessor channelAccessor = new DirectFieldAccessor(channel); - try { - Object iWrapper = channelAccessor.getPropertyValue("interceptors"); - if (iWrapper != null){ - DirectFieldAccessor iWrapperAccessor = new DirectFieldAccessor(iWrapper); - List interceptors = (List) iWrapperAccessor.getPropertyValue("interceptors"); - return interceptors; - } - } - catch (Exception e) { - logger.warn("Attempted to apply Global Channel iterceptors on the Channel that does not support interceptors"); - return null; - } - return null; + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; } - /* - * + + /** + * Adds any interceptor whose pattern matches against the channel's name. */ - private void addInterceptorsIfExist(MessageChannel channel, String beanName){ + private void addMatchingInterceptors(MessageChannel channel, String beanName) { List interceptors = this.getExistingInterceptors(channel); - if (interceptors != null){ + if (interceptors != null) { List tempInterceptors = new ArrayList(); - for (GlobalChannelInterceptorWrapper globalChannelInterceptorWrapper : positiveOrderInterceptors) { + for (GlobalChannelInterceptorWrapper globalChannelInterceptorWrapper : this.positiveOrderInterceptors) { String[] patterns = globalChannelInterceptorWrapper.getPatterns(); patterns = StringUtils.trimArrayElements(patterns); - if (PatternMatchUtils.simpleMatch(patterns, beanName)){ + if (PatternMatchUtils.simpleMatch(patterns, beanName)) { tempInterceptors.add(globalChannelInterceptorWrapper); } } - Collections.sort(tempInterceptors, comparator); + Collections.sort(tempInterceptors, this.comparator); interceptors.addAll(tempInterceptors); - tempInterceptors = new ArrayList(); - for (GlobalChannelInterceptorWrapper globalChannelInterceptorWrapper : negativeOrderInterceptors) { + for (GlobalChannelInterceptorWrapper globalChannelInterceptorWrapper : this.negativeOrderInterceptors) { String[] patterns = globalChannelInterceptorWrapper.getPatterns(); patterns = StringUtils.trimArrayElements(patterns); - if (PatternMatchUtils.simpleMatch(patterns, beanName)){ + if (PatternMatchUtils.simpleMatch(patterns, beanName)) { tempInterceptors.add(globalChannelInterceptorWrapper); } } Collections.sort(tempInterceptors, comparator); interceptors.addAll(0, tempInterceptors); } - else { - logger.warn("Attempted to apply Global Channel iterceptors on the Channel that does not support interceptors"); + else if (logger.isDebugEnabled()) { + logger.debug("Global Channel interceptors will not be applied to Channel: " + beanName); } } - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() - */ - public void afterPropertiesSet() throws Exception { - for (GlobalChannelInterceptorWrapper channelInterceptor : channelInterceptors) { - if (channelInterceptor.getOrder() >= 0){ - positiveOrderInterceptors.add(channelInterceptor); - } - else { - negativeOrderInterceptors.add(channelInterceptor); + + @SuppressWarnings("unchecked") + private List getExistingInterceptors(MessageChannel channel) { + DirectFieldAccessor channelAccessor = new DirectFieldAccessor(channel); + try { + Object interceptorListWrapper = channelAccessor.getPropertyValue("interceptors"); + if (interceptorListWrapper != null) { + return (List) new DirectFieldAccessor(interceptorListWrapper).getPropertyValue("interceptors"); } } + catch (Exception e) { + // interceptors not supported by this channel, will return null + } + return null; } + } From df0f40867961fbd11563c3f2cffb1e23e14dc9dc Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 9 Nov 2010 19:19:31 -0500 Subject: [PATCH 07/42] formatting --- .../GlobalChannelInterceptorWrapper.java | 78 ++++++++++--------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java index 3de0be02be..22bce00902 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java @@ -13,73 +13,77 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.channel.interceptor; import org.springframework.core.Ordered; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.ChannelInterceptor; +import org.springframework.util.Assert; /** * @author Oleg Zhurakousky * @since 2.0 */ -public class GlobalChannelInterceptorWrapper implements ChannelInterceptor, Ordered{ - private ChannelInterceptor channelInterceptor; - private String[] patterns; - private int order; - - public GlobalChannelInterceptorWrapper(ChannelInterceptor channelInterceptor){ +public class GlobalChannelInterceptorWrapper implements ChannelInterceptor, Ordered { + + private final ChannelInterceptor channelInterceptor; + + private volatile String[] patterns; + + private volatile int order; + + + public GlobalChannelInterceptorWrapper(ChannelInterceptor channelInterceptor) { + Assert.notNull(channelInterceptor, "channelInterceptor must not be null"); this.channelInterceptor = channelInterceptor; - // will set initial order for this interceptor wrapper to be the same as the - // underlying interceptor. Could be overridden with setOrder() method - if (channelInterceptor instanceof Ordered){ - order = ((Ordered)channelInterceptor).getOrder(); + // will set initial order for this interceptor wrapper to be the same as + // the underlying interceptor. Could be overridden with setOrder() method + if (channelInterceptor instanceof Ordered) { + order = ((Ordered) channelInterceptor).getOrder(); } } - public int getOrder() { - return order; + + public ChannelInterceptor getChannelInterceptor() { + return this.channelInterceptor; } - /** - * - * @param order - */ public void setOrder(int order) { this.order = order; } - public ChannelInterceptor getChannelInterceptor() { - return channelInterceptor; - } - - public String[] getPatterns() { - return patterns; + public int getOrder() { + return this.order; } public void setPatterns(String[] patterns) { this.patterns = patterns; } - public Message postReceive(Message message, MessageChannel channel) { - return channelInterceptor.postReceive(message, channel); - } - - public void postSend(Message message, MessageChannel channel, - boolean sent) { - channelInterceptor.postSend(message, channel, sent); - } - - public boolean preReceive(MessageChannel channel) { - return channelInterceptor.preReceive(channel); + public String[] getPatterns() { + return this.patterns; } public Message preSend(Message message, MessageChannel channel) { - return channelInterceptor.preSend(message, channel); + return this.channelInterceptor.preSend(message, channel); + } + + public void postSend(Message message, MessageChannel channel, boolean sent) { + this.channelInterceptor.postSend(message, channel, sent); } - - public String toString(){ - return channelInterceptor.toString(); + + public boolean preReceive(MessageChannel channel) { + return this.channelInterceptor.preReceive(channel); } + + public Message postReceive(Message message, MessageChannel channel) { + return this.channelInterceptor.postReceive(message, channel); + } + + public String toString() { + return this.channelInterceptor.toString(); + } + } From 8e1203be2cdd3417522e8e248a64f0265c3e9d4d Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 9 Nov 2010 19:39:05 -0500 Subject: [PATCH 08/42] refactored some eager parsing and formatted --- .../GlobalChannelInterceptorWrapper.java | 4 +- .../xml/GlobalChannelInterceptorParser.java | 102 ++++++++---------- 2 files changed, 47 insertions(+), 59 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java index 22bce00902..72bd09926e 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java @@ -32,7 +32,7 @@ public class GlobalChannelInterceptorWrapper implements ChannelInterceptor, Orde private volatile String[] patterns; - private volatile int order; + private volatile int order = 0; public GlobalChannelInterceptorWrapper(ChannelInterceptor channelInterceptor) { @@ -41,7 +41,7 @@ public class GlobalChannelInterceptorWrapper implements ChannelInterceptor, Orde // will set initial order for this interceptor wrapper to be the same as // the underlying interceptor. Could be overridden with setOrder() method if (channelInterceptor instanceof Ordered) { - order = ((Ordered) channelInterceptor).getOrder(); + this.order = ((Ordered) channelInterceptor).getOrder(); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java index 4ef9e756e6..7cab301a78 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.config.xml; +import org.w3c.dom.Element; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; @@ -25,83 +28,68 @@ import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; -import org.w3c.dom.Element; /** - * Will parse 'channel-interceptor-chain' element + * Parser for 'channel-interceptor' elements. * * @author Oleg Zhurakousky + * @author Mark Fisher * @since 2.0 */ public class GlobalChannelInterceptorParser extends AbstractBeanDefinitionParser { - private static final String CONFIG_PACKAGE = IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.interceptor."; + + private static final String BASE_PACKAGE = IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.interceptor."; + + private static final String CHANNEL_NAME_PATTERN_ATTRIBUTE = "pattern"; + + private static final String REF_ATTRIBUTE = "ref"; + + private static final String GLOBAL_POST_PROCESSOR_CLASSNAME = "GlobalChannelInterceptorBeanPostProcessor"; + + private final ManagedList globalInterceptors = new ManagedList(); - private final static String CHANNELL_NAME_PATTERN_ATTR = "pattern"; - private final static String REF_ATTR = "ref"; - private final static String ORDER_ATTR = "order"; - private final String GLOBAL_POST_PROCESSOR_CLASS = "GlobalChannelInterceptorBeanPostProcessor"; - - private boolean postProcessorCreated; - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext) - */ + + private volatile boolean postProcessorCreated; + + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { - this.createAndRegisterGlobalPostProcessorIfNeeded(parserContext); - - - - int order = this.getOrderAttribute(element); - String channelPattern = element.getAttribute(CHANNELL_NAME_PATTERN_ATTR); - + this.createAndRegisterGlobalPostProcessorIfNecessary(parserContext); + String channelPattern = element.getAttribute(CHANNEL_NAME_PATTERN_ATTRIBUTE); BeanDefinitionBuilder globalChannelInterceptorBuilder = - BeanDefinitionBuilder.genericBeanDefinition(CONFIG_PACKAGE + "GlobalChannelInterceptorWrapper"); + BeanDefinitionBuilder.genericBeanDefinition(BASE_PACKAGE + "GlobalChannelInterceptorWrapper"); BeanComponentDefinition interceptorBeanDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext); - if (interceptorBeanDefinition != null){ + if (interceptorBeanDefinition != null) { globalChannelInterceptorBuilder.addConstructorArgValue(interceptorBeanDefinition); - } else { - String beanName = element.getAttribute(REF_ATTR); + } + else { + String beanName = element.getAttribute(REF_ATTRIBUTE); globalChannelInterceptorBuilder.addConstructorArgValue(new RuntimeBeanReference(beanName)); } - - globalChannelInterceptorBuilder.addPropertyValue("order", order); - String[] patterns = null; - if (StringUtils.hasText(channelPattern)){ - patterns = StringUtils.commaDelimitedListToStringArray(channelPattern); - } else { - patterns = new String[]{"*"}; + IntegrationNamespaceUtils.setValueIfAttributeDefined(globalChannelInterceptorBuilder, element, "order"); + if (StringUtils.hasText(channelPattern)) { + globalChannelInterceptorBuilder.addPropertyValue("patterns", channelPattern); } - globalChannelInterceptorBuilder.addPropertyValue("patterns", patterns); - - String beanName = - BeanDefinitionReaderUtils.generateBeanName(globalChannelInterceptorBuilder.getBeanDefinition(), parserContext.getRegistry()); + else { + globalChannelInterceptorBuilder.addPropertyValue("patterns", new String[] {"*"}); + } + String beanName = BeanDefinitionReaderUtils.generateBeanName( + globalChannelInterceptorBuilder.getBeanDefinition(), parserContext.getRegistry()); parserContext.registerBeanComponent(new BeanComponentDefinition(globalChannelInterceptorBuilder.getBeanDefinition(), beanName)); - globalInterceptors.add(new RuntimeBeanReference(beanName)); + this.globalInterceptors.add(new RuntimeBeanReference(beanName)); return null; } - /* - * - */ - private int getOrderAttribute(Element element){ - String sOrder = element.getAttribute(ORDER_ATTR); - if (StringUtils.hasText(sOrder)){ - return Integer.parseInt(sOrder); - } - return 0; - } - /* - * - */ - private void createAndRegisterGlobalPostProcessorIfNeeded(ParserContext parserContext){ - if (!postProcessorCreated){ - BeanDefinitionBuilder postProcessorBuilder = - BeanDefinitionBuilder.genericBeanDefinition(CONFIG_PACKAGE + GLOBAL_POST_PROCESSOR_CLASS); + + private void createAndRegisterGlobalPostProcessorIfNecessary(ParserContext parserContext) { + if (!this.postProcessorCreated) { + BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition( + BASE_PACKAGE + GLOBAL_POST_PROCESSOR_CLASSNAME); + postProcessorBuilder.addConstructorArgValue(this.globalInterceptors); BeanDefinition beanDef = postProcessorBuilder.getBeanDefinition(); - postProcessorBuilder.addConstructorArgValue(globalInterceptors); - String beanName = - BeanDefinitionReaderUtils.generateBeanName(beanDef, parserContext.getRegistry()); + String beanName = BeanDefinitionReaderUtils.generateBeanName( + beanDef, parserContext.getRegistry()); parserContext.registerBeanComponent(new BeanComponentDefinition(beanDef, beanName)); - postProcessorCreated = true; + this.postProcessorCreated = true; } } + } From 7f9296d0c7f4db58447106e85baab82b14effdde Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Tue, 9 Nov 2010 19:50:16 -0500 Subject: [PATCH 09/42] more ppolishing on global interceptors --- .../interceptor/GlobalChannelInterceptorWrapper.java | 2 +- .../config/xml/GlobalChannelInterceptorParser.java | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java index 72bd09926e..b88f715d83 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java @@ -30,7 +30,7 @@ public class GlobalChannelInterceptorWrapper implements ChannelInterceptor, Orde private final ChannelInterceptor channelInterceptor; - private volatile String[] patterns; + private volatile String[] patterns = new String[]{"*"}; // default private volatile int order = 0; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java index 7cab301a78..14fe3a4fa6 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java @@ -54,7 +54,6 @@ public class GlobalChannelInterceptorParser extends AbstractBeanDefinitionParser protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { this.createAndRegisterGlobalPostProcessorIfNecessary(parserContext); - String channelPattern = element.getAttribute(CHANNEL_NAME_PATTERN_ATTRIBUTE); BeanDefinitionBuilder globalChannelInterceptorBuilder = BeanDefinitionBuilder.genericBeanDefinition(BASE_PACKAGE + "GlobalChannelInterceptorWrapper"); BeanComponentDefinition interceptorBeanDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext); @@ -66,12 +65,8 @@ public class GlobalChannelInterceptorParser extends AbstractBeanDefinitionParser globalChannelInterceptorBuilder.addConstructorArgValue(new RuntimeBeanReference(beanName)); } IntegrationNamespaceUtils.setValueIfAttributeDefined(globalChannelInterceptorBuilder, element, "order"); - if (StringUtils.hasText(channelPattern)) { - globalChannelInterceptorBuilder.addPropertyValue("patterns", channelPattern); - } - else { - globalChannelInterceptorBuilder.addPropertyValue("patterns", new String[] {"*"}); - } + IntegrationNamespaceUtils.setValueIfAttributeDefined(globalChannelInterceptorBuilder, element, CHANNEL_NAME_PATTERN_ATTRIBUTE, "patterns"); + String beanName = BeanDefinitionReaderUtils.generateBeanName( globalChannelInterceptorBuilder.getBeanDefinition(), parserContext.getRegistry()); parserContext.registerBeanComponent(new BeanComponentDefinition(globalChannelInterceptorBuilder.getBeanDefinition(), beanName)); From 35aeaff97bb2712dba78bc7c33c7271e6102460b Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 9 Nov 2010 22:31:29 -0500 Subject: [PATCH 10/42] INT-1597 Fix Sporadic Test Failure in IP --- .../integration/ip/tcp/TcpSendingMessageHandlerTests.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java index 7d8bf30b16..0856120dc6 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java @@ -27,7 +27,9 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.CountDownLatch; @@ -679,6 +681,7 @@ public class TcpSendingMessageHandlerTests { final CountDownLatch latch = new CountDownLatch(1); final Semaphore semaphore = new Semaphore(0); final AtomicBoolean done = new AtomicBoolean(); + final List serverSockets = new ArrayList(); Executors.newSingleThreadExecutor().execute(new Runnable() { public void run() { try { @@ -687,6 +690,7 @@ public class TcpSendingMessageHandlerTests { int i = 0; while (true) { Socket socket = server.accept(); + serverSockets.add(socket); semaphore.release(); byte[] b = new byte[9]; readFully(socket.getInputStream(), b); From 941b1127d142ce429e649fdc7684231138414d11 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 9 Nov 2010 12:29:35 +0000 Subject: [PATCH 11/42] INT-1561: add counters to message stores --- .../store/AbstractMessageGroupStore.java | 28 ++++++++++ .../integration/store/MessageGroupStore.java | 45 +++++++++++++--- .../integration/store/MessageStore.java | 15 +++++- .../integration/store/SimpleMessageStore.java | 20 +++++--- .../integration/store/MessageStoreTests.java | 40 +++++++++++---- .../integration/jdbc/JdbcMessageStore.java | 36 ++++++++++++- .../jdbc/JdbcMessageStoreTests.java | 40 ++++++++++++++- .../jmx/config/MessageStoreTests-context.xml | 21 ++++++++ .../jmx/config/MessageStoreTests.java | 51 +++++++++++++++++++ 9 files changed, 268 insertions(+), 28 deletions(-) create mode 100644 spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MessageStoreTests-context.xml create mode 100644 spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MessageStoreTests.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java b/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java index c307cb8ff8..5cf0bac475 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java @@ -19,6 +19,7 @@ import java.util.LinkedHashSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.jmx.export.annotation.ManagedAttribute; /** * @author Dave Syer @@ -69,6 +70,33 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It public abstract Iterator iterator(); + @ManagedAttribute + public int getMessageCountForAllMessageGroups() { + int count = 0; + for (MessageGroup group : this) { + count += group.size(); + } + return count; + } + + @ManagedAttribute + public int getMarkedMessageCountForAllMessageGroups() { + int count = 0; + for (MessageGroup group : this) { + count += group.getMarked().size(); + } + return count; + } + + @ManagedAttribute + public int getMessageGroupCount() { + int count = 0; + for (@SuppressWarnings("unused") MessageGroup group : this) { + count ++; + } + return count; + } + private void expire(MessageGroup group) { RuntimeException exception = null; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStore.java b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStore.java index 2c395a378b..4b4d1a697f 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStore.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStore.java @@ -13,6 +13,7 @@ package org.springframework.integration.store; import org.springframework.integration.Message; +import org.springframework.jmx.export.annotation.ManagedAttribute; /** * Interface for storage operations on groups of messages linked by a group id. @@ -24,6 +25,36 @@ import org.springframework.integration.Message; */ public interface MessageGroupStore { + /** + * Optional attribute giving the number of messages in the store over all groups. Implementations may decline to + * respond by throwing an exception. + * + * @return the number of messages + * @throws UnsupportedOperationException if not implemented + */ + @ManagedAttribute + int getMessageCountForAllMessageGroups(); + + /** + * Optional attribute giving the number of marked messages in the store for all groups. Implementations may decline + * to respond by throwing an exception. + * + * @return the number of marked messages in each group + * @throws UnsupportedOperationException if not implemented + */ + @ManagedAttribute + int getMarkedMessageCountForAllMessageGroups(); + + /** + * Optional attribute giving the number of message groups. Implementations may decline + * to respond by throwing an exception. + * + * @return the number message groups + * @throws UnsupportedOperationException if not implemented + */ + @ManagedAttribute + int getMessageGroupCount(); + /** * Return all Messages currently in the MessageStore that were stored using * {@link #addMessageToGroup(Object, Message)} with this group id. @@ -43,14 +74,14 @@ public interface MessageGroupStore { /** * Persist the mark on all the messages from the group. The group is modified in the process as all its unmarked * messages become marked. - * + * * @param group a MessageGroup with no unmarked messages */ MessageGroup markMessageGroup(MessageGroup group); /** - * Persist a deletion on a single message from the group. The group is modified to reflect that 'messageToRemove' is no - * longer present in the group. + * Persist a deletion on a single message from the group. The group is modified to reflect that 'messageToRemove' is + * no longer present in the group. * @param key the groupId for the group containing the message * @param messageToRemove the message to be removed */ @@ -66,14 +97,14 @@ public interface MessageGroupStore { /** * Remove the message group with this id. - * + * * @param groupId the id of the group to remove */ void removeMessageGroup(Object groupId); /** * Register a callback for when a message group is expired through {@link #expireMessageGroups(long)}. - * + * * @param callback a callback to execute when a message group is cleaned up */ void registerMessageGroupExpiryCallback(MessageGroupCallback callback); @@ -83,10 +114,10 @@ public interface MessageGroupStore { * each of the registered callbacks on them in turn. For example: call with a timeout of 100 to expire all groups * that were created more than 100 milliseconds ago, and are not yet complete. Use a timeout of 0 (or negative to be * on the safe side) to expire all message groups. - * + * * @param timeout the timeout threshold to use * @return the number of message groups expired - * + * * @see #registerMessageGroupExpiryCallback(MessageGroupCallback) */ int expireMessageGroups(long timeout); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageStore.java b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageStore.java index 99b6a44e0f..f5caa96edb 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageStore.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageStore.java @@ -19,6 +19,7 @@ package org.springframework.integration.store; import java.util.UUID; import org.springframework.integration.Message; +import org.springframework.jmx.export.annotation.ManagedAttribute; /** * Strategy interface for storing and retrieving messages. @@ -39,8 +40,8 @@ public interface MessageStore { /** * Put the provided Message into the MessageStore. The store may need to mutate the message internally, and if it * does then the return value can be different than the input. The id of the return value will be used as an index - * so that the {@link #getMessage(UUID)} and {@link #removeMessage(UUID)} behave properly. Since messages are immutable, putting - * the same message more than once is a no-op. + * so that the {@link #getMessage(UUID)} and {@link #removeMessage(UUID)} behave properly. Since messages are + * immutable, putting the same message more than once is a no-op. * * @return the message that was stored */ @@ -52,4 +53,14 @@ public interface MessageStore { */ Message removeMessage(UUID id); + /** + * Optional attribute giving the number of messages in the store. Implementations may decline to respond by throwing + * an exception. + * + * @return the number of messages + * @throws UnsupportedOperationException if not implemented + */ + @ManagedAttribute + int getMessageCount(); + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/SimpleMessageStore.java b/spring-integration-core/src/main/java/org/springframework/integration/store/SimpleMessageStore.java index dec3c394a8..019b5f53d2 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/SimpleMessageStore.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/SimpleMessageStore.java @@ -13,17 +13,19 @@ package org.springframework.integration.store; -import org.springframework.integration.Message; -import org.springframework.integration.MessagingException; -import org.springframework.integration.util.UpperBound; -import org.springframework.util.Assert; - import java.util.HashSet; import java.util.Iterator; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.springframework.integration.Message; +import org.springframework.integration.MessagingException; +import org.springframework.integration.util.UpperBound; +import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedResource; +import org.springframework.util.Assert; + /** * Map-based implementation of {@link MessageStore} and {@link MessageGroupStore}. Enforces a maximum capacity for the * store. @@ -34,6 +36,7 @@ import java.util.concurrent.ConcurrentMap; * * @since 2.0 */ +@ManagedResource public class SimpleMessageStore extends AbstractMessageGroupStore implements MessageStore, MessageGroupStore { private final ConcurrentMap> idToMessage; @@ -71,6 +74,11 @@ public class SimpleMessageStore extends AbstractMessageGroupStore implements Mes public SimpleMessageStore() { this(0); } + + @ManagedAttribute + public int getMessageCount() { + return idToMessage.size(); + } public Message addMessage(Message message) { if (!individualUpperBound.tryAcquire(0)) { @@ -93,7 +101,7 @@ public class SimpleMessageStore extends AbstractMessageGroupStore implements Mes else return null; } - + public MessageGroup getMessageGroup(Object groupId) { Assert.notNull(groupId, "'groupId' must not be null"); SimpleMessageGroup group = groupIdToMessageGroup.get(groupId); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreTests.java b/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreTests.java index 76386f6537..1577a81fc0 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/store/MessageStoreTests.java @@ -16,29 +16,32 @@ package org.springframework.integration.store; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + import org.junit.Test; import org.springframework.integration.Message; import org.springframework.integration.message.GenericMessage; import org.springframework.test.util.ReflectionTestUtils; -import java.util.*; - -import static org.junit.Assert.assertEquals; - /** * @author Dave Syer */ public class MessageStoreTests { - @Test public void shouldRegisterCallbacks() throws Exception { TestMessageStore store = new TestMessageStore(); - store.setExpiryCallbacks(Arrays.asList(new MessageGroupCallback() { + store.setExpiryCallbacks(Arrays. asList(new MessageGroupCallback() { public void execute(MessageGroupStore messageGroupStore, MessageGroup group) { } })); - assertEquals(1, ((Collection)ReflectionTestUtils.getField(store, "expiryCallbacks")).size()); + assertEquals(1, ((Collection) ReflectionTestUtils.getField(store, "expiryCallbacks")).size()); } @Test @@ -58,11 +61,30 @@ public class MessageStoreTests { assertEquals(0, store.getMessageGroup("bar").size()); } - + + @Test + public void testGroupCount() throws Exception { + TestMessageStore store = new TestMessageStore(); + assertEquals(1, store.getMessageGroupCount()); + } + + @Test + public void testGroupSizes() throws Exception { + TestMessageStore store = new TestMessageStore(); + assertEquals(1, store.getMessageCountForAllMessageGroups()); + } + + @Test + public void testMarkedGroupSizes() throws Exception { + TestMessageStore store = new TestMessageStore(); + assertEquals(0, store.getMarkedMessageCountForAllMessageGroups()); + } + private static class TestMessageStore extends AbstractMessageGroupStore { @SuppressWarnings("unchecked") MessageGroup testMessages = new SimpleMessageGroup(Arrays.asList(new GenericMessage("foo")), "bar"); + private boolean removed = false; @Override @@ -95,7 +117,7 @@ public class MessageStoreTests { removed = true; } } - + } } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcMessageStore.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcMessageStore.java index 71b4577817..48becc9884 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcMessageStore.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcMessageStore.java @@ -29,7 +29,6 @@ import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.core.serializer.Deserializer; import org.springframework.core.serializer.Serializer; import org.springframework.core.serializer.support.DeserializingConverter; @@ -49,6 +48,8 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.SingleColumnRowMapper; import org.springframework.jdbc.support.lob.DefaultLobHandler; import org.springframework.jdbc.support.lob.LobHandler; +import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -60,6 +61,7 @@ import org.springframework.util.StringUtils; * @author Dave Syer * @since 2.0 */ +@ManagedResource public class JdbcMessageStore extends AbstractMessageGroupStore implements MessageStore { private static final Log logger = LogFactory.getLog(JdbcMessageStore.class); @@ -71,6 +73,8 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa private static final String GET_MESSAGE = "SELECT MESSAGE_ID, CREATED_DATE, MESSAGE_BYTES from %PREFIX%MESSAGE where MESSAGE_ID=? and REGION=?"; + private static final String GET_MESSAGE_COUNT = "SELECT COUNT(MESSAGE_ID) from %PREFIX%MESSAGE where REGION=?"; + private static final String DELETE_MESSAGE = "DELETE from %PREFIX%MESSAGE where MESSAGE_ID=? and REGION=?"; private static final String CREATE_MESSAGE = "INSERT into %PREFIX%MESSAGE(MESSAGE_ID, REGION, CREATED_DATE, MESSAGE_BYTES)" @@ -78,6 +82,12 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa private static final String LIST_MESSAGES_BY_GROUP_KEY = "SELECT MESSAGE_ID, CREATED_DATE, GROUP_KEY, MESSAGE_BYTES, MARKED from %PREFIX%MESSAGE_GROUP where GROUP_KEY=? and REGION=? order by CREATED_DATE"; + private static final String COUNT_ALL_GROUPS = "SELECT COUNT(GROUP_KEY) from %PREFIX%MESSAGE_GROUP where REGION=?"; + + private static final String COUNT_ALL_MARKED_MESSAGES_IN_GROUPS = "SELECT COUNT(MESSAGE_ID) from %PREFIX%MESSAGE_GROUP where MARKED=1 AND REGION=?"; + + private static final String COUNT_ALL_MESSAGES_IN_GROUPS = "SELECT COUNT(MESSAGE_ID) from %PREFIX%MESSAGE_GROUP where REGION=?"; + private static final String MARK_MESSAGES_IN_GROUP = "UPDATE %PREFIX%MESSAGE_GROUP set UPDATED_DATE=?, MARKED=1 where MARKED=0 and GROUP_KEY=? and REGION=?"; private static final String MARK_MESSAGE_IN_GROUP = "UPDATE %PREFIX%MESSAGE_GROUP set UPDATED_DATE=?, MARKED=1 where MESSAGE_ID=? and MARKED=0 and GROUP_KEY=? and REGION=?"; @@ -238,6 +248,11 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa return null; } + @ManagedAttribute + public int getMessageCount() { + return jdbcTemplate.queryForInt(getQuery(GET_MESSAGE_COUNT), region); + } + public Message getMessage(UUID id) { List> list = jdbcTemplate.query(getQuery(GET_MESSAGE), new Object[] { getKey(id), region }, mapper); if (list.isEmpty()) { @@ -297,6 +312,21 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa } + @ManagedAttribute + public int getMessageGroupCount() { + return jdbcTemplate.queryForInt(getQuery(COUNT_ALL_GROUPS), region); + } + + @ManagedAttribute + public int getMessageCountForAllMessageGroups() { + return jdbcTemplate.queryForInt(getQuery(COUNT_ALL_MESSAGES_IN_GROUPS), region); + } + + @ManagedAttribute + public int getMarkedMessageCountForAllMessageGroups() { + return jdbcTemplate.queryForInt(getQuery(COUNT_ALL_MARKED_MESSAGES_IN_GROUPS), region); + } + public MessageGroup getMessageGroup(Object groupId) { String key = getKey(groupId); final List> marked = new ArrayList>(); @@ -305,12 +335,14 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa jdbcTemplate.query(getQuery(LIST_MESSAGES_BY_GROUP_KEY), new Object[] { key, region }, new RowCallbackHandler() { int count = 0; + public void processRow(ResultSet rs) throws SQLException { int markedFlag = rs.getInt("MARKED"); Message message = mapper.mapRow(rs, count++); if (markedFlag > 0) { marked.add(message); - } else { + } + else { unmarked.add(message); } date.set(rs.getTimestamp("CREATED_DATE")); diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests.java index 7bac021445..e31e52887a 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests.java @@ -37,7 +37,6 @@ import javax.sql.DataSource; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.serializer.Deserializer; import org.springframework.core.serializer.Serializer; @@ -85,6 +84,14 @@ public class JdbcMessageStoreTests { assertNotNull(result.getHeaders().get(JdbcMessageStore.CREATED_DATE_KEY)); } + @Test + @Transactional + public void testSize() throws Exception { + Message message = MessageBuilder.withPayload("foo").build(); + messageStore.addMessage(message); + assertEquals(1, messageStore.getMessageCount()); + } + @Test @Transactional public void testSerializer() throws Exception { @@ -181,6 +188,35 @@ public class JdbcMessageStoreTests { assertEquals(0, group.size()); } + @Test + @Transactional + public void testMessageGroupCount() throws Exception { + String groupId = "X"; + Message message = MessageBuilder.withPayload("foo").build(); + messageStore.addMessageToGroup(groupId, message); + assertEquals(1, messageStore.getMessageGroupCount()); + } + + @Test + @Transactional + public void testMessageGroupSizes() throws Exception { + String groupId = "X"; + Message message = MessageBuilder.withPayload("foo").build(); + messageStore.addMessageToGroup(groupId, message); + assertEquals(1, messageStore.getMessageCountForAllMessageGroups()); + } + + @Test + @Transactional + public void testMarkedMessageGroupSizes() throws Exception { + String groupId = "X"; + Message message = MessageBuilder.withPayload("foo").build(); + messageStore.addMessageToGroup(groupId, message); + assertEquals(0, messageStore.getMarkedMessageCountForAllMessageGroups()); + messageStore.markMessageGroup(messageStore.getMessageGroup(groupId)); + assertEquals(1, messageStore.getMarkedMessageCountForAllMessageGroups()); + } + @Test @Transactional public void testOrderInMessageGroup() throws Exception { @@ -213,7 +249,7 @@ public class JdbcMessageStoreTests { String groupId = "X"; Message message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build(); messageStore.addMessageToGroup(groupId, message); - messageStore.addMessageToGroup(groupId, MessageBuilder.withPayload("bar").setCorrelationId(groupId).build()); + messageStore.addMessageToGroup(groupId, MessageBuilder.withPayload("bar").setCorrelationId(groupId).build()); MessageGroup group = messageStore.markMessageFromGroup(groupId, message); assertEquals(1, group.getMarked().size()); } diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MessageStoreTests-context.xml b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MessageStoreTests-context.xml new file mode 100644 index 0000000000..ed6d339c21 --- /dev/null +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MessageStoreTests-context.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MessageStoreTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MessageStoreTests.java new file mode 100644 index 0000000000..2f47d37e92 --- /dev/null +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MessageStoreTests.java @@ -0,0 +1,51 @@ +/* + * 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.jmx.config; + +import static org.junit.Assert.assertEquals; + +import java.util.Set; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Dave Syer + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class MessageStoreTests { + + @Autowired + private MBeanServer server; + + @Test + public void testHandlerMBeanRegistration() throws Exception { + Set names = server.queryNames(new ObjectName("test.MessageStore:type=SimpleMessageStore,*"), null); + assertEquals(1, names.size()); + ObjectName name = names.iterator().next(); + assertEquals(0, server.getAttribute(name, "MessageCount")); + assertEquals(0, server.getAttribute(name, "MessageGroupCount")); + assertEquals(0, server.getAttribute(name, "MessageCountForAllMessageGroups")); + assertEquals(0, server.getAttribute(name, "MarkedMessageCountForAllMessageGroups")); + } + +} From 4b4a6fdb94d5d39ca0774539b33bab2c512af3b5 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 10 Nov 2010 09:25:49 +0000 Subject: [PATCH 12/42] Tweak test to defeat slow CI again --- .../monitor/ExponentialMovingAverageRatioTests.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/monitor/ExponentialMovingAverageRatioTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/monitor/ExponentialMovingAverageRatioTests.java index 2a20025a45..ce2de51154 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/monitor/ExponentialMovingAverageRatioTests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/monitor/ExponentialMovingAverageRatioTests.java @@ -113,7 +113,11 @@ public class ExponentialMovingAverageRatioTests { assertFalse(0==history.getStandardDeviation()); history.reset(); assertEquals(0, history.getStandardDeviation(), 0.01); - assertEquals("[[N=0, min=0.000000, max=0.000000, mean=1.000000, sigma=0.000000], timeSinceLast=0.000000]", history.toString()); + assertEquals(0, history.getCount()); + assertEquals(0, history.getTimeSinceLastMeasurement(), 0.01); + assertEquals(1, history.getMean(), 0.01); + assertEquals(0, history.getMin(), 0.01); + assertEquals(0, history.getMax(), 0.01); } private double average(double... values) { From f6fc5509663880c673abb47704ee9b6f51971551 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 08:06:13 -0500 Subject: [PATCH 13/42] GlobalChannelInterceptorWrapper no longer implements ChannelInterceptor itself. It only passes the wrapped instance into the channel. --- ...alChannelInterceptorBeanPostProcessor.java | 10 +++++++-- .../GlobalChannelInterceptorWrapper.java | 21 ++----------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java index d124715a53..9db4cce9fb 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java @@ -101,7 +101,9 @@ final class GlobalChannelInterceptorBeanPostProcessor implements BeanPostProcess } } Collections.sort(tempInterceptors, this.comparator); - interceptors.addAll(tempInterceptors); + for (GlobalChannelInterceptorWrapper next : tempInterceptors) { + interceptors.add(next.getChannelInterceptor()); + } tempInterceptors = new ArrayList(); for (GlobalChannelInterceptorWrapper globalChannelInterceptorWrapper : this.negativeOrderInterceptors) { String[] patterns = globalChannelInterceptorWrapper.getPatterns(); @@ -111,7 +113,11 @@ final class GlobalChannelInterceptorBeanPostProcessor implements BeanPostProcess } } Collections.sort(tempInterceptors, comparator); - interceptors.addAll(0, tempInterceptors); + if (!tempInterceptors.isEmpty()) { + for (int i = tempInterceptors.size() - 1; i >= 0; i--) { + interceptors.add(0, tempInterceptors.get(i).getChannelInterceptor()); + } + } } else if (logger.isDebugEnabled()) { logger.debug("Global Channel interceptors will not be applied to Channel: " + beanName); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java index b88f715d83..2f34935cc7 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorWrapper.java @@ -17,16 +17,15 @@ package org.springframework.integration.channel.interceptor; import org.springframework.core.Ordered; -import org.springframework.integration.Message; -import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.ChannelInterceptor; import org.springframework.util.Assert; /** * @author Oleg Zhurakousky + * @author Mark Fisher * @since 2.0 */ -public class GlobalChannelInterceptorWrapper implements ChannelInterceptor, Ordered { +public class GlobalChannelInterceptorWrapper implements Ordered { private final ChannelInterceptor channelInterceptor; @@ -66,22 +65,6 @@ public class GlobalChannelInterceptorWrapper implements ChannelInterceptor, Orde return this.patterns; } - public Message preSend(Message message, MessageChannel channel) { - return this.channelInterceptor.preSend(message, channel); - } - - public void postSend(Message message, MessageChannel channel, boolean sent) { - this.channelInterceptor.postSend(message, channel, sent); - } - - public boolean preReceive(MessageChannel channel) { - return this.channelInterceptor.preReceive(channel); - } - - public Message postReceive(Message message, MessageChannel channel) { - return this.channelInterceptor.postReceive(message, channel); - } - public String toString() { return this.channelInterceptor.toString(); } From 09afaed080fe31cb2ee209ff9cd5e83f1b72e591 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 08:14:14 -0500 Subject: [PATCH 14/42] polishing --- .../integration/router/PayloadTypeRouter.java | 42 ++++++++++--------- .../router/PayloadTypeRouterTests.java | 11 ++--- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/PayloadTypeRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/PayloadTypeRouter.java index a5baef6071..e3f3de1a39 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/PayloadTypeRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/PayloadTypeRouter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * 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. @@ -21,6 +21,7 @@ import java.util.List; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessageHandlingException; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -32,41 +33,41 @@ import org.springframework.util.StringUtils; * @author Oleg Zhurakousky */ public class PayloadTypeRouter extends AbstractMessageRouter { + /** - * Will select the most appropriate channel name matching channel identifiers - * which are fully qualifies class name to type available while traversing payload type. + * Selects the most appropriate channel name matching channel identifiers which are the + * fully qualified class names encountered while traversing the payload type hierarchy. * To resolve ties and conflicts (e.g., Serializable and String) it will match: * 1. Type name to channel identifier else... - * 2. Name of the subclass of the type to channel identifier elc... + * 2. Name of the subclass of the type to channel identifier else... * 3. Name of the Interface of the type to channel identifier while also - * preferring direct interface over in-direct subclass - * + * preferring direct interface over indirect subclass */ @Override protected List getChannelIndicatorList(Message message) { Class firstInterfaceMatch = null; Class type = message.getPayload().getClass(); - - while (type != null && !CollectionUtils.isEmpty(channelIdentifierMap)) { + while (type != null && !CollectionUtils.isEmpty(this.channelIdentifierMap)) { Class[] interfaces = type.getInterfaces(); - // first try to find a match amongst the interfaces and also check if there is more then one - for (Class interfase : interfaces) { - if (channelIdentifierMap.containsKey(interfase.getName())){ - if (firstInterfaceMatch != null){ - throw new IllegalStateException("Unresolvable ambiguity while attempting to find closest match for [" + - type.getName() + "]. Candidate types [" + firstInterfaceMatch.getName() + "] and [" + interfase.getName() + - "] have equal weight."); + // first try to find a match amongst the interfaces and also check if there is more than one + for (Class ifc : interfaces) { + if (this.channelIdentifierMap.containsKey(ifc.getName())) { + if (firstInterfaceMatch != null) { + throw new MessageHandlingException(message, + "Unresolvable ambiguity while attempting to find closest match for [" + type.getName() + + "]. Candidate types [" + firstInterfaceMatch.getName() + "] and [" + + ifc.getName() + "] have equal weight."); } else { - firstInterfaceMatch = interfase; + firstInterfaceMatch = ifc; } } } // the actual type should favor the possible interface match - String channelName = channelIdentifierMap.get(type.getName()); - if (!StringUtils.hasText(channelName)){ - if (firstInterfaceMatch != null){ - return Collections.singletonList((Object)channelIdentifierMap.get(firstInterfaceMatch.getName())); + String channelName = this.channelIdentifierMap.get(type.getName()); + if (!StringUtils.hasText(channelName)) { + if (firstInterfaceMatch != null) { + return Collections.singletonList((Object) this.channelIdentifierMap.get(firstInterfaceMatch.getName())); } } else { @@ -76,4 +77,5 @@ public class PayloadTypeRouter extends AbstractMessageRouter { } return null; } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java b/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java index f38c8478ae..f0c781fedb 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java @@ -218,8 +218,8 @@ public class PayloadTypeRouterTests { assertNotNull(result); } - @Test(expected = IllegalStateException.class) - public void ambiguityFailure() throws Throwable { + @Test(expected = MessageHandlingException.class) + public void ambiguityFailure() throws Exception { QueueChannel defaultChannel = new QueueChannel(); defaultChannel.setBeanName("defaultChannel"); QueueChannel serializableChannel = new QueueChannel(); @@ -242,12 +242,7 @@ public class PayloadTypeRouterTests { router.setDefaultOutputChannel(defaultChannel); Message message = new GenericMessage("test"); - try { - router.handleMessage(message); - } - catch (MessageHandlingException e) { - throw e.getCause(); - } + router.handleMessage(message); } @Test From 9aad89a7c0919420f52e1325998433fa12363ae9 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 10 Nov 2010 08:20:06 -0500 Subject: [PATCH 15/42] removed unnecessary import from GlobalChannelInterceptorParser --- .../config/xml/GlobalChannelInterceptorParser.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java index 14fe3a4fa6..9f4a04c467 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java @@ -16,8 +16,6 @@ package org.springframework.integration.config.xml; -import org.w3c.dom.Element; - import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; @@ -27,7 +25,7 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.util.StringUtils; +import org.w3c.dom.Element; /** * Parser for 'channel-interceptor' elements. From 0a82438e2ef878f1f8dc11b842024d6420a3d816 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 08:23:35 -0500 Subject: [PATCH 16/42] made the 'determineTargetChannels' private in AbstractMessageRouter --- .../router/AbstractMessageRouter.java | 31 +++++++++++-------- .../router/PayloadTypeRouterTests.java | 20 +++++------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java index b91370bdc0..5b4062e22b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java @@ -57,7 +57,7 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { private volatile boolean applySequence; private final MessagingTemplate messagingTemplate = new MessagingTemplate(); - + private volatile String prefix; private volatile String suffix; @@ -65,9 +65,10 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { private volatile ChannelResolver channelResolver; private volatile boolean ignoreChannelNameResolutionFailures; - + protected volatile Map channelIdentifierMap = new ConcurrentHashMap(); - + + /** * Specify the {@link ChannelResolver} strategy to use. * The default is a BeanFactoryChannelResolver. @@ -98,6 +99,7 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { public void setIgnoreChannelNameResolutionFailures(boolean ignoreChannelNameResolutionFailures) { this.ignoreChannelNameResolutionFailures = ignoreChannelNameResolutionFailures; } + /** * Allows you to set the map which will map channel identifiers to channel names. * Channel names will be resolve via {@link ChannelResolver} @@ -107,10 +109,11 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { this.channelIdentifierMap.clear(); this.channelIdentifierMap.putAll(channelIdentifierMap); } - + public void setChannelMapping(String channelIdentifier, String channelName){ this.channelIdentifierMap.put(channelIdentifier, channelName); } + /** * Removes channel mapping for a give channel identifier * @param channelIdentifier @@ -175,7 +178,7 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { protected MessagingTemplate getMessagingTemplate() { return this.messagingTemplate; } - + @Override public void onInit() { BeanFactory beanFactory = this.getBeanFactory(); @@ -183,26 +186,28 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { this.channelResolver = new BeanFactoryChannelResolver(beanFactory); } } - - protected Collection determineTargetChannels(Message message) { + + private Collection determineTargetChannels(Message message) { this.afterPropertiesSet(); Collection channels = new ArrayList(); Collection channelsReturned = this.getChannelIndicatorList(message); addToCollection(channels, channelsReturned, message); return channels; } - + protected ConversionService getRequiredConversionService() { if (this.getConversionService() == null) { this.setConversionService(ConversionServiceFactory.createDefaultConversionService()); } return this.getConversionService(); } + /** - * Subclasses must implement this method to return the channel indicators. + * Subclasses must implement this method to return the channel identifiers. */ protected abstract List getChannelIndicatorList(Message message); + @Override protected void handleMessageInternal(Message message) { boolean sent = false; @@ -259,7 +264,7 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { } return channel; } - + private void addChannelFromString(Collection channels, String channelIdentifier, Message message) { if (channelIdentifier.indexOf(',') != -1) { for (String name : StringUtils.commaDelimitedListToStringArray(channelIdentifier)) { @@ -278,10 +283,9 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { * to 'channelName' (e.g., PTR, EMETR) */ String channelName = channelIdentifier; - if (!CollectionUtils.isEmpty(channelIdentifierMap) && channelIdentifierMap.containsKey(channelIdentifier)){ + if (!CollectionUtils.isEmpty(channelIdentifierMap) && channelIdentifierMap.containsKey(channelIdentifier)) { channelName = channelIdentifierMap.get(channelIdentifier); } - if (this.channelResolver != null){ MessageChannel channel = resolveChannelForName(channelName, message); if (channel != null) { @@ -289,7 +293,7 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { } } } - + private void addToCollection(Collection channels, Collection channelIndicators, Message message) { if (channelIndicators == null) { return; @@ -325,4 +329,5 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { } } } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java b/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java index f0c781fedb..e159fe552c 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java @@ -28,7 +28,6 @@ import java.util.concurrent.ConcurrentHashMap; import org.junit.Test; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.integration.Message; -import org.springframework.integration.MessageChannel; import org.springframework.integration.MessageHandlingException; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.message.GenericMessage; @@ -57,20 +56,17 @@ public class PayloadTypeRouterTests { Message message1 = new GenericMessage("test"); Message message2 = new GenericMessage(123); - assertEquals(1, router.determineTargetChannels(message1).size()); - MessageChannel result1 = router.determineTargetChannels(message1).iterator().next(); - assertEquals(1, router.determineTargetChannels(message2).size()); - MessageChannel result2 = router.determineTargetChannels(message2).iterator().next(); + assertEquals(1, router.getChannelIndicatorList(message1).size()); + assertEquals("stringChannel", router.getChannelIndicatorList(message1).iterator().next()); + assertEquals(1, router.getChannelIndicatorList(message2).size()); + assertEquals("integerChannel", router.getChannelIndicatorList(message2).iterator().next()); - assertEquals(stringChannel, result1); - assertEquals(integerChannel, result2); // validate dynamics QueueChannel newChannel = new QueueChannel(); beanFactory.registerSingleton("newChannel", newChannel); router.setChannelMapping(String.class.getName(), "newChannel"); - assertEquals(1, router.determineTargetChannels(message1).size()); - result1 = router.determineTargetChannels(message1).iterator().next(); - assertEquals(newChannel, result1); + assertEquals(1, router.getChannelIndicatorList(message1).size()); + assertEquals("newChannel", router.getChannelIndicatorList(message1).iterator().next()); // validate nothing happens if mappings were removed and resolutionRequires = false router.removeChannelMapping(String.class.getName()); router.removeChannelMapping(Integer.class.getName()); @@ -112,7 +108,7 @@ public class PayloadTypeRouterTests { QueueChannel newChannel = new QueueChannel(); beanFactory.registerSingleton("newChannel", newChannel); router.setChannelMapping(Integer.class.getName(), "newChannel"); - assertEquals(1, router.determineTargetChannels(message).size()); + assertEquals(1, router.getChannelIndicatorList(message).size()); router.handleMessage(message); result = newChannel.receive(10); assertNotNull(result); @@ -212,7 +208,7 @@ public class PayloadTypeRouterTests { QueueChannel newChannel = new QueueChannel(); beanFactory.registerSingleton("newChannel", newChannel); router.setChannelMapping(Integer.class.getName(), "newChannel"); - assertEquals(1, router.determineTargetChannels(message).size()); + assertEquals(1, router.getChannelIndicatorList(message).size()); router.handleMessage(message); result = newChannel.receive(10); assertNotNull(result); From 38aa676cb9141a533bca80854411cdb579ccdc95 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 08:50:21 -0500 Subject: [PATCH 17/42] renamed getChannelIndicatorList method to getChannelIdentifiers on AbstractMessageRouter --- .../AbstractMessageProcessingRouter.java | 2 +- .../router/AbstractMessageRouter.java | 4 ++-- .../AbstractSingleChannelNameRouter.java | 2 +- .../ErrorMessageExceptionTypeRouter.java | 15 +++++++------- .../integration/router/HeaderValueRouter.java | 2 +- .../integration/router/PayloadTypeRouter.java | 2 +- .../router/RecipientListRouter.java | 2 +- .../router/MultiChannelRouterTests.java | 6 +++--- .../router/PayloadTypeRouterTests.java | 16 +++++++-------- .../integration/router/RouterTests.java | 20 +++++++++---------- .../router/config/RouterParserTests.java | 2 +- .../integration/xml/router/XPathRouter.java | 2 +- .../xml/router/XPathRouterTests.java | 20 +++++++++---------- 13 files changed, 47 insertions(+), 48 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageProcessingRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageProcessingRouter.java index 1e4d2611aa..adcaa76658 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageProcessingRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageProcessingRouter.java @@ -54,7 +54,7 @@ class AbstractMessageProcessingRouter extends AbstractMessageRouter { } @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { Object result = this.messageProcessor.processMessage(message); List asList = new ArrayList(); asList.add(result); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java index 5b4062e22b..5cc195f63c 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java @@ -190,7 +190,7 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { private Collection determineTargetChannels(Message message) { this.afterPropertiesSet(); Collection channels = new ArrayList(); - Collection channelsReturned = this.getChannelIndicatorList(message); + Collection channelsReturned = this.getChannelIdentifiers(message); addToCollection(channels, channelsReturned, message); return channels; } @@ -205,7 +205,7 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { /** * Subclasses must implement this method to return the channel identifiers. */ - protected abstract List getChannelIndicatorList(Message message); + protected abstract List getChannelIdentifiers(Message message); @Override diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractSingleChannelNameRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractSingleChannelNameRouter.java index ec5e5a84ca..3786647d8a 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractSingleChannelNameRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractSingleChannelNameRouter.java @@ -30,7 +30,7 @@ import org.springframework.integration.Message; public abstract class AbstractSingleChannelNameRouter extends AbstractMessageRouter { @Override - protected final List getChannelIndicatorList(Message message) { + protected final List getChannelIdentifiers(Message message) { List channelList = new ArrayList(); String channelName = determineTargetChannelName(message); if(channelName != null){ diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/ErrorMessageExceptionTypeRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/ErrorMessageExceptionTypeRouter.java index 1dcb2d051a..6be71ca33f 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/ErrorMessageExceptionTypeRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/ErrorMessageExceptionTypeRouter.java @@ -33,21 +33,20 @@ import org.springframework.integration.MessageChannel; public class ErrorMessageExceptionTypeRouter extends AbstractMessageRouter { @Override - protected List getChannelIndicatorList(Message message) { - String channelName = null; + protected List getChannelIdentifiers(Message message) { String channelIdentifier = null; Object payload = message.getPayload(); - if (payload != null && (payload instanceof Throwable)) { + if (payload != null && (payload instanceof Throwable) && this.channelIdentifierMap != null) { Throwable mostSpecificCause = (Throwable) payload; while (mostSpecificCause != null) { - channelIdentifier = mostSpecificCause.getClass().getName(); - if (channelIdentifierMap != null){ - String tempChannelName = channelIdentifierMap.get(channelIdentifier); - channelName = tempChannelName == null ? channelName : tempChannelName; + String mappedChannelIdentifier = this.channelIdentifierMap.get(mostSpecificCause.getClass().getName()); + if (mappedChannelIdentifier != null) { + channelIdentifier = mappedChannelIdentifier; } mostSpecificCause = mostSpecificCause.getCause(); } } - return Collections.singletonList((Object)channelName); + return Collections.singletonList((Object) channelIdentifier); } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/HeaderValueRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/HeaderValueRouter.java index dc6652d5d2..95b409be38 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/HeaderValueRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/HeaderValueRouter.java @@ -43,7 +43,7 @@ public class HeaderValueRouter extends AbstractMessageRouter { } @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { Object value = message.getHeaders().get(this.headerName); if (value instanceof String && ((String) value).indexOf(',') != -1) { value = StringUtils.tokenizeToStringArray((String) value, ",", true, true); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/PayloadTypeRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/PayloadTypeRouter.java index e3f3de1a39..43c5abccd4 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/PayloadTypeRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/PayloadTypeRouter.java @@ -44,7 +44,7 @@ public class PayloadTypeRouter extends AbstractMessageRouter { * preferring direct interface over indirect subclass */ @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { Class firstInterfaceMatch = null; Class type = message.getPayload().getClass(); while (type != null && !CollectionUtils.isEmpty(this.channelIdentifierMap)) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java index 543b003bf2..cb5635751e 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java @@ -91,7 +91,7 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia } @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { List channels = new ArrayList(); List recipientList = this.recipients; for (Recipient recipient : recipientList) { diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/MultiChannelRouterTests.java b/spring-integration-core/src/test/java/org/springframework/integration/router/MultiChannelRouterTests.java index 780e63fc25..9510075228 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/MultiChannelRouterTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/MultiChannelRouterTests.java @@ -40,7 +40,7 @@ public class MultiChannelRouterTests { public void routeWithChannelMapping() { AbstractMessageRouter router = new AbstractMessageRouter() { @SuppressWarnings("unchecked") - public List getChannelIndicatorList(Message message) { + public List getChannelIdentifiers(Message message) { return CollectionUtils.arrayToList(new String[] {"channel1", "channel2"}); } }; @@ -64,7 +64,7 @@ public class MultiChannelRouterTests { public void channelNameLookupFailure() { AbstractMessageRouter router = new AbstractMessageRouter() { @SuppressWarnings("unchecked") - public List getChannelIndicatorList(Message message) { + public List getChannelIdentifiers(Message message) { return CollectionUtils.arrayToList(new String[] {"noSuchChannel"} ); } }; @@ -78,7 +78,7 @@ public class MultiChannelRouterTests { public void channelMappingNotAvailable() { AbstractMessageRouter router = new AbstractMessageRouter() { @SuppressWarnings("unchecked") - public List getChannelIndicatorList(Message message) { + public List getChannelIdentifiers(Message message) { return CollectionUtils.arrayToList(new String[] {"noSuchChannel"}); } }; diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java b/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java index e159fe552c..ed82414937 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/PayloadTypeRouterTests.java @@ -56,17 +56,17 @@ public class PayloadTypeRouterTests { Message message1 = new GenericMessage("test"); Message message2 = new GenericMessage(123); - assertEquals(1, router.getChannelIndicatorList(message1).size()); - assertEquals("stringChannel", router.getChannelIndicatorList(message1).iterator().next()); - assertEquals(1, router.getChannelIndicatorList(message2).size()); - assertEquals("integerChannel", router.getChannelIndicatorList(message2).iterator().next()); + assertEquals(1, router.getChannelIdentifiers(message1).size()); + assertEquals("stringChannel", router.getChannelIdentifiers(message1).iterator().next()); + assertEquals(1, router.getChannelIdentifiers(message2).size()); + assertEquals("integerChannel", router.getChannelIdentifiers(message2).iterator().next()); // validate dynamics QueueChannel newChannel = new QueueChannel(); beanFactory.registerSingleton("newChannel", newChannel); router.setChannelMapping(String.class.getName(), "newChannel"); - assertEquals(1, router.getChannelIndicatorList(message1).size()); - assertEquals("newChannel", router.getChannelIndicatorList(message1).iterator().next()); + assertEquals(1, router.getChannelIdentifiers(message1).size()); + assertEquals("newChannel", router.getChannelIdentifiers(message1).iterator().next()); // validate nothing happens if mappings were removed and resolutionRequires = false router.removeChannelMapping(String.class.getName()); router.removeChannelMapping(Integer.class.getName()); @@ -108,7 +108,7 @@ public class PayloadTypeRouterTests { QueueChannel newChannel = new QueueChannel(); beanFactory.registerSingleton("newChannel", newChannel); router.setChannelMapping(Integer.class.getName(), "newChannel"); - assertEquals(1, router.getChannelIndicatorList(message).size()); + assertEquals(1, router.getChannelIdentifiers(message).size()); router.handleMessage(message); result = newChannel.receive(10); assertNotNull(result); @@ -208,7 +208,7 @@ public class PayloadTypeRouterTests { QueueChannel newChannel = new QueueChannel(); beanFactory.registerSingleton("newChannel", newChannel); router.setChannelMapping(Integer.class.getName(), "newChannel"); - assertEquals(1, router.getChannelIndicatorList(message).size()); + assertEquals(1, router.getChannelIdentifiers(message).size()); router.handleMessage(message); result = newChannel.receive(10); assertNotNull(result); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/RouterTests.java b/spring-integration-core/src/test/java/org/springframework/integration/router/RouterTests.java index eb71440f2b..0f6abbf4c5 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/RouterTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/RouterTests.java @@ -43,7 +43,7 @@ public class RouterTests { public void nullChannelIgnoredByDefault() { AbstractMessageRouter router = new AbstractMessageRouter() { @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return null; } }; @@ -55,7 +55,7 @@ public class RouterTests { public void nullChannelThrowsExceptionWhenResolutionRequired() { AbstractMessageRouter router = new AbstractMessageRouter() { @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return null; } }; @@ -68,7 +68,7 @@ public class RouterTests { public void emptyChannelListIgnoredByDefault() { AbstractMessageRouter router = new AbstractMessageRouter() { @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return null; } }; @@ -80,7 +80,7 @@ public class RouterTests { public void emptyChannelListThrowsExceptionWhenResolutionRequired() { AbstractMessageRouter router = new AbstractMessageRouter() { @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return null; } }; @@ -93,7 +93,7 @@ public class RouterTests { public void nullChannelNameArrayIgnoredByDefault() { AbstractMessageRouter router = new AbstractMessageRouter() { @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return null; } }; @@ -106,7 +106,7 @@ public class RouterTests { @Test(expected = MessageDeliveryException.class) public void nullChannelNameArrayThrowsExceptionWhenResolutionRequired() { AbstractMessageRouter router = new AbstractMessageRouter() { - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return null; } }; @@ -121,7 +121,7 @@ public class RouterTests { @Test public void emptyChannelNameArrayIgnoredByDefault() { AbstractMessageRouter router = new AbstractMessageRouter() { - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return new ArrayList(); } }; @@ -135,7 +135,7 @@ public class RouterTests { public void emptyChannelNameArrayThrowsExceptionWhenResolutionRequired() { AbstractMessageRouter router = new AbstractMessageRouter() { @SuppressWarnings("unchecked") - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return CollectionUtils.arrayToList(new String[] {}); } }; @@ -161,7 +161,7 @@ public class RouterTests { public void channelMappingIsRequiredWhenResolvingChannelNamesWithMultiChannelRouter() { AbstractMessageRouter router = new AbstractMessageRouter() { @SuppressWarnings("unchecked") - protected List getChannelIndicatorList(Message message){ + protected List getChannelIdentifiers(Message message){ return CollectionUtils.arrayToList(new String[] { "notImportant" }); } }; @@ -189,7 +189,7 @@ public class RouterTests { public void beanFactoryWithMultiChannelRouter() { AbstractMessageRouter router = new AbstractMessageRouter() { @SuppressWarnings("unchecked") - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return CollectionUtils.arrayToList(new String[] { "testChannel" }); } }; diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/config/RouterParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/router/config/RouterParserTests.java index 41744a273b..9bb53576db 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/config/RouterParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/config/RouterParserTests.java @@ -206,7 +206,7 @@ public class RouterParserTests { @Override - protected List getChannelIndicatorList(Message message) { + protected List getChannelIdentifiers(Message message) { return Collections.singletonList((Object)this.channel); } } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/router/XPathRouter.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/router/XPathRouter.java index 5833613cc1..92a301272f 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/router/XPathRouter.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/router/XPathRouter.java @@ -112,7 +112,7 @@ public class XPathRouter extends AbstractMessageRouter { @Override @SuppressWarnings("unchecked") - public List getChannelIndicatorList(Message message) { + public List getChannelIdentifiers(Message message) { Node node = getConverter().convertToNode(message.getPayload()); return getXPathExpression().evaluate(node, this.nodeMapper); } diff --git a/spring-integration-xml/src/test/java/org/springframework/integration/xml/router/XPathRouterTests.java b/spring-integration-xml/src/test/java/org/springframework/integration/xml/router/XPathRouterTests.java index d340a9275d..3ed037057c 100644 --- a/spring-integration-xml/src/test/java/org/springframework/integration/xml/router/XPathRouterTests.java +++ b/spring-integration-xml/src/test/java/org/springframework/integration/xml/router/XPathRouterTests.java @@ -41,7 +41,7 @@ public class XPathRouterTests { Document doc = XmlTestUtil.getDocumentForString(""); XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathRouter router = new XPathRouter(expression); - Object[] channelNames = router.getChannelIndicatorList(new GenericMessage(doc)).toArray(); + Object[] channelNames = router.getChannelIdentifiers(new GenericMessage(doc)).toArray(); assertEquals("Wrong number of channels returned", 1, channelNames.length); assertEquals("Wrong channel name", "one", channelNames[0]); } @@ -52,7 +52,7 @@ public class XPathRouterTests { Document doc = XmlTestUtil.getDocumentForString("bOnebTwo"); XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/book"); XPathRouter router = new XPathRouter(expression); - Object[] channelNames = router.getChannelIndicatorList(new GenericMessage(doc)).toArray(); + Object[] channelNames = router.getChannelIdentifiers(new GenericMessage(doc)).toArray(); assertEquals("Wrong number of channels returned", 2, channelNames.length); assertEquals("Wrong channel name", "bOne", channelNames[0]); assertEquals("Wrong channel name", "bTwo", channelNames[1]); @@ -63,7 +63,7 @@ public class XPathRouterTests { public void multipleNodeValuesAsString() throws Exception { XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/book"); XPathRouter router = new XPathRouter(expression); - Object[] channelNames = router.getChannelIndicatorList(new GenericMessage("bOnebTwo")).toArray(); + Object[] channelNames = router.getChannelIdentifiers(new GenericMessage("bOnebTwo")).toArray(); assertEquals("Wrong number of channels returned", 2, channelNames.length); assertEquals("Wrong channel name", "bOne", channelNames[0]); assertEquals("Wrong channel name", "bTwo", channelNames[1]); @@ -73,14 +73,14 @@ public class XPathRouterTests { public void nonNodePayload() throws Exception { XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathRouter router = new XPathRouter(expression); - router.getChannelIndicatorList(new GenericMessage("test")); + router.getChannelIdentifiers(new GenericMessage("test")); } @Test public void nodePayload() throws Exception { XPathRouter router = new XPathRouter("./three/text()"); Document testDocument = XmlTestUtil.getDocumentForString("bobdave"); - Object[] channelNames = router.getChannelIndicatorList(new GenericMessage(testDocument.getElementsByTagName("two").item(0))).toArray(); + Object[] channelNames = router.getChannelIdentifiers(new GenericMessage(testDocument.getElementsByTagName("two").item(0))).toArray(); assertEquals("bob",channelNames[0]); assertEquals("dave",channelNames[1]); } @@ -90,7 +90,7 @@ public class XPathRouterTests { Document doc = XmlTestUtil.getDocumentForString(""); XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathRouter router = new XPathRouter(expression); - Object channelName = router.getChannelIndicatorList(new GenericMessage(doc)).toArray()[0]; + Object channelName = router.getChannelIdentifiers(new GenericMessage(doc)).toArray()[0]; assertEquals("Wrong channel name", "one", channelName); } @@ -98,7 +98,7 @@ public class XPathRouterTests { public void testSimpleStringDoc() throws Exception { XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathRouter router = new XPathRouter(expression); - Object channelName = router.getChannelIndicatorList(new GenericMessage("")).toArray()[0]; + Object channelName = router.getChannelIdentifiers(new GenericMessage("")).toArray()[0]; assertEquals("Wrong channel name", "one", channelName); } @@ -106,14 +106,14 @@ public class XPathRouterTests { public void testNonNodePayload() throws Exception { XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathRouter router = new XPathRouter(expression); - router.getChannelIndicatorList(new GenericMessage("test")); + router.getChannelIdentifiers(new GenericMessage("test")); } @Test public void testNodePayload() throws Exception { XPathRouter router = new XPathRouter("./three/text()"); Document testDocument = XmlTestUtil.getDocumentForString("bob"); - Object[] channelNames = router.getChannelIndicatorList(new GenericMessage(testDocument + Object[] channelNames = router.getChannelIdentifiers(new GenericMessage(testDocument .getElementsByTagName("two").item(0))).toArray(); assertEquals("bob", channelNames[0]); } @@ -123,7 +123,7 @@ public class XPathRouterTests { Document doc = XmlTestUtil.getDocumentForString(""); XPathExpression expression = XPathExpressionFactory.createXPathExpression("/somethingelse/@type"); XPathRouter router = new XPathRouter(expression); - List channelNames = router.getChannelIndicatorList(new GenericMessage(doc)); + List channelNames = router.getChannelIdentifiers(new GenericMessage(doc)); assertEquals(0, channelNames.size()); } From 8e2daea5031c820e0237fc35b1684ab86ed61d72 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 08:55:20 -0500 Subject: [PATCH 18/42] INT-1377 removed unused AbstractSingleChannelNameRouter class --- .../AbstractSingleChannelNameRouter.java | 47 -------------- .../integration/router/RouterTests.java | 63 ++++++++++--------- 2 files changed, 32 insertions(+), 78 deletions(-) delete mode 100644 spring-integration-core/src/main/java/org/springframework/integration/router/AbstractSingleChannelNameRouter.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractSingleChannelNameRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractSingleChannelNameRouter.java deleted file mode 100644 index 3786647d8a..0000000000 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractSingleChannelNameRouter.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.router; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.integration.Message; - -/** - * Extends {@link AbstractMessageRouter} to support router - * implementations that always return a single channel name (or null). - * - * @author Mark Fisher - */ -public abstract class AbstractSingleChannelNameRouter extends AbstractMessageRouter { - - @Override - protected final List getChannelIdentifiers(Message message) { - List channelList = new ArrayList(); - String channelName = determineTargetChannelName(message); - if(channelName != null){ - channelList.add(channelName); - } - return channelList; - } - - /** - * Subclasses must implement this method to return the channel name. - */ - protected abstract String determineTargetChannelName(Message message); - -} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/router/RouterTests.java b/spring-integration-core/src/test/java/org/springframework/integration/router/RouterTests.java index 0f6abbf4c5..6cfa951012 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/router/RouterTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/router/RouterTests.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.junit.Test; @@ -90,7 +91,7 @@ public class RouterTests { } @Test - public void nullChannelNameArrayIgnoredByDefault() { + public void nullChannelIdentifierIgnoredByDefault() { AbstractMessageRouter router = new AbstractMessageRouter() { @Override protected List getChannelIdentifiers(Message message) { @@ -103,8 +104,22 @@ public class RouterTests { router.handleMessage(message); } + @Test + public void nullChannelIdentifierInListIgnoredByDefault() { + AbstractMessageRouter router = new AbstractMessageRouter() { + @Override + protected List getChannelIdentifiers(Message message) { + return Collections.singletonList(null); + } + }; + TestChannelResolver channelResolver = new TestChannelResolver(); + router.setChannelResolver(channelResolver); + Message message = new GenericMessage("test"); + router.handleMessage(message); + } + @Test(expected = MessageDeliveryException.class) - public void nullChannelNameArrayThrowsExceptionWhenResolutionRequired() { + public void nullChannelIdentifierTriggersExceptionWhenResolutionRequired() { AbstractMessageRouter router = new AbstractMessageRouter() { protected List getChannelIdentifiers(Message message) { return null; @@ -117,6 +132,19 @@ public class RouterTests { router.handleMessage(message); } + @Test(expected = MessageDeliveryException.class) + public void nullChannelIdentifierInListTriggersExceptionWhenResolutionRequired() { + AbstractMessageRouter router = new AbstractMessageRouter() { + protected List getChannelIdentifiers(Message message) { + return Collections.singletonList(null); + } + }; + TestChannelResolver channelResolver = new TestChannelResolver(); + router.setChannelResolver(channelResolver); + router.setResolutionRequired(true); + Message message = new GenericMessage("test"); + router.handleMessage(message); + } @Test public void emptyChannelNameArrayIgnoredByDefault() { @@ -147,18 +175,7 @@ public class RouterTests { } @Test(expected = MessagingException.class) - public void channelMappingIsRequiredWhenResolvingChannelNamesWithSingleChannelRouter() { - AbstractSingleChannelNameRouter router = new AbstractSingleChannelNameRouter() { - public String determineTargetChannelName(Message message) { - return "notImportant"; - } - }; - router.setBeanFactory(mock(BeanFactory.class)); - router.handleMessage(new GenericMessage("this should fail")); - } - - @Test(expected = MessagingException.class) - public void channelMappingIsRequiredWhenResolvingChannelNamesWithMultiChannelRouter() { + public void channelMappingIsRequiredWhenResolvingChannelNames() { AbstractMessageRouter router = new AbstractMessageRouter() { @SuppressWarnings("unchecked") protected List getChannelIdentifiers(Message message){ @@ -170,23 +187,7 @@ public class RouterTests { } @Test - public void beanFactoryWithSingleChannelRouter() { - AbstractSingleChannelNameRouter router = new AbstractSingleChannelNameRouter() { - public String determineTargetChannelName(Message message) { - return "testChannel"; - } - }; - QueueChannel testChannel = new QueueChannel(); - GenericApplicationContext context = new GenericApplicationContext(); - context.getBeanFactory().registerSingleton("testChannel", testChannel); - router.setBeanFactory(context); - router.handleMessage(new GenericMessage("test")); - Message reply = testChannel.receive(0); - assertEquals("test", reply.getPayload()); - } - - @Test - public void beanFactoryWithMultiChannelRouter() { + public void beanFactoryWithRouter() { AbstractMessageRouter router = new AbstractMessageRouter() { @SuppressWarnings("unchecked") protected List getChannelIdentifiers(Message message) { From 30e106eeb0612706063cd7e57ccc9061f3e271d5 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 10 Nov 2010 10:52:53 -0500 Subject: [PATCH 19/42] INT-1501 initial refactoing of the transformer section to address better docs for header-enricher --- docs/src/reference/docbook/claim-check.xml | 11 ++++++++ .../reference/docbook/content-enrichment.xml | 28 +++++++++++++++++++ .../docbook/message-transformation.xml | 3 +- docs/src/reference/docbook/transformer.xml | 9 ++---- 4 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 docs/src/reference/docbook/claim-check.xml create mode 100644 docs/src/reference/docbook/content-enrichment.xml diff --git a/docs/src/reference/docbook/claim-check.xml b/docs/src/reference/docbook/claim-check.xml new file mode 100644 index 0000000000..67d6da2174 --- /dev/null +++ b/docs/src/reference/docbook/claim-check.xml @@ -0,0 +1,11 @@ + +
+ Claim Check + +
+ Introduction + +
+ +
diff --git a/docs/src/reference/docbook/content-enrichment.xml b/docs/src/reference/docbook/content-enrichment.xml new file mode 100644 index 0000000000..016a06bc5c --- /dev/null +++ b/docs/src/reference/docbook/content-enrichment.xml @@ -0,0 +1,28 @@ + +
+ Content Enricher + +
+ Introduction + + Some time you may have a requirement to enhance a request with more information then it was + provided by the target system. Content Enricher pattern + describes various scenarios as well as the component (Enricher), which allows you to address such requirements. + +
+ +
+ Header Enricher + + + If you only need to add headers to a Message, and they are not dynamically determined by Message content, + then referencing a custom implementation may be overkill. For that reason, Spring Integration provides the + 'header-enricher' element. +
+
+ ]]> + +
+ +
diff --git a/docs/src/reference/docbook/message-transformation.xml b/docs/src/reference/docbook/message-transformation.xml index c8644b35f4..6a4f708876 100644 --- a/docs/src/reference/docbook/message-transformation.xml +++ b/docs/src/reference/docbook/message-transformation.xml @@ -5,5 +5,6 @@ Message Transformation - + + diff --git a/docs/src/reference/docbook/transformer.xml b/docs/src/reference/docbook/transformer.xml index f0a6341e90..108f249878 100644 --- a/docs/src/reference/docbook/transformer.xml +++ b/docs/src/reference/docbook/transformer.xml @@ -88,13 +88,8 @@ ]]> - - If you only need to add headers to a Message, and they are not dynamically determined by Message content, - then referencing a custom implementation may be overkill. For that reason, Spring Integration provides the - 'header-enricher' element. -
-
- ]]> + + Object-to-Map Transformer As added convenience, Spring Integration also provides Object-to-Map and Map-to-Object transformers which From 30669119161fb63e08d92c92b9e326329c456950 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 10 Nov 2010 12:10:19 -0500 Subject: [PATCH 20/42] INT-1501 enhanced documentation to include examples and explanation on header enricher sub-element including SpEL support --- .../reference/docbook/content-enrichment.xml | 72 +++++++++++++++++-- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/docs/src/reference/docbook/content-enrichment.xml b/docs/src/reference/docbook/content-enrichment.xml index 016a06bc5c..2e6660ff84 100644 --- a/docs/src/reference/docbook/content-enrichment.xml +++ b/docs/src/reference/docbook/content-enrichment.xml @@ -16,13 +16,73 @@ Header Enricher - If you only need to add headers to a Message, and they are not dynamically determined by Message content, - then referencing a custom implementation may be overkill. For that reason, Spring Integration provides the - 'header-enricher' element. -
-
- ]]> + If you only need to add headers to a Message, and they are not dynamically determined by the Message content, + then referencing a custom implementation of the Transformer may be an overkill. For that reason, + Spring Integration provides the Header Enricher which is exposed via <header-enricher> element. + + + + +]]> + + + Header Enricher also provides helpful sub-elements to set well known header names. + + + + + + +]]> + + In the above configuration you can clearly see that for well known headers such as errorChannel, + correlationId, priority, replyChanneletc., instead of using generic <header> + sub-element where you would have to provide both header 'name' and 'value', you can use convenient sub-elements + allowing you to set those values directly. + + + SpEL Support + + + In Spring Integration 2.0 we are introducing convenience of + Spring Expression Language (SpEL) + to help configure many different components. Header Enricher is one of them. + A lot of times, header value cannot be defined statically and has to be computed dynamically. That is why + Header Enricher allows you to also specify bean 'ref' and 'method' that will calculate the + header value. Let's look at the following configuration: + + + + + +]]> + + + + As you can see that the computation logic to determine the header value is actually pretty simple and the + natural question would be is there a simpler way to accomplish this? And that is where SpEL shows its true power. + + + +]]> + + As you can see, with SpEL for simple cases like above we no longer have to provide a separate class and configure + it in the application context. All we need is to use expression attribute and provide a valid + SpEL expression. You can also see that 'payload' and 'headers' are bound as variables to the SpEL Evaluation Context + giving you full access to the incoming Message. + + + Adapter specific Header Enrichers + + + As you go through the manual you will see that as an added convenience + Spring Integration provides adapter specific Header Enrichers (e.g., WS, XMPP, etc.) + From ceba3895ee70b9802d4653f52648566d40897f32 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 12:36:32 -0500 Subject: [PATCH 21/42] INT-1377 moved prefix/suffix setting to after the channelIdentifier mapping lookup --- .../router/AbstractMessageRouter.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java index 5cc195f63c..bf084f1749 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java @@ -187,14 +187,6 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { } } - private Collection determineTargetChannels(Message message) { - this.afterPropertiesSet(); - Collection channels = new ArrayList(); - Collection channelsReturned = this.getChannelIdentifiers(message); - addToCollection(channels, channelsReturned, message); - return channels; - } - protected ConversionService getRequiredConversionService() { if (this.getConversionService() == null) { this.setConversionService(ConversionServiceFactory.createDefaultConversionService()); @@ -245,6 +237,13 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { } } + private Collection determineTargetChannels(Message message) { + Collection channels = new ArrayList(); + Collection channelsReturned = this.getChannelIdentifiers(message); + addToCollection(channels, channelsReturned, message); + return channels; + } + private MessageChannel resolveChannelForName(String channelName, Message message) { Assert.state(this.channelResolver != null, "unable to resolve channel names, no ChannelResolver available"); @@ -272,21 +271,23 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { } return; } - if (this.prefix != null) { - channelIdentifier = this.prefix + channelIdentifier; - } - if (this.suffix != null) { - channelIdentifier = channelIdentifier + suffix; - } - /* - * Some routers due to their complex nature will already resolve 'channelIdentifier' - * to 'channelName' (e.g., PTR, EMETR) - */ + + // if the channelIdentifierMap contains a mapping, we'll use the mapped value + // otherwise, the String-based channelIdentifier itself will be used as the channel name String channelName = channelIdentifier; if (!CollectionUtils.isEmpty(channelIdentifierMap) && channelIdentifierMap.containsKey(channelIdentifier)) { channelName = channelIdentifierMap.get(channelIdentifier); } - if (this.channelResolver != null){ + if (this.prefix != null) { + channelName = this.prefix + channelName; + } + if (this.suffix != null) { + channelName = channelName + suffix; + } + if (this.channelResolver == null) { + this.onInit(); + } + if (this.channelResolver != null) { MessageChannel channel = resolveChannelForName(channelName, message); if (channel != null) { channels.add(channel); From 12b9fb5d9164d381ed6eb1e724c15a957d28ccc6 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 10 Nov 2010 13:13:34 -0500 Subject: [PATCH 22/42] INT-1560 added documentation for Feed Adapter. Removed dead code from Feed adapter --- .../reference/docbook/content-enrichment.xml | 2 +- docs/src/reference/docbook/feed.xml | 70 ++++++++++++++++++- .../feed/inbound/FeedEntryMessageSource.java | 8 --- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/docs/src/reference/docbook/content-enrichment.xml b/docs/src/reference/docbook/content-enrichment.xml index 2e6660ff84..b9b363327e 100644 --- a/docs/src/reference/docbook/content-enrichment.xml +++ b/docs/src/reference/docbook/content-enrichment.xml @@ -81,7 +81,7 @@ As you go through the manual you will see that as an added convenience - Spring Integration provides adapter specific Header Enrichers (e.g., WS, XMPP, etc.) + Spring Integration provides adapter specific Header Enrichers (e.g., MAIL, XMPP, etc.) diff --git a/docs/src/reference/docbook/feed.xml b/docs/src/reference/docbook/feed.xml index f897e0d2e7..4badba3890 100644 --- a/docs/src/reference/docbook/feed.xml +++ b/docs/src/reference/docbook/feed.xml @@ -3,12 +3,78 @@ xmlns:xlink="http://www.w3.org/1999/xlink"> Feed Adapter - Spring Integration provides support for Feed (RSS, Atom) + Spring Integration provides support for Syndication via Feed Adapters
Introduction - TODO + As we know Web syndication is a form of syndication where material such as news items, press releases that is + available to any website is also made available via we feeds such as RSS, ATOM etc. + + + Spring integration provides support for Web Syndication via FEED adapter which comes with a convenient + namespace-based configuration. + To configure FEED namespace include the following elements into the headers of your XML configuration file: + + +
+
+ Feed Inbound Channel Adapter + + The only adapter that is really needed to provide support for retrieving feeds is an inbound channel adapter + which allows you to subscribe to a particular URL. Below is the configuration for such adapter: + + + +]]> + + In the above configuration we are subscribing to a URL identified by url attribute. + + + As news items are retrieved they will be converted to a Message and sent to a channel identified by channel attribute. + The payload of such message will be com.sun.syndication.feed.synd.SyndEntry which encapsulates + various data (i.e., content, dates, authors etc.) about a news item. + + + You can also see that Inbound Feed Channel Adapter is a Polling consumer which means you have to + provide a poller configuration. However, one important thing you must understand with regard to Feed sinc its inner-workings + are slightly different then any other poling consumer. When Inbound Feed adapter is started it does the first poll and + receives com.sun.syndication.feed.synd.SyndEntryyFeed which is an object that contains multiple + SyndEntry objects. Each entry is stored in the local entry queue and is released based on + the value in the max-messages-per-poll attribute where each Message will contain a single entry. + If during retrieval of the entries from the entry queue the queue had become empty the adapter will attempt to update + the Feed populating the queue with more entries (SyndEntry) if available, otherwise the next attempt to poll for a feed will + be determined by the trigger of the poller (e.g., every 10 seconds in the above configuration). + + + + Duplicate Entries + + + Polling for a Feed might result in the entries that have already been processed ("I already read that news item, why are you showing it to me again?"). + Spring Integration provides a convenient mechanism to eliminate the need to worry about duplicate entries. + Each feed entry will have publish date field. Every time the new Message is generated and sent, + Spring Integration will store the value of the publish date in the instance of the + org.springframework.integration.store.MetadataStore which is a strategy interface designed to store various + types of meta-data (e.g., publish date of the last feed entry that has been processed) to help components such as Feed to deal with + duplicates. + + + The default rule for locating this meta-data store is as follows; Spring Integration will look for a bean of type + org.springframework.integration.store.MetadataStore in the ApplicationContext. If one found then it will be used, + otherwise it will create a new instance of SimpleMetadataStore which is a simple in-memory implementation that + will only persist meta-data within the life-cycle of the application context. This means that upon restart you may end up with + duplicate entries. If you need to persist meta-data between Application Context restarts, you may use + PropertiesPersistingMetadataStore which is a property file based persister or provide your own + implementation of the MetedataStore interface (e.g.,JdbcMetadatStore) and configure it as bean in the Application Context. + + ]]> + +
diff --git a/spring-integration-feed/src/main/java/org/springframework/integration/feed/inbound/FeedEntryMessageSource.java b/spring-integration-feed/src/main/java/org/springframework/integration/feed/inbound/FeedEntryMessageSource.java index 44eb19cbed..7f6c94cf96 100644 --- a/spring-integration-feed/src/main/java/org/springframework/integration/feed/inbound/FeedEntryMessageSource.java +++ b/spring-integration-feed/src/main/java/org/springframework/integration/feed/inbound/FeedEntryMessageSource.java @@ -60,8 +60,6 @@ public class FeedEntryMessageSource extends IntegrationObjectSupport implements private final FeedFetcher feedFetcher; - private final Queue feeds = new ConcurrentLinkedQueue(); - private final Queue entries = new ConcurrentLinkedQueue(); private volatile String metadataKey; @@ -253,12 +251,6 @@ public class FeedEntryMessageSource extends IntegrationObjectSupport implements logger.debug("\tEVENT: Feed Polled. URL = " + event.getUrlString()); } } - else if (FetcherEvent.EVENT_TYPE_FEED_RETRIEVED.equals(eventType)) { - if (logger.isDebugEnabled()) { - logger.debug("\tEVENT: Feed Retrieved. URL = " + event.getUrlString()); - } - feeds.add(event.getFeed()); - } else if (FetcherEvent.EVENT_TYPE_FEED_UNCHANGED.equals(eventType)) { if (logger.isDebugEnabled()) { logger.debug("\tEVENT: Feed Unchanged. URL = " + event.getUrlString()); From ffe193459b9294b2cb6a46afd71920616c1f2615 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 13:54:28 -0500 Subject: [PATCH 23/42] polishing --- .../router/AbstractMessageRouter.java | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java index bf084f1749..1a2c42e7d4 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java @@ -92,14 +92,6 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { this.suffix = suffix; } - /** - * Specify whether this router should ignore any failure to resolve a channel name to - * an actual MessageChannel instance when delegating to the ChannelResolver strategy. - */ - public void setIgnoreChannelNameResolutionFailures(boolean ignoreChannelNameResolutionFailures) { - this.ignoreChannelNameResolutionFailures = ignoreChannelNameResolutionFailures; - } - /** * Allows you to set the map which will map channel identifiers to channel names. * Channel names will be resolve via {@link ChannelResolver} @@ -148,6 +140,14 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { this.resolutionRequired = resolutionRequired; } + /** + * Specify whether this router should ignore any failure to resolve a channel name to + * an actual MessageChannel instance when delegating to the ChannelResolver strategy. + */ + public void setIgnoreChannelNameResolutionFailures(boolean ignoreChannelNameResolutionFailures) { + this.ignoreChannelNameResolutionFailures = ignoreChannelNameResolutionFailures; + } + /** * Specify whether send failures for one or more of the recipients should be ignored. By default this is * false meaning that an Exception will be thrown whenever a send fails. To override this and suppress @@ -245,6 +245,9 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { } private MessageChannel resolveChannelForName(String channelName, Message message) { + if (this.channelResolver == null) { + this.onInit(); + } Assert.state(this.channelResolver != null, "unable to resolve channel names, no ChannelResolver available"); MessageChannel channel = null; @@ -284,14 +287,9 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { if (this.suffix != null) { channelName = channelName + suffix; } - if (this.channelResolver == null) { - this.onInit(); - } - if (this.channelResolver != null) { - MessageChannel channel = resolveChannelForName(channelName, message); - if (channel != null) { - channels.add(channel); - } + MessageChannel channel = resolveChannelForName(channelName, message); + if (channel != null) { + channels.add(channel); } } From 5bb4d8ba680358d0c439d05368345e9d7bbe76a8 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 10 Nov 2010 14:38:09 -0500 Subject: [PATCH 24/42] INT-1585 added documentation for header-filter --- docs/src/reference/docbook/filter.xml | 2 +- docs/src/reference/docbook/transformer.xml | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/src/reference/docbook/filter.xml b/docs/src/reference/docbook/filter.xml index b31c71189d..2ce0087447 100644 --- a/docs/src/reference/docbook/filter.xml +++ b/docs/src/reference/docbook/filter.xml @@ -31,7 +31,7 @@
- The <filter> Element + Namespace support for Filter - <filter> Element The <filter> element is used to create a Message-selecting endpoint. In addition to "input-channel" and "output-channel" attributes, it requires a "ref". The "ref" may point to a MessageSelector implementation: diff --git a/docs/src/reference/docbook/transformer.xml b/docs/src/reference/docbook/transformer.xml index 108f249878..12e925f523 100644 --- a/docs/src/reference/docbook/transformer.xml +++ b/docs/src/reference/docbook/transformer.xml @@ -28,7 +28,7 @@
- The <transformer> Element + Namespace support for Transformer - <transformer> Element The <transformer> element is used to create a Message-transforming endpoint. In addition to "input-channel" and "output-channel" attributes, it requires a "ref". The "ref" may either point to an Object that contains the @@ -156,7 +156,7 @@ public class Kid{
- The @Transformer Annotation + Annotation support for Transformer - @Transformer The @Transformer annotation can also be added to methods that expect either the Message type or the message payload type. The return value will be handled in the @@ -174,5 +174,22 @@ Order generateOrder(String productId, @Header("customerName") String customer) { }
+ +
+ Header Filter + + Some time your transformation use case might be as simple as removing a few headers. + For this type of use cases Spring Integration provides Header Filter which allows you to specify which header should be + removed from the output Message. + Basically Header Filter is the opposite of Header Enricher + that is discussed in + + ]]> + + As you can see, configuration of Header Filter is quite simple. It is a typical endpoint with input/output channels + and header-names attribute which allows you to specify the names of the headers (delimited by coma if multiple) + that need to be removed. So, in the above example headers with the name 'lastName' and 'state' will be removed. +
From 23f7d7d60b718051d0e65e621d1b80ab216dab8d Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 14:49:50 -0500 Subject: [PATCH 25/42] INT-1562 restructuring packages for inbound/outbound --- ...pRemoteFileSystemSynchronizingMessageSourceFactoryBean.java | 3 +++ .../integration/ftp/FtpSendingMessageHandlerFactoryBean.java | 1 + .../integration/ftp/FtpsSendingMessageHandlerFactoryBean.java | 2 +- .../integration/ftp/config/FtpNamespaceHandler.java | 1 + .../ftp/{ => filters}/FtpPatternMatchingFileListFilter.java | 2 +- .../{ => inbound}/FtpInboundRemoteFileSystemSynchronizer.java | 2 +- .../FtpInboundRemoteFileSystemSynchronizingMessageSource.java | 3 ++- .../ftp/{ => outbound}/FtpSendingMessageHandler.java | 3 ++- .../integration/ftp/FtpParserOutboundTests.java | 1 + 9 files changed, 13 insertions(+), 5 deletions(-) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => filters}/FtpPatternMatchingFileListFilter.java (95%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => inbound}/FtpInboundRemoteFileSystemSynchronizer.java (98%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => inbound}/FtpInboundRemoteFileSystemSynchronizingMessageSource.java (93%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => outbound}/FtpSendingMessageHandler.java (98%) diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java index 6f41cb7bc1..59cdbcd64c 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -30,6 +30,9 @@ import org.springframework.core.io.ResourceEditor; import org.springframework.core.io.ResourceLoader; import org.springframework.integration.file.filters.CompositeFileListFilter; import org.springframework.integration.file.filters.FileListFilter; +import org.springframework.integration.ftp.filters.FtpPatternMatchingFileListFilter; +import org.springframework.integration.ftp.inbound.FtpInboundRemoteFileSystemSynchronizer; +import org.springframework.integration.ftp.inbound.FtpInboundRemoteFileSystemSynchronizingMessageSource; import org.springframework.util.StringUtils; /** diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java index 25321e32a0..1b15fc131e 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java @@ -19,6 +19,7 @@ package org.springframework.integration.ftp; import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.integration.file.FileNameGenerator; +import org.springframework.integration.ftp.outbound.FtpSendingMessageHandler; /** * A factory bean implementation that handles constructing an outbound FTP diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java index e89447842d..38d2944482 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java @@ -20,7 +20,7 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.TrustManager; /** - * Sends files to a remote FTPS file system. Based heavily on {@link org.springframework.integration.ftp.FtpSendingMessageHandler} + * Sends files to a remote FTPS file system. Based heavily on {@link org.springframework.integration.ftp.outbound.FtpSendingMessageHandler} * * @author Josh Long * @author Iwein Fuld diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java index e2403b5e51..84249701d3 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java @@ -17,6 +17,7 @@ package org.springframework.integration.ftp.config; import org.apache.commons.net.ftp.FTP; + import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler; import java.util.HashMap; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpPatternMatchingFileListFilter.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/filters/FtpPatternMatchingFileListFilter.java similarity index 95% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpPatternMatchingFileListFilter.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/filters/FtpPatternMatchingFileListFilter.java index 4aa8069a23..c8ee914989 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpPatternMatchingFileListFilter.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/filters/FtpPatternMatchingFileListFilter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.filters; import java.util.regex.Pattern; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizer.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizer.java similarity index 98% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizer.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizer.java index 17233791eb..4c5f098c93 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizer.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.inbound; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizingMessageSource.java similarity index 93% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizingMessageSource.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizingMessageSource.java index 9b48d4e055..b6b22cc113 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpInboundRemoteFileSystemSynchronizingMessageSource.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizingMessageSource.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.inbound; import org.apache.commons.net.ftp.FTPFile; import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; +import org.springframework.integration.ftp.FtpClientPool; /** * A {@link org.springframework.integration.core.MessageSource} implementation for FTP. diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandler.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandler.java similarity index 98% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandler.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandler.java index b58e10a561..d1109af059 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandler.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.outbound; import java.io.File; import java.io.FileInputStream; @@ -33,6 +33,7 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageDeliveryException; import org.springframework.integration.file.DefaultFileNameGenerator; import org.springframework.integration.file.FileNameGenerator; +import org.springframework.integration.ftp.FtpClientPool; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpParserOutboundTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpParserOutboundTests.java index 73820956ff..14a7fb4f0f 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpParserOutboundTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpParserOutboundTests.java @@ -28,6 +28,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.file.FileNameGenerator; +import org.springframework.integration.ftp.outbound.FtpSendingMessageHandler; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; From ab7838db8821b013662bd6c2a17e614b243ff23d Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 10 Nov 2010 14:53:04 -0500 Subject: [PATCH 26/42] INT-1552 IP Doc Polishing --- docs/src/reference/docbook/ip.xml | 224 ++++++++++++++++++++++++++++-- 1 file changed, 210 insertions(+), 14 deletions(-) diff --git a/docs/src/reference/docbook/ip.xml b/docs/src/reference/docbook/ip.xml index 9f3dd98307..d8a668888b 100644 --- a/docs/src/reference/docbook/ip.xml +++ b/docs/src/reference/docbook/ip.xml @@ -140,14 +140,18 @@ the connection factory will not function without one). A reference to a server connection factory can also be provided to an outbound adapter; that adapter can then be used to send replies to incoming messages to the same connection. - Reply messages will only be routed to the connection if the reply contains - the header $ip_connection_id that was inserted into the original message by - the connection factory. - This is the extent of message correlation performed when sharing connection + + Reply messages will only be routed to the connection if the reply contains + the header ip_connection_id that was inserted into the original message by + the connection factory. + + This is the extent of message correlation performed when sharing connection factories between inbound and outbound adapters. Such sharing allows for asynchronous two-way communication over TCP. Only payload information is transferred using TCP; therefore any message correlation must be performed - by downstream components such as aggregators or other endpoints. + by downstream components such as aggregators or other endpoints. + For more information refer to + TCP Message Correlationn A maximum of one adapter of each type may be given a reference to a connection @@ -406,7 +410,7 @@ can process a single request/response at a time. - The intbound gateway, after constructing a message with the incoming payload and sending + The intbound gateway, after constructing a message with the incoming payload and sending it to the requestChannel, waits for a response and sends the payload from the response message by writing it to the connection. @@ -416,9 +420,9 @@ Communications over the connections are single-threaded. Users should be aware that only one message can be handled at a time and, if another thread attempts to send a message before the current response has been received, it will block until - any previous requests are complete (or time out). - If, however, the client connection factory is configured for single-use connections - each new request gets its own connection and is processed immediately. + any previous requests are complete (or time out). + If, however, the client connection factory is configured for single-use connections + each new request gets its own connection and is processed immediately. +
+ TCP Message Correlation +
+ Overview + + One goal of the IP Endpoints is to provide communication with systems other + than another Spring Integration application. For this reason, only + message payloads are sent + and received. No message correlation is provided by the framework, + except when using the gateways, or collaborating channel adapters on the + server side. In the paragraphs below we discuss the various + correlation techniques available to applications. In most cases, this + requires specific application-level correlation of messages, even when + message payloads contain some natural correlation data (such as an order + number). + +
+
+ Gateways + + The gateways will automatically correlate messages. However, an outbound + gateway should only be used for relatively low-volume use. + When the connection + factory is configured for 'single-use="false"', a single shared connection is + used for all message pairs, and only one message can be processed at a time. + A new message will have to wait until the reply to the previous message has + been received. + When a connection + factory is configured for 'single-use="true"' connections, the above + restriction does not apply because each message pair is + carried over a separate connection. While this may give higher throughput + than a shared connection environment, it comes with the overhead of opening + and closing a new connection for each message pair. + + + Therefore, for high-volume messages, consider using a collaborating pair of + channel adapters. However, you will need to provide collaboration logic. + +
+
+ Collaborating Outbound and Inbound Channel Adapters + + To achieve high-volume throughput (avoiding the pitfalls of using gateways + as mentioned above) you may consider configuring a pair of collaborating + outbound and inbound channel adapters. On the server side, message + correlation is automatically handled by the adapters because the inbound + adapter adds a header allowing the outbound adapter to determine which + connection to use to send the reply message. On the client side, however, + the application will have to provide its own correlation logic. This can + be done in a number of ways. + + + If the message payload has some natural correlation data, such as a + transaction id or an order number, AND there is no need to retain any + information (such as a reply channel header) from the original outbound message, + the correlation is simple and would done at the application level in any case. + + + If the message payload has some natural correlation data, such as a + transaction id or an order number, but there is a need to retain some + information (such as a reply channel header) from the original outbound message, + you may need to retain a copy of the original outbound message (perhaps + by using a publish-subscribe channel) and use an aggregator to recombine + the necessary data. + + + For either of the previous two paragraphs, if the payload has no natural + correlation data, you may need to provide a transformer upstream of the + outbound channel adapter to enhance the payload with such data. Such a + transformer may transform the original payload to a new object containing + both the original payload and some subset of the message headers. Of course, + live objects (such as reply channels) from the headers can not be + included in the transformed payload. + + + If such a strategy is chosen you will need to ensure the connection factory + has an appropriate serializer/deserializer pair to handle such a payload, + such as the DefaultSerializer/Deserializer which use java + serialization, or a custom serializer and deserializer. + The ByteArray*Serializer options + mentioned in Connection Factories, + including the default ByteArrayCrLfSerializer, + do not support such payloads, + unless the transformed payload is a String or + byte[], + +
+
IP Configuration Attributes @@ -611,7 +703,7 @@ using nio, specifies the number of tcp fragments that are concurrently reassembled into complete messages. It only applies in this sense if task-executor is not configured. - However, pool-size is also used for the server socket backlog, + However, pool-size is also used for the server socket backlog, regardless of whether an external task executor is used. Defaults to 5. @@ -830,6 +922,70 @@ + + TCP Inbound Channel Adapter Attributes + + + + + + + + Attribute Name + Allowed Values + Attribute Description + + + + + channel + + The channel to which inbound messages will be sent. + + + connection-factory + + If the connection factory has a type 'server', the factory is 'owned' + by this adapter. If it has a type 'client', it is 'owned' by an + outbound channel adapter and this adapter will receive any + incoming messages on the connection created by the + outbound adapter. + + + +
+ + TCP Outbound Channel Adapter Attributes + + + + + + + + Attribute Name + Allowed Values + Attribute Description + + + + + channel + + The channel on which outbound messages arrive. + + + connection-factory + + If the connection factory has a type 'client', the factory is + 'owned' by this adapter. If it has a type 'server', it is 'owned' + by an inbound channel adapter and this adapter will attempt + to correlate messages to the connection on which an + original inbound message was received. + + + +
TCP Inbound Gateway Attributes @@ -846,9 +1002,26 @@ - port + connection-factory - The port on which the gateway listens. + The connection factory must be of type server. + + + request-channel + + The channel to which incoming messages will be sent. + + + reply-channel + + The channel on which reply messages may arrive. Usually replies will + arrive on a temporary reply channel added to the inbound message + header + + + reply-timeout + + The time in milliseconds for which the gateway will wait for a reply. @@ -869,9 +1042,32 @@ - host + connection-factory - The host name or ip address of the destination. + The connection factory must be of type client. + + + request-channel + + The channel on which outgoing messages will arrive. + + + reply-channel + + Optional. The channel to which reply messages may be sent if the + original outbound message did not contain a reply channel header. + + + + reply-timeout + + The time in milliseconds for which the gateway will wait for a reply. + + + request-timeout + + If a single-use connection factory is not being used, The time in milliseconds + for which the gateway will wait to get access to the shared connection. From 41b45d211235a98571c9e60bf9f4e4ef36de54e0 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 10 Nov 2010 15:48:39 -0500 Subject: [PATCH 27/42] INT-1552 IP Doc Polishing --- docs/src/reference/docbook/ip.xml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/src/reference/docbook/ip.xml b/docs/src/reference/docbook/ip.xml index d8a668888b..ebca4d775a 100644 --- a/docs/src/reference/docbook/ip.xml +++ b/docs/src/reference/docbook/ip.xml @@ -470,15 +470,14 @@ The gateways will automatically correlate messages. However, an outbound gateway should only be used for relatively low-volume use. - When the connection - factory is configured for 'single-use="false"', a single shared connection is - used for all message pairs, and only one message can be processed at a time. + When the connection factory is configured for a single shared connection + to be used for all message pairs ('single-use="false"'), + only one message can be processed at a time. A new message will have to wait until the reply to the previous message has been received. - When a connection - factory is configured for 'single-use="true"' connections, the above - restriction does not apply because each message pair is - carried over a separate connection. While this may give higher throughput + When a connection factory is configured for each new message to use a new connection + ('single-use="true"'), the above restriction does not apply. + While this may give higher throughput than a shared connection environment, it comes with the overhead of opening and closing a new connection for each message pair. From e3fa54f8569879d5a4d15e3b086ae2d7bff92b5e Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 16:09:35 -0500 Subject: [PATCH 28/42] INT-1562 created 'client' package --- .../integration/ftp/ClientFactorySupport.java | 98 ------------- .../AbstractFtpClientFactory.java | 4 +- .../{ => client}/DefaultFtpClientFactory.java | 3 +- .../DefaultFtpsClientFactory.java | 4 +- .../ftp/{ => client}/FtpClientFactory.java | 2 +- .../ftp/{ => client}/FtpClientPool.java | 3 +- .../ftp/{ => client}/QueuedFtpClientPool.java | 3 +- ...geSendingConsumerBeanDefinitionParser.java | 3 +- .../FtpMessageSourceBeanDefinitionParser.java | 2 +- ...geSendingConsumerBeanDefinitionParser.java | 2 +- ...FtpsMessageSourceBeanDefinitionParser.java | 2 +- ...tpInboundRemoteFileSystemSynchronizer.java | 6 +- ...eFileSystemSynchronizingMessageSource.java | 2 +- ...SynchronizingMessageSourceFactoryBean.java | 135 ++++++++++-------- ...SynchronizingMessageSourceFactoryBean.java | 41 ++++-- .../outbound/FtpSendingMessageHandler.java | 2 +- .../FtpSendingMessageHandlerFactoryBean.java | 18 ++- .../FtpsSendingMessageHandlerFactoryBean.java | 56 +++++--- 18 files changed, 177 insertions(+), 209 deletions(-) delete mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/ClientFactorySupport.java rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => client}/AbstractFtpClientFactory.java (98%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => client}/DefaultFtpClientFactory.java (94%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => client}/DefaultFtpsClientFactory.java (97%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => client}/FtpClientFactory.java (95%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => client}/FtpClientPool.java (97%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => client}/QueuedFtpClientPool.java (98%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => inbound}/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java (57%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => inbound}/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java (68%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => outbound}/FtpSendingMessageHandlerFactoryBean.java (78%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{ => outbound}/FtpsSendingMessageHandlerFactoryBean.java (59%) diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/ClientFactorySupport.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/ClientFactorySupport.java deleted file mode 100644 index 3f7302aada..0000000000 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/ClientFactorySupport.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.ftp; - -import org.springframework.util.StringUtils; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.TrustManager; - -/** - * Factors out the client factory creation. - * - * @author Josh Long - */ -public class ClientFactorySupport { - - public static DefaultFtpsClientFactory ftpsClientFactory(String host, int port, String remoteWorkingDirectory, String user, String password, int fileType, - int clientMode, String prot, String protocol, String authValue, - Boolean implicit, TrustManager trustManager, KeyManager keyManager, - Boolean sessionCreation, Boolean useClientMode, - Boolean wantsClientAuth, Boolean needClientAuth, String[] cipherSuites) { - DefaultFtpsClientFactory defaultFtpClientFactory = new DefaultFtpsClientFactory(); - defaultFtpClientFactory.setHost(host); - defaultFtpClientFactory.setPassword(password); - defaultFtpClientFactory.setPort(port); - defaultFtpClientFactory.setRemoteWorkingDirectory(remoteWorkingDirectory); - defaultFtpClientFactory.setUsername(user); - defaultFtpClientFactory.setFileType(fileType); - defaultFtpClientFactory.setClientMode(clientMode); - if (cipherSuites != null) { - defaultFtpClientFactory.setCipherSuites(cipherSuites); - } - if (StringUtils.hasText(prot)) { - defaultFtpClientFactory.setProt(prot); - } - if (StringUtils.hasText(protocol)) { - defaultFtpClientFactory.setProtocol(protocol); - } - if (StringUtils.hasText(authValue)) { - defaultFtpClientFactory.setAuthValue(authValue); - } - if (null != implicit) { - defaultFtpClientFactory.setImplicit(implicit); - } - if (trustManager != null) { - defaultFtpClientFactory.setTrustManager(trustManager); - } - if (keyManager != null) { - defaultFtpClientFactory.setKeyManager(keyManager); - } - if (needClientAuth != null) { - defaultFtpClientFactory.setNeedClientAuth(needClientAuth); - } - if (wantsClientAuth != null) { - defaultFtpClientFactory.setWantsClientAuth(wantsClientAuth); - } - if (sessionCreation != null) { - defaultFtpClientFactory.setSessionCreation(sessionCreation); - } - if (useClientMode != null) { - defaultFtpClientFactory.setUseClientMode(useClientMode); - } - return defaultFtpClientFactory; - } - - public static DefaultFtpClientFactory ftpClientFactory(String host, - int port, - String remoteWorkingDirectory, - String user, - String password, - int clientMode, - int fileType) { - DefaultFtpClientFactory defaultFtpClientFactory = new DefaultFtpClientFactory(); - defaultFtpClientFactory.setHost(host); - defaultFtpClientFactory.setPassword(password); - defaultFtpClientFactory.setPort(port); - defaultFtpClientFactory.setRemoteWorkingDirectory(remoteWorkingDirectory); - defaultFtpClientFactory.setUsername(user); - defaultFtpClientFactory.setClientMode(clientMode); - defaultFtpClientFactory.setFileType(fileType); - return defaultFtpClientFactory; - } - -} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpClientFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/AbstractFtpClientFactory.java similarity index 98% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpClientFactory.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/AbstractFtpClientFactory.java index 6fe73191fa..d82dc30cf1 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpClientFactory.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/AbstractFtpClientFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.client; import java.io.IOException; import java.net.SocketException; @@ -31,7 +31,7 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * base class for the other {@link org.springframework.integration.ftp.FtpClientFactory} implementations. + * base class for the other {@link org.springframework.integration.ftp.client.FtpClientFactory} implementations. * Most of this came out of the {@link DefaultFtpClientFactory} and was refactored into a base class * * @author Iwein Fuld diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpClientFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/DefaultFtpClientFactory.java similarity index 94% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpClientFactory.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/DefaultFtpClientFactory.java index 57b408c252..bf4b03cc76 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpClientFactory.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/DefaultFtpClientFactory.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.client; import org.apache.commons.net.ftp.FTPClient; + /** * Default implementation of FtpClientFactory. * diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpsClientFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/DefaultFtpsClientFactory.java similarity index 97% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpsClientFactory.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/DefaultFtpsClientFactory.java index 4156d195ea..8c50a6c7d5 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpsClientFactory.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/DefaultFtpsClientFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.client; import java.io.IOException; import java.net.SocketException; @@ -28,7 +28,7 @@ import org.apache.commons.net.ftp.FTPSClient; import org.springframework.util.StringUtils; /** - * provides a working FTPS implementation. Based heavily on {@link org.springframework.integration.ftp.DefaultFtpClientFactory} + * provides a working FTPS implementation. Based heavily on {@link org.springframework.integration.ftp.client.DefaultFtpClientFactory} * * @author Josh Long * @author Iwein Fuld diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/FtpClientFactory.java similarity index 95% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientFactory.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/FtpClientFactory.java index dbda4859f6..a0f32f1b1b 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientFactory.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/FtpClientFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.client; import org.apache.commons.net.ftp.FTPClient; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/FtpClientPool.java similarity index 97% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/FtpClientPool.java index ebf0c816f8..adf4471dd4 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/FtpClientPool.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.client; import org.apache.commons.net.ftp.FTPClient; + /** * A pool of {@link FTPClient} instances. The pool can be used to control the * number of open FTP connections and reuse these connections efficiently. diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/QueuedFtpClientPool.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/QueuedFtpClientPool.java similarity index 98% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/QueuedFtpClientPool.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/QueuedFtpClientPool.java index 4b165c7774..92a26044f2 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/QueuedFtpClientPool.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/client/QueuedFtpClientPool.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.client; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.net.ftp.FTPClient; + import org.springframework.util.Assert; import java.io.IOException; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java index 35eeab87e8..d10c3ca3f8 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java @@ -21,7 +21,8 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.ftp.FtpSendingMessageHandlerFactoryBean; +import org.springframework.integration.ftp.outbound.FtpSendingMessageHandlerFactoryBean; + import org.w3c.dom.Element; /** diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java index 734cc8ccfc..62f092fdd3 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java @@ -29,7 +29,7 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.ftp.FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean; +import org.springframework.integration.ftp.inbound.FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean; /** * Parser for the FTP inbound-channel-adapter. diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java index 5a16cd5546..f4d1783583 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java @@ -23,7 +23,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.ftp.FtpsSendingMessageHandlerFactoryBean; +import org.springframework.integration.ftp.outbound.FtpsSendingMessageHandlerFactoryBean; /** * Parser for the FTPS outbound-channel-adapter diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java index 1e41895911..3b085b6795 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java @@ -27,7 +27,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.ftp.FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean; +import org.springframework.integration.ftp.inbound.FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean; /** * Parser for the FTPS inbound-channel-adapter diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizer.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizer.java index 4c5f098c93..e6f11290f2 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizer.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizer.java @@ -22,7 +22,7 @@ import org.springframework.core.io.Resource; import org.springframework.integration.MessagingException; import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSychronizer; import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; -import org.springframework.integration.ftp.FtpClientPool; +import org.springframework.integration.ftp.client.FtpClientPool; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.support.PeriodicTrigger; import org.springframework.util.Assert; @@ -52,9 +52,9 @@ public class FtpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemot } /** - * The {@link org.springframework.integration.ftp.FtpClientPool} that holds references to {@link org.apache.commons.net.ftp.FTPClient} instances + * The {@link org.springframework.integration.ftp.client.FtpClientPool} that holds references to {@link org.apache.commons.net.ftp.FTPClient} instances * - * @param clientPool the {@link org.springframework.integration.ftp.FtpClientPool} + * @param clientPool the {@link org.springframework.integration.ftp.client.FtpClientPool} */ public void setClientPool(FtpClientPool clientPool) { this.clientPool = clientPool; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizingMessageSource.java index b6b22cc113..7503ad1a2a 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizingMessageSource.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizingMessageSource.java @@ -19,7 +19,7 @@ package org.springframework.integration.ftp.inbound; import org.apache.commons.net.ftp.FTPFile; import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; -import org.springframework.integration.ftp.FtpClientPool; +import org.springframework.integration.ftp.client.FtpClientPool; /** * A {@link org.springframework.integration.core.MessageSource} implementation for FTP. diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java similarity index 57% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java index 59cdbcd64c..84dc827915 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.inbound; import java.io.File; @@ -30,9 +30,10 @@ import org.springframework.core.io.ResourceEditor; import org.springframework.core.io.ResourceLoader; import org.springframework.integration.file.filters.CompositeFileListFilter; import org.springframework.integration.file.filters.FileListFilter; +import org.springframework.integration.ftp.client.AbstractFtpClientFactory; +import org.springframework.integration.ftp.client.DefaultFtpClientFactory; +import org.springframework.integration.ftp.client.QueuedFtpClientPool; import org.springframework.integration.ftp.filters.FtpPatternMatchingFileListFilter; -import org.springframework.integration.ftp.inbound.FtpInboundRemoteFileSystemSynchronizer; -import org.springframework.integration.ftp.inbound.FtpInboundRemoteFileSystemSynchronizingMessageSource; import org.springframework.util.StringUtils; /** @@ -44,57 +45,45 @@ import org.springframework.util.StringUtils; public class FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends AbstractFactoryBean implements ResourceLoaderAware { - protected volatile String port; + private volatile String autoCreateDirectories; - protected volatile String autoCreateDirectories; + private volatile String filenamePattern; - protected volatile String filenamePattern; + volatile String host; - protected volatile String username; + volatile String port; - protected volatile String password; + volatile String username; - protected volatile String host; + volatile String password; - protected volatile String remoteDirectory; + volatile String remoteDirectory; - protected volatile String localWorkingDirectory; + volatile int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE; - protected volatile ResourceLoader resourceLoader; + volatile int fileType = FTP.BINARY_FILE_TYPE; - protected volatile Resource localDirectoryResource; + volatile String defaultFtpInboundFolderName = "ftpInbound"; - protected volatile FileListFilter filter; + private volatile String localWorkingDirectory; - protected volatile int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE; + private volatile Resource localDirectoryResource; - protected volatile int fileType = FTP.BINARY_FILE_TYPE; + private volatile ResourceLoader resourceLoader; + + private volatile FileListFilter filter; private volatile String autoDeleteRemoteFilesOnSync; - protected String defaultFtpInboundFolderName = "ftpInbound"; - - public void setFileType(int fileType) { - this.fileType = fileType; - } - - public void setAutoDeleteRemoteFilesOnSync(String autoDeleteRemoteFilesOnSync) { - this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync; + public void setHost(String host) { + this.host = host; } public void setPort(String port) { this.port = port; } - public void setAutoCreateDirectories(String autoCreateDirectories) { - this.autoCreateDirectories = autoCreateDirectories; - } - - public void setFilenamePattern(String filenamePattern) { - this.filenamePattern = filenamePattern; - } - public void setUsername(String username) { this.username = username; } @@ -103,8 +92,16 @@ public class FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean this.password = password; } - public void setHost(String host) { - this.host = host; + public void setFileType(int fileType) { + this.fileType = fileType; + } + + public void setAutoCreateDirectories(String autoCreateDirectories) { + this.autoCreateDirectories = autoCreateDirectories; + } + + public void setAutoDeleteRemoteFilesOnSync(String autoDeleteRemoteFilesOnSync) { + this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync; } public void setRemoteDirectory(String remoteDirectory) { @@ -115,12 +112,16 @@ public class FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean this.localWorkingDirectory = localWorkingDirectory; } + public void setClientMode(int clientMode) { + this.clientMode = clientMode; + } + public void setFilter(FileListFilter filter) { this.filter = filter; } - public void setClientMode(int clientMode) { - this.clientMode = clientMode; + public void setFilenamePattern(String filenamePattern) { + this.filenamePattern = filenamePattern; } public void setResourceLoader(ResourceLoader resourceLoader) { @@ -132,54 +133,62 @@ public class FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean return FtpInboundRemoteFileSystemSynchronizingMessageSource.class; } - private Resource fromText(String path) { + private Resource resolveResource(String path) { ResourceEditor resourceEditor = new ResourceEditor(this.resourceLoader); resourceEditor.setAsText(path); return (Resource) resourceEditor.getValue(); } protected AbstractFtpClientFactory defaultClientFactory() throws Exception { - return ClientFactorySupport.ftpClientFactory(this.host, - Integer.parseInt(this.port), this.remoteDirectory, this.username, - this.password, this.clientMode, this.fileType); + DefaultFtpClientFactory factory = new DefaultFtpClientFactory(); + factory.setHost(this.host); + if (StringUtils.hasText(this.port)) { + factory.setPort(Integer.parseInt(this.port)); + } + factory.setUsername(this.username); + factory.setPassword(this.password); + factory.setRemoteWorkingDirectory(this.remoteDirectory); + factory.setClientMode(this.clientMode); + factory.setFileType(this.fileType); + return factory; } @Override protected FtpInboundRemoteFileSystemSynchronizingMessageSource createInstance() throws Exception { boolean autoCreatDirs = Boolean.parseBoolean(this.autoCreateDirectories); boolean ackRemoteDir = Boolean.parseBoolean(this.autoDeleteRemoteFilesOnSync); - FtpInboundRemoteFileSystemSynchronizingMessageSource ftpRemoteFileSystemSynchronizingMessageSource = + FtpInboundRemoteFileSystemSynchronizingMessageSource messageSource = new FtpInboundRemoteFileSystemSynchronizingMessageSource(); - ftpRemoteFileSystemSynchronizingMessageSource.setAutoCreateDirectories(autoCreatDirs); + messageSource.setAutoCreateDirectories(autoCreatDirs); if (!StringUtils.hasText(this.localWorkingDirectory)) { - File tmp = new File(SystemUtils.getJavaIoTmpDir(), defaultFtpInboundFolderName); + File tmp = new File(SystemUtils.getJavaIoTmpDir(), this.defaultFtpInboundFolderName); this.localWorkingDirectory = "file://" + tmp.getAbsolutePath(); } - this.localDirectoryResource = this.fromText(this.localWorkingDirectory); - CompositeFileListFilter compositeFtpFileListFilter = new CompositeFileListFilter(); + this.localDirectoryResource = this.resolveResource(this.localWorkingDirectory); + CompositeFileListFilter compositeFilter = new CompositeFileListFilter(); if (StringUtils.hasText(this.filenamePattern)) { FtpPatternMatchingFileListFilter ftpFilePatternMatchingFileListFilter = - new FtpPatternMatchingFileListFilter(filenamePattern); - compositeFtpFileListFilter.addFilter(ftpFilePatternMatchingFileListFilter); + new FtpPatternMatchingFileListFilter(this.filenamePattern); + compositeFilter.addFilter(ftpFilePatternMatchingFileListFilter); } if (this.filter != null) { - compositeFtpFileListFilter.addFilter(this.filter); + compositeFilter.addFilter(this.filter); } - QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15, defaultClientFactory()); - FtpInboundRemoteFileSystemSynchronizer ftpRemoteFileSystemSynchronizer = new FtpInboundRemoteFileSystemSynchronizer(); - ftpRemoteFileSystemSynchronizer.setClientPool(queuedFtpClientPool); - ftpRemoteFileSystemSynchronizer.setLocalDirectory(this.localDirectoryResource); - ftpRemoteFileSystemSynchronizer.setShouldDeleteSourceFile(ackRemoteDir); - ftpRemoteFileSystemSynchronizer.setFilter(compositeFtpFileListFilter); - ftpRemoteFileSystemSynchronizingMessageSource.setRemotePredicate(compositeFtpFileListFilter); - ftpRemoteFileSystemSynchronizingMessageSource.setSynchronizer(ftpRemoteFileSystemSynchronizer); - ftpRemoteFileSystemSynchronizingMessageSource.setClientPool(queuedFtpClientPool); - ftpRemoteFileSystemSynchronizingMessageSource.setLocalDirectory(this.localDirectoryResource); - ftpRemoteFileSystemSynchronizingMessageSource.setBeanFactory(this.getBeanFactory()); - ftpRemoteFileSystemSynchronizingMessageSource.setAutoStartup(true); - ftpRemoteFileSystemSynchronizingMessageSource.afterPropertiesSet(); - ftpRemoteFileSystemSynchronizingMessageSource.start(); - return ftpRemoteFileSystemSynchronizingMessageSource; + QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15, this.defaultClientFactory()); + FtpInboundRemoteFileSystemSynchronizer synchronizer = new FtpInboundRemoteFileSystemSynchronizer(); + synchronizer.setClientPool(queuedFtpClientPool); + synchronizer.setLocalDirectory(this.localDirectoryResource); + synchronizer.setShouldDeleteSourceFile(ackRemoteDir); + synchronizer.setFilter(compositeFilter); + messageSource.setRemotePredicate(compositeFilter); + messageSource.setSynchronizer(synchronizer); + messageSource.setClientPool(queuedFtpClientPool); + messageSource.setLocalDirectory(this.localDirectoryResource); + messageSource.setBeanFactory(this.getBeanFactory()); + messageSource.setAutoStartup(true); + messageSource.afterPropertiesSet(); + messageSource.start(); + return messageSource; } } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java similarity index 68% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java index 846f1e2227..762f2bbdad 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -14,10 +14,14 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.inbound; import org.apache.commons.net.ftp.FTPClient; +import org.springframework.integration.ftp.client.AbstractFtpClientFactory; +import org.springframework.integration.ftp.client.DefaultFtpsClientFactory; +import org.springframework.util.StringUtils; + import javax.net.ssl.KeyManager; import javax.net.ssl.TrustManager; @@ -111,14 +115,33 @@ public class FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean extends F } protected AbstractFtpClientFactory defaultClientFactory() throws Exception { - DefaultFtpsClientFactory factory = ClientFactorySupport.ftpsClientFactory(this.host, - Integer.parseInt(this.port), this.remoteDirectory, - this.username, this.password, this.fileType, this.clientMode, - this.prot, this.protocol, this.authValue, this.implicit, - this.trustManager, this.keyManager, this.sessionCreation, - this.useClientMode, this.wantsClientAuth, this.needClientAuth, - this.cipherSuites); - + DefaultFtpsClientFactory factory = new DefaultFtpsClientFactory(); + factory.setHost(this.host); + if (StringUtils.hasText(this.port)) { + factory.setPort(Integer.parseInt(this.port)); + } + factory.setUsername(this.username); + factory.setPassword(this.password); + factory.setRemoteWorkingDirectory(this.remoteDirectory); + factory.setFileType(this.fileType); + factory.setClientMode(this.clientMode); + factory.setCipherSuites(this.cipherSuites); + factory.setAuthValue(this.authValue); + factory.setTrustManager(this.trustManager); + factory.setKeyManager(this.keyManager); + factory.setNeedClientAuth(this.needClientAuth); + factory.setWantsClientAuth(this.wantsClientAuth); + factory.setSessionCreation(this.sessionCreation); + factory.setUseClientMode(this.useClientMode); + if (StringUtils.hasText(this.prot)) { + factory.setProt(this.prot); + } + if (StringUtils.hasText(this.protocol)) { + factory.setProtocol(this.protocol); + } + if (this.implicit != null) { + factory.setImplicit(this.implicit); + } return factory; } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandler.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandler.java index d1109af059..90072ae61a 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandler.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandler.java @@ -33,7 +33,7 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageDeliveryException; import org.springframework.integration.file.DefaultFileNameGenerator; import org.springframework.integration.file.FileNameGenerator; -import org.springframework.integration.ftp.FtpClientPool; +import org.springframework.integration.ftp.client.FtpClientPool; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerFactoryBean.java similarity index 78% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerFactoryBean.java index 1b15fc131e..6a8ff07afa 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerFactoryBean.java @@ -14,12 +14,14 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.outbound; import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.integration.file.FileNameGenerator; -import org.springframework.integration.ftp.outbound.FtpSendingMessageHandler; +import org.springframework.integration.ftp.client.AbstractFtpClientFactory; +import org.springframework.integration.ftp.client.DefaultFtpClientFactory; +import org.springframework.integration.ftp.client.QueuedFtpClientPool; /** * A factory bean implementation that handles constructing an outbound FTP @@ -91,9 +93,15 @@ public class FtpSendingMessageHandlerFactoryBean extends AbstractFactoryBean clientFactory() { - return ClientFactorySupport.ftpClientFactory(this.host, this.port, - this.remoteDirectory, this.username, this.password, - this.clientMode, this.fileType); + DefaultFtpClientFactory defaultFtpClientFactory = new DefaultFtpClientFactory(); + defaultFtpClientFactory.setHost(this.host); + defaultFtpClientFactory.setPort(this.port); + defaultFtpClientFactory.setUsername(this.username); + defaultFtpClientFactory.setPassword(this.password); + defaultFtpClientFactory.setRemoteWorkingDirectory(this.remoteDirectory); + defaultFtpClientFactory.setClientMode(this.clientMode); + defaultFtpClientFactory.setFileType(this.fileType); + return defaultFtpClientFactory; } @Override diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpsSendingMessageHandlerFactoryBean.java similarity index 59% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpsSendingMessageHandlerFactoryBean.java index 38d2944482..1f76b51456 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpsSendingMessageHandlerFactoryBean.java @@ -14,11 +14,15 @@ * limitations under the License. */ -package org.springframework.integration.ftp; +package org.springframework.integration.ftp.outbound; import javax.net.ssl.KeyManager; import javax.net.ssl.TrustManager; +import org.springframework.integration.ftp.client.AbstractFtpClientFactory; +import org.springframework.integration.ftp.client.DefaultFtpsClientFactory; +import org.springframework.util.StringUtils; + /** * Sends files to a remote FTPS file system. Based heavily on {@link org.springframework.integration.ftp.outbound.FtpSendingMessageHandler} * @@ -28,7 +32,7 @@ import javax.net.ssl.TrustManager; public class FtpsSendingMessageHandlerFactoryBean extends FtpSendingMessageHandlerFactoryBean { /** - * Sets whether the connection is implicit. Local testing reveals this to be a good choice. + * Sets whether the connection is implicit. Default is FALSE. */ protected volatile Boolean implicit = Boolean.FALSE; @@ -42,23 +46,23 @@ public class FtpsSendingMessageHandlerFactoryBean extends FtpSendingMessageHandl */ protected volatile String prot; - private KeyManager keyManager; + private volatile KeyManager keyManager; - private TrustManager trustManager; + private volatile TrustManager trustManager; protected volatile String authValue; - private Boolean sessionCreation; + private volatile Boolean sessionCreation; - private Boolean useClientMode; + private volatile Boolean useClientMode; - private Boolean needClientAuth; + private volatile Boolean needClientAuth; - private Boolean wantsClientAuth; + private volatile Boolean wantsClientAuth; - private String[] cipherSuites; + private volatile String[] cipherSuites; - private int fileType; + private volatile int fileType; public void setImplicit(Boolean implicit) { @@ -111,13 +115,31 @@ public class FtpsSendingMessageHandlerFactoryBean extends FtpSendingMessageHandl @Override protected AbstractFtpClientFactory clientFactory() { - DefaultFtpsClientFactory factory = ClientFactorySupport.ftpsClientFactory(this.host, - (this.port), this.remoteDirectory, this.username, - this.password, this.fileType, this.clientMode, this.prot, - this.protocol, this.authValue, this.implicit, - this.trustManager, this.keyManager, this.sessionCreation, - this.useClientMode, this.wantsClientAuth, this.needClientAuth, - this.cipherSuites); + DefaultFtpsClientFactory factory = new DefaultFtpsClientFactory(); + factory.setHost(this.host); + factory.setPort(this.port); + factory.setUsername(this.username); + factory.setPassword(this.password); + factory.setRemoteWorkingDirectory(this.remoteDirectory); + factory.setFileType(this.fileType); + factory.setClientMode(this.clientMode); + factory.setCipherSuites(this.cipherSuites); + factory.setAuthValue(this.authValue); + factory.setTrustManager(this.trustManager); + factory.setKeyManager(this.keyManager); + factory.setNeedClientAuth(this.needClientAuth); + factory.setWantsClientAuth(this.wantsClientAuth); + factory.setSessionCreation(this.sessionCreation); + factory.setUseClientMode(this.useClientMode); + if (StringUtils.hasText(this.prot)) { + factory.setProt(this.prot); + } + if (StringUtils.hasText(this.protocol)) { + factory.setProtocol(this.protocol); + } + if (this.implicit != null) { + factory.setImplicit(this.implicit); + } return factory; } From 1b5be152b22a65555e5a53a488f359f8ac3f505b Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 10 Nov 2010 16:13:18 -0500 Subject: [PATCH 29/42] INT-1598 added documentation for Claim Check --- docs/src/reference/docbook/claim-check.xml | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/docs/src/reference/docbook/claim-check.xml b/docs/src/reference/docbook/claim-check.xml index 67d6da2174..026e2a38fe 100644 --- a/docs/src/reference/docbook/claim-check.xml +++ b/docs/src/reference/docbook/claim-check.xml @@ -5,7 +5,81 @@
Introduction + + In the earlier sections we've covered several Content Enricher type components that helps you deal with situations where a + message is missing a piece of data. We also discussed Content Filtering which lets you remove uninteresting data items from a message. + However there are times when we want to remove some data temporarily. For example; In a distributed system we may receive a + Message with a very large payload. Some intermittent message processing steps may not need access to this payload and some may only + need to access parts of the payload so carrying the large Message through each processing step may cause performance degradation + and makes debugging harder. + + + Claim Check pattern describes mechanism that allows you + to store data in a well known place while only maintaining a pointer (Claim Check) to where that data is and pass such + pointer around as a payload of a new Message allowing any component within the message flow to get the actual data as soon as + it needs it. This approach is very similar to the Certified Mail process where you'll get Claim Check in your mailbox and + would have to go to the Post Office to claim your actual package or mail. + + + + Spring Integration provides two types of Claim Check transformers - Incoming Claim Check Transformer and + Outgoing Claim Check Transformer as well as convenient namespace-based mechanism to configure them. + + +
+ Incoming Claim Check Transformer + + + Incoming Claim Check Transformer - will transform incoming Message by storing it in the Message Store + identified by message-store attribute. + + ]]> + + In the above configuration the Message that is received on the input-channel will be persisted to the + Message Store identified with message-store attribute and indexed with generated ID. That ID is the Claim Check for that Message. + This Claim Check will also become the payload of the new (transformed) Message that will be sent to the output-channel. + + + + Now, lets assume that at some point you do need access to the actual Message. You can of course access the Message Store + manually and get the contents of the Message or you can use the same approach as before except now you will be transforming + the Claim Check to the actual Message by using Outgoing Claim Check Transformer. + +
+ +
+ Outgoing Claim Check Transformer + + + Incoming Claim Check Transformer allows you to transform a Message from the Message with just a Claim Check + to the Message with the original content. + ]]> + + In the above configuration the Message that is received on the input-channel has a Claim Check as a payload + and Outgoing Claim Check Transformer will transform it into an original Message by simply querying the + Message store for a Message identified by a Claim Check provided and sending the new Message to the output-channel. + +
+ + Although we rarely care about the protocol of the claim checks as long as they work, but it is still worth knowing that current + implementation of the actual Claim Check (the pointer) in Spring Integration is UUID to ensure uniqueness. + + + + A word on Message Store + + + org.springframework.integration.store.MessageStore is a strategy interface for storing and retrieving messages. + Spring Integration provides two convenient implementations of it. SimpleMessageStore - In memory Map-based + implementation (default, good for testing) and JdbcMessageStore - Implementation of MessageStore + that uses relational database via JDBC. +
From e14bbbe21d7ca36f8b40862e39d461f3a7fb88ea Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 16:24:41 -0500 Subject: [PATCH 30/42] INT-1562 restructured packages: inbound, outbound, session --- .../config/SftpMessageSendingConsumerFactoryBean.java | 6 +++--- ...ileSystemSynchronizingMessageSourceFactoryBean.java | 10 +++++----- .../integration/sftp/config/SftpSessionUtils.java | 2 +- .../SftpPatternMatchingFileListFilter.java | 2 +- .../SftpInboundRemoteFileSystemSynchronizer.java | 8 ++++---- ...oundRemoteFileSystemSynchronizingMessageSource.java | 6 +++--- .../sftp/{ => outbound}/SftpSendingMessageHandler.java | 5 ++++- .../sftp/{ => session}/QueuedSftpSessionPool.java | 2 +- .../integration/sftp/{ => session}/SftpSession.java | 2 +- .../sftp/{ => session}/SftpSessionFactory.java | 2 +- .../sftp/{ => session}/SftpSessionPool.java | 2 +- .../SftpInboundRemoteFileSystemSynchronizerTests.java | 3 ++- 12 files changed, 27 insertions(+), 23 deletions(-) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{ => filters}/SftpPatternMatchingFileListFilter.java (95%) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{impl => inbound}/SftpInboundRemoteFileSystemSynchronizer.java (94%) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{impl => inbound}/SftpInboundRemoteFileSystemSynchronizingMessageSource.java (94%) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{ => outbound}/SftpSendingMessageHandler.java (96%) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{ => session}/QueuedSftpSessionPool.java (98%) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{ => session}/SftpSession.java (99%) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{ => session}/SftpSessionFactory.java (98%) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{ => session}/SftpSessionPool.java (95%) diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java index 9092020559..a28c45bdb2 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java @@ -17,9 +17,9 @@ package org.springframework.integration.sftp.config; import org.springframework.beans.factory.FactoryBean; -import org.springframework.integration.sftp.QueuedSftpSessionPool; -import org.springframework.integration.sftp.SftpSendingMessageHandler; -import org.springframework.integration.sftp.SftpSessionFactory; +import org.springframework.integration.sftp.outbound.SftpSendingMessageHandler; +import org.springframework.integration.sftp.session.QueuedSftpSessionPool; +import org.springframework.integration.sftp.session.SftpSessionFactory; /** * Supports the construction of a MessagHandler that knows how to take inbound File objects diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java index 5aa5e9142d..d5234eb575 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -27,11 +27,11 @@ import org.springframework.core.io.ResourceEditor; import org.springframework.core.io.ResourceLoader; import org.springframework.integration.file.filters.CompositeFileListFilter; import org.springframework.integration.file.filters.FileListFilter; -import org.springframework.integration.sftp.QueuedSftpSessionPool; -import org.springframework.integration.sftp.SftpPatternMatchingFileListFilter; -import org.springframework.integration.sftp.SftpSessionFactory; -import org.springframework.integration.sftp.impl.SftpInboundRemoteFileSystemSynchronizer; -import org.springframework.integration.sftp.impl.SftpInboundRemoteFileSystemSynchronizingMessageSource; +import org.springframework.integration.sftp.filters.SftpPatternMatchingFileListFilter; +import org.springframework.integration.sftp.inbound.SftpInboundRemoteFileSystemSynchronizer; +import org.springframework.integration.sftp.inbound.SftpInboundRemoteFileSystemSynchronizingMessageSource; +import org.springframework.integration.sftp.session.QueuedSftpSessionPool; +import org.springframework.integration.sftp.session.SftpSessionFactory; import org.springframework.util.StringUtils; import com.jcraft.jsch.ChannelSftp; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpSessionUtils.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpSessionUtils.java index d9aaeae580..eaa527bd78 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpSessionUtils.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpSessionUtils.java @@ -16,7 +16,7 @@ package org.springframework.integration.sftp.config; -import org.springframework.integration.sftp.SftpSessionFactory; +import org.springframework.integration.sftp.session.SftpSessionFactory; /** * Utility methods for SFTP Session management. diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpPatternMatchingFileListFilter.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/filters/SftpPatternMatchingFileListFilter.java similarity index 95% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpPatternMatchingFileListFilter.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/filters/SftpPatternMatchingFileListFilter.java index 41eac3a7ff..1ed85366aa 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpPatternMatchingFileListFilter.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/filters/SftpPatternMatchingFileListFilter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.sftp; +package org.springframework.integration.sftp.filters; import java.util.regex.Pattern; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizer.java similarity index 94% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizer.java index 5aaa661441..4c8f77622c 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizer.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.sftp.impl; +package org.springframework.integration.sftp.inbound; import com.jcraft.jsch.ChannelSftp; import org.apache.commons.io.IOUtils; @@ -23,8 +23,8 @@ import org.springframework.core.io.Resource; import org.springframework.integration.MessagingException; import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSychronizer; import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; -import org.springframework.integration.sftp.SftpSession; -import org.springframework.integration.sftp.SftpSessionPool; +import org.springframework.integration.sftp.session.SftpSession; +import org.springframework.integration.sftp.session.SftpSessionPool; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.support.PeriodicTrigger; import org.springframework.util.Assert; @@ -48,7 +48,7 @@ public class SftpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemo private volatile String remotePath; /** - * the pool of {@link org.springframework.integration.sftp.SftpSessionPool} SFTP sessions + * the pool of {@link org.springframework.integration.sftp.session.SftpSessionPool} SFTP sessions */ private volatile SftpSessionPool clientPool; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizingMessageSource.java similarity index 94% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizingMessageSource.java index 180f5ba07f..cc39dbd0f5 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizingMessageSource.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizingMessageSource.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.integration.sftp.impl; +package org.springframework.integration.sftp.inbound; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.SftpATTRS; import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSynchronizingMessageSource; -import org.springframework.integration.sftp.SftpSession; -import org.springframework.integration.sftp.SftpSessionPool; +import org.springframework.integration.sftp.session.SftpSession; +import org.springframework.integration.sftp.session.SftpSessionPool; import org.springframework.util.Assert; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSendingMessageHandler.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandler.java similarity index 96% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSendingMessageHandler.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandler.java index b369622276..cefcb5ae1c 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSendingMessageHandler.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.sftp; +package org.springframework.integration.sftp.outbound; import java.io.File; import java.io.FileInputStream; @@ -38,6 +38,9 @@ import org.springframework.integration.MessagingException; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.file.DefaultFileNameGenerator; import org.springframework.integration.file.FileNameGenerator; +import org.springframework.integration.sftp.SftpHeaders; +import org.springframework.integration.sftp.session.SftpSession; +import org.springframework.integration.sftp.session.SftpSessionPool; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/QueuedSftpSessionPool.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/QueuedSftpSessionPool.java similarity index 98% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/QueuedSftpSessionPool.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/QueuedSftpSessionPool.java index d22d8ca7c5..ec4664f77c 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/QueuedSftpSessionPool.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/QueuedSftpSessionPool.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.sftp; +package org.springframework.integration.sftp.session; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSession.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java similarity index 99% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSession.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java index 4a4b8fecec..d006539ff4 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSession.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.sftp; +package org.springframework.integration.sftp.session; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSch; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSessionFactory.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactory.java similarity index 98% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSessionFactory.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactory.java index e990e9b009..f4c7769dc2 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSessionFactory.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.sftp; +package org.springframework.integration.sftp.session; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSessionPool.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionPool.java similarity index 95% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSessionPool.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionPool.java index 0767329f46..fc5f6abddf 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpSessionPool.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionPool.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.sftp; +package org.springframework.integration.sftp.session; /** * Holds instances of {@link SftpSession} since they are stateful diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizerTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizerTests.java index 378f3fa6b2..7b9d659c71 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizerTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/impl/SftpInboundRemoteFileSystemSynchronizerTests.java @@ -26,7 +26,8 @@ import org.junit.Test; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.integration.file.synchronization.AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy; -import org.springframework.integration.sftp.SftpSession; +import org.springframework.integration.sftp.inbound.SftpInboundRemoteFileSystemSynchronizer; +import org.springframework.integration.sftp.session.SftpSession; import org.springframework.util.ReflectionUtils; import com.jcraft.jsch.ChannelSftp; From fbaeead0a77dcaf0129c169de4c830458272b5fa Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 17:07:35 -0500 Subject: [PATCH 31/42] INT-1562 removed the sftp remote directory header, but added support for an expression on the outbound adapter --- .../integration/sftp/SftpHeaders.java | 27 ------------- ...SftpMessageSendingConsumerFactoryBean.java | 32 +++++++++------ .../sftp/config/SftpNamespaceHandler.java | 23 ++++++++++- .../outbound/SftpSendingMessageHandler.java | 39 ++++++------------- .../config/spring-integration-sftp-2.0.xsd | 22 ++--------- 5 files changed, 57 insertions(+), 86 deletions(-) delete mode 100644 spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpHeaders.java diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpHeaders.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpHeaders.java deleted file mode 100644 index 84b176e236..0000000000 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/SftpHeaders.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.sftp; - -/** - * @author Josh Long - * @since 2.0 - */ -public abstract class SftpHeaders { - - public static final String REMOTE_DIRECTORY = "sftp_remoteDirectory"; - -} diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java index a28c45bdb2..52eef55988 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java @@ -17,6 +17,8 @@ package org.springframework.integration.sftp.config; import org.springframework.beans.factory.FactoryBean; +import org.springframework.expression.Expression; +import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.sftp.outbound.SftpSendingMessageHandler; import org.springframework.integration.sftp.session.QueuedSftpSessionPool; import org.springframework.integration.sftp.session.SftpSessionFactory; @@ -37,7 +39,7 @@ public class SftpMessageSendingConsumerFactoryBean implements FactoryBean getObjectType() { diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpNamespaceHandler.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpNamespaceHandler.java index ef05ab94dd..18b0376fc3 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpNamespaceHandler.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpNamespaceHandler.java @@ -26,12 +26,15 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.StringUtils; /** * Provides namespace support for using SFTP. * This is largely based on the FTP support by Iwein Fuld. * * @author Josh Long + * @author Mark Fisher + * @since 2.0 */ public class SftpNamespaceHandler extends AbstractIntegrationNamespaceHandler { @@ -49,9 +52,27 @@ public class SftpNamespaceHandler extends AbstractIntegrationNamespaceHandler { @Override protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SftpMessageSendingConsumerFactoryBean.class.getName()); - for (String p : "auto-create-directories,username,password,host,port,key-file,key-file-password,remote-directory,charset".split(",")) { + for (String p : "auto-create-directories,username,password,host,port,key-file,key-file-password,charset".split(",")) { IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, p); } + String remoteDirectory = element.getAttribute("remote-directory"); + String remoteDirectoryExpression = element.getAttribute("remote-directory-expression"); + boolean hasLiteralRemoteDirectory = StringUtils.hasText(remoteDirectory); + boolean hasRemoteDirectoryExpression = StringUtils.hasText(remoteDirectoryExpression); + if (hasLiteralRemoteDirectory ^ hasRemoteDirectoryExpression) { + if (hasLiteralRemoteDirectory) { + builder.addPropertyValue("remoteDirectory", remoteDirectory); + } + else { + BeanDefinitionBuilder expressionDefBuilder = BeanDefinitionBuilder.genericBeanDefinition( + "org.springframework.integration.config.ExpressionFactoryBean"); + expressionDefBuilder.addConstructorArgValue(remoteDirectoryExpression); + builder.addPropertyValue("remoteDirectoryExpression", expressionDefBuilder.getBeanDefinition()); + } + } + else { + parserContext.getReaderContext().error("exactly one of 'remote-directory' or 'remote-directory-expression' is required", element); + } return builder.getBeanDefinition(); } } diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandler.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandler.java index cefcb5ae1c..d789f88d1a 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandler.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandler.java @@ -31,16 +31,16 @@ import org.apache.commons.lang.SystemUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; +import org.springframework.expression.Expression; import org.springframework.integration.Message; import org.springframework.integration.MessageDeliveryException; -import org.springframework.integration.MessageHeaders; import org.springframework.integration.MessagingException; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.file.DefaultFileNameGenerator; import org.springframework.integration.file.FileNameGenerator; -import org.springframework.integration.sftp.SftpHeaders; import org.springframework.integration.sftp.session.SftpSession; import org.springframework.integration.sftp.session.SftpSessionPool; +import org.springframework.integration.util.AbstractExpressionEvaluator; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; @@ -53,14 +53,14 @@ import com.jcraft.jsch.ChannelSftp; * @author Josh Long * @since 2.0 */ -public class SftpSendingMessageHandler implements MessageHandler, InitializingBean { +public class SftpSendingMessageHandler extends AbstractExpressionEvaluator implements MessageHandler, InitializingBean { private static final String TEMPORARY_FILE_SUFFIX = ".writing"; private volatile SftpSessionPool pool; - private volatile String remoteDirectory; + private volatile Expression remoteDirectoryExpression; private volatile FileNameGenerator fileNameGenerator = new DefaultFileNameGenerator(); @@ -68,8 +68,6 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe private volatile Resource temporaryBufferFolder = new FileSystemResource(SystemUtils.getJavaIoTmpDir()); - private volatile boolean initialized; - private volatile String charset = Charset.defaultCharset().name(); @@ -86,12 +84,8 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe this.fileNameGenerator = fileNameGenerator; } - public void setRemoteDirectory(final String remoteDirectory) { - this.remoteDirectory = remoteDirectory; - } - - public String getRemoteDirectory() { - return this.remoteDirectory; + public void setRemoteDirectoryExpression(Expression remoteDirectoryExpression) { + this.remoteDirectoryExpression = remoteDirectoryExpression; } public void setCharset(String charset) { @@ -101,12 +95,6 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe public void afterPropertiesSet() throws Exception { Assert.notNull(this.pool, "the pool must not be null"); this.temporaryBufferFolderFile = this.temporaryBufferFolder.getFile(); - if (!this.initialized) { - if (StringUtils.isEmpty(this.remoteDirectory)) { - this.remoteDirectory = null; - } - this.initialized = true; - } } private File handleFileMessage(File sourceFile, File tempFile, File resultFile) throws IOException { @@ -182,16 +170,11 @@ public class SftpSendingMessageHandler implements MessageHandler, InitializingBe InputStream fileInputStream = null; try { fileInputStream = new FileInputStream(file); - String baseOfRemotePath = StringUtils.isEmpty(this.remoteDirectory) ? StringUtils.EMPTY : remoteDirectory; // the safe default - String dynRd = null; - MessageHeaders messageHeaders = null; - if (message != null) { - messageHeaders = message.getHeaders(); - if ((messageHeaders != null) && messageHeaders.containsKey(SftpHeaders.REMOTE_DIRECTORY)) { - dynRd = (String) messageHeaders.get(SftpHeaders.REMOTE_DIRECTORY); - if (!StringUtils.isEmpty(dynRd)) { - baseOfRemotePath = dynRd; - } + String baseOfRemotePath = ""; + if (this.remoteDirectoryExpression != null) { + String result = this.evaluateExpression(this.remoteDirectoryExpression, message, String.class); + if (result != null) { + baseOfRemotePath = result; } } if (!StringUtils.defaultString(baseOfRemotePath).endsWith("/")) { diff --git a/spring-integration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-2.0.xsd b/spring-integration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-2.0.xsd index 0e99067556..755603e903 100644 --- a/spring-integration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-2.0.xsd +++ b/spring-integration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-2.0.xsd @@ -33,9 +33,7 @@ @@ -49,16 +47,14 @@ - - + + - - @@ -67,14 +63,13 @@ poller element to determine at what frequency to scan the remote directory. There is support for automatically deleting remote files upon synchornization. This adapter supports two connectivity options: -
  1. Password authentication: using this opton, authentication is done using a username and a password.
  2. -
  3. Key-based authentication: using this option, you may specify a key that will be used to authenticate. If they key itself is encrypted and requires a password, you may specify that, as well.
  4. +
  5. Key-based authentication: using this option, you may specify a key that will be used to authenticate. + If they key itself is encrypted and requires a password, you may specify that, as well.
]]>
@@ -92,7 +87,6 @@ - @@ -102,26 +96,18 @@ - - - - - - -
- From 4b6488273f9da91320ed8518da7e33ef60a04452 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 17:12:20 -0500 Subject: [PATCH 32/42] INT-1562 moved SftpSessionUtils into the 'session' package --- .../sftp/config/SftpMessageSendingConsumerFactoryBean.java | 1 + ...pRemoteFileSystemSynchronizingMessageSourceFactoryBean.java | 1 + .../integration/sftp/{config => session}/SftpSessionUtils.java | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/{config => session}/SftpSessionUtils.java (94%) diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java index 52eef55988..d74e774ed6 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java @@ -22,6 +22,7 @@ import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.sftp.outbound.SftpSendingMessageHandler; import org.springframework.integration.sftp.session.QueuedSftpSessionPool; import org.springframework.integration.sftp.session.SftpSessionFactory; +import org.springframework.integration.sftp.session.SftpSessionUtils; /** * Supports the construction of a MessagHandler that knows how to take inbound File objects diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java index d5234eb575..ae10c64013 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -32,6 +32,7 @@ import org.springframework.integration.sftp.inbound.SftpInboundRemoteFileSystemS import org.springframework.integration.sftp.inbound.SftpInboundRemoteFileSystemSynchronizingMessageSource; import org.springframework.integration.sftp.session.QueuedSftpSessionPool; import org.springframework.integration.sftp.session.SftpSessionFactory; +import org.springframework.integration.sftp.session.SftpSessionUtils; import org.springframework.util.StringUtils; import com.jcraft.jsch.ChannelSftp; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpSessionUtils.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionUtils.java similarity index 94% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpSessionUtils.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionUtils.java index eaa527bd78..2a241da1a0 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpSessionUtils.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionUtils.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package org.springframework.integration.sftp.config; +package org.springframework.integration.sftp.session; -import org.springframework.integration.sftp.session.SftpSessionFactory; /** * Utility methods for SFTP Session management. From da8aa2bf0f8d2a13a7b75c53e71482d94e2e0582 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 17:19:05 -0500 Subject: [PATCH 33/42] INT-1562 moved FactoryBeans into the 'config' package --- .../config/FtpMessageSendingConsumerBeanDefinitionParser.java | 1 - .../ftp/config/FtpMessageSourceBeanDefinitionParser.java | 1 - ...RemoteFileSystemSynchronizingMessageSourceFactoryBean.java | 4 +++- .../FtpSendingMessageHandlerFactoryBean.java | 3 ++- .../FtpsMessageSendingConsumerBeanDefinitionParser.java | 1 - .../ftp/config/FtpsMessageSourceBeanDefinitionParser.java | 1 - ...RemoteFileSystemSynchronizingMessageSourceFactoryBean.java | 2 +- .../FtpsSendingMessageHandlerFactoryBean.java | 2 +- 8 files changed, 7 insertions(+), 8 deletions(-) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{inbound => config}/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java (96%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{outbound => config}/FtpSendingMessageHandlerFactoryBean.java (96%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{inbound => config}/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java (98%) rename spring-integration-ftp/src/main/java/org/springframework/integration/ftp/{outbound => config}/FtpsSendingMessageHandlerFactoryBean.java (98%) diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java index d10c3ca3f8..347fb68d26 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java @@ -21,7 +21,6 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.ftp.outbound.FtpSendingMessageHandlerFactoryBean; import org.w3c.dom.Element; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java index 62f092fdd3..50863b8512 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java @@ -29,7 +29,6 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.ftp.inbound.FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean; /** * Parser for the FTP inbound-channel-adapter. diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java similarity index 96% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java index 84dc827915..870f2aa7b2 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp.inbound; +package org.springframework.integration.ftp.config; import java.io.File; @@ -34,6 +34,8 @@ import org.springframework.integration.ftp.client.AbstractFtpClientFactory; import org.springframework.integration.ftp.client.DefaultFtpClientFactory; import org.springframework.integration.ftp.client.QueuedFtpClientPool; import org.springframework.integration.ftp.filters.FtpPatternMatchingFileListFilter; +import org.springframework.integration.ftp.inbound.FtpInboundRemoteFileSystemSynchronizer; +import org.springframework.integration.ftp.inbound.FtpInboundRemoteFileSystemSynchronizingMessageSource; import org.springframework.util.StringUtils; /** diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpSendingMessageHandlerFactoryBean.java similarity index 96% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpSendingMessageHandlerFactoryBean.java index 6a8ff07afa..a215b61546 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpSendingMessageHandlerFactoryBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp.outbound; +package org.springframework.integration.ftp.config; import org.springframework.beans.factory.config.AbstractFactoryBean; @@ -22,6 +22,7 @@ import org.springframework.integration.file.FileNameGenerator; import org.springframework.integration.ftp.client.AbstractFtpClientFactory; import org.springframework.integration.ftp.client.DefaultFtpClientFactory; import org.springframework.integration.ftp.client.QueuedFtpClientPool; +import org.springframework.integration.ftp.outbound.FtpSendingMessageHandler; /** * A factory bean implementation that handles constructing an outbound FTP diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java index f4d1783583..bd3d432434 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java @@ -23,7 +23,6 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.ftp.outbound.FtpsSendingMessageHandlerFactoryBean; /** * Parser for the FTPS outbound-channel-adapter diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java index 3b085b6795..c15c65bbc1 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java @@ -27,7 +27,6 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; -import org.springframework.integration.ftp.inbound.FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean; /** * Parser for the FTPS inbound-channel-adapter diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java similarity index 98% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java index 762f2bbdad..b931a84cdb 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/inbound/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp.inbound; +package org.springframework.integration.ftp.config; import org.apache.commons.net.ftp.FTPClient; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpsSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsSendingMessageHandlerFactoryBean.java similarity index 98% rename from spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpsSendingMessageHandlerFactoryBean.java rename to spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsSendingMessageHandlerFactoryBean.java index 1f76b51456..d6d5d89477 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpsSendingMessageHandlerFactoryBean.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsSendingMessageHandlerFactoryBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.integration.ftp.outbound; +package org.springframework.integration.ftp.config; import javax.net.ssl.KeyManager; import javax.net.ssl.TrustManager; From 6560444c168d2b6da268a3e931834094314a83a4 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 10 Nov 2010 17:36:42 -0500 Subject: [PATCH 34/42] INT-1562 renamed SftpSessionFactory to SftpSessionFactoryBean since it implements FactoryBean, and refactored it to extend AbstractFactoryBean as well --- ...SftpMessageSendingConsumerFactoryBean.java | 4 +- ...SynchronizingMessageSourceFactoryBean.java | 4 +- .../sftp/session/QueuedSftpSessionPool.java | 6 +- ...ctory.java => SftpSessionFactoryBean.java} | 65 +++++++++---------- .../sftp/session/SftpSessionUtils.java | 4 +- 5 files changed, 41 insertions(+), 42 deletions(-) rename spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/{SftpSessionFactory.java => SftpSessionFactoryBean.java} (78%) diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java index d74e774ed6..6b95ffb049 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpMessageSendingConsumerFactoryBean.java @@ -21,7 +21,7 @@ import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.sftp.outbound.SftpSendingMessageHandler; import org.springframework.integration.sftp.session.QueuedSftpSessionPool; -import org.springframework.integration.sftp.session.SftpSessionFactory; +import org.springframework.integration.sftp.session.SftpSessionFactoryBean; import org.springframework.integration.sftp.session.SftpSessionUtils; /** @@ -87,7 +87,7 @@ public class SftpMessageSendingConsumerFactoryBean implements FactoryBean queue; - private final SftpSessionFactory sftpSessionFactory; + private final SftpSessionFactoryBean sftpSessionFactory; private final int maxPoolSize; - public QueuedSftpSessionPool(SftpSessionFactory factory) { + public QueuedSftpSessionPool(SftpSessionFactoryBean factory) { this(DEFAULT_POOL_SIZE, factory); } - public QueuedSftpSessionPool(int maxPoolSize, SftpSessionFactory sessionFactory) { + public QueuedSftpSessionPool(int maxPoolSize, SftpSessionFactoryBean sessionFactory) { this.sftpSessionFactory = sessionFactory; this.maxPoolSize = maxPoolSize; } diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactory.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactoryBean.java similarity index 78% rename from spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactory.java rename to spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactoryBean.java index f4c7769dc2..308b0324a6 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactory.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionFactoryBean.java @@ -16,8 +16,7 @@ package org.springframework.integration.sftp.session; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -28,34 +27,41 @@ import org.springframework.util.StringUtils; * @author Josh Long * @author Mario Gray */ -public class SftpSessionFactory implements FactoryBean, InitializingBean { +public class SftpSessionFactoryBean extends AbstractFactoryBean { - private volatile String knownHosts; + private volatile String remoteHost; + + private volatile int port = 22; // the default + + private volatile String user; private volatile String password; + private volatile String knownHosts; + private volatile String privateKey; private volatile String privateKeyPassphrase; - private volatile String remoteHost; - private volatile String user; + public void setRemoteHost(String remoteHost) { + this.remoteHost = remoteHost; + } - private volatile int port = 22; // the default + public void setPort(int port) { + this.port = port; + } - - - public void setKnownHosts(String knownHosts) { - this.knownHosts = knownHosts; + public void setUser(String user) { + this.user = user; } public void setPassword(String password) { this.password = password; } - public void setPort(int port) { - this.port = port; + public void setKnownHosts(String knownHosts) { + this.knownHosts = knownHosts; } public void setPrivateKey(String privateKey) { @@ -66,32 +72,25 @@ public class SftpSessionFactory implements FactoryBean, Initializin this.privateKeyPassphrase = privateKeyPassphrase; } - public void setRemoteHost(String remoteHost) { - this.remoteHost = remoteHost; - } - public void setUser(String user) { - this.user = user; - } - - public void afterPropertiesSet() throws Exception { - Assert.hasText(this.remoteHost, "remoteHost must not be empty"); - Assert.hasText(this.user, "user mut not be empty"); - Assert.state(StringUtils.hasText(this.password) || StringUtils.hasText(this.privateKey) || StringUtils.hasText(this.privateKeyPassphrase), - "either a password or a private key and/or a private key passphrase is required"); - Assert.state(this.port >= 0, "port must be a positive number"); - } - - public SftpSession getObject() throws Exception { - return new SftpSession(this.user, this.remoteHost, this.password, this.port, this.knownHosts, null, this.privateKey, this.privateKeyPassphrase); - } - - public Class getObjectType() { + @Override + public Class getObjectType() { return SftpSession.class; } + @Override public boolean isSingleton() { return false; } + @Override + protected SftpSession createInstance() throws Exception { + Assert.hasText(this.remoteHost, "remoteHost must not be empty"); + Assert.hasText(this.user, "user must not be empty"); + Assert.isTrue(this.port >= 0, "port must be a positive number"); + Assert.isTrue(StringUtils.hasText(this.password) || StringUtils.hasText(this.privateKey) || StringUtils.hasText(this.privateKeyPassphrase), + "either a password or a private key and/or a private key passphrase is required"); + return new SftpSession(this.user, this.remoteHost, this.password, this.port, this.knownHosts, null, this.privateKey, this.privateKeyPassphrase); + } + } diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionUtils.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionUtils.java index 2a241da1a0..4dc2e3dfc6 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionUtils.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSessionUtils.java @@ -39,8 +39,8 @@ public abstract class SftpSessionUtils { * commands against a remote SFTP/SSH filesystem * @throws Exception thrown in case of darned near anything */ - public static SftpSessionFactory buildSftpSessionFactory(String host, String pw, String usr, String pvKey, String pvKeyPass, int port) throws Exception { - SftpSessionFactory sftpSessionFactory = new SftpSessionFactory(); + public static SftpSessionFactoryBean buildSftpSessionFactory(String host, String pw, String usr, String pvKey, String pvKeyPass, int port) throws Exception { + SftpSessionFactoryBean sftpSessionFactory = new SftpSessionFactoryBean(); sftpSessionFactory.setPassword(pw); sftpSessionFactory.setPort(port); sftpSessionFactory.setRemoteHost(host); From 374f70fb31bdd4079a7f6e74096327bca346fa09 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 11 Nov 2010 08:03:45 -0500 Subject: [PATCH 35/42] INT-1558 added XMPP documentation --- docs/src/reference/docbook/xmpp.xml | 525 +++++++++------------------- 1 file changed, 164 insertions(+), 361 deletions(-) diff --git a/docs/src/reference/docbook/xmpp.xml b/docs/src/reference/docbook/xmpp.xml index 9576a9fe9c..691df3c57b 100644 --- a/docs/src/reference/docbook/xmpp.xml +++ b/docs/src/reference/docbook/xmpp.xml @@ -7,10 +7,6 @@
Introduction - - Spring Integration provides adapters for sending and receiving both XMPP messages and status changes from other - entries in your roster as well as XMPP. - XMPP describes a way for multiple agents to communicate with each other in a distributed system. The canonical use case is to send and receive instant messages, though XMPP can be, and is, used for far more @@ -19,12 +15,8 @@ as broadcast status changes. - XMPP provides the messaging fabric that underlies some of the biggest Instant Messaging networks in the world, - including Google Talk (GTalk) - - which is also available from within GMail - and Facebook Chat. + including Google Talk (GTalk) - which is also available from within GMail - and Facebook Chat. There are many good open-source XMPP servers available. Two popular implementations are Openfire @@ -33,76 +25,28 @@ ejabberd - . - In XMPP, - rosters - (the roster corresponds to the notion of a "buddy list" in your typical IM client) are used to manage a list of - other agents ("contacts", or "buddies", in an IM client) - in the system, calledroster items. - The roster item contains - at a minimum - the roster item's JID which is its unique ID on the network. - An actor may subscribe to the state changes of another actor in the system. The subscription can be bidirectional, - as well. - The subscription settings determine whose status updates are broadcast, and to whom. - These subscriptions are stored on the XMPP server, and are thus durable. - + Spring integration provides support for XMPP via XMPP adapters which support sending and receiving both XMPP messages and + status changes from other entries in your roster. As many other adapters, XMPP adapters come with a convenient namespace-based + configuration. + To configure XMPP namespace include the following elements into the headers of your XML configuration file: + + + - -
-
- Using The Spring Integration XMPP Namespace - - - Using the Spring Integration XMPP namespace support is simple. - - Its use is like any other module in the Spring framework: import the XML schema, and use it to define elements. - - A prototypical XMPP-based integration might feature the following header. We won't repeat this in subsequent - examples, because it is uninteresting. - - - - - - ... - - -]]> - - - -
- - +
XMPP Connection - To participate in the network, an actor must connect to an XMPP server. Typically this requires - at a minimum - a - user, apassword, ahost, and aport. + Before using inbound or outbound XMPP adapters to participate in the XMPP network actor must establish XMPP connection. This + connection object could be shared by all XMPP adapters connected to a particular account. Typically this requires - at a minimum - + user, password, host. - To create an XMPP connection, you may use the XML namespace. + To create a basic XMPP connection, you can utilize the convenience of the namespace. + subscription-mode="accept_all"/> ]]>
@@ -120,218 +63,62 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd
XMPP Messages
- Inbound Message Adapter + Inbound Message Channel Adapter The Spring Integration adapters support receiving messages from other users in the system. To do this, the - adapter "logs in" as a user on your behalf and receives the messages sent to that user. Those messages are then + Inbound Message Channel Adapter "logs in" as a user on your behalf and receives the messages sent to that user. Those messages are then forwarded to your Spring Integration client. The payload of the inbound Spring Integration message may be of the raw type org.jivesoftware.smack.packet.Message, or of the type - java.lang.String - - which is the type of the raw - Message's - body - property - - depending on whether you specify - extract-payload - on the adapter's configuration or not. - Inbound Messages are typically small and are text-oriented. Messages received using the adapter have - a pretty standard layout, with known headers (all headers have keys defined on - org.springframework.integration.xmpp.XmppHeaders): - - -
- Header Values - - - - - - - - - Header Name - What It Describes - - - - - XmppHeaders.TYPE - The value of the - the - - org.jivesoftware.smack.packet.Message.Type - - enum that describes the inbound message. Possible values are: - normal, - chat, - groupchat, - headline, - error. - - - - - XmppHeaders.CHAT - A reference to the - org.jivesoftware.smack.Chat - class which represents the - threaded conversation containing the message. - - - - - - -
- - - - This adapter requires a reference to an XMPP Connection. You may - use the - xmpp-connection - element to define one. - - An example might look as follows: - - - - - - - - - - - - ]]> - - - ]]> - -]]> - - + java.lang.String if you set extract-payload value attribute to 'true' + when configuring an adapter. + Configuration support for XMPP Inbound Message Channel Adapter is provided via message-inbound-channel-adapter element. + + ]]> + + As you can see amongst the usual attributes this adapter also requires a reference to an XMPP Connection. - In this example, the message is received from the XMPP adapter and passed to a - service-activator - component. Here's the declaration of theservice-activator. - + It is also important to mention that XMPP inbound adapter is an event driven adapter and a LifeCycle object. + When started it will register a PacketListener which will listen for the incoming XMPP Messages forwarding them to the underlying + adapter which will convert them to Spring Integration Messages and send them to the output-channel. It will + unregister PacketListener when it is stopped. -
+ +
- Outbound Message Adapter - - You may also send messages to other users on XMPP using the - outbound-message-channel-adapter - adapter. The is configured like the - - xmpp-message-inbound-channel-adapter. The - adapter takes an - xmpp-connection - reference. - - - Here is a (necessarily) contrived example solution using the outbound adapter. - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - The adapter expects as its input - at a minimum - a payload of type java.lang.String, and - a header value - for - XmppHeaders.CHAT_TO_USER - that specifies to which the user the payload body should be sent to. - To create a message destined for theoutbound-message-channel-adapter, you might use the following - Java code: - - - xmppOutboundMsg = MessageBuilder.withPayload("Hello, world!" ) - .setHeader(XmppHeaders.CHAT_TO_USER, "userhandle") - .build(); -]]> - - - - It's easy enough to use Java to update the XmppHeaders.CHAT_TO_USER header, and this has the advantage of dynamically updating the header at runtime in Java code. - If, however, the target is more static in nature, you can - configure it using the - XMPP enricher support. Here is an example using the enricher. The enricher enriches the Spring Integration - message - to support the header values that the outbound XMPP adapters expect. - - - - - - - -]]> - - + Outbound Message Channel Adapter + + + You may also send messages to other users on XMPP using the Outbound Message Channel Adapter. + Configuration support for XMPP Outbound Message Channel Adapter is provided via message-outbound-channel-adapter element. + + ]]> + + The adapter expects as its input - at a minimum - a payload of type java.lang.String, and a header value + for XmppHeaders.CHAT_TO_USER that specifies to which user the Message should be sent to. To + create a message you might use the following Java code: + + xmppOutboundMsg = MessageBuilder.withPayload("Hello, XMPP!" ) + .setHeader(XmppHeaders.CHAT_TO_USER, "userhandle") + .build();]]> + + Another mechanism of setting such header is by using the XMPP enricher support. Here is an example using the enricher. + + + +]]> +
+ +
XMPP Presence @@ -345,98 +132,114 @@ public class XmppMessageConsumer { If you would like to receive notification, or notify others, of state changes, you can use Spring Integration's "presence" adapters. - - The most important data for these adapters resides in the headers. The header keys are enumerated on - the org.springframework.integration.xmpp.XmppHeaders class. - - The header keys specific to these "presence" adapters start with the token "PRESENCE_". - - Not all headers are available for both inbound and outbound. - - - Header Values - - - - - - - - - Header Name - What It Describes - - - - - - - XmppHeaders.PRESENCE_LANGUAGE - The java.lang.String language in which the message was written. - - - - XmppHeaders.PRESENCE_PRIORITY - - The priority (int) of the message. Arbitrary, but it can be used to help assign relevance to a message which - in turn might be used in its handling. - - - - XmppHeaders.PRESENCE_MODE - - An instance of the enum org.jivesoftware.smack.packet.Presence.Mode that has one of the following values: - chat, available, away, - xa, dnd - - - - XmppHeaders.PRESENCE_TYPE - - An instance of the enum org.jivesoftware.smack.packet.Presence.Type - that has one of the following values: - available, unavailable, subscribe, subscribed, - unsubscribe, unsubscribed, and error. - - - - - XmppHeaders.PRESENCE_STATUS - - A java.lang.String string representing the status of the agent. This corresponds to an agents "away" message. - - - - XmppHeaders.PRESENCE_FROM - - A java.lang.String string representing the handle of the user whose state is being received. - - - - - - - -
-
- Inbound Presence Adapter + + +
+ Inbound Roster Message Channel Adapter - The first adapter supports receiving messages whenever an agent on your roster has updated its - state. Most of the important data comes in through the headers. - + Spring Integration provides Inbound Roster Message Channel Adapter which supports receiving Presence (Roster) + events from other users in the system. To do this, the Inbound Roster Message Channel Adapter "logs in" as a user + on your behalf, registers RosterListener and forwards received roster events as Messages to the channel + identified by the channel attribute. The payload of the Message could either be org.jivesoftware.smack.packet.Presence + object (see http://www.igniterealtime.org/builds/smack/docs/3.1.0/javadoc/org/jivesoftware/smack/packet/Presence.html) or + java.util.Collection<String> representing roster entries. + + + Configuration support for XMPP Inbound Roster Message Channel Adapter is provided via + roster-event-inbound-channel-adapter element. + ]]> + + As you can see amongst the usual attributes this adapter also requires a reference to an XMPP Connection. + It is also important to mention that this adapter is an event driven adapter and a LifeCycle object. + It will register RosterListener when started and will unregister RosterListener + when stopped.
-
- Outbound Presence Adapter - TBD + +
+ Outbound Roster Message Channel Adapter + + + Spring Integration also supports sending Presence (Roster) events to be seen by other users in the network. When you send a Message + to the Outbound Roster Message Channel Adapter it extracts the payload which is expected to be of + type org.jivesoftware.smack.packet.Presence + (see http://www.igniterealtime.org/builds/smack/docs/3.1.0/javadoc/org/jivesoftware/smack/packet/Presence.html) and sends it to + the XMPP Connection, thus advertising your presence events to the rest of the network. + + + Configuration support for XMPP Outbound Roster Message Channel Adapter is provided via + roster-event-outbound-channel-adapter element. + + ]]> + + It can also be a polling consumer (if it receives Messages from the Polling Channel) in which case you would + need to register a Poller. + + + +]]> + + Similar to its Inbound counterpart it requires a reference to an XMPP Connection. +
- - +
+ From a7eddb14e57912ce93a1a11bea0b905c4025e734 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 11 Nov 2010 08:28:30 -0500 Subject: [PATCH 36/42] added more details to the docs on wire-tap sync/async behavior --- docs/src/reference/docbook/channel.xml | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/src/reference/docbook/channel.xml b/docs/src/reference/docbook/channel.xml index 00a5ece6c2..1876792ff9 100644 --- a/docs/src/reference/docbook/channel.xml +++ b/docs/src/reference/docbook/channel.xml @@ -584,6 +584,49 @@ public Message receive(final PollableChannel channel) { ... }]]>true enables logging of all headers in addition to the payload. + + + A little more on Wite Tap + + + One of the common misconception about the wire tap and some time other similar components () + that they are asynchronous in nature. Wire-tap as a component is neither sync nor async. + In fact non of the components in SI are sync or async except for. . . well read on. + + What makes certain parts of the message flow sync or async is the Message Channel + abstraction. That is why from the inception of the framework we always emphasize the need and the value of the Message Channel + and that is why Spring Integration is the only framework at the time of writing where Message Channel + is a "first class citizen" of the framework (not an internal realization of EIP pattern) fulle exposed to you - the end user. + + So, Wire-tap component is ONLY responsible to perform the following 3 tasks: + + + wire-tap into a message flow by tapping into a channel (e.g., channelA) + + + grab a copy of a message + + + send it to another channel (e.g., channelB) + + + + + Look at it as a variation of the Bridge (nothing more). But by bridging one channel with another wire-tap is essentially + initiates (forks) another message flow. Is this flow synchronous or asynchronous? + That is the ultimate question and the answer simply depends on the type of Message Channel 'channelB' is. + And as you know we have: Direct Channel, Pollable Channel and Executor Channel. + The last two do break the thread boundary making communication via such channels asynchronous simply because + the dispatching of the message from the channel happens on the different thread then the one that sent the message to that channel + and that is what is going to make your wire-tap flow sync or async. + It is consistent with other components within the framework (e.g., Message Publisher) and if you think about it its in a way + brings a level of simplicity by sparing you form worrying in advance (other then writing thread safe code) wether a + particular piece of code should be implemented as sync or async. In fact its always neither, + the code is just a function. The actual wiring of two pieces of code (component A and component B) via Message Channel + is what's going to make their collaboration sync or async. You may even want to change + from sync to async in the future and Message Channel is what's going + to allow you to do it swiftly without ever touching the code +
From 35c767a93b94e917911bda65797721fd870eca86 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Thu, 11 Nov 2010 09:31:38 -0500 Subject: [PATCH 37/42] INT-1586 added 'receive-timeout', 'recovery-interval', and 'idle-consumer-limit' attributes --- .../JmsMessageDrivenEndpointParser.java | 7 ++- .../jms/config/spring-integration-jms-2.0.xsd | 6 +++ .../config/JmsInboundGatewayParserTests.java | 36 ++++++++++++++ ...essageDrivenChannelAdapterParserTests.java | 49 +++++++++++++++++++ .../jmsGatewayWithContainerSettings.xml | 21 ++++++++ .../jmsInboundWithContainerSettings.xml | 45 +++++++++++++++++ 6 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithContainerSettings.xml diff --git a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsMessageDrivenEndpointParser.java b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsMessageDrivenEndpointParser.java index b9ec0d79ee..1dc54971d0 100644 --- a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsMessageDrivenEndpointParser.java +++ b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsMessageDrivenEndpointParser.java @@ -56,7 +56,9 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition JmsAdapterParserUtils.DESTINATION_NAME_ATTRIBUTE, "destination-resolver", "transaction-manager", "concurrent-consumers", "max-concurrent-consumers", - "max-messages-per-task", "idle-task-execution-limit", "selector" + "max-messages-per-task", "selector", + "receive-timeout", "recovery-interval", + "idle-consumer-limit", "idle-task-execution-limit" }; @@ -141,6 +143,9 @@ public class JmsMessageDrivenEndpointParser extends AbstractSingleBeanDefinition IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "concurrent-consumers"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "max-concurrent-consumers"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "max-messages-per-task"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "receive-timeout"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "recovery-interval"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "idle-consumer-limit"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "idle-task-execution-limit"); builder.addPropertyValue("autoStartup", false); return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry()); diff --git a/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.0.xsd b/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.0.xsd index fa12d56026..732751df32 100644 --- a/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.0.xsd +++ b/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.0.xsd @@ -401,6 +401,9 @@ + + + @@ -544,6 +547,9 @@ + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundGatewayParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundGatewayParserTests.java index 2a858df9be..ae07536421 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundGatewayParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundGatewayParserTests.java @@ -236,6 +236,30 @@ public class JmsInboundGatewayParserTests { gateway.stop(); } + @Test + public void testGatewayWithReceiveTimeout() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsGatewayWithContainerSettings.xml", this.getClass()); + JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("gatewayWithReceiveTimeout"); + gateway.start(); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) + new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + assertEquals(1111L, new DirectFieldAccessor(container).getPropertyValue("receiveTimeout")); + gateway.stop(); + } + + @Test + public void testGatewayWithRecoveryInterval() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsGatewayWithContainerSettings.xml", this.getClass()); + JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("gatewayWithRecoveryInterval"); + gateway.start(); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) + new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + assertEquals(2222L, new DirectFieldAccessor(container).getPropertyValue("recoveryInterval")); + gateway.stop(); + } + @Test public void testGatewayWithIdleTaskExecutionLimit() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( @@ -248,6 +272,18 @@ public class JmsInboundGatewayParserTests { gateway.stop(); } + @Test + public void testGatewayWithIdleConsumerLimit() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsGatewayWithContainerSettings.xml", this.getClass()); + JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("gatewayWithIdleConsumerLimit"); + gateway.start(); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) + new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + assertEquals(33, new DirectFieldAccessor(container).getPropertyValue("idleConsumerLimit")); + gateway.stop(); + } + @Test public void testGatewayWithContainerReference() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java index d8438bd53b..3059a11aad 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java @@ -30,6 +30,7 @@ import org.springframework.integration.core.PollableChannel; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.jms.JmsMessageDrivenEndpoint; import org.springframework.integration.test.util.TestUtils; +import org.springframework.jms.listener.AbstractMessageListenerContainer; import org.springframework.jms.support.destination.JmsDestinationAccessor; /** @@ -65,4 +66,52 @@ public class JmsMessageDrivenChannelAdapterParserTests { assertEquals(Boolean.TRUE, container.isPubSubDomain()); } + @Test + public void testGatewayWithReceiveTimeout() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsInboundWithContainerSettings.xml", this.getClass()); + JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("adapterWithReceiveTimeout"); + gateway.start(); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) + new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + assertEquals(1111L, new DirectFieldAccessor(container).getPropertyValue("receiveTimeout")); + gateway.stop(); + } + + @Test + public void testGatewayWithRecoveryInterval() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsInboundWithContainerSettings.xml", this.getClass()); + JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("adapterWithRecoveryInterval"); + gateway.start(); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) + new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + assertEquals(2222L, new DirectFieldAccessor(container).getPropertyValue("recoveryInterval")); + gateway.stop(); + } + + @Test + public void testGatewayWithIdleTaskExecutionLimit() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsInboundWithContainerSettings.xml", this.getClass()); + JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("adapterWithIdleTaskExecutionLimit"); + gateway.start(); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) + new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + assertEquals(7, new DirectFieldAccessor(container).getPropertyValue("idleTaskExecutionLimit")); + gateway.stop(); + } + + @Test + public void testGatewayWithIdleConsumerLimit() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsInboundWithContainerSettings.xml", this.getClass()); + JmsMessageDrivenEndpoint gateway = (JmsMessageDrivenEndpoint) context.getBean("adapterWithIdleConsumerLimit"); + gateway.start(); + AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) + new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); + assertEquals(33, new DirectFieldAccessor(container).getPropertyValue("idleConsumerLimit")); + gateway.stop(); + } + } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsGatewayWithContainerSettings.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsGatewayWithContainerSettings.xml index cc8e17c82a..7c65299df3 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsGatewayWithContainerSettings.xml +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsGatewayWithContainerSettings.xml @@ -35,6 +35,20 @@ request-pub-sub-domain="false" max-messages-per-task="99"/> + + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithContainerSettings.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithContainerSettings.xml new file mode 100644 index 0000000000..7c119b409b --- /dev/null +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithContainerSettings.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + From 834f6ca26520814aa3d95d338436abcc6232fcca Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 11 Nov 2010 09:53:44 -0500 Subject: [PATCH 38/42] INT-1600 polished namespace elements t be more consistent --- .../config/TwitterNamespaceHandler.java | 10 +- .../TwitterReceivingMessageSourceParser.java | 6 +- .../TwitterSendingMessageHandlerParser.java | 4 +- .../config/spring-integration-twitter-2.0.xsd | 197 +++++++----------- ...stReceivingMessageSourceParser-context.xml | 12 +- ...estSendingMessageHandlerParser-context.xml | 4 +- .../TestReceivingUsingNamespace-context.xml | 13 +- .../TestSendingDMsUsingNamespace-context.xml | 2 +- ...stSendingUpdatesUsingNamespace-context.xml | 2 +- 9 files changed, 101 insertions(+), 149 deletions(-) diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterNamespaceHandler.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterNamespaceHandler.java index 8765c3cab7..b39e351272 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterNamespaceHandler.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterNamespaceHandler.java @@ -31,13 +31,13 @@ public class TwitterNamespaceHandler extends AbstractIntegrationNamespaceHandler public void init() { // inbound - registerBeanDefinitionParser("inbound-update-channel-adapter", new TwitterReceivingMessageSourceParser()); - registerBeanDefinitionParser("inbound-dm-channel-adapter", new TwitterReceivingMessageSourceParser()); - registerBeanDefinitionParser("inbound-mention-channel-adapter", new TwitterReceivingMessageSourceParser()); + registerBeanDefinitionParser("inbound-channel-adapter", new TwitterReceivingMessageSourceParser()); + registerBeanDefinitionParser("dm-inbound-channel-adapter", new TwitterReceivingMessageSourceParser()); + registerBeanDefinitionParser("mentions-inbound-channel-adapter", new TwitterReceivingMessageSourceParser()); // outbound - registerBeanDefinitionParser("outbound-update-channel-adapter", new TwitterSendingMessageHandlerParser()); - registerBeanDefinitionParser("outbound-dm-channel-adapter", new TwitterSendingMessageHandlerParser()); + registerBeanDefinitionParser("outbound-channel-adapter", new TwitterSendingMessageHandlerParser()); + registerBeanDefinitionParser("dm-outbound-channel-adapter", new TwitterSendingMessageHandlerParser()); } } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterReceivingMessageSourceParser.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterReceivingMessageSourceParser.java index 2c4202ee45..a653da452a 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterReceivingMessageSourceParser.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterReceivingMessageSourceParser.java @@ -40,13 +40,13 @@ public class TwitterReceivingMessageSourceParser extends AbstractPollingInboundC protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) { String elementName = element.getLocalName().trim(); String className = null; - if ("inbound-update-channel-adapter".equals(elementName)) { + if ("inbound-channel-adapter".equals(elementName)) { className = BASE_PACKAGE + ".inbound.TimelineUpdateReceivingMessageSource"; } - else if ("inbound-dm-channel-adapter".equals(elementName)) { + else if ("dm-inbound-channel-adapter".equals(elementName)) { className = BASE_PACKAGE + ".inbound.DirectMessageReceivingMessageSource"; } - else if ("inbound-mention-channel-adapter".equals(elementName)) { + else if ("mentions-inbound-channel-adapter".equals(elementName)) { className = BASE_PACKAGE + ".inbound.MentionReceivingMessageSource"; } else { diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterSendingMessageHandlerParser.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterSendingMessageHandlerParser.java index f20180ee3d..2e8bd8bf73 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterSendingMessageHandlerParser.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/config/TwitterSendingMessageHandlerParser.java @@ -37,10 +37,10 @@ public class TwitterSendingMessageHandlerParser extends AbstractOutboundChannelA protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { String elementName = element.getLocalName().trim(); String className = null; - if ("outbound-update-channel-adapter".equals(elementName)) { + if ("outbound-channel-adapter".equals(elementName)) { className = BASE_PACKAGE + ".outbound.TimelineUpdateSendingMessageHandler"; } - else if ("outbound-dm-channel-adapter".equals(elementName)) { + else if ("dm-outbound-channel-adapter".equals(elementName)) { className = BASE_PACKAGE + ".outbound.DirectMessageSendingMessageHandler"; } BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(className); diff --git a/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd b/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd index f2f8f2dbaa..cb51616490 100644 --- a/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd +++ b/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.0.xsd @@ -14,9 +14,23 @@ schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.0.xsd"/> - + + + + Configures an inbound channel adapter that consumes message (representing your friends' timeline updates) + from twitter and sends Messages whose payloads are Tweet objects. + + + + + + + + + + Configures an inbound channel adapter that consumes message (representing mentions of your handle) @@ -24,105 +38,30 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + - + Configures an inbound channel adapter that consumes direct messages and forwards them to Spring Integration - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - Configures an inbound channel adapter that consumes message (representing your friends' timeline updates) - from twitter and sends Messages whose payloads are Tweet objects. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Configures an inbound channel adapter that consumes message (representing your friends' timeline updates) @@ -130,12 +69,60 @@ - + + + + + + + + + + + Configures an inbound channel adapter that consumes message (representing your friends' timeline updates) + from twitter and sends Messages whose payloads are Tweet objects. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -149,39 +136,5 @@ - - - - - - - - Configures an inbound channel adapter that consumes message (representing your friends' timeline updates) - from twitter and sends Messages whose payloads are Tweet objects. - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml index a121b53d08..39fe73cf9c 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestReceivingMessageSourceParser-context.xml @@ -21,25 +21,25 @@ - - + - - - + - + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml index 430171a956..fce383db4d 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml @@ -24,9 +24,9 @@ - + - + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml index b08710a045..3867d94ffe 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestReceivingUsingNamespace-context.xml @@ -33,20 +33,19 @@ - - + - + - + - + - + - + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml index 32e6ff2dfd..c00e2e1256 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml @@ -31,7 +31,7 @@ - + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml index a9bcdf6b3f..81a604006f 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml @@ -31,7 +31,7 @@ - + From 8c63cc003a7702ef411aaf727ab06389272a28ec Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 11 Nov 2010 10:07:59 -0500 Subject: [PATCH 39/42] INT-1602 changed to receive HOME Timeline updates instead of Friends --- .../integration/twitter/core/Twitter4jTemplate.java | 8 ++++---- .../integration/twitter/core/TwitterOperations.java | 4 ++-- .../inbound/TimelineUpdateReceivingMessageSource.java | 4 ++-- .../integration/twitter/core/Twitter4jTemplateTests.java | 8 ++++---- .../TimelineUpdateReceivingMessageSourceTests.java | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java index f0b2edb754..e37e2e6ecf 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java @@ -115,9 +115,9 @@ public class Twitter4jTemplate implements TwitterOperations{ } } @Override - public List getFriendsTimeline() { + public List getHomeTimeline() { try { - ResponseList timelines = twitter.getFriendsTimeline(); + ResponseList timelines = twitter.getHomeTimeline(); return this.buildTweetsFromTwitterResponses(timelines); } catch (Exception e) { @@ -125,9 +125,9 @@ public class Twitter4jTemplate implements TwitterOperations{ } } @Override - public List getFriendsTimeline(long sinceId) { + public List getHomeTimeline(long sinceId) { try { - ResponseList timelines = twitter.getFriendsTimeline(new Paging(sinceId)); + ResponseList timelines = twitter.getHomeTimeline(new Paging(sinceId)); return this.buildTweetsFromTwitterResponses(timelines); } catch (Exception e) { diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java index 183cbb0866..e001582cc3 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java @@ -36,9 +36,9 @@ public interface TwitterOperations { List getMentions(long sinceId); - List getFriendsTimeline(); + List getHomeTimeline(); - List getFriendsTimeline(long sinceId); + List getHomeTimeline(long sinceId); void sendDirectMessage(String userName, String text); diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSource.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSource.java index ab4c592c00..29b0b0d4d2 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSource.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSource.java @@ -48,8 +48,8 @@ public class TimelineUpdateReceivingMessageSource extends AbstractTwitterMessage long sinceId = getMarkerId(); if (tweets.size() <= prefetchThreshold){ List tweets = !hasMarkedStatus() - ? twitter.getFriendsTimeline() - : twitter.getFriendsTimeline(sinceId); + ? twitter.getHomeTimeline() + : twitter.getHomeTimeline(sinceId); forwardAll(tweets); } } catch (Exception e) { diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java index 2560377f7c..021cc1c4d8 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java @@ -89,10 +89,10 @@ public class Twitter4jTemplateTests { @Test public void testGetFriendsTimeline() throws Exception{ - template.getFriendsTimeline(); - template.getFriendsTimeline(123); - verify(twitter, times(1)).getFriendsTimeline(); - verify(twitter, times(1)).getFriendsTimeline(Mockito.any(Paging.class)); + template.getHomeTimeline(); + template.getHomeTimeline(123); + verify(twitter, times(1)).getHomeTimeline(); + verify(twitter, times(1)).getHomeTimeline(Mockito.any(Paging.class)); } @Test diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSourceTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSourceTests.java index a585fd6372..62045b0f4d 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSourceTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/TimelineUpdateReceivingMessageSourceTests.java @@ -131,7 +131,7 @@ public class TimelineUpdateReceivingMessageSourceTests { Tweet message = (Tweet) msg.poll(); assertEquals(2000, message.getId()); Thread.sleep(1000); - verify(twitter, times(1)).getFriendsTimeline(2000); + verify(twitter, times(1)).getHomeTimeline(2000); // based on the Mock, the Queue shoud now have 2 mopre messages third and fourth assertTrue(((Queue)TestUtils.getPropertyValue(source, "tweets")).size() == 2); source.stop(); @@ -209,12 +209,12 @@ public class TimelineUpdateReceivingMessageSourceTests { SampleResoponceList testMessages = new SampleResoponceList(); testMessages.add(firstMessage); testMessages.add(secondMessage); - when(tw.getFriendsTimeline()).thenReturn(testMessages); + when(tw.getHomeTimeline()).thenReturn(testMessages); testMessages = new SampleResoponceList(); testMessages.add(thirdMessage); testMessages.add(fourthMessage); - when(tw.getFriendsTimeline(Mockito.any(Paging.class))).thenReturn(testMessages); + when(tw.getHomeTimeline(Mockito.any(Paging.class))).thenReturn(testMessages); } @SuppressWarnings({ "rawtypes", "serial" }) From bfd9a848320ddc80a45e5cb3e60583f1235c6ce3 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 11 Nov 2010 11:03:58 -0500 Subject: [PATCH 40/42] INT-1603 first round of changes to introduce search methods to TwitterOperation and Twiter4JTemplate as well as allign TwitterOperation with its counterpart in Spring Social. Also copied SearchResult from Spring Social --- .../twitter/core/SearchResults.java | 70 ++++++++++++++ .../twitter/core/Twitter4jTemplate.java | 93 +++++++++++++++++-- .../twitter/core/TwitterOperations.java | 88 +++++++++++++++++- .../TimelineUpdateSendingMessageHandler.java | 15 ++- .../twitter/core/Twitter4jTemplateTests.java | 5 +- ...elineUpdateSendingMessageHandlerTests.java | 4 +- 6 files changed, 257 insertions(+), 18 deletions(-) create mode 100644 spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/SearchResults.java diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/SearchResults.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/SearchResults.java new file mode 100644 index 0000000000..3d39534b49 --- /dev/null +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/SearchResults.java @@ -0,0 +1,70 @@ +/* + * Copyright 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.twitter.core; + +import java.util.List; + +/** + * Represents the results of a Twitter search, including matching {@link Tweet}s + * and any metadata associated with that search. + * + * @author Craig Walls + * + */ +public class SearchResults { + private List tweets; + private long maxId; + private long sinceId; + private boolean lastPage; + + public SearchResults(List tweets, long maxId, long sinceId, boolean lastPage) { + this.tweets = tweets; + this.maxId = maxId; + this.sinceId = sinceId; + this.lastPage = lastPage; + } + + /** + * Returns the list of matching {@link Tweet}s + */ + public List getTweets() { + return tweets; + } + + /** + * Returns the maximum {@link Tweet} ID in the search results + */ + public long getMaxId() { + return maxId; + } + + /** + * Returns the {@link Tweet} ID after which all of the matching + * {@link Tweet}s were created + */ + public long getSinceId() { + return sinceId; + } + + /** + * Returns true if this is the last page of matching + * {@link Tweet}s; false if there are more pages that follow + * this one. + */ + public boolean isLastPage() { + return lastPage; + } +} diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java index e37e2e6ecf..7dafbf34be 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java @@ -15,13 +15,19 @@ */ package org.springframework.integration.twitter.core; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import org.apache.commons.lang.NotImplementedException; + import org.springframework.util.Assert; import twitter4j.DirectMessage; +import twitter4j.IDs; import twitter4j.Paging; +import twitter4j.Query; +import twitter4j.QueryResult; import twitter4j.ResponseList; import twitter4j.Status; import twitter4j.StatusUpdate; @@ -159,13 +165,10 @@ public class Twitter4jTemplate implements TwitterOperations{ } @Override - public void updateStatus(Tweet statusTweet) { - Assert.notNull(statusTweet, "'statusTweet' must not be null"); + public void updateStatus(String statusTweet) { + Assert.hasText(statusTweet, "'statusTweet' must not be null"); try { - StatusUpdate status = new StatusUpdate(statusTweet.getText()); - if (statusTweet.getToUserId() != null){ - status.setInReplyToStatusId(statusTweet.getToUserId()); - } + StatusUpdate status = new StatusUpdate(statusTweet); twitter.updateStatus(status); } catch (Exception e) { @@ -173,10 +176,67 @@ public class Twitter4jTemplate implements TwitterOperations{ } } + @Override + public List getFriends(String screenName) { + throw new NotImplementedException("This method is not implemented since it is not used by any of the " + + "Spring Integration adapters. It exists strictly for being compliant with Spring Social interface untill" + + "migration to use Spring Social is complete in Spring Integration 2.1.0"); + } + + @Override + public void retweet(long tweetId) { + throw new NotImplementedException("This method is not implemented since it is not used by any of the " + + "Spring Integration adapters. It exists strictly for being compliant with Spring Social interface untill" + + "migration to use Spring Social is complete in Spring Integration 2.1.0"); + } + + @Override + public SearchResults search(String query) { + Assert.hasText(query, "'query' must not be null"); + Query q = new Query(query); + return this.search(q); + } + + @Override + public SearchResults search(String query, int page, int pageSize) { + Assert.hasText(query, "'query' must not be null"); + Query q = new Query(query); + q.setPage(page); + return this.search(q); + } + + @Override + public SearchResults search(String query, int page, int resultsPerPage, + int sinceId, int maxId) { + Assert.hasText(query, "'query' must not be null"); + Query q = new Query(query); + q.setPage(page); + q.setSinceId(sinceId); + q.setMaxId(maxId); + return this.search(q); + } + public Twitter getUnderlyingTwitter(){ return this.twitter; } + private SearchResults search(Query query){ + try { + QueryResult result = twitter.search(query); + + if (result != null){ + List t4jTweets = result.getTweets(); + List tweets = this.buildTweetsFromTwitterResponses(t4jTweets); + SearchResults results = new SearchResults(tweets, result.getMaxId(), result.getSinceId(), false); + return results; + } + } + catch (Exception e) { + throw new TwitterOperationException("Failed to send Status update. ", e); + } + return null; + } + private List buildTweetsFromTwitterResponses(List responses){ List tweets = new LinkedList(); if (responses != null){ @@ -184,9 +244,15 @@ public class Twitter4jTemplate implements TwitterOperations{ if (response instanceof Status){ tweets.add(this.buildTweetFromStatus((Status) response)); } - else { + else if (response instanceof DirectMessage) { tweets.add(this.buildTweetFromDm((DirectMessage) response)); } + else if (response instanceof twitter4j.Tweet){ + tweets.add(this.buildTweetFromT4jTweet((twitter4j.Tweet) response)); + } + else { + throw new TwitterOperationException("Unsupported response type: " + response.getClass()); + } } } return tweets; @@ -215,4 +281,17 @@ public class Twitter4jTemplate implements TwitterOperations{ tweet.setText(status.getText()); return tweet; } + + private Tweet buildTweetFromT4jTweet(twitter4j.Tweet t4jTweet){ + Tweet tweet = new Tweet(); + tweet.setCreatedAt(t4jTweet.getCreatedAt()); + tweet.setFromUser(t4jTweet.getFromUser()); + tweet.setFromUserId(t4jTweet.getFromUserId()); + tweet.setId(t4jTweet.getId()); + tweet.setLanguageCode(t4jTweet.getIsoLanguageCode()); + tweet.setProfileImageUrl(t4jTweet.getProfileImageUrl()); + tweet.setSource(t4jTweet.getSource()); + tweet.setText(t4jTweet.getText()); + return tweet; + } } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java index e001582cc3..a6bc676cf6 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/TwitterOperations.java @@ -20,14 +20,95 @@ import java.util.List; import twitter4j.Twitter; /** + * @author Craig Walls * @author Oleg Zhurakousky * @since 2.0 * */ public interface TwitterOperations { + /** + * Retrieves the user's Twitter screen name. + * + * @return the user's screen name at Twitter + */ String getProfileId(); + /** + * Retrieves a list of users that the given user follows. + * + * @param screenName + * The user's Twitter screen name + * @return a list of user screen names + */ + List getFriends(String screenName); + + /** + * Updates the user's status. + * + * @param status + * The status message + * + */ + void updateStatus(String status); + + /** + * Posts a retweet of an existing tweet. + * + * @param tweetId + * The ID of the tweet to be retweeted + * + * @throws SocialException + * if an error response is received from Twitter + */ + void retweet(long tweetId); + + /** + * Searches Twitter, returning the first 50 matching {@link Tweet}s + * + * @param query + * The search query string + * @return a {@link SearchResults} containing {@link Tweet}s + * + */ + SearchResults search(String query); + + /** + * Searches Twitter, returning a specific page out of the complete set of + * results. + * + * @param query + * The search query string + * @param page + * The page to return + * @param pageSize + * The number of {@link Tweet}s per page + * + * @return a {@link SearchResults} containing {@link Tweet}s + * + */ + SearchResults search(String query, int page, int pageSize); + + /** + * Searches Twitter, returning a specific page out of the complete set of + * results. Results are filtered to those whose ID falls between sinceId and + * maxId + * + * @param query + * The search query string + * @param page + * The page to return + * @param pageSize + * The number of {@link Tweet}s per page + * @param sinceId + * The minimum {@link Tweet} ID to return in the results + * @param maxId + * The maximum {@link Tweet} ID to return in the results + * + * @return a {@link SearchResults} containing {@link Tweet}s + */ + SearchResults search(String query, int page, int resultsPerPage, int sinceId, int maxId); + List getDirectMessages(); List getDirectMessages(long sinceId); @@ -43,8 +124,9 @@ public interface TwitterOperations { void sendDirectMessage(String userName, String text); void sendDirectMessage(int userId, String text); - - void updateStatus(Tweet status); - + + /** + * Temporary method. Should be removed one migrated to Spring Social + */ Twitter getUnderlyingTwitter(); } diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandler.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandler.java index 6f75ed630a..c0f1c36847 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandler.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandler.java @@ -16,6 +16,7 @@ package org.springframework.integration.twitter.outbound; import org.springframework.integration.Message; +import org.springframework.integration.MessageHandlingException; import org.springframework.integration.twitter.core.Tweet; import org.springframework.integration.twitter.core.TwitterOperations; @@ -35,8 +36,18 @@ public class TimelineUpdateSendingMessageHandler extends AbstractOutboundTwitter @Override protected void handleMessageInternal(Message message) throws Exception { - Tweet tweet = this.outboundMaper.fromMessage(message); - this.twitter.updateStatus(tweet); + Object payload = message.getPayload(); + String statusText = null; + if (payload instanceof Tweet){ + statusText = ((Tweet)payload).getText(); + } + else if (payload instanceof String){ + statusText = (String) payload; + } + else { + throw new MessageHandlingException(message, "Unsupported payload type '" + payload.getClass().getName() + "'"); + } + this.twitter.updateStatus(statusText); } } diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java index 021cc1c4d8..7ccf455005 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java @@ -105,10 +105,7 @@ public class Twitter4jTemplateTests { @Test public void testUpdateStatus() throws Exception{ - Tweet tweet = new Tweet(); - tweet.setToUserId((long) 123); - tweet.setText("writing twitter test"); - template.updateStatus(tweet); + template.updateStatus("writing twitter test"); verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); } } diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandlerTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandlerTests.java index 2da212438e..26c1d0917f 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandlerTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/outbound/TimelineUpdateSendingMessageHandlerTests.java @@ -62,7 +62,7 @@ public class TimelineUpdateSendingMessageHandlerTests { Tweet tweet = new Tweet(); tweet.setText("writing twitter tests"); handler.handleMessage(new GenericMessage(tweet)); - verify(twitterOperations, times(1)).updateStatus(Mockito.any(Tweet.class)); + verify(twitterOperations, times(1)).updateStatus(Mockito.any(String.class)); verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); } @Test @@ -75,7 +75,7 @@ public class TimelineUpdateSendingMessageHandlerTests { .setHeader(TwitterHeaders.DISPLAY_COORDINATES, true) .build(); handler.handleMessage(message); - verify(twitterOperations, times(1)).updateStatus(Mockito.any(Tweet.class)); + verify(twitterOperations, times(1)).updateStatus(Mockito.any(String.class)); verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); } } From 800d5e6617612571d9066805cd425811d41e976a Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 11 Nov 2010 11:33:02 -0500 Subject: [PATCH 41/42] INT-1603 added test for search to Twitter4JTemplateTest --- .../twitter/core/Twitter4jTemplateTests.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java index 7ccf455005..faf9d4094e 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java @@ -16,6 +16,7 @@ package org.springframework.integration.twitter.core; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -23,6 +24,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; import org.junit.Before; import org.junit.Test; @@ -30,6 +33,8 @@ import org.mockito.Mockito; import org.springframework.integration.test.util.TestUtils; import twitter4j.Paging; +import twitter4j.Query; +import twitter4j.QueryResult; import twitter4j.StatusUpdate; import twitter4j.Twitter; import twitter4j.http.AccessToken; @@ -108,4 +113,28 @@ public class Twitter4jTemplateTests { template.updateStatus("writing twitter test"); verify(twitter, times(1)).updateStatus(Mockito.any(StatusUpdate.class)); } + + @Test + public void testSearch() throws Exception{ + // set up test + QueryResult result = mock(QueryResult.class); + List t4jTweets = new ArrayList(); + t4jTweets.add(mock(twitter4j.Tweet.class)); + t4jTweets.add(mock(twitter4j.Tweet.class)); + t4jTweets.add(mock(twitter4j.Tweet.class)); + + when(result.getTweets()).thenReturn(t4jTweets); + + when(twitter.search(Mockito.any(Query.class))).thenReturn(result); + // end setup test + + SearchResults results = template.search("#s2gx"); + List tweets = results.getTweets(); + assertNotNull(tweets); + assertEquals(3, tweets.size()); + assertTrue(tweets.get(0) instanceof Tweet); + assertTrue(tweets.get(1) instanceof Tweet); + assertTrue(tweets.get(2) instanceof Tweet); + + } } From fa135f9c6cc3c66a71ad0a0704a0c02c68b73a59 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 11 Nov 2010 12:58:05 -0500 Subject: [PATCH 42/42] INT-1603 added SearchReceivingMessageSource --- .../twitter/core/Twitter4jTemplate.java | 7 +- .../inbound/SearchReceivingMessageSource.java | 93 +++++++++++++++++++ .../twitter/core/Twitter4jTemplateTests.java | 2 +- .../SearchReceivingMessageSourceTests.java | 58 ++++++++++++ 4 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/SearchReceivingMessageSource.java create mode 100644 spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/SearchReceivingMessageSourceTests.java diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java index 7dafbf34be..a01f8c047c 100644 --- a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/core/Twitter4jTemplate.java @@ -70,7 +70,12 @@ public class Twitter4jTemplate implements TwitterOperations{ @Override public String getProfileId() { try { - return twitter.getScreenName(); + if (twitter.isOAuthEnabled()){ + return twitter.getScreenName(); + } + else { + return "twitter-anonymous"; + } } catch (Exception e) { throw new TwitterOperationException("Failed to obtain Profile ID. ", e); diff --git a/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/SearchReceivingMessageSource.java b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/SearchReceivingMessageSource.java new file mode 100644 index 0000000000..23e263c54b --- /dev/null +++ b/spring-integration-twitter/src/main/java/org/springframework/integration/twitter/inbound/SearchReceivingMessageSource.java @@ -0,0 +1,93 @@ +/* + * 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.twitter.inbound; + +import java.util.List; + +import org.springframework.integration.MessagingException; +import org.springframework.integration.twitter.core.SearchResults; +import org.springframework.integration.twitter.core.Tweet; +import org.springframework.integration.twitter.core.TwitterOperations; +import org.springframework.util.Assert; + +/** + * + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class SearchReceivingMessageSource extends AbstractTwitterMessageSource { + /* since Twitter return 15 entries per page we need to be able to manage + * how many pages deep are we willing to go. Not sure yet about exposing this attribute via namespace + * but setting default to 10. + */ + private volatile int pageDepth = 10; + + private volatile int currentPage = 1; + private volatile String query; + + public SearchReceivingMessageSource(TwitterOperations twitter){ + super(twitter); + } + + public void setQuery(String query) { + Assert.hasText(query, "'query' must no be null"); + this.query = query; + } + + @Override + public String getComponentType() { + return "twitter:search-inbound-channel-adapter"; + } + + @Override + Runnable getApiCallback() { + Runnable apiCallback = new Runnable() { + public void run() { + try { + if (tweets.size() <= prefetchThreshold){ + if (currentPage == pageDepth){ + currentPage = 1; + } + SearchResults results = twitter.search(query, currentPage, 0); + List twetList = results.getTweets(); + if (currentPage == 1){ + forwardAll(twetList); + } + else { + for (Tweet tweet : twetList) { + tweets.add(tweet); + } + } + if (twetList != null && twetList.size() > 0){ + currentPage++; + } + else { + currentPage = 1; + } + } + } catch (Exception e) { + if (e instanceof RuntimeException){ + throw (RuntimeException)e; + } + else { + throw new MessagingException("Failed to poll for Twitter mentions updates", e); + } + } + } + }; + return apiCallback; + } +} diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java index faf9d4094e..bd4236cf62 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/core/Twitter4jTemplateTests.java @@ -135,6 +135,6 @@ public class Twitter4jTemplateTests { assertTrue(tweets.get(0) instanceof Tweet); assertTrue(tweets.get(1) instanceof Tweet); assertTrue(tweets.get(2) instanceof Tweet); - } + } diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/SearchReceivingMessageSourceTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/SearchReceivingMessageSourceTests.java new file mode 100644 index 0000000000..bb14791b3b --- /dev/null +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/inbound/SearchReceivingMessageSourceTests.java @@ -0,0 +1,58 @@ +/** + * + */ +package org.springframework.integration.twitter.inbound; + +import org.junit.Ignore; +import org.junit.Test; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessagingException; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.endpoint.SourcePollingChannelAdapter; +import org.springframework.integration.twitter.core.Tweet; +import org.springframework.integration.twitter.core.Twitter4jTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +/** + * @author ozhurakousky + * + */ +public class SearchReceivingMessageSourceTests { + + @Test + @Ignore + public void testSearchReceiving() throws Exception{ + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.afterPropertiesSet(); + bf.registerSingleton("taskScheduler", scheduler); + + SearchReceivingMessageSource ms = new SearchReceivingMessageSource(new Twitter4jTemplate()); + + DirectChannel channel = new DirectChannel(); + channel.subscribe(new MessageHandler() { + public void handleMessage(Message message) throws MessagingException { + System.out.println("Message: " + ((Tweet)message.getPayload()).getCreatedAt() + " - " + ((Tweet)message.getPayload()).getText()); + } + }); + SourcePollingChannelAdapter adapter = new SourcePollingChannelAdapter(); + adapter.setSource(ms); + adapter.setBeanFactory(bf); + adapter.setOutputChannel(channel); + adapter.afterPropertiesSet(); + adapter.start(); + + ms.setBeanFactory(bf); + ms.setQuery("#springintegration"); + + ms.setTaskScheduler(scheduler); + ms.afterPropertiesSet(); + ms.start(); + + System.in.read(); + } +}