From b68fa47fcc5279a7dddf7e8e64bda05425517d76 Mon Sep 17 00:00:00 2001 From: Jonas Partner Date: Tue, 5 Aug 2008 13:47:39 +0000 Subject: [PATCH] Added SubscribabelMailSource and namespace support for pollable and subscribable mail source --- .../spring-integration-adapters-1.0.xsd | 30 ++++++ .../adapter/mail/DefaultFolderConnection.java | 38 +++++--- .../adapter/mail/PollingMailSource.java | 12 +++ .../adapter/mail/SubscribableMailSource.java | 17 +++- .../mail/config/PollingMailSourceParser.java | 93 +++++++++++++++++++ .../SubscribableImapIdleMailSourceParser.java | 88 ++++++++++++++++++ .../META-INF/spring-integration.parsers | 2 + .../mail/SubscribableMailSourceTests.java | 2 +- .../config/PollingMailSourceParserTests.java | 31 +++++++ .../config/pollingMailSourceParserTests.xml | 13 +++ 10 files changed, 309 insertions(+), 17 deletions(-) create mode 100644 org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/config/PollingMailSourceParser.java create mode 100644 org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/config/SubscribableImapIdleMailSourceParser.java create mode 100644 org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/config/PollingMailSourceParserTests.java create mode 100644 org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/config/pollingMailSourceParserTests.xml diff --git a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/config/spring-integration-adapters-1.0.xsd b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/config/spring-integration-adapters-1.0.xsd index 0ce24d1710..ac4a1dc19b 100644 --- a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/config/spring-integration-adapters-1.0.xsd +++ b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/config/spring-integration-adapters-1.0.xsd @@ -227,6 +227,36 @@ + + + + + + Defines a polling mail source. + + + + + + + + + + + + + Defines a polling mail source. + + + + + + + + + + + diff --git a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/DefaultFolderConnection.java b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/DefaultFolderConnection.java index 2eaa44ae85..4781c7801c 100644 --- a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/DefaultFolderConnection.java +++ b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/DefaultFolderConnection.java @@ -36,8 +36,11 @@ import org.springframework.integration.adapter.mail.monitor.MonitoringStrategy; import org.springframework.util.Assert; /** + * A Connection to a mail folder capable of retrieving mail by utilising the + * given instance of {@link MonitoringStrategy} + * * @author Jonas Partner - * + * */ public class DefaultFolderConnection implements Lifecycle, InitializingBean, DisposableBean, FolderConnection { @@ -46,7 +49,7 @@ public class DefaultFolderConnection implements Lifecycle, InitializingBean, private final URLName storeUri; - private final Session session; + private Session session; private final MonitoringStrategy monitoringStrategy; @@ -56,14 +59,16 @@ public class DefaultFolderConnection implements Lifecycle, InitializingBean, private Folder folder; - public DefaultFolderConnection(String storeUri, Properties javaMailProperties, + private Properties javaMailProperties = new Properties(); + + public DefaultFolderConnection(String storeUri, MonitoringStrategy monitoringStrategy, boolean polling) { this.storeUri = new URLName(storeUri); - this.session = Session.getInstance(javaMailProperties); this.monitoringStrategy = monitoringStrategy; this.polling = polling; if (!polling - && monitoringStrategy.getClass().isAssignableFrom(AsyncMonitoringStrategy.class)) { + && monitoringStrategy.getClass().isAssignableFrom( + AsyncMonitoringStrategy.class)) { throw new ConfigurationException( "Folder connection requires an AsyncMonitoringStragey if polling is disabled"); } @@ -72,21 +77,21 @@ public class DefaultFolderConnection implements Lifecycle, InitializingBean, public void afterPropertiesSet() throws Exception { Assert.notNull(storeUri, "Property 'storeUri' is required"); - Assert.notNull(session, "Property 'JavaMailProperties' is required"); Assert.notNull(monitoringStrategy, "An instantce of MonitoringStrategy' is required"); - start(); + // } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.springframework.integration.adapter.mail.FolderConnectionI#receive() */ public synchronized Message[] receive() { if (!isRunning()) { - throw new org.springframework.integration.message.MessagingException( - "Folder connection is not running"); + start(); } - + try { if (!polling) { ((AsyncMonitoringStrategy) monitoringStrategy) @@ -105,7 +110,7 @@ public class DefaultFolderConnection implements Lifecycle, InitializingBean, } public synchronized boolean isRunning() { - return (folder!= null && folder.isOpen()); + return (folder != null && folder.isOpen()); } public synchronized void start() { @@ -143,6 +148,7 @@ public class DefaultFolderConnection implements Lifecycle, InitializingBean, } private void openSession() throws MessagingException { + session = Session.getInstance(javaMailProperties); store = session.getStore(storeUri); if (logger.isDebugEnabled()) { logger.debug("Connecting to store [" @@ -152,4 +158,12 @@ public class DefaultFolderConnection implements Lifecycle, InitializingBean, store.connect(); } + public Properties getJavaMailProperties() { + return javaMailProperties; + } + + public void setJavaMailProperties(Properties javaMailProperties) { + this.javaMailProperties = javaMailProperties; + } + } diff --git a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/PollingMailSource.java b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/PollingMailSource.java index 0fa5f86466..12b0c8c47f 100644 --- a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/PollingMailSource.java +++ b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/PollingMailSource.java @@ -81,4 +81,16 @@ public class PollingMailSource implements PollableSource { this.mailMessageStore = mailMessageStore; } + public FolderConnection getFolderConnection() { + return folderConnection; + } + + public MailMessageConverter getConverter() { + return converter; + } + + public LocalMailMessageStore getMailMessageStore() { + return mailMessageStore; + } + } diff --git a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/SubscribableMailSource.java b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/SubscribableMailSource.java index 48ccb2fb54..ea396b689a 100644 --- a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/SubscribableMailSource.java +++ b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/SubscribableMailSource.java @@ -18,15 +18,20 @@ package org.springframework.integration.adapter.mail; import javax.mail.Message; import javax.mail.internet.MimeMessage; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.context.Lifecycle; import org.springframework.core.task.TaskExecutor; +import org.springframework.integration.adapter.mail.monitor.AsyncMonitoringStrategy; import org.springframework.integration.dispatcher.BroadcastingDispatcher; import org.springframework.integration.message.MessageTarget; import org.springframework.integration.message.SubscribableSource; /** - * Broadcasts all mail messages receoved to subscribed {@link MessageTarget} + * Broadcasts all mail messages recovered to subscribed {@link MessageTarget} + * The given {@link FolderConnection} should be using an + * {@link AsyncMonitoringStrategy} to retrieve mail * * @author Jonas Partner * @@ -42,13 +47,12 @@ public class SubscribableMailSource implements SubscribableSource, Lifecycle, private boolean monitorRunning = false; - private final FolderConnection folderConnection; + private final Log logger = LogFactory.getLog(getClass()); private MailMessageConverter converter = new DefaultMailMessageConverter(); public SubscribableMailSource(FolderConnection folderConnection, TaskExecutor taskExecutor) { - this.folderConnection = folderConnection; this.monitorRunnable = new MonitorRunnable(folderConnection); this.taskExecutor = taskExecutor; @@ -75,11 +79,16 @@ public class SubscribableMailSource implements SubscribableSource, Lifecycle, } public void start() { + logger.info("Starting to monitor mailbox"); startMonitor(); + logger.info("Started to monitor mailbox"); + } public void stop() { + logger.info("Stopping monitoring of mailbox"); stopMonitor(); + logger.info("Stopped monitoring mailbox"); } public boolean isRunning() { @@ -107,7 +116,7 @@ public class SubscribableMailSource implements SubscribableSource, Lifecycle, this.folderConnection = folderConnection; } - public void interrupt() { + public synchronized void interrupt() { thread.interrupt(); } diff --git a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/config/PollingMailSourceParser.java b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/config/PollingMailSourceParser.java new file mode 100644 index 0000000000..2cbcf34acb --- /dev/null +++ b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/config/PollingMailSourceParser.java @@ -0,0 +1,93 @@ +/* + * Copyright 2002-2007 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.adapter.mail.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.ConfigurationException; +import org.springframework.integration.adapter.mail.DefaultFolderConnection; +import org.springframework.integration.adapter.mail.PollingMailSource; +import org.springframework.integration.adapter.mail.monitor.MonitoringStrategy; +import org.springframework.integration.adapter.mail.monitor.PollingMonitoringStrategy; +import org.springframework.integration.adapter.mail.monitor.Pop3PollingMonitoringStrategy; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * + * @author Jonas Partner + * + */ +public class PollingMailSourceParser extends AbstractSingleBeanDefinitionParser { + + protected Class getBeanClass(Element element) { + return PollingMailSource.class; + } + + protected boolean shouldGenerateId() { + return false; + } + + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + protected void doParse(Element element, ParserContext parserContext, + BeanDefinitionBuilder builder) { + String mailConvertorRef = element.getAttribute("convertor"); + String uri = element.getAttribute("store-uri"); + String propertiesRef = element.getAttribute("javaMailProperties"); + if (!StringUtils.hasLength(uri)) { + throw new ConfigurationException("A store-uri is required"); + } + BeanDefinitionBuilder folderConnectionBuilder = BeanDefinitionBuilder + .genericBeanDefinition(DefaultFolderConnection.class); + String storeType = uri.substring(0, 4).toLowerCase(); + MonitoringStrategy monitoringStrategy = null; + + if (storeType.equals("pop3")) { + monitoringStrategy = new Pop3PollingMonitoringStrategy(); + } else if (storeType.equals("imap")) { + monitoringStrategy = new PollingMonitoringStrategy(); + } else { + throw new ConfigurationException( + "No monitoring strategy for store-uri " + uri); + } + + folderConnectionBuilder.addConstructorArgValue(uri); + folderConnectionBuilder.addConstructorArgValue(monitoringStrategy); + // set polling true + folderConnectionBuilder.addConstructorArgValue(true); + + if (StringUtils.hasText(propertiesRef)) { + folderConnectionBuilder.addPropertyReference("javaMailProperties", + propertiesRef); + } + String folderConnectionName = parserContext.getReaderContext() + .registerWithGeneratedName( + folderConnectionBuilder.getBeanDefinition()); + + builder.addDependsOn(folderConnectionName); + builder.addConstructorArgValue(folderConnectionBuilder + .getBeanDefinition()); + + if (StringUtils.hasText(mailConvertorRef)) { + builder.addPropertyReference("convertor", mailConvertorRef); + } + + } +} \ No newline at end of file diff --git a/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/config/SubscribableImapIdleMailSourceParser.java b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/config/SubscribableImapIdleMailSourceParser.java new file mode 100644 index 0000000000..f5f88c286f --- /dev/null +++ b/org.springframework.integration.adapter/src/main/java/org/springframework/integration/adapter/mail/config/SubscribableImapIdleMailSourceParser.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002-2007 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.adapter.mail.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.ConfigurationException; +import org.springframework.integration.adapter.mail.DefaultFolderConnection; +import org.springframework.integration.adapter.mail.SubscribableMailSource; +import org.springframework.integration.adapter.mail.monitor.ImapIdleMonitoringStrategy; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * + * @author Jonas Partner + * + */ +public class SubscribableImapIdleMailSourceParser extends + AbstractSingleBeanDefinitionParser { + protected Class getBeanClass(Element element) { + return SubscribableMailSource.class; + } + + protected boolean shouldGenerateId() { + return false; + } + + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + protected void doParse(Element element, ParserContext parserContext, + BeanDefinitionBuilder builder) { + String mailConvertorRef = element.getAttribute("convertor"); + String uri = element.getAttribute("store-uri"); + String taskExecutorRef = element.getAttribute("task-executor"); + String propertiesRef = element.getAttribute("javaMailProperties"); + if (!StringUtils.hasLength(uri)) { + throw new ConfigurationException( + "A value for the store-uri attribue is required"); + } + if (!StringUtils.hasLength(taskExecutorRef)) { + throw new ConfigurationException( + "A value for the task-executor attribute is required"); + } + + BeanDefinitionBuilder folderConnectionBuilder = BeanDefinitionBuilder + .genericBeanDefinition(DefaultFolderConnection.class); + String storeType = uri.substring(0, 4).toLowerCase(); + + if (!storeType.equals("imap")) { + throw new ConfigurationException( + "store-uri must start with imap for the imap idle source"); + } + folderConnectionBuilder.addConstructorArgValue(uri); + folderConnectionBuilder + .addConstructorArgValue(new ImapIdleMonitoringStrategy()); + // set polling false + folderConnectionBuilder.addConstructorArgValue(false); + if (StringUtils.hasText(propertiesRef)) { + folderConnectionBuilder.addPropertyReference("javaMailProperties", + propertiesRef); + } + + builder.addConstructorArgValue(folderConnectionBuilder + .getBeanDefinition()); + builder.addConstructorArgReference(taskExecutorRef); + + if (StringUtils.hasText(mailConvertorRef)) { + builder.addPropertyReference("convertor", mailConvertorRef); + } + } +} diff --git a/org.springframework.integration.adapter/src/main/resources/META-INF/spring-integration.parsers b/org.springframework.integration.adapter/src/main/resources/META-INF/spring-integration.parsers index a52616036d..24f2fe7899 100644 --- a/org.springframework.integration.adapter/src/main/resources/META-INF/spring-integration.parsers +++ b/org.springframework.integration.adapter/src/main/resources/META-INF/spring-integration.parsers @@ -9,5 +9,7 @@ jms-gateway=org.springframework.integration.adapter.jms.config.JmsGatewayParser jms-source=org.springframework.integration.adapter.jms.config.JmsSourceParser jms-target=org.springframework.integration.adapter.jms.config.JmsTargetParser mail-target=org.springframework.integration.adapter.mail.config.MailTargetParser +polling-mail-source=org.springframework.integration.adapter.mail.config.PollingMailSourceParser +imap-idle-mail-source=org.springframework.integration.adapter.mail.config.SubscribableImapIdleMailSourceParser rmi-gateway=org.springframework.integration.adapter.rmi.config.RmiGatewayParser rmi-handler=org.springframework.integration.adapter.rmi.config.RmiHandlerParser \ No newline at end of file diff --git a/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/SubscribableMailSourceTests.java b/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/SubscribableMailSourceTests.java index 7b0015a6ce..285fda11b0 100644 --- a/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/SubscribableMailSourceTests.java +++ b/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/SubscribableMailSourceTests.java @@ -20,7 +20,7 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor; /** * * @author Jonas Partner - * + * */ public class SubscribableMailSourceTests { diff --git a/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/config/PollingMailSourceParserTests.java b/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/config/PollingMailSourceParserTests.java new file mode 100644 index 0000000000..6627f2ce1b --- /dev/null +++ b/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/config/PollingMailSourceParserTests.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2007 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.adapter.mail.config; + +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.adapter.mail.PollingMailSource; + +public class PollingMailSourceParserTests { + + @Test + public void testPop3(){ + ApplicationContext context = new ClassPathXmlApplicationContext("pollingMailSourceParserTests.xml", PollingMailSourceParserTests.class); + PollingMailSource mailSource = (PollingMailSource)context.getBean("pollingPop3"); + } + +} diff --git a/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/config/pollingMailSourceParserTests.xml b/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/config/pollingMailSourceParserTests.xml new file mode 100644 index 0000000000..23fa41f489 --- /dev/null +++ b/org.springframework.integration.adapter/src/test/java/org/springframework/integration/adapter/mail/config/pollingMailSourceParserTests.xml @@ -0,0 +1,13 @@ + + + + + + +