From a5e437f94a26aebefb59957d87d6c6e08c266891 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 17 Jan 2019 16:01:44 -0500 Subject: [PATCH] INT-2543: Relax mail host when mail-props present JIRA: https://jira.spring.io/browse/INT-2543 We may have a mail server host configured in the properties and the target session will resolve it properly from provided `javaMailProperties`. The same applies for `username` * Do not require `host` and `username`, when `javaMailProperties` is provided * Add additional factory methods into the `Mail` factory for Java DSL * Add Java DSL sample into the `mail.adoc` --- .../MailOutboundChannelAdapterParser.java | 30 ++++++--- .../integration/mail/dsl/Mail.java | 35 +++++++++- .../dsl/MailSendingMessageHandlerSpec.java | 28 ++++++-- .../config/spring-integration-mail-5.1.xsd | 4 +- ...MailOutboundChannelAdapterParserTests.java | 10 +-- ...OutboundWithJavamailProperties-context.xml | 15 +++-- src/reference/asciidoc/mail.adoc | 65 ++++++++++++++++++- 7 files changed, 155 insertions(+), 32 deletions(-) diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParser.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParser.java index 0a20f3dce2..f5b41b3aaa 100644 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParser.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2019 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. @@ -23,7 +23,6 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; 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.mail.MailSendingMessageHandler; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.util.Assert; @@ -33,6 +32,7 @@ import org.springframework.util.StringUtils; * Parser for the <outbound-channel-adapter/> element of the 'mail' namespace. * * @author Mark Fisher + * @author Artem Bilan */ public class MailOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { @@ -41,20 +41,28 @@ public class MailOutboundChannelAdapterParser extends AbstractOutboundChannelAda BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MailSendingMessageHandler.class); String mailSenderRef = element.getAttribute("mail-sender"); String host = element.getAttribute("host"); + boolean hasHost = StringUtils.hasText(host); String port = element.getAttribute("port"); String username = element.getAttribute("username"); String password = element.getAttribute("password"); + String javaMailProperties = element.getAttribute("java-mail-properties"); + boolean hasJavaMailProperties = StringUtils.hasText(javaMailProperties); if (StringUtils.hasText(mailSenderRef)) { - Assert.isTrue(!StringUtils.hasText(host) && !StringUtils.hasText(username) && !StringUtils.hasText(password), + Assert.isTrue(!hasHost + && !StringUtils.hasText(username) + && !StringUtils.hasText(password), "The 'host', 'username', and 'password' properties " + - "should not be provided when using a 'mail-sender' reference."); + "should not be provided when using a 'mail-sender' reference."); builder.addConstructorArgReference(mailSenderRef); } else { - Assert.hasText(host, "Either a 'mail-sender' reference or 'host' property is required."); + Assert.isTrue(!hasHost || !hasJavaMailProperties, + "Either a 'mail-sender' or 'java-mail-properties' reference or 'host' property is required."); BeanDefinitionBuilder mailSenderBuilder = BeanDefinitionBuilder.genericBeanDefinition(JavaMailSenderImpl.class); - mailSenderBuilder.addPropertyValue("host", host); + if (hasHost) { + mailSenderBuilder.addPropertyValue("host", host); + } if (StringUtils.hasText(username)) { mailSenderBuilder.addPropertyValue("username", username); } @@ -64,11 +72,13 @@ public class MailOutboundChannelAdapterParser extends AbstractOutboundChannelAda if (StringUtils.hasText(port)) { mailSenderBuilder.addPropertyValue("port", port); } - IntegrationNamespaceUtils.setReferenceIfAttributeDefined( - mailSenderBuilder, element, "java-mail-properties", "javaMailProperties"); + if (hasJavaMailProperties) { + mailSenderBuilder.addPropertyReference("javaMailProperties", javaMailProperties); + } - String mailSenderBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName( - mailSenderBuilder.getBeanDefinition(), parserContext.getRegistry()); + String mailSenderBeanName = + BeanDefinitionReaderUtils.registerWithGeneratedName(mailSenderBuilder.getBeanDefinition(), + parserContext.getRegistry()); builder.addConstructorArgReference(mailSenderBeanName); } return builder.getBeanDefinition(); diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/Mail.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/Mail.java index 2a171457b2..864d6adc8b 100644 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/Mail.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/Mail.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2019 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. @@ -17,22 +17,53 @@ package org.springframework.integration.mail.dsl; import org.springframework.integration.mail.ImapMailReceiver; +import org.springframework.integration.mail.MailSendingMessageHandler; import org.springframework.integration.mail.Pop3MailReceiver; import org.springframework.integration.mail.transformer.MailToStringTransformer; +import org.springframework.lang.Nullable; +import org.springframework.mail.MailSender; /** * The factory for Spring Integration Mail components. * * @author Gary Russell * @author Artem Bilan + * * @since 5.0 */ public final class Mail { - public static MailSendingMessageHandlerSpec outboundAdapter(String host) { + /** + * A {@link MailSendingMessageHandlerSpec} factory. + * Note: the Java Mail properties must be provided with the particular host. + * @return the {@link MailSendingMessageHandlerSpec} instance. + * @since 5.1.3 + * @see MailSendingMessageHandlerSpec#javaMailProperties + */ + public static MailSendingMessageHandlerSpec outboundAdapter() { + return new MailSendingMessageHandlerSpec(null); + } + + /** + * A {@link MailSendingMessageHandlerSpec} factory based on provide {@code host}. + * @param host the mail host to connect to. + * @return the {@link MailSendingMessageHandlerSpec} instance. + */ + public static MailSendingMessageHandlerSpec outboundAdapter(@Nullable String host) { return new MailSendingMessageHandlerSpec(host); } + /** + * A convenient factory method to produce {@link MailSendingMessageHandler} + * based on provided {@link MailSender}. + * @param mailSender the {@link MailSender} to use mail sending operations. + * @return the {@link MailSendingMessageHandler} instance. + * @since 5.1.3 + */ + public static MailSendingMessageHandler outboundAdapter(MailSender mailSender) { + return new MailSendingMessageHandler(mailSender); + } + /** * A {@link Pop3MailInboundChannelAdapterSpec} factory using a default * {@link Pop3MailReceiver}. diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailSendingMessageHandlerSpec.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailSendingMessageHandlerSpec.java index 7bfc0aac15..164d116829 100644 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailSendingMessageHandlerSpec.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailSendingMessageHandlerSpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2019 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. @@ -24,11 +24,13 @@ import javax.activation.FileTypeMap; import org.springframework.integration.dsl.MessageHandlerSpec; import org.springframework.integration.mail.MailSendingMessageHandler; import org.springframework.integration.support.PropertiesBuilder; +import org.springframework.lang.Nullable; import org.springframework.mail.javamail.JavaMailSenderImpl; /** * @author Gary Russell * @author Artem Bilan + * * @since 5.0 */ public class MailSendingMessageHandlerSpec @@ -36,7 +38,7 @@ public class MailSendingMessageHandlerSpec private final JavaMailSenderImpl sender = new JavaMailSenderImpl(); - MailSendingMessageHandlerSpec(String host) { + MailSendingMessageHandlerSpec(@Nullable String host) { this.sender.setHost(host); this.target = new MailSendingMessageHandler(this.sender); } @@ -70,7 +72,7 @@ public class MailSendingMessageHandlerSpec * @return the spec. * @see JavaMailSenderImpl#setProtocol(String) */ - public MailSendingMessageHandlerSpec protocol(String protocol) { + public MailSendingMessageHandlerSpec protocol(@Nullable String protocol) { this.sender.setProtocol(protocol); return this; } @@ -94,19 +96,33 @@ public class MailSendingMessageHandlerSpec * @see JavaMailSenderImpl#setUsername(String) * @see JavaMailSenderImpl#setPassword(String) */ - public MailSendingMessageHandlerSpec credentials(String username, String password) { + public MailSendingMessageHandlerSpec credentials(@Nullable String username, @Nullable String password) { this.sender.setUsername(username); this.sender.setPassword(password); return this; } + /** + * Set the mail user password. + * A convenient method when {@code username} is provided in the Java mail properties. + * @param password the password. + * @return the spec. + * @since 5.1.3 + * @see JavaMailSenderImpl#setPassword(String) + * @see #javaMailProperties(Properties) + */ + public MailSendingMessageHandlerSpec password(@Nullable String password) { + this.sender.setPassword(password); + return this; + } + /** * Set the default encoding. * @param defaultEncoding the default encoding. * @return the spec. * @see JavaMailSenderImpl#setDefaultEncoding(String) */ - public MailSendingMessageHandlerSpec defaultEncoding(String defaultEncoding) { + public MailSendingMessageHandlerSpec defaultEncoding(@Nullable String defaultEncoding) { this.sender.setDefaultEncoding(defaultEncoding); return this; } @@ -117,7 +133,7 @@ public class MailSendingMessageHandlerSpec * @return the spec. * @see JavaMailSenderImpl#setDefaultFileTypeMap(FileTypeMap) */ - public MailSendingMessageHandlerSpec defaultFileTypeMap(FileTypeMap defaultFileTypeMap) { + public MailSendingMessageHandlerSpec defaultFileTypeMap(@Nullable FileTypeMap defaultFileTypeMap) { this.sender.setDefaultFileTypeMap(defaultFileTypeMap); return this; } diff --git a/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-5.1.xsd b/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-5.1.xsd index 219cbaca32..8f84394dc1 100644 --- a/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-5.1.xsd +++ b/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-5.1.xsd @@ -29,8 +29,8 @@ - - + + diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParserTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParserTests.java index 601ea14e1a..0939c572b2 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParserTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -40,6 +40,7 @@ import org.springframework.messaging.support.GenericMessage; * @author Mark Fisher * @author Gary Russell * @author Gunnar Hillert + * @author Artem Bilan */ public class MailOutboundChannelAdapterParserTests { @@ -66,7 +67,7 @@ public class MailOutboundChannelAdapterParserTests { Object adapter = context.getBean("advised.adapter"); MessageHandler handler = (MessageHandler) new DirectFieldAccessor(adapter).getPropertyValue("handler"); - handler.handleMessage(new GenericMessage("foo")); + handler.handleMessage(new GenericMessage<>("foo")); assertEquals(1, adviceCalled); context.close(); } @@ -105,7 +106,7 @@ public class MailOutboundChannelAdapterParserTests { MailSender mailSender = (MailSender) fieldAccessor.getPropertyValue("mailSender"); assertNotNull(mailSender); Properties javaMailProperties = (Properties) TestUtils.getPropertyValue(mailSender, "javaMailProperties"); - assertEquals(7, javaMailProperties.size()); + assertEquals(9, javaMailProperties.size()); assertNotNull(javaMailProperties); assertEquals("true", javaMailProperties.get("mail.smtps.auth")); context.close(); @@ -114,10 +115,11 @@ public class MailOutboundChannelAdapterParserTests { public static class FooAdvice extends AbstractRequestHandlerAdvice { @Override - protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) { adviceCalled++; return null; } } + } diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundWithJavamailProperties-context.xml b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundWithJavamailProperties-context.xml index a0634aca65..afa183d550 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundWithJavamailProperties-context.xml +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundWithJavamailProperties-context.xml @@ -1,15 +1,16 @@ + xmlns:int-mail="http://www.springframework.org/schema/integration/mail" + xmlns:util="http://www.springframework.org/schema/util"> + id="adapterWithHostProperty" + password="somepassword" + java-mail-properties="javaMailProperties"/> javax.net.ssl.SSLSocketFactory @@ -18,6 +19,8 @@ smtps true true + somehost + someuser false diff --git a/src/reference/asciidoc/mail.adoc b/src/reference/asciidoc/mail.adoc index e42d14c7f8..c5138d7e9f 100644 --- a/src/reference/asciidoc/mail.adoc +++ b/src/reference/asciidoc/mail.adoc @@ -232,6 +232,18 @@ Alternatively, you can provide the host, username, and password, as the followin ---- ==== +Starting with version 5.1.3, the `host`, `username` ane `mail-sender` can be omitted, if `java-mail-properties` is provided. +However the `host` and `username` has to be configured with appropriate Java mail properties, e.g. for SMTP: + +==== +[source] +---- +mail.user=someuser@gmail.com +mail.smtp.host=smtp.gmail.com +mail.smtp.port=587 +---- +==== + NOTE: As with any outbound Channel Adapter, if the referenced channel is a `PollableChannel`, you should provide a `` element (see <>). When you use the namespace support, you can also use a `header-enricher` message transformer. @@ -322,8 +334,7 @@ By default, the `ImapMailReceiver` searches for messages based on the default `S * hHave not been processed by this mail receiver (enabled by the use of the custom USER flag or simply NOT FLAGGED if not supported) The custom user flag is `spring-integration-mail-adapter`, but you can configure it. -Since version 2.2, the `SearchTerm` used by the `ImapMailReceiver` is fully configurable with `SearchTermStrategy`, -which you can inject by using the `search-term-strategy` attribute. +Since version 2.2, the `SearchTerm` used by the `ImapMailReceiver` is fully configurable with `SearchTermStrategy`, which you can inject by using the `search-term-strategy` attribute. `SearchTermStrategy` is a strategy interface with a single method that lets you create an instance of the `SearchTerm` used by the `ImapMailReceiver`. The following listing shows the `SearchTermStrategy` interface: @@ -548,3 +559,53 @@ public class Mover { ==== IMPORTANT: For the message to be still available for manipulation after the transaction, _should-delete-messages_ must be set to 'false'. + +[[java-dsl-configuration]] +=== Configuring channel adapters with the Java DSL + +To configure mail mail component in Java DSL, the framework provides a `o.s.i.mail.dsl.Mail` factory, which can be used like this: + +==== +[source, java] +---- +@SpringBootApplication +public class MailApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder(MailApplication.class) + .web(false) + .run(args); + } + + @Bean + public IntegrationFlow imapMailFlow() { + return IntegrationFlows + .from(Mail.imapInboundAdapter("imap://user:pw@host:port/INBOX") + .searchTermStrategy(this::fromAndNotSeenTerm) + .userFlag("testSIUserFlag") + .simpleContent(true) + .javaMailProperties(p -> p.put("mail.debug", "false")), + e -> e.autoStartup(true) + .poller(p -> p.fixedDelay(1000))) + .channel(MessageChannels.queue("imapChannel")) + .get(); + } + + @Bean + public IntegrationFlow sendMailFlow() { + return IntegrationFlows.from("sendMailChannel") + .enrichHeaders(Mail.headers() + .subjectFunction(m -> "foo") + .from("foo@bar") + .toFunction(m -> new String[] { "bar@baz" })) + .handle(Mail.outboundAdapter("gmail") + .port(smtpServer.getPort()) + .credentials("user", "pw") + .protocol("smtp")), + e -> e.id("sendMailEndpoint")) + .get(); + } +} +---- +==== +