/*
* Copyright (c) 2011 Lockheed Martin Corporation
*
* 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.eurekastreams.server.service.email;
import java.util.ArrayList;
import java.util.List;
import javax.mail.FetchProfile;
import javax.mail.Flags.Flag;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Store;
import org.apache.commons.lang.StringUtils;
import org.eurekastreams.commons.logging.LogFactory;
import org.eurekastreams.server.service.actions.strategies.EmailerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Ingests email from a mailbox and processes it.
*/
public class ImapEmailIngester
{
/** Log. */
private final Logger log = LoggerFactory.getLogger(LogFactory.getClassName());
/** For getting a connection to the mail server. */
private final ImapStoreFactory storeFactory;
/** Does the actual work on each message. */
private final MessageProcessor messageProcessor;
/** Name of folder containing messages to process. */
private final String inputFolderName;
/** Name of folder to receive messages that failed processing. */
private final String errorFolderName;
/** Name of folder to receive messages that were successfully processed. */
private final String successFolderName;
/** Name of folder to receive messages that are discarded (no-reply). */
private final String discardFolderName;
/** For sending response emails. */
private final EmailerFactory emailerFactory;
/**
* Constructor.
*
* @param inStoreFactory
* For getting a connection to the mail server.
* @param inMessageProcessor
* Does the actual work on each message.
* @param inEmailerFactory
* For sending response emails.
* @param inInputFolderName
* Name of folder containing messages to process.
* @param inErrorFolderName
* Name of folder to receive messages that failed processing.
* @param inSuccessFolderName
* Name of folder to receive messages that were successfully processed.
* @param inDiscardFolderName
* Name of folder to receive messages that are discarded (no-reply).
*/
public ImapEmailIngester(final ImapStoreFactory inStoreFactory, final MessageProcessor inMessageProcessor,
final EmailerFactory inEmailerFactory, final String inInputFolderName, final String inErrorFolderName,
final String inSuccessFolderName, final String inDiscardFolderName)
{
storeFactory = inStoreFactory;
messageProcessor = inMessageProcessor;
emailerFactory = inEmailerFactory;
inputFolderName = inInputFolderName;
errorFolderName = inErrorFolderName;
successFolderName = inSuccessFolderName;
discardFolderName = inDiscardFolderName;
}
/**
* Ingests email from a mailbox.
*/
public void execute()
{
// get message store
Store store;
try
{
long startTime = System.nanoTime();
store = storeFactory.getStore();
log.debug("Connected to mail store in {}ns", System.nanoTime() - startTime);
}
catch (MessagingException ex)
{
log.error("Error getting message store.", ex);
return;
}
try
{
// get folders
Folder inputFolder = store.getFolder(inputFolderName);
if (!inputFolder.exists())
{
log.error("Input folder {} does not exist.", inputFolderName);
return;
}
Folder successFolder = null;
if (StringUtils.isNotBlank(successFolderName))
{
successFolder = store.getFolder(successFolderName);
if (!successFolder.exists())
{
log.error("Success folder {} does not exist.", successFolderName);
return;
}
}
Folder discardFolder = null;
if (StringUtils.isNotBlank(discardFolderName))
{
discardFolder = store.getFolder(discardFolderName);
if (!discardFolder.exists())
{
log.error("Discard folder {} does not exist.", discardFolderName);
return;
}
}
Folder errorFolder = null;
if (StringUtils.isNotBlank(errorFolderName))
{
errorFolder = store.getFolder(errorFolderName);
if (!errorFolder.exists())
{
log.error("Error folder {} does not exist.", errorFolderName);
return;
}
}
inputFolder.open(Folder.READ_WRITE);
// fetch messages
// Note: Not preloading CONTENT_INFO. For some reason, preloading the content info (IMAP BODYSTRUCTURE)
// causes the call to getContent to return empty. (As if there was a bug where getContent saw the cached
// body structure and thought that the content itself was cached, but I'd think a bug like that would have
// been found by many people and fixed long ago, so I'm assuming it's something else.)
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
Message[] msgs = inputFolder.getMessages();
inputFolder.fetch(msgs, fp);
log.debug("About to process {} messages", msgs.length);
// process each message
if (msgs.length > 0)
{
List<Message> successMessages = new ArrayList<Message>();
List<Message> errorMessages = new ArrayList<Message>();
List<Message> discardMessages = new ArrayList<Message>();
List<Message> responseMessages = new ArrayList<Message>();
for (int i = 0; i < msgs.length; i++)
{
Message message = msgs[i];
try
{
boolean processed = messageProcessor.execute(message, responseMessages);
(processed ? successMessages : discardMessages).add(message);
}
catch (Exception ex)
{
log.error("Failed to process email message.", ex);
errorMessages.add(message);
}
}
// send response messages
for (Message responseMessage : responseMessages)
{
emailerFactory.sendMail(responseMessage);
}
// move and purge messages
if (successFolder != null && !successMessages.isEmpty())
{
inputFolder.copyMessages(successMessages.toArray(new Message[successMessages.size()]),
successFolder);
}
if (discardFolder != null && !discardMessages.isEmpty())
{
inputFolder.copyMessages(discardMessages.toArray(new Message[discardMessages.size()]),
discardFolder);
}
if (errorFolder != null && !errorMessages.isEmpty())
{
inputFolder.copyMessages(errorMessages.toArray(new Message[errorMessages.size()]), errorFolder);
}
for (int i = 0; i < msgs.length; i++)
{
msgs[i].setFlag(Flag.DELETED, true);
}
log.info("{} messages processed: {} successful, {} discarded, {} failed.", new Object[] {
msgs.length, successMessages.size(), discardMessages.size(), errorMessages.size() });
}
// close folder
inputFolder.close(true);
}
catch (MessagingException ex)
{
log.error("Error ingesting email.", ex);
}
catch (Exception ex)
{
log.error("Error ingesting email.", ex);
}
finally
{
// close store
try
{
store.close();
}
catch (MessagingException ex)
{
log.error("Error closing message store.", ex);
}
}
}
}