INT-4159: Port Mail DSL and Improvements

JIRA: https://jira.spring.io/browse/INT-4159

Port over DSL for the Mail module

- make tests compatible with the s-i-test TestMailServer
- add `simpleContent` boolean to inbound spec

Polishing - add tx() to ImapIdleChannelAdapterSpec

More Polishing

Rename `Mail.fromMail()` to `Mail.toStringTransformer()` to better reflect reality
This commit is contained in:
Gary Russell
2016-11-10 17:03:53 -05:00
committed by Artem Bilan
parent f96c42d156
commit 803d9f0875
10 changed files with 1642 additions and 17 deletions

View File

@@ -77,8 +77,8 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
}
/**
* Configure a list of {@link Advice} objects to be applied, in nested order, to the endpoint's handler.
* The advice objects are applied only to the handler.
* Configure a list of {@link Advice} objects to be applied, in nested order, to the
* endpoint's handler. The advice objects are applied only to the handler.
* @param advice the advice chain.
* @return the endpoint spec.
*/
@@ -88,9 +88,9 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
}
/**
* Specify a {@link TransactionInterceptor} {@link Advice} with the
* provided {@code PlatformTransactionManager} and default {@link DefaultTransactionAttribute}
* for the {@code pollingTask}.
* Specify a {@link TransactionInterceptor} {@link Advice} with the provided
* {@code PlatformTransactionManager} and default {@link DefaultTransactionAttribute}
* for the {@link MessageHandler}.
* @param transactionManager the {@link PlatformTransactionManager} to use.
* @return the spec.
*/
@@ -99,13 +99,14 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
}
/**
* Specify a {@link TransactionInterceptor} {@link Advice} with the
* provided {@code PlatformTransactionManager} and default {@link DefaultTransactionAttribute}
* for the {@code pollingTask}.
* Specify a {@link TransactionInterceptor} {@link Advice} with the provided
* {@code PlatformTransactionManager} and default {@link DefaultTransactionAttribute}
* for the {@link MessageHandler}.
* @param transactionManager the {@link PlatformTransactionManager} to use.
* @param handleMessageAdvice the flag to indicate the target {@link Advice} type:
* {@code false} - regular {@link TransactionInterceptor};
* {@code true} - {@link org.springframework.integration.transaction.TransactionHandleMessageAdvice} extension.
* {@code false} - regular {@link TransactionInterceptor}; {@code true} -
* {@link org.springframework.integration.transaction.TransactionHandleMessageAdvice}
* extension.
* @return the spec.
*/
public S transactional(PlatformTransactionManager transactionManager, boolean handleMessageAdvice) {
@@ -115,7 +116,7 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
}
/**
* Specify a {@link TransactionInterceptor} {@link Advice} for the {@code pollingTask}.
* Specify a {@link TransactionInterceptor} {@link Advice} for the {@link MessageHandler}.
* @param transactionInterceptor the {@link TransactionInterceptor} to use.
* @return the spec.
* @see TransactionInterceptorBuilder
@@ -125,8 +126,9 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
}
/**
* Specify a {@link TransactionInterceptor} {@link Advice} with default {@code PlatformTransactionManager}
* and {@link DefaultTransactionAttribute} for the {@code pollingTask}.
* Specify a {@link TransactionInterceptor} {@link Advice} with default
* {@code PlatformTransactionManager} and {@link DefaultTransactionAttribute} for the
* {@link MessageHandler}.
* @return the spec.
*/
public S transactional() {
@@ -134,11 +136,13 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
}
/**
* Specify a {@link TransactionInterceptor} {@link Advice} with default {@code PlatformTransactionManager}
* and {@link DefaultTransactionAttribute} for the {@code pollingTask}.
* Specify a {@link TransactionInterceptor} {@link Advice} with default
* {@code PlatformTransactionManager} and {@link DefaultTransactionAttribute} for the
* {@link MessageHandler}.
* @param handleMessageAdvice the flag to indicate the target {@link Advice} type:
* {@code false} - regular {@link TransactionInterceptor};
* {@code true} - {@link org.springframework.integration.transaction.TransactionHandleMessageAdvice} extension.
* {@code false} - regular {@link TransactionInterceptor}; {@code true} -
* {@link org.springframework.integration.transaction.TransactionHandleMessageAdvice}
* extension.
* @return the spec.
*/
public S transactional(boolean handleMessageAdvice) {

View File

@@ -0,0 +1,359 @@
/*
* Copyright 2014-2016 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.mail.dsl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import org.aopalliance.aop.Advice;
import org.springframework.expression.Expression;
import org.springframework.integration.dsl.ComponentsRegistration;
import org.springframework.integration.dsl.MessageProducerSpec;
import org.springframework.integration.expression.FunctionExpression;
import org.springframework.integration.mail.ImapIdleChannelAdapter;
import org.springframework.integration.mail.ImapMailReceiver;
import org.springframework.integration.mail.SearchTermStrategy;
import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.integration.support.PropertiesBuilder;
import org.springframework.integration.transaction.TransactionInterceptorBuilder;
import org.springframework.integration.transaction.TransactionSynchronizationFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.Assert;
/**
* A {@link MessageProducerSpec} for a {@link ImapIdleChannelAdapter}.
*
* @author Gary Russell
* @author Artem Bilan
*
* @since 5.0
*/
public class ImapIdleChannelAdapterSpec
extends MessageProducerSpec<ImapIdleChannelAdapterSpec, ImapIdleChannelAdapter>
implements ComponentsRegistration {
private final ImapMailReceiver receiver;
private final Collection<Object> componentsToRegister = new ArrayList<Object>();
private final List<Advice> adviceChain = new LinkedList<>();
protected final boolean externalReceiver;
private boolean sessionProvided;
ImapIdleChannelAdapterSpec(ImapMailReceiver receiver) {
this(receiver, false);
}
ImapIdleChannelAdapterSpec(ImapMailReceiver receiver, boolean externalReceiver) {
super(new ImapIdleChannelAdapter(receiver));
this.target.setAdviceChain(this.adviceChain);
this.receiver = receiver;
this.componentsToRegister.add(receiver);
this.externalReceiver = externalReceiver;
}
/**
* Configure a SpEL expression to select messages. The root object for the expression
* evaluation is a {@link javax.mail.internet.MimeMessage} which should return a boolean
* result (true means select the message).
* @param selectorExpression the selectorExpression.
* @return the spec.
*/
public ImapIdleChannelAdapterSpec selectorExpression(String selectorExpression) {
return selectorExpression(PARSER.parseExpression(selectorExpression));
}
/**
* Configure an {@link Expression} to select messages. The root object for the expression
* evaluation is a {@link javax.mail.internet.MimeMessage} which should return a boolean
* result (true means select the message).
* @param selectorExpression the selectorExpression.
* @return the spec.
*/
public ImapIdleChannelAdapterSpec selectorExpression(Expression selectorExpression) {
assertReceiver();
this.receiver.setSelectorExpression(selectorExpression);
return this;
}
private void assertReceiver() {
Assert.state(!this.externalReceiver, "An external 'receiver' [" + this.receiver + "] can't be modified.");
}
/**
* Configure a {@link Function} to select messages. The argument for the function
* is a {@link javax.mail.internet.MimeMessage}; {@code apply} returns a boolean
* result (true means select the message).
* @param selectorFunction the selectorFunction.
* @return the spec.
* @see FunctionExpression
*/
public ImapIdleChannelAdapterSpec selector(Function<MimeMessage, Boolean> selectorFunction) {
return selectorExpression(new FunctionExpression<MimeMessage>(selectorFunction));
}
/**
* A Java Mail {@link Session} to use.
* @param session the session.
* @return the spec.
* @see ImapMailReceiver#setSession(Session)
*/
public ImapIdleChannelAdapterSpec session(Session session) {
assertReceiver();
this.receiver.setSession(session);
this.sessionProvided = true;
return this;
}
/**
* @param javaMailProperties the javaMailProperties.
* @return the spec.
* @see ImapMailReceiver#setJavaMailProperties(Properties)
*/
public ImapIdleChannelAdapterSpec javaMailProperties(Properties javaMailProperties) {
assertReceiver();
assertSession();
this.receiver.setJavaMailProperties(javaMailProperties);
return this;
}
private void assertSession() {
Assert.state(!this.sessionProvided, "Neither 'javaMailProperties' nor 'javaMailAuthenticator' "
+ "references are allowed when a 'session' reference has been provided.");
}
/**
* Configure the {@code javaMailProperties} by invoking a {@link Consumer} callback which
* is invoked with a {@link PropertiesBuilder}.
* @param configurer the configurer.
* @return the spec.
* @see ImapMailReceiver#setJavaMailProperties(Properties)
*/
public ImapIdleChannelAdapterSpec javaMailProperties(Consumer<PropertiesBuilder> configurer) {
PropertiesBuilder properties = new PropertiesBuilder();
configurer.accept(properties);
return javaMailProperties(properties.get());
}
/**
* @param javaMailAuthenticator the javaMailAuthenticator.
* @return the spec.
* @see ImapMailReceiver#setJavaMailAuthenticator(Authenticator)
*/
public ImapIdleChannelAdapterSpec javaMailAuthenticator(Authenticator javaMailAuthenticator) {
assertReceiver();
assertSession();
this.receiver.setJavaMailAuthenticator(javaMailAuthenticator);
return this;
}
/**
* @param maxFetchSize the maxFetchSize.
* @return the spec.
* @see ImapMailReceiver#setMaxFetchSize(int)
*/
public ImapIdleChannelAdapterSpec maxFetchSize(int maxFetchSize) {
assertReceiver();
this.receiver.setMaxFetchSize(maxFetchSize);
return this;
}
/**
* @param shouldDeleteMessages the shouldDeleteMessages.
* @return the spec.
* @see ImapMailReceiver#setShouldDeleteMessages(boolean)
*/
public ImapIdleChannelAdapterSpec shouldDeleteMessages(boolean shouldDeleteMessages) {
assertReceiver();
this.receiver.setShouldDeleteMessages(shouldDeleteMessages);
return this;
}
/**
* @param searchTermStrategy the searchTermStrategy.
* @return the spec.
* @see ImapMailReceiver#setSearchTermStrategy(SearchTermStrategy)
*/
public ImapIdleChannelAdapterSpec searchTermStrategy(SearchTermStrategy searchTermStrategy) {
assertReceiver();
this.receiver.setSearchTermStrategy(searchTermStrategy);
return this;
}
/**
* @param shouldMarkMessagesAsRead the shouldMarkMessagesAsRead.
* @return the spec.
* @see ImapMailReceiver#setShouldMarkMessagesAsRead(Boolean)
*/
public ImapIdleChannelAdapterSpec shouldMarkMessagesAsRead(boolean shouldMarkMessagesAsRead) {
assertReceiver();
this.receiver.setShouldMarkMessagesAsRead(shouldMarkMessagesAsRead);
return this;
}
/**
* Set the name of the flag to use to flag messages when the server does
* not support \Recent but supports user flags;
* default {@value ImapMailReceiver#DEFAULT_SI_USER_FLAG}.
* @param userFlag the flag.
* @return the spec.
* @see ImapMailReceiver#setUserFlag(String)
*/
public ImapIdleChannelAdapterSpec userFlag(String userFlag) {
assertReceiver();
this.receiver.setUserFlag(userFlag);
return _this();
}
/**
* Set the header mapper; if a header mapper is not provided, the message payload is
* a {@link MimeMessage}, when provided, the headers are mapped and the payload is
* the {@link MimeMessage} content.
* @param headerMapper the header mapper.
* @return the spec.
* @see ImapMailReceiver#setUserFlag(String)
* @see #embeddedPartsAsBytes(boolean)
*/
public ImapIdleChannelAdapterSpec headerMapper(HeaderMapper<MimeMessage> headerMapper) {
assertReceiver();
this.receiver.setHeaderMapper(headerMapper);
return _this();
}
/**
* When a header mapper is provided determine whether an embedded {@link Part} (e.g
* {@link Message} or {@link javax.mail.Multipart} content is rendered as a byte[] in
* the payload. Otherwise, leave as a {@link Part}. These objects are not suitable for
* downstream serialization. Default: true.
* <p>
* This has no effect if there is no header mapper, in that case the payload is the
* {@link MimeMessage}.
* @param embeddedPartsAsBytes the embeddedPartsAsBytes to set.
* @return the spec.
* @see #headerMapper(HeaderMapper)
*/
public ImapIdleChannelAdapterSpec embeddedPartsAsBytes(boolean embeddedPartsAsBytes) {
assertReceiver();
this.receiver.setEmbeddedPartsAsBytes(embeddedPartsAsBytes);
return _this();
}
/**
* Configure a {@link TransactionSynchronizationFactory}. Usually used to synchronize
* message deletion with some external transaction manager.
* @param transactionSynchronizationFactory the transactionSynchronizationFactory.
* @return the spec.
*/
public ImapIdleChannelAdapterSpec transactionSynchronizationFactory(
TransactionSynchronizationFactory transactionSynchronizationFactory) {
this.target.setTransactionSynchronizationFactory(transactionSynchronizationFactory);
return this;
}
/**
* Configure a chain of {@link Advice} objects for message delivery, applied to
* the downstream flow.
* @param adviceChain the advice chain.
* @return the spec.
*/
public ImapIdleChannelAdapterSpec adviceChain(Advice... adviceChain) {
this.adviceChain.addAll(Arrays.asList(adviceChain));
return this;
}
/**
* Specify a {@link TransactionInterceptor} {@link Advice} with the provided
* {@code PlatformTransactionManager} and default {@link DefaultTransactionAttribute}
* for the downstream flow.
* @param transactionManager the {@link PlatformTransactionManager} to use.
* @return the spec.
*/
public ImapIdleChannelAdapterSpec transactional(PlatformTransactionManager transactionManager) {
return transactional(new TransactionInterceptorBuilder(false)
.transactionManager(transactionManager)
.build());
}
/**
* Specify a {@link TransactionInterceptor} {@link Advice} for the
* downstream flow.
* @param transactionInterceptor the {@link TransactionInterceptor} to use.
* @return the spec.
* @see TransactionInterceptorBuilder
*/
public ImapIdleChannelAdapterSpec transactional(TransactionInterceptor transactionInterceptor) {
return adviceChain(transactionInterceptor);
}
/**
* Specify a {@link TransactionInterceptor} {@link Advice} with default
* {@code PlatformTransactionManager} and {@link DefaultTransactionAttribute} for the
* downstream flow.
* @return the spec.
*/
public ImapIdleChannelAdapterSpec transactional() {
TransactionInterceptor transactionInterceptor = new TransactionInterceptorBuilder(false).build();
this.componentsToRegister.add(transactionInterceptor);
return transactional(transactionInterceptor);
}
/**
* Specify a task executor to be used to send messages to the downstream flow.
* @param sendingTaskExecutor the sendingTaskExecutor.
* @return the spec.
* @see ImapIdleChannelAdapter#setSendingTaskExecutor(Executor)
*/
public ImapIdleChannelAdapterSpec sendingTaskExecutor(Executor sendingTaskExecutor) {
this.target.setSendingTaskExecutor(sendingTaskExecutor);
return this;
}
/**
* @param shouldReconnectAutomatically the shouldReconnectAutomatically.
* @return the spec.
* @see ImapIdleChannelAdapter#setShouldReconnectAutomatically(boolean)
*/
public ImapIdleChannelAdapterSpec shouldReconnectAutomatically(boolean shouldReconnectAutomatically) {
this.target.setShouldReconnectAutomatically(shouldReconnectAutomatically);
return this;
}
@Override
public Collection<Object> getComponentsToRegister() {
return this.componentsToRegister;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2014-2016 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.mail.dsl;
import org.springframework.integration.mail.ImapMailReceiver;
import org.springframework.integration.mail.SearchTermStrategy;
/**
* A {@link MailInboundChannelAdapterSpec} for IMAP.
*
* @author Gary Russell
* @author Artem Bilan
* @since 5.0
*/
public class ImapMailInboundChannelAdapterSpec
extends MailInboundChannelAdapterSpec<ImapMailInboundChannelAdapterSpec, ImapMailReceiver> {
ImapMailInboundChannelAdapterSpec() {
super(new ImapMailReceiver());
}
ImapMailInboundChannelAdapterSpec(ImapMailReceiver imapMailReceiver) {
super(imapMailReceiver, true);
}
ImapMailInboundChannelAdapterSpec(String url) {
super(new ImapMailReceiver(url), false);
}
/**
* A {@link SearchTermStrategy} to use.
* @param searchTermStrategy the searchTermStrategy.
* @return the spec.
* @see ImapMailReceiver#setSearchTermStrategy(SearchTermStrategy)
*/
public ImapMailInboundChannelAdapterSpec searchTermStrategy(SearchTermStrategy searchTermStrategy) {
assertReceiver();
this.receiver.setSearchTermStrategy(searchTermStrategy);
return this;
}
/**
* A flag to determine if message should be marked as read.
* @param shouldMarkMessagesAsRead the shouldMarkMessagesAsRead.
* @return the spec.
* @see ImapMailReceiver#setShouldMarkMessagesAsRead(Boolean)
*/
public ImapMailInboundChannelAdapterSpec shouldMarkMessagesAsRead(boolean shouldMarkMessagesAsRead) {
assertReceiver();
this.receiver.setShouldMarkMessagesAsRead(shouldMarkMessagesAsRead);
return this;
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2014-2016 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.mail.dsl;
import org.springframework.integration.mail.ImapMailReceiver;
import org.springframework.integration.mail.Pop3MailReceiver;
import org.springframework.integration.mail.transformer.MailToStringTransformer;
/**
* 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) {
return new MailSendingMessageHandlerSpec(host);
}
/**
* A {@link Pop3MailInboundChannelAdapterSpec} factory using a default
* {@link Pop3MailReceiver}.
* @return the {@link Pop3MailInboundChannelAdapterSpec} instance.
*/
public static Pop3MailInboundChannelAdapterSpec pop3InboundAdapter() {
return new Pop3MailInboundChannelAdapterSpec();
}
/**
* A {@link Pop3MailInboundChannelAdapterSpec} factory based on the provided
* {@link Pop3MailReceiver}.
* @param pop3MailReceiver the {@link Pop3MailReceiver} to use.
* @return the {@link Pop3MailInboundChannelAdapterSpec} instance.
*/
public static Pop3MailInboundChannelAdapterSpec pop3InboundAdapter(Pop3MailReceiver pop3MailReceiver) {
return new Pop3MailInboundChannelAdapterSpec(pop3MailReceiver);
}
/**
* A {@link Pop3MailInboundChannelAdapterSpec} factory based on the provided url.
* @param url the pop3 url
* @return the {@link Pop3MailInboundChannelAdapterSpec} instance.
*/
public static Pop3MailInboundChannelAdapterSpec pop3InboundAdapter(String url) {
return new Pop3MailInboundChannelAdapterSpec(url);
}
/**
* A {@link Pop3MailInboundChannelAdapterSpec} factory based on the provided host,
* user, password and the default port.
* @param host the host.
* @param username the user.
* @param password the password.
* @return the {@link Pop3MailInboundChannelAdapterSpec} instance.
*/
public static Pop3MailInboundChannelAdapterSpec pop3InboundAdapter(String host, String username, String password) {
return pop3InboundAdapter(host, -1, username, password);
}
/**
* A {@link Pop3MailInboundChannelAdapterSpec} factory based on the provided host,
* port, user, and password.
* @param host the host.
* @param port the port.
* @param username the user.
* @param password the password.
* @return the {@link Pop3MailInboundChannelAdapterSpec} instance.
*/
public static Pop3MailInboundChannelAdapterSpec pop3InboundAdapter(String host, int port, String username,
String password) {
return new Pop3MailInboundChannelAdapterSpec(host, port, username, password);
}
/**
* An {@link ImapMailInboundChannelAdapterSpec} factory using a default {@link ImapMailReceiver}.
* @return the {@link ImapMailInboundChannelAdapterSpec} instance.
*/
public static ImapMailInboundChannelAdapterSpec imapInboundAdapter() {
return new ImapMailInboundChannelAdapterSpec();
}
/**
* An {@link ImapMailInboundChannelAdapterSpec} factory based on the provided {@link ImapMailReceiver}.
* @param imapMailReceiver the {@link ImapMailReceiver} to use.
* @return the {@link ImapMailInboundChannelAdapterSpec} instance.
*/
public static ImapMailInboundChannelAdapterSpec imapInboundAdapter(ImapMailReceiver imapMailReceiver) {
return new ImapMailInboundChannelAdapterSpec(imapMailReceiver);
}
/**
* A {@link ImapMailInboundChannelAdapterSpec} factory based on the provided url.
* @param url the imap url
* @return the {@link ImapMailInboundChannelAdapterSpec} instance.
*/
public static ImapMailInboundChannelAdapterSpec imapInboundAdapter(String url) {
return new ImapMailInboundChannelAdapterSpec(url);
}
/**
* An {@link ImapIdleChannelAdapterSpec} factory using a default {@link ImapMailReceiver}.
* @return the {@link ImapIdleChannelAdapterSpec} instance.
*/
public static ImapIdleChannelAdapterSpec imapIdleAdapter() {
return new ImapIdleChannelAdapterSpec(new ImapMailReceiver());
}
/**
* A {@link ImapIdleChannelAdapterSpec} factory based on the provided url.
* @param url the imap url
* @return the {@link ImapIdleChannelAdapterSpec} instance.
*/
public static ImapIdleChannelAdapterSpec imapIdleAdapter(String url) {
return new ImapIdleChannelAdapterSpec(new ImapMailReceiver(url));
}
/**
* An {@link ImapIdleChannelAdapterSpec} factory based on the provided {@link ImapMailReceiver}.
* @param imapMailReceiver the {@link ImapMailReceiver} to use.
* @return the {@link ImapIdleChannelAdapterSpec} instance.
*/
public static ImapIdleChannelAdapterSpec imapIdleAdapter(ImapMailReceiver imapMailReceiver) {
return new ImapIdleChannelAdapterSpec(imapMailReceiver, true);
}
/**
* A {@link MailHeadersBuilder} factory.
* @return the factory.
*/
public static MailHeadersBuilder headers() {
return new MailHeadersBuilder();
}
/**
* A {@link MailToStringTransformer} factory.
* @return the transformer.
*/
public static MailToStringTransformer toStringTransformer() {
return toStringTransformer(null);
}
/**
* A {@link MailToStringTransformer} factory.
* @param charset the charset to use when the default is not appropriate.
* @return the transformer.
*/
public static MailToStringTransformer toStringTransformer(String charset) {
MailToStringTransformer transformer = new MailToStringTransformer();
if (charset != null) {
transformer.setCharset(charset);
}
return transformer;
}
private Mail() {
super();
}
}

View File

@@ -0,0 +1,305 @@
/*
* Copyright 2014-2016 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.mail.dsl;
import java.util.function.Function;
import org.springframework.integration.expression.FunctionExpression;
import org.springframework.integration.mail.MailHeaders;
import org.springframework.integration.support.MapBuilder;
import org.springframework.messaging.Message;
/**
* The Mail specific {@link MapBuilder} implementation.
*
* @author Artem Bilan
* @author Gary Russell
* @since 5.0
*/
public class MailHeadersBuilder extends MapBuilder<MailHeadersBuilder, String, Object> {
/**
* Set the subject.
* @param subject the subject.
* @return the builder.
*/
public MailHeadersBuilder subject(String subject) {
return put(MailHeaders.SUBJECT, subject);
}
/**
* Set the expression that will be evaluated to determine th subject.
* @param subject the subject expression.
* @return the builder.
*/
public MailHeadersBuilder subjectExpression(String subject) {
return putExpression(MailHeaders.SUBJECT, subject);
}
/**
* Set a function that will be invoked to return the subject based on the message.
* @param subject the function.
* @param <P> the message payload type.
* @return the builder.
*/
public <P> MailHeadersBuilder subjectFunction(Function<Message<P>, String> subject) {
return put(MailHeaders.SUBJECT, new FunctionExpression<Message<P>>(subject));
}
/**
* Set the To: addresses.
* @param to the addresses.
* @return the builder.
*/
public MailHeadersBuilder to(String... to) {
return put(MailHeaders.TO, to);
}
/**
* Set the expression that will be evaluated to determine the To: addresses.
* @param to the expression.
* @return the builder.
*/
public MailHeadersBuilder toExpression(String to) {
return putExpression(MailHeaders.TO, to);
}
/**
* Set a function that will be invoked to determine the To: addresses based on the
* message.
* @param to the function.
* @param <P> the message payload type.
* @return the builder.
*/
public <P> MailHeadersBuilder toFunction(Function<Message<P>, String[]> to) {
return put(MailHeaders.TO, new FunctionExpression<Message<P>>(to));
}
/**
* Set the cc: addresses.
* @param cc the addresses.
* @return the builder.
*/
public MailHeadersBuilder cc(String... cc) {
return put(MailHeaders.CC, cc);
}
/**
* Set the expression that will be evaluated to determine the cc: addresses.
* @param cc the expression.
* @return the builder.
*/
public MailHeadersBuilder ccExpression(String cc) {
return putExpression(MailHeaders.CC, cc);
}
/**
* Set a function that will be invoked to determine the cc: addresses based on the
* message.
* @param cc the function.
* @param <P> the message payload type.
* @return the builder.
*/
public <P> MailHeadersBuilder ccFunction(Function<Message<P>, String[]> cc) {
return put(MailHeaders.CC, new FunctionExpression<Message<P>>(cc));
}
/**
* Set the bcc: addresses.
* @param bcc the addresses.
* @return the builder.
*/
public MailHeadersBuilder bcc(String... bcc) {
return put(MailHeaders.BCC, bcc);
}
/**
* Set the expression that will be evaluated to determine the bcc: addresses.
* @param bcc the expression.
* @return the builder.
*/
public MailHeadersBuilder bccExpression(String bcc) {
return putExpression(MailHeaders.BCC, bcc);
}
/**
* Set a function that will be invoked to determine the bcc: addresses based on the
* message.
* @param bcc the function.
* @param <P> the message payload type.
* @return the builder.
*/
public <P> MailHeadersBuilder bccFunction(Function<Message<P>, String[]> bcc) {
return put(MailHeaders.BCC, new FunctionExpression<Message<P>>(bcc));
}
/**
* Set the From: address.
* @param from the address.
* @return the builder.
*/
public MailHeadersBuilder from(String from) {
return put(MailHeaders.FROM, from);
}
/**
* Set the expression that will be evaluated to determine the From: address.
* @param from the expression.
* @return the builder.
*/
public MailHeadersBuilder fromExpression(String from) {
return putExpression(MailHeaders.FROM, from);
}
/**
* Set a function that will be invoked to determine the From: address based on the
* message.
* @param from the function.
* @param <P> the message payload type.
* @return the builder.
*/
public <P> MailHeadersBuilder fromFunction(Function<Message<P>, String> from) {
return put(MailHeaders.FROM, new FunctionExpression<Message<P>>(from));
}
/**
* Set the ReplyTo: address.
* @param replyTo the address.
* @return the builder.
*/
public MailHeadersBuilder replyTo(String replyTo) {
return put(MailHeaders.REPLY_TO, replyTo);
}
/**
* Set the expression that will be evaluated to determine the ReplyTo: address.
* @param replyTo the expression.
* @return the builder.
*/
public MailHeadersBuilder replyToExpression(String replyTo) {
return putExpression(MailHeaders.REPLY_TO, replyTo);
}
/**
* Set a function that will be invoked to determine the ReplyTo: address based on the
* message.
* @param replyTo the function.
* @param <P> the message payload type.
* @return the builder.
*/
public <P> MailHeadersBuilder replyToFunction(Function<Message<P>, String> replyTo) {
return put(MailHeaders.REPLY_TO, new FunctionExpression<Message<P>>(replyTo));
}
/**
* Set a multipart mode to use.
* Possible values are 0 through 3.
* @param multipartMode header value
* @return this
* @see org.springframework.mail.javamail.MimeMessageHelper
*/
public MailHeadersBuilder multipartMode(int multipartMode) {
return put(MailHeaders.MULTIPART_MODE, multipartMode);
}
/**
* Set an expression that is evaluated to determine a multipart mode to use.
* Possible values are 0 through 3.
* @param multipartMode header value.
* @return the builder.
* @see org.springframework.mail.javamail.MimeMessageHelper
*/
public MailHeadersBuilder multipartModeExpression(String multipartMode) {
return putExpression(MailHeaders.MULTIPART_MODE, multipartMode);
}
/**
* Set a function that is invoked to determine a multipart mode to use.
* Possible values are 0 through 3.
* @param multipartMode header value
* @param <P> the message payload type.
* @return the builder.
* @see org.springframework.mail.javamail.MimeMessageHelper
*/
public <P> MailHeadersBuilder multipartModeFunction(Function<Message<P>, Integer> multipartMode) {
return put(MailHeaders.MULTIPART_MODE, new FunctionExpression<Message<P>>(multipartMode));
}
/**
* Set a filename for the attachment.
* @param attachmentFilename the file name.
* @return the builder.
*/
public MailHeadersBuilder attachmentFilename(String attachmentFilename) {
return put(MailHeaders.ATTACHMENT_FILENAME, attachmentFilename);
}
/**
* Set an expression that will be evaluated to determine the filename for the attachment.
* @param attachmentFilename the expression.
* @return the builder.
*/
public MailHeadersBuilder attachmentFilenameExpression(String attachmentFilename) {
return putExpression(MailHeaders.ATTACHMENT_FILENAME, attachmentFilename);
}
/**
* Set a function that will be invoked to determine the filename for the attachment.
* @param attachmentFilename the function.
* @param <P> the message payload type.
* @return the builder.
*/
public <P> MailHeadersBuilder attachmentFilenameFunction(Function<Message<P>, String> attachmentFilename) {
return put(MailHeaders.ATTACHMENT_FILENAME, new FunctionExpression<Message<P>>(attachmentFilename));
}
/**
* Set the content type.
* @param contentType the content type.
* @return the builder.
*/
public MailHeadersBuilder contentType(String contentType) {
return put(MailHeaders.CONTENT_TYPE, contentType);
}
/**
* Set an expression that will be evaluated to determine the content type.
* @param contentType the expression.
* @return the builder.
*/
public MailHeadersBuilder contentTypeExpression(String contentType) {
return putExpression(MailHeaders.CONTENT_TYPE, contentType);
}
/**
* Set a function that will be invoked to determine the content type.
* @param contentType the expression.
* @param <P> the message payload type.
* @return the builder.
*/
public <P> MailHeadersBuilder contentTypeFunction(Function<Message<P>, String> contentType) {
return put(MailHeaders.CONTENT_TYPE, new FunctionExpression<Message<P>>(contentType));
}
private MailHeadersBuilder putExpression(String key, String expression) {
return put(key, PARSER.parseExpression(expression));
}
MailHeadersBuilder() {
}
}

View File

@@ -0,0 +1,265 @@
/*
* Copyright 2014-2016 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.mail.dsl;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import org.springframework.expression.Expression;
import org.springframework.integration.dsl.ComponentsRegistration;
import org.springframework.integration.dsl.MessageSourceSpec;
import org.springframework.integration.expression.FunctionExpression;
import org.springframework.integration.mail.AbstractMailReceiver;
import org.springframework.integration.mail.MailReceivingMessageSource;
import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.integration.support.PropertiesBuilder;
import org.springframework.util.Assert;
/**
* A {@link MessageSourceSpec} for a {@link MailReceivingMessageSource}.
*
*
* @param <S> the target {@link MailInboundChannelAdapterSpec} implementation type.
* @param <R> the target {@link AbstractMailReceiver} implementation type.
*
* @author Gary Russell
* @author Artem Bilan
* @since 5.0
*/
public abstract class
MailInboundChannelAdapterSpec<S extends MailInboundChannelAdapterSpec<S, R>, R extends AbstractMailReceiver>
extends MessageSourceSpec<S, MailReceivingMessageSource>
implements ComponentsRegistration {
protected final R receiver;
protected final boolean externalReceiver;
private boolean sessionProvided;
protected MailInboundChannelAdapterSpec(R receiver) {
this(receiver, false);
}
protected MailInboundChannelAdapterSpec(R receiver, boolean externalReceiver) {
this.receiver = receiver;
this.externalReceiver = externalReceiver;
}
/**
* Configure a SpEL expression to select messages. The root object for the expression
* evaluation is a {@link javax.mail.internet.MimeMessage} which should return a boolean
* result (true means select the message).
* @param selectorExpression the selectorExpression.
* @return the spec.
*/
public S selectorExpression(String selectorExpression) {
assertReceiver();
this.receiver.setSelectorExpression(PARSER.parseExpression(selectorExpression));
return _this();
}
protected void assertReceiver() {
Assert.state(!this.externalReceiver, "An external 'receiver' [" + this.receiver + "] can't be modified.");
}
/**
* Configure a SpEL expression to select messages. The root object for the expression
* evaluation is a {@link javax.mail.internet.MimeMessage} which should return a boolean
* result (true means select the message).
* @param selectorExpression the selectorExpression.
* @return the spec.
*/
public S selectorExpression(Expression selectorExpression) {
assertReceiver();
this.receiver.setSelectorExpression(selectorExpression);
return _this();
}
/**
* Configure a {@link Function} to select messages. The argument for the function
* is a {@link javax.mail.internet.MimeMessage}; {@code apply} returns a boolean
* result (true means select the message).
* @param selectorFunction the selectorFunction.
* @return the spec.
* @see FunctionExpression
*/
public S selector(Function<MimeMessage, Boolean> selectorFunction) {
assertReceiver();
this.receiver.setSelectorExpression(new FunctionExpression<MimeMessage>(selectorFunction));
return _this();
}
/**
* Provide the Java Mail {@link Session} to use.
* @param session the session.
* @return the spec.
* @see AbstractMailReceiver#setSession(Session)
*/
public S session(Session session) {
assertReceiver();
this.receiver.setSession(session);
this.sessionProvided = true;
return _this();
}
/**
* The Java Mail properties.
* @param javaMailProperties the javaMailProperties.
* @return the spec.
* @see AbstractMailReceiver#setJavaMailProperties(Properties)
*/
public S javaMailProperties(Properties javaMailProperties) {
assertReceiver();
assertSession();
this.receiver.setJavaMailProperties(javaMailProperties);
return _this();
}
private void assertSession() {
Assert.state(!this.sessionProvided, "Neither 'javaMailProperties' nor 'javaMailAuthenticator' "
+ "references are allowed when a 'session' reference has been provided.");
}
/**
* Configure the {@code javaMailProperties} by invoking a {@link Consumer} callback which
* is invoked with a {@link PropertiesBuilder}.
* @param configurer the configurer.
* @return the spec.
* @see AbstractMailReceiver#setJavaMailProperties(Properties)
*/
public S javaMailProperties(Consumer<PropertiesBuilder> configurer) {
PropertiesBuilder properties = new PropertiesBuilder();
configurer.accept(properties);
return javaMailProperties(properties.get());
}
/**
* The Java Mail {@link Authenticator}.
* @param javaMailAuthenticator the javaMailAuthenticator.
* @return the spec.
* @see AbstractMailReceiver#setJavaMailAuthenticator(Authenticator)
*/
public S javaMailAuthenticator(Authenticator javaMailAuthenticator) {
assertSession();
assertReceiver();
this.receiver.setJavaMailAuthenticator(javaMailAuthenticator);
return _this();
}
/**
* The maximum for fetch size.
* @param maxFetchSize the maxFetchSize.
* @return the spec.
* @see AbstractMailReceiver#setMaxFetchSize(int)
*/
public S maxFetchSize(int maxFetchSize) {
assertReceiver();
this.receiver.setMaxFetchSize(maxFetchSize);
return _this();
}
/**
* A flag to specify if messages should be deleted after receive.
* @param shouldDeleteMessages the shouldDeleteMessages.
* @return the spec.
* @see AbstractMailReceiver#setShouldDeleteMessages(boolean)
*/
public S shouldDeleteMessages(boolean shouldDeleteMessages) {
assertReceiver();
this.receiver.setShouldDeleteMessages(shouldDeleteMessages);
return _this();
}
/**
* Set the name of the flag to use to flag messages when the server does
* not support \Recent but supports user flags;
* default {@value AbstractMailReceiver#DEFAULT_SI_USER_FLAG}.
* @param userFlag the flag.
* @return the spec.
* @see AbstractMailReceiver#setUserFlag(String)
*/
public S userFlag(String userFlag) {
assertReceiver();
this.receiver.setUserFlag(userFlag);
return _this();
}
/**
* Set the header mapper; if a header mapper is not provided, the message payload is
* a {@link MimeMessage}, when provided, the headers are mapped and the payload is
* the {@link MimeMessage} content.
* @param headerMapper the header mapper.
* @return the spec.
* @see AbstractMailReceiver#setUserFlag(String)
* @see #embeddedPartsAsBytes(boolean)
*/
public S headerMapper(HeaderMapper<MimeMessage> headerMapper) {
assertReceiver();
this.receiver.setHeaderMapper(headerMapper);
return _this();
}
/**
* When a header mapper is provided determine whether an embedded {@link Part} (e.g
* {@link Message} or {@link javax.mail.Multipart} content is rendered as a byte[] in the
* payload. Otherwise, leave as a {@link Part}. These objects are not suitable for
* downstream serialization. Default: true.
* <p>This has no effect if there is no header mapper, in that case the payload is the
* {@link MimeMessage}.
* @param embeddedPartsAsBytes the embeddedPartsAsBytes to set.
* @return the spec.
* @see #headerMapper(HeaderMapper)
*/
public S embeddedPartsAsBytes(boolean embeddedPartsAsBytes) {
assertReceiver();
this.receiver.setEmbeddedPartsAsBytes(embeddedPartsAsBytes);
return _this();
}
/**
* Determine how the content is rendered.
* @param simpleContent true for simple content.
* @return the spec.
* @see AbstractMailReceiver#setSimpleContent(boolean)
*/
public S simpleContent(boolean simpleContent) {
assertReceiver();
this.receiver.setSimpleContent(simpleContent);
return _this();
}
@Override
public Collection<Object> getComponentsToRegister() {
return Collections.<Object>singletonList(this.receiver);
}
@Override
public MailReceivingMessageSource doGet() {
return new MailReceivingMessageSource(this.receiver);
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2014-2016 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.mail.dsl;
import java.util.Properties;
import java.util.function.Consumer;
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.mail.javamail.JavaMailSenderImpl;
/**
* @author Gary Russell
* @author Artem Bilan
* @since 5.0
*/
public class MailSendingMessageHandlerSpec
extends MessageHandlerSpec<MailSendingMessageHandlerSpec, MailSendingMessageHandler> {
private final JavaMailSenderImpl sender = new JavaMailSenderImpl();
MailSendingMessageHandlerSpec(String host) {
this.sender.setHost(host);
this.target = new MailSendingMessageHandler(this.sender);
}
/**
* Set the javamail properties.
* @param javaMailProperties the properties.
* @return the spec.
* @see JavaMailSenderImpl#setJavaMailProperties(Properties)
*/
public MailSendingMessageHandlerSpec javaMailProperties(Properties javaMailProperties) {
this.sender.setJavaMailProperties(javaMailProperties);
return this;
}
/**
* Set a {@link Consumer} that will be invoked with a {@link PropertiesBuilder}; most often
* used with a lambda expression.
* @param propertiesConfigurer the consumer.
* @return the spec.
*/
public MailSendingMessageHandlerSpec javaMailProperties(Consumer<PropertiesBuilder> propertiesConfigurer) {
PropertiesBuilder properties = new PropertiesBuilder();
propertiesConfigurer.accept(properties);
return javaMailProperties(properties.get());
}
/**
* Set the protocol.
* @param protocol the protocol.
* @return the spec.
* @see JavaMailSenderImpl#setProtocol(String)
*/
public MailSendingMessageHandlerSpec protocol(String protocol) {
this.sender.setProtocol(protocol);
return this;
}
/**
* Set the port.
* @param port the port.
* @return the spec.
* @see JavaMailSenderImpl#setPort(int)
*/
public MailSendingMessageHandlerSpec port(int port) {
this.sender.setPort(port);
return this;
}
/**
* Set the credentials.
* @param username the user name.
* @param password the password.
* @return the spec.
* @see JavaMailSenderImpl#setUsername(String)
* @see JavaMailSenderImpl#setPassword(String)
*/
public MailSendingMessageHandlerSpec credentials(String username, String password) {
this.sender.setUsername(username);
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) {
this.sender.setDefaultEncoding(defaultEncoding);
return this;
}
/**
* Set the default type map.
* @param defaultFileTypeMap the default type map.
* @return the spec.
* @see JavaMailSenderImpl#setDefaultFileTypeMap(FileTypeMap)
*/
public MailSendingMessageHandlerSpec defaultFileTypeMap(FileTypeMap defaultFileTypeMap) {
this.sender.setDefaultFileTypeMap(defaultFileTypeMap);
return this;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2014-2016 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.mail.dsl;
import org.springframework.integration.mail.Pop3MailReceiver;
/**
* A {@link MailInboundChannelAdapterSpec} for POP3.
*
* @author Gary Russell
* @author Artem Bilan
* @since 5.0
*/
public class Pop3MailInboundChannelAdapterSpec
extends MailInboundChannelAdapterSpec<Pop3MailInboundChannelAdapterSpec, Pop3MailReceiver> {
Pop3MailInboundChannelAdapterSpec() {
super(new Pop3MailReceiver());
}
Pop3MailInboundChannelAdapterSpec(Pop3MailReceiver receiver) {
super(receiver, true);
}
Pop3MailInboundChannelAdapterSpec(String url) {
super(new Pop3MailReceiver(url));
}
Pop3MailInboundChannelAdapterSpec(String host, String username, String password) {
super(new Pop3MailReceiver(host, username, password));
}
Pop3MailInboundChannelAdapterSpec(String host, int port, String username, String password) {
super(new Pop3MailReceiver(host, port, username, password));
}
}

View File

@@ -0,0 +1,4 @@
/**
* Provides Mail Components for the Java DSL.
*/
package org.springframework.integration.mail.dsl;

View File

@@ -0,0 +1,268 @@
/*
* Copyright 2014-2016 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.mail.dsl;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Properties;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.mail.search.AndTerm;
import javax.mail.search.FlagTerm;
import javax.mail.search.FromTerm;
import javax.mail.search.SearchTerm;
import org.junit.AfterClass;
import org.junit.BeforeClass;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.channel.MessageChannels;
import org.springframework.integration.mail.ImapIdleChannelAdapter;
import org.springframework.integration.mail.MailHeaders;
import org.springframework.integration.mail.support.DefaultMailHeaderMapper;
import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.mail.TestMailServer;
import org.springframework.integration.test.mail.TestMailServer.ImapServer;
import org.springframework.integration.test.mail.TestMailServer.Pop3Server;
import org.springframework.integration.test.mail.TestMailServer.SmtpServer;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class MailTests {
private final static SmtpServer smtpServer = TestMailServer.smtp(0);
private final static Pop3Server pop3Server = TestMailServer.pop3(0);
private final static ImapServer imapServer = TestMailServer.imap(0);
private final static ImapServer imapIdleServer = TestMailServer.imap(0);
@BeforeClass
public static void setup() throws InterruptedException {
int n = 0;
while (n++ < 100 && (!smtpServer.isListening() || !pop3Server.isListening()
|| !imapServer.isListening()) || !imapIdleServer.isListening()) {
Thread.sleep(100);
}
assertTrue(n < 100);
}
@AfterClass
public static void tearDown() {
smtpServer.stop();
pop3Server.stop();
imapServer.stop();
imapIdleServer.stop();
}
@Autowired
private MessageChannel sendMailChannel;
@Autowired
@Qualifier("sendMailEndpoint.handler")
private MessageHandler sendMailHandler;
@Autowired
private PollableChannel pop3Channel;
@Autowired
private PollableChannel imapChannel;
@Autowired
private PollableChannel imapIdleChannel;
@Autowired
private ImapIdleChannelAdapter imapIdleAdapter;
@Test
public void testSmtp() throws Exception {
assertEquals("localhost", TestUtils.getPropertyValue(this.sendMailHandler, "mailSender.host"));
Properties javaMailProperties = TestUtils.getPropertyValue(this.sendMailHandler,
"mailSender.javaMailProperties", Properties.class);
assertEquals("false", javaMailProperties.getProperty("mail.debug"));
this.sendMailChannel.send(MessageBuilder.withPayload("foo").build());
int n = 0;
while (n++ < 100 && smtpServer.getMessages().size() == 0) {
Thread.sleep(100);
}
assertTrue(smtpServer.getMessages().size() > 0);
String message = smtpServer.getMessages().get(0);
assertThat(message, endsWith("foo\n"));
assertThat(message, containsString("foo@bar"));
assertThat(message, containsString("bar@baz"));
assertThat(message, containsString("user:user"));
assertThat(message, containsString("password:pw"));
}
@Test
public void testPop3() throws Exception {
Message<?> message = this.pop3Channel.receive(10000);
assertNotNull(message);
MessageHeaders headers = message.getHeaders();
assertEquals("Foo <foo@bar>", headers.get(MailHeaders.TO, String[].class)[0]);
assertEquals("Bar <bar@baz>", headers.get(MailHeaders.FROM));
assertEquals("Test Email", headers.get(MailHeaders.SUBJECT));
assertEquals("foo\r\n\r\n", message.getPayload());
}
@Test
public void testImap() throws Exception {
Message<?> message = this.imapChannel.receive(10000);
assertNotNull(message);
MimeMessage mm = (MimeMessage) message.getPayload();
assertEquals("Foo <foo@bar>", mm.getRecipients(RecipientType.TO)[0].toString());
assertEquals("Bar <bar@baz>", mm.getFrom()[0].toString());
assertEquals("Test Email", mm.getSubject());
assertThat(mm.getContent(), equalTo(TestMailServer.MailServer.MailHandler.BODY + "\r\n"));
}
@Test
public void testImapIdle() throws Exception {
Message<?> message = this.imapIdleChannel.receive(10000);
assertNotNull(message);
MessageHeaders headers = message.getHeaders();
assertEquals("Foo <foo@bar>", headers.get(MailHeaders.TO, String[].class)[0]);
assertEquals("Bar <bar@baz>", headers.get(MailHeaders.FROM));
assertEquals("Test Email", headers.get(MailHeaders.SUBJECT));
assertThat(message.getPayload(), equalTo(TestMailServer.MailServer.MailHandler.MESSAGE + "\r\n"));
this.imapIdleAdapter.stop();
assertFalse(TestUtils.getPropertyValue(this.imapIdleAdapter, "shouldReconnectAutomatically", Boolean.class));
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@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("localhost")
.port(smtpServer.getPort())
.credentials("user", "pw")
.protocol("smtp")
.javaMailProperties(p -> p.put("mail.debug", "false")),
e -> e.id("sendMailEndpoint"))
.get();
}
@Bean
public IntegrationFlow pop3MailFlow() {
return IntegrationFlows
.from(Mail.pop3InboundAdapter("localhost", pop3Server.getPort(), "user", "pw")
.javaMailProperties(p -> p.put("mail.debug", "false"))
.headerMapper(mailHeaderMapper()),
e -> e.autoStartup(true).poller(p -> p.fixedDelay(1000)))
.enrichHeaders(s -> s.headerExpressions(c -> c.put(MailHeaders.SUBJECT, "payload.subject")
.put(MailHeaders.FROM, "payload.from[0].toString()")))
.channel(MessageChannels.queue("pop3Channel"))
.get();
}
@Bean
public IntegrationFlow imapMailFlow() {
return IntegrationFlows
.from(Mail.imapInboundAdapter("imap://user:pw@localhost:" + imapServer.getPort() + "/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 imapIdleFlow() {
return IntegrationFlows
.from(Mail.imapIdleAdapter("imap://user:pw@localhost:" + imapIdleServer.getPort() + "/INBOX")
.autoStartup(true)
.searchTermStrategy(this::fromAndNotSeenTerm)
.userFlag("testSIUserFlag")
.javaMailProperties(p -> p.put("mail.debug", "false")
.put("mail.imap.connectionpoolsize", "5"))
.shouldReconnectAutomatically(false)
.headerMapper(mailHeaderMapper()))
.channel(MessageChannels.queue("imapIdleChannel"))
.get();
}
@Bean
public HeaderMapper<MimeMessage> mailHeaderMapper() {
return new DefaultMailHeaderMapper();
}
private SearchTerm fromAndNotSeenTerm(Flags supportedFlags, Folder folder) {
try {
FromTerm fromTerm = new FromTerm(new InternetAddress("bar@baz"));
return new AndTerm(fromTerm, new FlagTerm(new Flags(Flags.Flag.SEEN), false));
}
catch (AddressException e) {
throw new RuntimeException(e);
}
}
}
}