Al Grant
Al Grant

Reputation: 2354

Java Mail - Check for new mail with attachment from specific sender

I have written code that checks hourly for new email with an attachment from a specific sender. An email from the specific user, with an attachment, is expected every hour on the hour (so not using idle function).

When new email is detected, all new emails from that user are saved to an array and the newest email is checked for a csv attachment, manipulated, and saved to disk.

The code which checks for new email looks very verbose and I would like to know if there is any refinements that can be made.

Specifically (1) comparing the unread message count between calls would fail if for example the user logged in and read and email.

(2) array of new emails only looks at the newest email (length()-1).

public class Mail implements Runnable {
private static final long KEEP_ALIVE_FREQ = 3000; // time between mail checks

@Override
public void run() {

    String host = "imap.gmail.com";
    String username = "[email protected]";
    String password = "passwd";
    Properties props = new Properties();
    props.setProperty("mail.imap.ssl.enable", "true");
    Folder inbox;
    int messageCount;
    Store store = null;
    Session session = Session.getInstance(props);

    try {
        store = session.getStore("imap");
        store.connect(host,username,password);
        inbox = store.getFolder("inbox");
        inbox.open(Folder.READ_WRITE);
        messageCount = inbox.getUnreadMessageCount();
        inbox.close(true);

        while (!Thread.interrupted()) {
            inbox.open(Folder.READ_WRITE);
            try {
                if (inbox.getUnreadMessageCount() > messageCount) {
                    System.out.println("New mail has arrived");
                    // Construct array of unseen messages from specified user
                    FlagTerm unseenFlagTerm = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
                    FromTerm senderFlagTerm = new FromTerm(new InternetAddress("[email protected]"));
                    SearchTerm searchTerm = new AndTerm(unseenFlagTerm, senderFlagTerm);
                    Message messages[] = inbox.search(searchTerm);
                    // check that messages array contains at least one message from senderFlag
                    if (messages.length>0) {
                        Message msg = messages[messages.length - 1]; // get most recent message from sender
                        if (hasAttachments(msg)) {
                            System.out.println("Message has an attachment - processing");
                            String csv = attachmentToString(msg);
                            String cleanCsv = cleanCsv(csv);
                            FileUtils.writeStringToFile(new File("C:\\tmp\\somefile.csv"), cleanCsv, StandardCharsets.UTF_8, false);
                            // delete message
                            msg.setFlag(Flags.Flag.DELETED, true);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            // reset unread message count
            messageCount = inbox.getUnreadMessageCount();
            // close inbox
            inbox.close(true);
            System.out.println("About to sleep");
            Thread.sleep(KEEP_ALIVE_FREQ);
        }

    } catch (NoSuchProviderException e) {
        e.printStackTrace();
    } catch (MessagingException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public String attachmentToString(Message message) throws IOException, MessagingException {
    Multipart multipart = (Multipart) message.getContent();
    String theString = null;
    for (int i = 0; i < multipart.getCount(); i++) {
        BodyPart bodyPart = multipart.getBodyPart(i);
        if (!Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()) &&
                StringUtils.isBlank(bodyPart.getFileName())) {
            continue; // dealing with attachments only
        }
        InputStream inputStream = bodyPart.getInputStream();
        StringWriter writer = new StringWriter();
        IOUtils.copy(inputStream, writer, "UTF-8");
        theString = writer.toString();
    }
    return theString;
}

public String cleanCsv(String csv) {
    csv = csv.replaceAll("\"", "");
    csv = csv.replaceAll("\\s*,", ",");
    return csv;
}

boolean hasAttachments(Message msg) throws MessagingException, IOException {
    if (msg.isMimeType("multipart/mixed")) {
        Multipart mp = (Multipart)msg.getContent();
        if (mp.getCount() > 1)
            return true;
    }
    return false;
    }
}

Since I know that the emails always arrive on the hour +/- a minute or so - would it be simpler code and more reliable to schedule the mail check?

Upvotes: 0

Views: 1851

Answers (1)

Bill Shannon
Bill Shannon

Reputation: 29971

As @arnt says, keep track of the UID of the last processed message and then search for newer messages that meet your criteria:

FromTerm senderFromTerm = new FromTerm(new InternetAddress("[email protected]")); 
// first, find all the new messages since we last looked
Message messages[] = ((UIDFolder)inbox).getMessagesByUID(lastProcessedUID, UIDFolder.LASTUID);
// now search within the new messages for those from our user
messages = inbox.search(senderFromTerm, messages);

Upvotes: 1

Related Questions