From f80c0db0df33b8f2a2c34899e3bab9ec366fb307 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 17 Nov 2015 14:29:58 -0500 Subject: [PATCH] INT-3861: Mail Namespace Support for userFlag JIRA: https://jira.spring.io/browse/INT-3861 Allow the custom `userFlag` to be configured using the namespace. Polishing according PR comments --- .../mail/AbstractMailReceiver.java | 35 ++++++++----------- .../config/ImapIdleChannelAdapterParser.java | 3 +- .../MailInboundChannelAdapterParser.java | 6 ++-- .../mail/config/MailReceiverFactoryBean.java | 18 ++++++++-- .../config/spring-integration-mail-4.3.xsd | 10 ++++++ ...pIdleChannelAdapterParserTests-context.xml | 15 ++++---- .../ImapIdleChannelAdapterParserTests.java | 7 ++-- ...boundChannelAdapterParserTests-context.xml | 7 ++-- .../InboundChannelAdapterParserTests.java | 15 +++++--- src/reference/asciidoc/mail.adoc | 2 +- src/reference/asciidoc/whats-new.adoc | 6 ++++ 11 files changed, 81 insertions(+), 43 deletions(-) diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java index b963e8f914..56ae5ef7cc 100755 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java @@ -52,20 +52,15 @@ import org.springframework.util.Assert; * @author Oleg Zhurakousky * @author Gary Russell */ -public abstract class AbstractMailReceiver extends IntegrationObjectSupport implements MailReceiver, DisposableBean{ - - public final static String DEFAULT_SI_USER_FLAG = "spring-integration-mail-adapter"; +public abstract class AbstractMailReceiver extends IntegrationObjectSupport implements MailReceiver, DisposableBean { /** * Default user flag for marking messages as seen by this receiver: * {@value #DEFAULT_SI_USER_FLAG}. - * @deprecated - this constant will be removed in 4.3; see - * {@link #setUserFlag(String)} and {@link #DEFAULT_SI_USER_FLAG} */ - @Deprecated - public final static String SI_USER_FLAG = DEFAULT_SI_USER_FLAG; + public final static String DEFAULT_SI_USER_FLAG = "spring-integration-mail-adapter"; - protected final Log logger = LogFactory.getLog(this.getClass()); + protected final Log logger = LogFactory.getLog(getClass()); private final URLName url; @@ -155,7 +150,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl } protected Properties getJavaMailProperties() { - return javaMailProperties; + return this.javaMailProperties; } /** @@ -197,7 +192,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl } protected String getUserFlag() { - return userFlag; + return this.userFlag; } /** @@ -286,7 +281,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl if (logger.isInfoEnabled()) { logger.info("attempting to receive mail from folder [" + this.getFolder().getFullName() + "]"); } - Message[] messages = this.searchForNewMessages(); + Message[] messages = searchForNewMessages(); if (this.maxFetchSize > 0 && messages.length > this.maxFetchSize) { Message[] reducedMessages = new Message[this.maxFetchSize]; System.arraycopy(messages, 0, reducedMessages, 0, this.maxFetchSize); @@ -296,16 +291,16 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl logger.debug("found " + messages.length + " new messages"); } if (messages.length > 0) { - this.fetchMessages(messages); + fetchMessages(messages); } if (logger.isDebugEnabled()) { logger.debug("Received " + messages.length + " messages"); } - Message[] filteredMessages = this.filterMessagesThruSelector(messages); + Message[] filteredMessages = filterMessagesThruSelector(messages); - this.postProcessFilteredMessages(filteredMessages); + postProcessFilteredMessages(filteredMessages); return filteredMessages; } @@ -316,10 +311,10 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl } private void postProcessFilteredMessages(Message[] filteredMessages) throws MessagingException { - this.setMessageFlags(filteredMessages); + setMessageFlags(filteredMessages); - if (this.shouldDeleteMessages()) { - this.deleteMessages(filteredMessages); + if (shouldDeleteMessages()) { + deleteMessages(filteredMessages); } // Copy messages to cause an eager fetch for (int i = 0; i < filteredMessages.length; i++) { @@ -331,7 +326,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl private void setMessageFlags(Message[] filteredMessages) throws MessagingException { boolean recentFlagSupported = false; - Flags flags = this.getFolder().getPermanentFlags(); + Flags flags = getFolder().getPermanentFlags(); if (flags != null){ recentFlagSupported = flags.contains(Flags.Flag.RECENT); @@ -355,7 +350,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl message.setFlag(Flags.Flag.FLAGGED, true); } } - this.setAdditionalFlags(message); + setAdditionalFlags(message); } } @@ -438,7 +433,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl protected void onInit() throws Exception { super.onInit(); this.folderOpenMode = Folder.READ_WRITE; - this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory()); this.initialized = true; } diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParser.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParser.java index 9450ff1803..55d5009d39 100644 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParser.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -62,6 +62,7 @@ public class ImapIdleChannelAdapterParser extends AbstractChannelAdapterParser { private BeanDefinition parseImapMailReceiver(Element element, ParserContext parserContext) { BeanDefinitionBuilder receiverBuilder = BeanDefinitionBuilder.genericBeanDefinition(ImapMailReceiver.class); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(receiverBuilder, element, "search-term-strategy"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(receiverBuilder, element, "user-flag"); Object source = parserContext.extractSource(element); String uri = element.getAttribute("store-uri"); if (StringUtils.hasText(uri)) { diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailInboundChannelAdapterParser.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailInboundChannelAdapterParser.java index 8f90cc6462..68d6e5b0c5 100644 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailInboundChannelAdapterParser.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailInboundChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -16,6 +16,8 @@ package org.springframework.integration.mail.config; +import org.w3c.dom.Element; + import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -26,7 +28,6 @@ import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.integration.mail.MailReceivingMessageSource; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Element; /** * Parser for the <inbound-channel-adapter> element of Spring Integration's 'mail' namespace. @@ -52,6 +53,7 @@ public class MailInboundChannelAdapterParser extends AbstractPollingInboundChann IntegrationNamespaceUtils.setValueIfAttributeDefined(receiverBuilder, element, "store-uri"); IntegrationNamespaceUtils.setValueIfAttributeDefined(receiverBuilder, element, "protocol"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(receiverBuilder, element, "search-term-strategy"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(receiverBuilder, element, "user-flag"); String session = element.getAttribute("session"); if (StringUtils.hasText(session)) { if (element.hasAttribute("java-mail-properties") || element.hasAttribute("authenticator")) { diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java index 9083443fe6..42d50a4d8f 100644 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2015 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. @@ -42,6 +42,7 @@ import org.springframework.util.StringUtils; /** * @author Mark Fisher * @author Oleg Zhurakousky + * @author Gary Russell * @since 1.0.3 */ public class MailReceiverFactoryBean implements FactoryBean, DisposableBean, BeanFactoryAware { @@ -74,6 +75,8 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo private volatile SearchTermStrategy searchTermStrategy; + private volatile String userFlag; + private volatile BeanFactory beanFactory; public void setStoreUri(String storeUri) { @@ -120,11 +123,16 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo this.searchTermStrategy = searchTermStrategy; } + public void setUserFlag(String userFlag) { + this.userFlag = userFlag; + } + @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } + @Override public MailReceiver getObject() throws Exception { if (this.receiver == null) { this.receiver = this.createReceiver(); @@ -132,10 +140,12 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo return this.receiver; } + @Override public Class getObjectType() { return (this.receiver != null) ? this.receiver.getClass() : MailReceiver.class; } + @Override public boolean isSingleton() { return true; } @@ -165,7 +175,7 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo AbstractMailReceiver receiver = isPop3 ? new Pop3MailReceiver(this.storeUri) : new ImapMailReceiver(this.storeUri); if (this.session != null) { Assert.isNull(this.javaMailProperties, "JavaMail Properties are not allowed when a Session has been provided."); - Assert.isNull(this.authenticator, "A JavaMail Authenticator is not allowed when a Session has been provied."); + Assert.isNull(this.authenticator, "A JavaMail Authenticator is not allowed when a Session has been provided."); receiver.setSession(this.session); } if (this.searchTermStrategy != null) { @@ -185,6 +195,9 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo } receiver.setMaxFetchSize(this.maxFetchSize); receiver.setSelectorExpression(selectorExpression); + if (StringUtils.hasText(this.userFlag)) { + receiver.setUserFlag(this.userFlag); + } if (isPop3) { if (this.isShouldMarkMessagesAsRead() && this.logger.isWarnEnabled()) { @@ -201,6 +214,7 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo return receiver; } + @Override public void destroy() throws Exception { if (this.receiver != null && this.receiver instanceof DisposableBean) { ((DisposableBean) this.receiver).destroy(); diff --git a/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-4.3.xsd b/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-4.3.xsd index 99cef05dec..af8906e680 100644 --- a/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-4.3.xsd +++ b/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-4.3.xsd @@ -253,6 +253,16 @@ ]]> + + + + + diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests-context.xml b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests-context.xml index d475a80e48..3ed6af317d 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests-context.xml +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests-context.xml @@ -18,7 +18,7 @@ http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/mail http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd"> - + @@ -36,21 +36,22 @@ channel="channel" auto-startup="false" should-delete-messages="true"/> - + - + - + - + - \ No newline at end of file + diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests.java index 3d04a1b083..a6391d7ba7 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -30,15 +30,16 @@ import javax.mail.search.SearchTerm; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; -import org.springframework.messaging.MessageChannel; import org.springframework.integration.mail.ImapIdleChannelAdapter; import org.springframework.integration.mail.ImapMailReceiver; import org.springframework.integration.mail.SearchTermStrategy; import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -120,6 +121,7 @@ public class ImapIdleChannelAdapterParserTests { assertEquals(2, properties.size()); assertEquals(Boolean.TRUE, receiverAccessor.getPropertyValue("shouldDeleteMessages")); assertEquals(Boolean.TRUE, receiverAccessor.getPropertyValue("shouldMarkMessagesAsRead")); + assertEquals("flagged", receiverAccessor.getPropertyValue("userFlag")); } @Test @@ -191,6 +193,7 @@ public class ImapIdleChannelAdapterParserTests { } public static class TestSearchTermStrategy implements SearchTermStrategy { + @Override public SearchTerm generateSearchTerm(Flags supportedFlags, Folder folder) { return null; } diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests-context.xml b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests-context.xml index e68d951233..0bd4d9688a 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests-context.xml +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests-context.xml @@ -91,9 +91,9 @@ - + - + @@ -106,7 +106,8 @@ - diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests.java index af35d96184..a39d8cb63e 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -37,7 +37,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.messaging.MessageChannel; import org.springframework.integration.endpoint.SourcePollingChannelAdapter; import org.springframework.integration.mail.AbstractMailReceiver; import org.springframework.integration.mail.ImapIdleChannelAdapter; @@ -45,12 +44,14 @@ import org.springframework.integration.mail.ImapMailReceiver; import org.springframework.integration.mail.Pop3MailReceiver; import org.springframework.integration.mail.SearchTermStrategy; import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Mark Fisher * @author Oleg Zhurakousky + * @author Gary Russell * @since 1.0.5 */ @ContextConfiguration @@ -278,7 +279,8 @@ public class InboundChannelAdapterParserTests { public void inboundChannelAdapterRequiresShouldDeleteMessages() { try { new ClassPathXmlApplicationContext( - "org/springframework/integration/mail/config/InboundChannelAdapterParserTests-invalidContext.xml"); + "org/springframework/integration/mail/config/InboundChannelAdapterParserTests-invalidContext.xml") + .close(); fail("expected a parser error"); } catch(BeanDefinitionStoreException e) { @@ -293,16 +295,19 @@ public class InboundChannelAdapterParserTests { public void imapWithSearchTermStrategy() { AbstractMailReceiver receiver = this.getReceiver("imapWithSearch"); assertEquals(ImapMailReceiver.class, receiver.getClass()); - Object sts = new DirectFieldAccessor(receiver).getPropertyValue("searchTermStrategy"); + DirectFieldAccessor receiverAccessor = new DirectFieldAccessor(receiver); + Object sts = receiverAccessor.getPropertyValue("searchTermStrategy"); assertNotNull(sts); assertSame(context.getBean(SearchTermStrategy.class), sts); + assertEquals("flagged", receiverAccessor.getPropertyValue("userFlag")); } @Test public void pop3WithSearchTermStrategy() { try { new ClassPathXmlApplicationContext( - "org/springframework/integration/mail/config/InboundChannelAdapterParserTests-pop3Search-context.xml"); + "org/springframework/integration/mail/config/InboundChannelAdapterParserTests-pop3Search-context.xml") + .close(); fail("expected a parser error"); } catch(BeanCreationException e) { diff --git a/src/reference/asciidoc/mail.adoc b/src/reference/asciidoc/mail.adoc index 63cf4bea5a..b6c39bc89f 100644 --- a/src/reference/asciidoc/mail.adoc +++ b/src/reference/asciidoc/mail.adoc @@ -271,7 +271,7 @@ As discussed in <>, the default `SearchTermStrategy` will ignore me Starting with _version 4.2.2_, the name of the user flag can be set using `setUserFlag` on the `MailReceiver` - this allows multiple receivers to use a different flag (as long as the mail server supports user flags). -Changing the user flag is not currently supported by the XML namespace. +The attribute `user-flag` is available when configuring the adapter with the namespace. [[mail-filtering]] === Email Message Filtering diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 29f9625004..335611ac11 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -20,3 +20,9 @@ It was completely ignored; the gateway's reply goes to the next chain element, o if the gateway is the last element. This condition is now detected and disallowed. If you have such configuration, simply remove the `reply-channel`. + +==== Mail Changes + +The customizable `userFlag` added in 4.2.2 to provide customization of the flag used to denote that the mail has been +seen is now available using the XML namespace. +See <> for more information.