part of INT-261 adding polling support for IMAP and POP3 mailboxes based on the strategies implemented in Spring WS
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.adapter.mail;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.mail.Address;
|
||||
import javax.mail.Message.RecipientType;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.integration.adapter.MessageHeaderMapper;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHeaders;
|
||||
import org.springframework.integration.message.MessagingException;
|
||||
import org.springframework.mail.javamail.MimeMailMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jonas Partner
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractMailHeaderMapper implements MessageHeaderMapper<MimeMessage> {
|
||||
|
||||
/**
|
||||
* Retrieve the subject of an e-mail message from an integration message.
|
||||
*
|
||||
* @param message the integration {@link Message}
|
||||
* @return the e-mail message subject
|
||||
*/
|
||||
protected abstract String getSubject(MessageHeaders message);
|
||||
|
||||
/**
|
||||
* Retrieve the recipients list from an integration message.
|
||||
*
|
||||
* @param message the integration {@link Message}
|
||||
* @return recipients list (TO)
|
||||
*/
|
||||
protected abstract String[] getTo(MessageHeaders message);
|
||||
|
||||
/**
|
||||
* Retrieve the CC recipients list from an integration message.
|
||||
*
|
||||
* @param message the integration {@link Message}
|
||||
* @return CC recipients list (e-mail addresses)
|
||||
*/
|
||||
protected abstract String[] getCc(MessageHeaders message);
|
||||
|
||||
/**
|
||||
* Retrieve the BCC recipients list from an integration message.
|
||||
*
|
||||
* @param message the integration {@link Message}
|
||||
* @return BCC recipients list (e-mail addresses)
|
||||
*/
|
||||
protected abstract String[] getBcc(MessageHeaders message);
|
||||
|
||||
/**
|
||||
* Retrieve the From: e-mail address from an integration message.
|
||||
*
|
||||
* @param message the integration {@link Message}
|
||||
* @return the From: e-mail address
|
||||
*/
|
||||
protected abstract String getFrom(MessageHeaders message);
|
||||
|
||||
/**
|
||||
* Retrieve the Reply To: e-mail address from an integration message.
|
||||
*
|
||||
* @param message the integration {@link Message}
|
||||
* @return the ReplyTo: e-mail address
|
||||
*/
|
||||
protected abstract String getReplyTo(MessageHeaders message);
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
public void mapFromMessageHeaders(MessageHeaders headers, MimeMessage mailMessage) {
|
||||
MimeMailMessage message = new MimeMailMessage(mailMessage);
|
||||
|
||||
final String subject = getSubject(headers);
|
||||
final String[] to = getTo(headers);
|
||||
final String[] cc = getCc(headers);
|
||||
final String[] bcc = getBcc(headers);
|
||||
final String from = getFrom(headers);
|
||||
final String replyTo = getReplyTo(headers);
|
||||
if (subject != null) {
|
||||
message.setSubject(subject);
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn("no 'SUBJECT' property available for mail message");
|
||||
}
|
||||
if (to != null) {
|
||||
message.setTo(to);
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn("no 'TO' property available for mail message");
|
||||
}
|
||||
if (cc != null) {
|
||||
message.setCc(cc);
|
||||
}
|
||||
if (bcc != null) {
|
||||
message.setBcc(bcc);
|
||||
}
|
||||
if (from != null) {
|
||||
message.setFrom(from);
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn("no 'FROM' property available for mail message");
|
||||
}
|
||||
if (replyTo != null) {
|
||||
message.setReplyTo(replyTo);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String,Object> mapToMessageHeaders(MimeMessage mailMessage) {
|
||||
try {
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
headers.put(MailAttributeKeys.FROM, toStringArray(mailMessage.getFrom()));
|
||||
headers.put(MailAttributeKeys.BCC, toStringArray(mailMessage.getRecipients(RecipientType.BCC)));
|
||||
headers.put(MailAttributeKeys.CC, toStringArray(mailMessage.getRecipients(RecipientType.CC)));
|
||||
headers.put(MailAttributeKeys.TO, toStringArray(mailMessage.getRecipients(RecipientType.TO)));
|
||||
headers.put(MailAttributeKeys.REPLY_TO, toStringArray(mailMessage.getReplyTo()));
|
||||
headers.put(MailAttributeKeys.SUBJECT, mailMessage.getSubject());
|
||||
return headers;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessagingException("Conversion of MailMessage headers failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected String retrieveAsString(MessageHeaders headers, String key) {
|
||||
Object value = headers.get(key);
|
||||
return (value instanceof String) ? (String) value : null;
|
||||
}
|
||||
|
||||
protected String[] retrieveAsStringArray(MessageHeaders headers, String key) {
|
||||
Object value = headers.get(key);
|
||||
if (value instanceof String[]) {
|
||||
return (String[]) value;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return new String[] { (String) value };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected String[] toStringArray(Address[] addresses) {
|
||||
if (addresses != null) {
|
||||
String[] addressStrings = new String[addresses.length];
|
||||
for (int i = 0; i < addresses.length; i++) {
|
||||
addressStrings[i] = addresses[i].toString();
|
||||
}
|
||||
return addressStrings;
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.adapter.mail;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessagingException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jonas Partner
|
||||
*
|
||||
*/
|
||||
public class DefaultMailMessageConverter implements MailMessageConverter {
|
||||
|
||||
private DefaultMailMessageHeaderMapper headerMapper = new DefaultMailMessageHeaderMapper();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Message create(MimeMessage mailMessage) {
|
||||
try {
|
||||
Map<String, Object> header = headerMapper.mapToMessageHeaders(mailMessage);
|
||||
GenericMessage<Object> message = new GenericMessage<Object>(mailMessage.getContent(), header);
|
||||
return message;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessagingException("Conversion of MailMessage failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.adapter.mail;
|
||||
|
||||
import org.springframework.integration.message.MessageHeaders;
|
||||
|
||||
/**
|
||||
* @author Jonas Partner
|
||||
*
|
||||
*/
|
||||
public class DefaultMailMessageHeaderMapper extends AbstractMailHeaderMapper {
|
||||
|
||||
@Override
|
||||
protected String getSubject(MessageHeaders headers) {
|
||||
return this.retrieveAsString(headers, MailAttributeKeys.SUBJECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getTo(MessageHeaders headers) {
|
||||
return this.retrieveAsStringArray(headers, MailAttributeKeys.TO);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getCc(MessageHeaders headers) {
|
||||
return this.retrieveAsStringArray(headers, MailAttributeKeys.CC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getBcc(MessageHeaders headers) {
|
||||
return this.retrieveAsStringArray(headers, MailAttributeKeys.BCC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFrom(MessageHeaders headers) {
|
||||
return this.retrieveAsString(headers, MailAttributeKeys.FROM);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getReplyTo(MessageHeaders headers) {
|
||||
return this.retrieveAsString(headers, MailAttributeKeys.REPLY_TO);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.adapter.mail;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.integration.message.Message;
|
||||
|
||||
/**
|
||||
* Converts a {@link MimeMessage} to a {@link Message}
|
||||
* @author Jonas Partner
|
||||
*
|
||||
*/
|
||||
public interface MailMessageConverter {
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Message create(MimeMessage mailMessage);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.adapter.mail;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Store;
|
||||
import javax.mail.URLName;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.adapter.mail.monitor.DefaultLocalMailMessageStore;
|
||||
import org.springframework.integration.adapter.mail.monitor.LocalMailMessageStore;
|
||||
import org.springframework.integration.adapter.mail.monitor.MailTransportUtils;
|
||||
import org.springframework.integration.adapter.mail.monitor.MonitoringStrategy;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageSource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link MessageSource} implementation which delegates to a
|
||||
* {@link MonitoringStrategy} to poll a mailbox Each poll of the mailbox may
|
||||
* return more than one message which will then be stored locally using the
|
||||
* provided {@link LocalMailMessageStore}
|
||||
* @author Jonas Partner
|
||||
*
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class PollingMailSource implements MessageSource, DisposableBean, Lifecycle {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final MonitoringStrategy monitoringStrategy;
|
||||
|
||||
private Session session;
|
||||
|
||||
private Store store;
|
||||
|
||||
private Folder folder;
|
||||
|
||||
private URLName storeUri;
|
||||
|
||||
private MailMessageConverter converter = new DefaultMailMessageConverter();
|
||||
|
||||
private LocalMailMessageStore mailMessageStore = new DefaultLocalMailMessageStore();
|
||||
|
||||
public PollingMailSource(MonitoringStrategy monitoringStrategy) {
|
||||
this.monitoringStrategy = monitoringStrategy;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Message receive() {
|
||||
Message received = null;
|
||||
javax.mail.Message mailMessage = mailMessageStore.getNext();
|
||||
if (mailMessage == null) {
|
||||
try {
|
||||
javax.mail.Message[] messages = monitoringStrategy.receive(folder);
|
||||
mailMessageStore.addLast(messages);
|
||||
mailMessage = mailMessageStore.getNext();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new org.springframework.integration.message.MessagingException("Excpetion receiving mail", e);
|
||||
}
|
||||
}
|
||||
if (mailMessage != null) {
|
||||
received = converter.create((MimeMessage) mailMessage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received message " + received);
|
||||
}
|
||||
}
|
||||
return received;
|
||||
}
|
||||
|
||||
public void setJavaMailProperties(Properties javaMailProperties) {
|
||||
session = Session.getInstance(javaMailProperties, null);
|
||||
}
|
||||
|
||||
public void setJavaMailsession(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void setStoreUri(String storeUri) {
|
||||
this.storeUri = new URLName(storeUri);
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(storeUri, "Property 'storeUri' is required");
|
||||
Assert.notNull(session, "Property 'JavaMailProperties' is required");
|
||||
Assert.notNull(converter, "An instantce of MailMessageConverter' is required");
|
||||
openSession();
|
||||
openFolder();
|
||||
}
|
||||
|
||||
private void openFolder() throws MessagingException {
|
||||
if (folder != null && folder.isOpen()) {
|
||||
return;
|
||||
}
|
||||
folder = store.getFolder(storeUri);
|
||||
if (folder == null || !folder.exists()) {
|
||||
throw new IllegalStateException("No default folder to receive from");
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opening folder [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
|
||||
}
|
||||
folder.open(monitoringStrategy.getFolderOpenMode());
|
||||
}
|
||||
|
||||
private void openSession() throws MessagingException {
|
||||
store = session.getStore(storeUri);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Connecting to store [" + MailTransportUtils.toPasswordProtectedString(storeUri) + "]");
|
||||
}
|
||||
store.connect();
|
||||
}
|
||||
|
||||
public void setConverter(MailMessageConverter converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
stop();
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return folder.isOpen();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
openSession();
|
||||
openFolder();
|
||||
}
|
||||
catch (MessagingException messageE) {
|
||||
throw new org.springframework.integration.message.MessagingException("Excpetion starting MailSource",
|
||||
messageE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
MailTransportUtils.closeFolder(folder);
|
||||
MailTransportUtils.closeService(store);
|
||||
}
|
||||
|
||||
public void setMailMessageStore(LocalMailMessageStore mailMessageStore) {
|
||||
this.mailMessageStore = mailMessageStore;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2007 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.adapter.mail.monitor;
|
||||
|
||||
import javax.mail.FetchProfile;
|
||||
import javax.mail.Flags;
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.search.AndTerm;
|
||||
import javax.mail.search.FlagTerm;
|
||||
import javax.mail.search.SearchTerm;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Abstract base class for the {@link MonitoringStrategy} interface. Exposes a {@link #setDeleteMessages(boolean)
|
||||
* deleteMessages} property, and includes a basic workflow for message monitoring.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class AbstractMonitoringStrategy implements MonitoringStrategy {
|
||||
|
||||
/** Logger available to subclasses. */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private boolean deleteMessages = true;
|
||||
|
||||
private int maxMessagesPerReceive = -1;
|
||||
|
||||
/**
|
||||
* Sets whether messages should be marked as {@link javax.mail.Flags.Flag#DELETED DELETED} after they have been
|
||||
* read. Default is <code>true</code>.
|
||||
*/
|
||||
public void setDeleteMessages(boolean deleteMessages) {
|
||||
this.deleteMessages = deleteMessages;
|
||||
}
|
||||
|
||||
public int getFolderOpenMode() {
|
||||
return deleteMessages ? Folder.READ_WRITE : Folder.READ_ONLY;
|
||||
}
|
||||
|
||||
public void setMaxMessagePerDownload(int maxMessagesPerReceive){
|
||||
this.maxMessagesPerReceive = maxMessagesPerReceive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors the given folder, and returns any new messages when they arrive. This implementation calls {@link
|
||||
* #waitForNewMessages(Folder)}, then searches for new messages using {@link #searchForNewMessages(Folder)}, fetches
|
||||
* the messages using {@link #fetchMessages(Folder, Message[])}, and finally {@link #setDeleteMessages(boolean)
|
||||
* deletes} the messages, if {@link #setDeleteMessages(boolean) deleteMessages} is <code>true</code>.
|
||||
*
|
||||
* @param folder the folder to monitor
|
||||
* @return the new messages
|
||||
* @throws MessagingException in case of JavaMail errors
|
||||
* @throws InterruptedException when a thread is interrupted
|
||||
*/
|
||||
public final Message[] receive(Folder folder) throws MessagingException, InterruptedException {
|
||||
logger.info("Receiving for folder" + folder.getFullName());
|
||||
if(!folder.isOpen()){
|
||||
folder.open(getFolderOpenMode());
|
||||
}
|
||||
folder.getMessageCount();
|
||||
Message[] messages = searchForNewMessages(folder);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found " + messages.length + " new messages");
|
||||
}
|
||||
if(maxMessagesPerReceive > 0 && messages.length > maxMessagesPerReceive){
|
||||
Message[] reducedMessages = new Message[maxMessagesPerReceive];
|
||||
System.arraycopy(messages, 0, reducedMessages, 0, maxMessagesPerReceive);
|
||||
messages = reducedMessages;
|
||||
}
|
||||
|
||||
if (messages.length > 0) {
|
||||
fetchMessages(folder, messages);
|
||||
}
|
||||
if (deleteMessages) {
|
||||
deleteMessages(folder, messages);
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves new messages from the given folder. This implementation creates a {@link SearchTerm} that searches for
|
||||
* all messages in the folder that are {@link javax.mail.Flags.Flag#RECENT RECENT}, not {@link
|
||||
* javax.mail.Flags.Flag#ANSWERED ANSWERED}, and not {@link javax.mail.Flags.Flag#DELETED DELETED}. The search term
|
||||
* is used to {@link Folder#search(SearchTerm) search} for new messages.
|
||||
*
|
||||
* @param folder the folder to retrieve new messages from
|
||||
* @return the new messages
|
||||
* @throws MessagingException in case of JavaMail errors
|
||||
*/
|
||||
protected Message[] searchForNewMessages(Folder folder) throws MessagingException {
|
||||
if (!folder.isOpen()) {
|
||||
return new Message[0];
|
||||
}
|
||||
Flags supportedFlags = folder.getPermanentFlags();
|
||||
SearchTerm searchTerm = null;
|
||||
if (supportedFlags != null) {
|
||||
if (supportedFlags.contains(Flags.Flag.RECENT)) {
|
||||
searchTerm = new FlagTerm(new Flags(Flags.Flag.RECENT), true);
|
||||
}
|
||||
if (supportedFlags.contains(Flags.Flag.ANSWERED)) {
|
||||
FlagTerm answeredTerm = new FlagTerm(new Flags(Flags.Flag.ANSWERED), false);
|
||||
if (searchTerm == null) {
|
||||
searchTerm = answeredTerm;
|
||||
}
|
||||
else {
|
||||
searchTerm = new AndTerm(searchTerm, answeredTerm);
|
||||
}
|
||||
}
|
||||
if (supportedFlags.contains(Flags.Flag.DELETED)) {
|
||||
FlagTerm deletedTerm = new FlagTerm(new Flags(Flags.Flag.DELETED), false);
|
||||
if (searchTerm == null) {
|
||||
searchTerm = deletedTerm;
|
||||
}
|
||||
else {
|
||||
searchTerm = new AndTerm(searchTerm, deletedTerm);
|
||||
}
|
||||
}
|
||||
}
|
||||
return searchTerm != null ? folder.search(searchTerm) : folder.getMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the specified messages from the specified folder. Default implementation {@link Folder#fetch(Message[],
|
||||
* FetchProfile) fetches} every {@link javax.mail.FetchProfile.Item}.
|
||||
*
|
||||
* @param folder the folder to fetch messages from
|
||||
* @param messages the messages to fetch
|
||||
* @throws MessagingException in case of JavMail errors
|
||||
*/
|
||||
protected void fetchMessages(Folder folder, Message[] messages) throws MessagingException {
|
||||
FetchProfile contentsProfile = new FetchProfile();
|
||||
contentsProfile.add(FetchProfile.Item.ENVELOPE);
|
||||
contentsProfile.add(FetchProfile.Item.CONTENT_INFO);
|
||||
contentsProfile.add(FetchProfile.Item.FLAGS);
|
||||
folder.fetch(messages, contentsProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given messages from the given folder. Only invoked when {@link #setDeleteMessages(boolean)} is
|
||||
* <code>true</code>.
|
||||
*
|
||||
* @param folder the folder to delete messages from
|
||||
* @param messages the messages to delete
|
||||
* @throws MessagingException in case of JavaMail errors
|
||||
*/
|
||||
protected void deleteMessages(Folder folder, Message[] messages) throws MessagingException {
|
||||
for (int i = 0; i < messages.length; i++) {
|
||||
messages[i].setFlag(Flags.Flag.DELETED, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.adapter.mail.monitor;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import javax.mail.Message;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jonas Partner
|
||||
*
|
||||
*/
|
||||
public class DefaultLocalMailMessageStore implements LocalMailMessageStore {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private ConcurrentLinkedQueue<Message> messages = new ConcurrentLinkedQueue<Message>();
|
||||
|
||||
public void addLast(Message[] newMessages) {
|
||||
for (Message message : newMessages) {
|
||||
messages.add(message);
|
||||
}
|
||||
logger.info("LocalMailMessageStore size is now" + messages.size());
|
||||
}
|
||||
|
||||
public Message getNext() {
|
||||
logger.info("Message store size " + messages.size());
|
||||
return messages.poll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2007 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.adapter.mail.monitor;
|
||||
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.event.MessageCountAdapter;
|
||||
import javax.mail.event.MessageCountEvent;
|
||||
import javax.mail.event.MessageCountListener;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link MonitoringStrategy} interface that uses the IMAP IDLE command for asynchronous message
|
||||
* detection.
|
||||
* <p/>
|
||||
* <b>Note</b> that this implementation is only suitable for use with IMAP servers which support the IDLE command.
|
||||
* Additionally, this strategy requires JavaMail version 1.4.1.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ImapIdleMonitoringStrategy extends AbstractMonitoringStrategy {
|
||||
|
||||
private MessageCountListener messageCountListener;
|
||||
|
||||
protected void waitForNewMessages(Folder folder) throws MessagingException, InterruptedException {
|
||||
// Assert.isInstanceOf(IMAPFolder.class, folder);
|
||||
//IMAPFolder imapFolder = (IMAPFolder) folder;
|
||||
// retrieve unseen messages before we enter the blocking idle call
|
||||
if (searchForNewMessages(folder).length > 0) {
|
||||
return;
|
||||
}
|
||||
if (messageCountListener == null) {
|
||||
createMessageCountListener();
|
||||
}
|
||||
folder.addMessageCountListener(messageCountListener);
|
||||
try {
|
||||
//TODO: add this back in when we have java mail 1.4.1 in the repository imapFolder.idle();
|
||||
}
|
||||
finally {
|
||||
folder.removeMessageCountListener(messageCountListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void createMessageCountListener() {
|
||||
messageCountListener = new MessageCountAdapter() {
|
||||
public void messagesAdded(MessageCountEvent e) {
|
||||
Message[] messages = e.getMessages();
|
||||
for (int i = 0; i < messages.length; i++) {
|
||||
try {
|
||||
// this will return the flow to the idle call, above
|
||||
messages[i].getLineCount();
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.adapter.mail.monitor;
|
||||
|
||||
import javax.mail.Message;
|
||||
|
||||
/**
|
||||
* Acts as a buffer for downloaded MailMessages
|
||||
* @author Jonas Partner
|
||||
*
|
||||
*/
|
||||
public interface LocalMailMessageStore {
|
||||
|
||||
public Message getNext();
|
||||
|
||||
public void addLast(Message[] newMessages);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2007 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.adapter.mail.monitor;
|
||||
|
||||
|
||||
/**
|
||||
* Declares Mail-specific transport constants.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public interface MailTransportConstants {
|
||||
|
||||
/**
|
||||
* The "mail" URI scheme.
|
||||
*/
|
||||
String MAIL_URI_SCHEME = "mailto";
|
||||
|
||||
/**
|
||||
* The "In-Reply-To" header.
|
||||
*/
|
||||
String HEADER_IN_REPLY_TO = "In-Reply-To";
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2007 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.adapter.mail.monitor;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.Service;
|
||||
import javax.mail.Store;
|
||||
import javax.mail.Transport;
|
||||
import javax.mail.URLName;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Collection of utility methods to work with Mail transports.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class MailTransportUtils {
|
||||
|
||||
private static final Pattern TO_PATTERN = Pattern.compile("^([^\\?]+)");
|
||||
|
||||
private static final Pattern SUBJECT_PATTERN = Pattern.compile("subject=([^\\&]+)");
|
||||
|
||||
private static final Log logger = LogFactory.getLog(MailTransportUtils.class);
|
||||
|
||||
private MailTransportUtils() {
|
||||
}
|
||||
|
||||
public static InternetAddress getTo(URI uri) {
|
||||
Matcher matcher = TO_PATTERN.matcher(uri.getSchemeSpecificPart());
|
||||
if (matcher.find()) {
|
||||
for (int i = 1; i <= matcher.groupCount(); i++) {
|
||||
String group = matcher.group(i);
|
||||
if (group != null) {
|
||||
try {
|
||||
return new InternetAddress(group);
|
||||
}
|
||||
catch (AddressException e) {
|
||||
// try next group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getSubject(URI uri) {
|
||||
Matcher matcher = SUBJECT_PATTERN.matcher(uri.getSchemeSpecificPart());
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given JavaMail Service and ignore any thrown exception. This is useful for typical <code>finally</code>
|
||||
* blocks in manual JavaMail code.
|
||||
*
|
||||
* @param service the JavaMail Service to close (may be <code>null</code>)
|
||||
* @see Transport
|
||||
* @see Store
|
||||
*/
|
||||
public static void closeService(Service service) {
|
||||
if (service != null) {
|
||||
try {
|
||||
service.close();
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
logger.debug("Could not close JavaMail Service", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given JavaMail Folder and ignore any thrown exception. This is useful for typical <code>finally</code>
|
||||
* blocks in manual JavaMail code.
|
||||
*
|
||||
* @param folder the JavaMail Folder to close (may be <code>null</code>)
|
||||
*/
|
||||
|
||||
public static void closeFolder(Folder folder) {
|
||||
closeFolder(folder, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given JavaMail Folder and ignore any thrown exception. This is useful for typical <code>finally</code>
|
||||
* blocks in manual JavaMail code.
|
||||
*
|
||||
* @param folder the JavaMail Folder to close (may be <code>null</code>)
|
||||
* @param expunge whether all deleted messages should be expunged from the folder
|
||||
*/
|
||||
public static void closeFolder(Folder folder, boolean expunge) {
|
||||
if (folder != null && folder.isOpen()) {
|
||||
try {
|
||||
folder.close(expunge);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
logger.debug("Could not close JavaMail Folder", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a string representation of the given {@link URLName}, where the password has been protected. */
|
||||
public static String toPasswordProtectedString(URLName name) {
|
||||
String protocol = name.getProtocol();
|
||||
String username = name.getUsername();
|
||||
String password = name.getPassword();
|
||||
String host = name.getHost();
|
||||
int port = name.getPort();
|
||||
String file = name.getFile();
|
||||
String ref = name.getRef();
|
||||
StringBuffer tempURL = new StringBuffer();
|
||||
if (protocol != null) {
|
||||
tempURL.append(protocol).append(':');
|
||||
}
|
||||
|
||||
if (StringUtils.hasLength(username) || StringUtils.hasLength(host)) {
|
||||
tempURL.append("//");
|
||||
if (StringUtils.hasLength(username)) {
|
||||
tempURL.append(username);
|
||||
if (StringUtils.hasLength(password)) {
|
||||
tempURL.append(":*****");
|
||||
}
|
||||
tempURL.append("@");
|
||||
}
|
||||
if (StringUtils.hasLength(host)) {
|
||||
tempURL.append(host);
|
||||
}
|
||||
if (port != -1) {
|
||||
tempURL.append(':').append(Integer.toString(port));
|
||||
}
|
||||
if (StringUtils.hasLength(file)) {
|
||||
tempURL.append('/');
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasLength(file)) {
|
||||
tempURL.append(file);
|
||||
}
|
||||
if (StringUtils.hasLength(ref)) {
|
||||
tempURL.append('#').append(ref);
|
||||
}
|
||||
return tempURL.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given internet address into a <code>mailto</code> URI.
|
||||
*
|
||||
* @param to the To: address
|
||||
* @param subject the subject, may be <code>null</code>
|
||||
* @return a mailto URI
|
||||
*/
|
||||
public static URI toUri(InternetAddress to, String subject) throws URISyntaxException {
|
||||
if (StringUtils.hasLength(subject)) {
|
||||
return new URI(MailTransportConstants.MAIL_URI_SCHEME, to.getAddress() + "?subject=" + subject, null);
|
||||
}
|
||||
else {
|
||||
return new URI(MailTransportConstants.MAIL_URI_SCHEME, to.getAddress(), null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2007 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.adapter.mail.monitor;
|
||||
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
/**
|
||||
* Defines the contract for objects that monitor a given folder for new messages. Allows for multiple implementation
|
||||
* strategies, including polling, or event-driven techniques such as IMAP's <code>IDLE</code> command.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public interface MonitoringStrategy {
|
||||
|
||||
/**
|
||||
* Monitors the given folder, and returns any new messages when they arrive.
|
||||
*
|
||||
* @param folder the folder in which to look for new messages
|
||||
* @return the new messages
|
||||
* @throws MessagingException in case of JavaMail errors
|
||||
* @throws InterruptedException if a thread is interrupted
|
||||
*/
|
||||
Message[] receive(Folder folder) throws MessagingException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Returns the folder open mode to be used by this strategy. Can be either {@link Folder#READ_ONLY} or {@link
|
||||
* Folder#READ_WRITE}.
|
||||
*/
|
||||
int getFolderOpenMode();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2007 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.adapter.mail.monitor;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of the {@link MonitoringStrategy} interface that uses a simple polling mechanism. Defines a {@link
|
||||
* #setPollingInterval(long) polling interval} property which defines the interval in between message polls.
|
||||
* <p/>
|
||||
* <b>Note</b> that this implementation is not suitable for use with POP3 servers. Use the {@link
|
||||
* Pop3PollingMonitoringStrategy} instead.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class PollingMonitoringStrategy extends AbstractMonitoringStrategy {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2007 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.adapter.mail.monitor;
|
||||
|
||||
import javax.mail.Folder;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link MonitoringStrategy} interface that uses a simple polling mechanism suitable for POP3
|
||||
* servers. Since POP3 does not have a native mechanism to determine which messages are "new", this implementation
|
||||
* simply retrieves all messages in the {@link Folder}, and delete them afterwards. All messages in the POP3 mailbox are
|
||||
* therefore, by definition, new.
|
||||
* <p/>
|
||||
* Setting the {@link #setDeleteMessages(boolean) deleteMessages} property is therefore ignored: messages are always
|
||||
* deleted.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class Pop3PollingMonitoringStrategy extends PollingMonitoringStrategy {
|
||||
|
||||
public Pop3PollingMonitoringStrategy() {
|
||||
super.setDeleteMessages(true);
|
||||
}
|
||||
|
||||
public void setDeleteMessages(boolean deleteMessages) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply returns {@link Folder#getMessages()}.
|
||||
*/
|
||||
protected Message[] searchForNewMessages(Folder folder) throws MessagingException {
|
||||
return folder.getMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given messages from the given folder, and closes it to expunge deleted messages.
|
||||
*
|
||||
* @param folder the folder to delete messages from
|
||||
* @param messages the messages to delete
|
||||
* @throws MessagingException in case of JavaMail errors
|
||||
*/
|
||||
protected void deleteMessages(Folder folder, Message[] messages) throws MessagingException {
|
||||
super.deleteMessages(folder, messages);
|
||||
// expunge deleted mails, and make sure we've retrieved them before closing the folder
|
||||
for (int i = 0; i < messages.length; i++) {
|
||||
new MimeMessage((MimeMessage) messages[i]);
|
||||
}
|
||||
MailTransportUtils.closeFolder(folder, true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user