diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/ConsumerEndpointSpec.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/ConsumerEndpointSpec.java index 98413d017a..8e234e19ba 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/ConsumerEndpointSpec.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/ConsumerEndpointSpec.java @@ -77,8 +77,8 @@ public abstract class ConsumerEndpointSpec, } /** - * 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, } /** - * 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, } /** - * 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, } /** - * 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, } /** - * 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, } /** - * 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) { diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/ImapIdleChannelAdapterSpec.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/ImapIdleChannelAdapterSpec.java new file mode 100644 index 0000000000..674ad08f12 --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/ImapIdleChannelAdapterSpec.java @@ -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 + implements ComponentsRegistration { + + private final ImapMailReceiver receiver; + + private final Collection componentsToRegister = new ArrayList(); + + private final List 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 selectorFunction) { + return selectorExpression(new FunctionExpression(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 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 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. + *

+ * 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 getComponentsToRegister() { + return this.componentsToRegister; + } + +} diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/ImapMailInboundChannelAdapterSpec.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/ImapMailInboundChannelAdapterSpec.java new file mode 100644 index 0000000000..a6ab944049 --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/ImapMailInboundChannelAdapterSpec.java @@ -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() { + 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; + } + +} 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 new file mode 100644 index 0000000000..2a171457b2 --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/Mail.java @@ -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(); + } + +} diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailHeadersBuilder.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailHeadersBuilder.java new file mode 100644 index 0000000000..f490cf9b2b --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailHeadersBuilder.java @@ -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 { + + /** + * 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

the message payload type. + * @return the builder. + */ + public

MailHeadersBuilder subjectFunction(Function, String> subject) { + return put(MailHeaders.SUBJECT, new FunctionExpression>(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

the message payload type. + * @return the builder. + */ + public

MailHeadersBuilder toFunction(Function, String[]> to) { + return put(MailHeaders.TO, new FunctionExpression>(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

the message payload type. + * @return the builder. + */ + public

MailHeadersBuilder ccFunction(Function, String[]> cc) { + return put(MailHeaders.CC, new FunctionExpression>(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

the message payload type. + * @return the builder. + */ + public

MailHeadersBuilder bccFunction(Function, String[]> bcc) { + return put(MailHeaders.BCC, new FunctionExpression>(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

the message payload type. + * @return the builder. + */ + public

MailHeadersBuilder fromFunction(Function, String> from) { + return put(MailHeaders.FROM, new FunctionExpression>(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

the message payload type. + * @return the builder. + */ + public

MailHeadersBuilder replyToFunction(Function, String> replyTo) { + return put(MailHeaders.REPLY_TO, new FunctionExpression>(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

the message payload type. + * @return the builder. + * @see org.springframework.mail.javamail.MimeMessageHelper + */ + public

MailHeadersBuilder multipartModeFunction(Function, Integer> multipartMode) { + return put(MailHeaders.MULTIPART_MODE, new FunctionExpression>(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

the message payload type. + * @return the builder. + */ + public

MailHeadersBuilder attachmentFilenameFunction(Function, String> attachmentFilename) { + return put(MailHeaders.ATTACHMENT_FILENAME, new FunctionExpression>(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

the message payload type. + * @return the builder. + */ + public

MailHeadersBuilder contentTypeFunction(Function, String> contentType) { + return put(MailHeaders.CONTENT_TYPE, new FunctionExpression>(contentType)); + } + + private MailHeadersBuilder putExpression(String key, String expression) { + return put(key, PARSER.parseExpression(expression)); + } + + MailHeadersBuilder() { + } + +} diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailInboundChannelAdapterSpec.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailInboundChannelAdapterSpec.java new file mode 100644 index 0000000000..63e41d6cf1 --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailInboundChannelAdapterSpec.java @@ -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 the target {@link MailInboundChannelAdapterSpec} implementation type. + * @param the target {@link AbstractMailReceiver} implementation type. + * + * @author Gary Russell + * @author Artem Bilan + * @since 5.0 + */ +public abstract class + MailInboundChannelAdapterSpec, R extends AbstractMailReceiver> + extends MessageSourceSpec + 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 selectorFunction) { + assertReceiver(); + this.receiver.setSelectorExpression(new FunctionExpression(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 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 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. + *

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 getComponentsToRegister() { + return Collections.singletonList(this.receiver); + } + + @Override + public MailReceivingMessageSource doGet() { + return new MailReceivingMessageSource(this.receiver); + } + +} 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 new file mode 100644 index 0000000000..7bfc0aac15 --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/MailSendingMessageHandlerSpec.java @@ -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 { + + 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 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; + } + +} diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/Pop3MailInboundChannelAdapterSpec.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/Pop3MailInboundChannelAdapterSpec.java new file mode 100644 index 0000000000..da35fefe5c --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/Pop3MailInboundChannelAdapterSpec.java @@ -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() { + 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)); + } + +} diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/package-info.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/package-info.java new file mode 100644 index 0000000000..e15dcb2e9d --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/dsl/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides Mail Components for the Java DSL. + */ +package org.springframework.integration.mail.dsl; diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/dsl/MailTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/dsl/MailTests.java new file mode 100644 index 0000000000..c2ca984513 --- /dev/null +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/dsl/MailTests.java @@ -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 ", headers.get(MailHeaders.TO, String[].class)[0]); + assertEquals("Bar ", 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 ", mm.getRecipients(RecipientType.TO)[0].toString()); + assertEquals("Bar ", 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 ", headers.get(MailHeaders.TO, String[].class)[0]); + assertEquals("Bar ", 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 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); + } + + } + + } + +}