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`
This commit is contained in:
Artem Bilan
2019-01-17 16:01:44 -05:00
committed by Gary Russell
parent 0ac766040f
commit a5e437f94a
7 changed files with 155 additions and 32 deletions

View File

@@ -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();

View File

@@ -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}.

View File

@@ -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;
}

View File

@@ -29,8 +29,8 @@
</xsd:documentation>
</xsd:annotation>
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
<xsd:element ref="integration:poller" minOccurs="0"/>
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0"/>
</xsd:choice>
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
<xsd:attribute name="mail-sender" type="xsd:string">

View File

@@ -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<String>("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;
}
}
}

View File

@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/mail http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
xmlns:util="http://www.springframework.org/schema/util">
xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
xmlns:util="http://www.springframework.org/schema/util">
<int-mail:outbound-channel-adapter
id="adapterWithHostProperty" host="somehost" username="someuser"
password="somepassword" java-mail-properties="javaMailProperties"/>
id="adapterWithHostProperty"
password="somepassword"
java-mail-properties="javaMailProperties"/>
<util:properties id="javaMailProperties">
<prop key="mail.imap.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
@@ -18,6 +19,8 @@
<prop key="mail.transport.protocol">smtps</prop>
<prop key="mail.smtps.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.host">somehost</prop>
<prop key="mail.smtp.user">someuser</prop>
<prop key="mail.debug">false</prop>
</util:properties>
</beans>

View File

@@ -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 `<poller>` element (see <<endpoint-namespace>>).
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();
}
}
----
====