google-site-verification: google61fe8ba583a51912.html
POP3/IMAP Email Reader in Java

Overview

A comprehensive email reader implementation supporting both POP3 and IMAP protocols with features for reading, searching, and processing emails.

Dependencies

Maven Dependencies

<dependencies> <!-- JavaMail API --> <dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.6.2</version> </dependency> <!-- Apache Commons --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.36</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> </dependencies>

Core Implementation

1. Configuration Classes

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.Properties; @JsonIgnoreProperties(ignoreUnknown = true) public class EmailConfig { private String protocol; // "pop3", "imap", "imaps" private String host; private int port; private String username; private String password; private boolean useSSL; private boolean useTLS; private int connectionTimeout = 30000; private int readTimeout = 30000; private int batchSize = 50; // Default configurations for common providers public static EmailConfig gmailImapConfig(String username, String password) { EmailConfig config = new EmailConfig(); config.setProtocol("imaps"); config.setHost("imap.gmail.com"); config.setPort(993); config.setUsername(username); config.setPassword(password); config.setUseSSL(true); config.setUseTLS(true); return config; } public static EmailConfig outlookImapConfig(String username, String password) { EmailConfig config = new EmailConfig(); config.setProtocol("imaps"); config.setHost("outlook.office365.com"); config.setPort(993); config.setUsername(username); config.setPassword(password); config.setUseSSL(true); config.setUseTLS(true); return config; } public static EmailConfig yahooPop3Config(String username, String password) { EmailConfig config = new EmailConfig(); config.setProtocol("pop3"); config.setHost("pop.mail.yahoo.com"); config.setPort(995); config.setUsername(username); config.setPassword(password); config.setUseSSL(true); return config; } public Properties toJavaMailProperties() { Properties props = new Properties(); // Set protocol-specific properties String protocol = this.protocol.toLowerCase(); props.put("mail." + protocol + ".host", host); props.put("mail." + protocol + ".port", String.valueOf(port)); // Authentication props.put("mail." + protocol + ".auth", "true"); // SSL/TLS configuration if (useSSL) { props.put("mail." + protocol + ".ssl.enable", "true"); props.put("mail." + protocol + ".ssl.trust", host); } if (useTLS) { props.put("mail." + protocol + ".starttls.enable", "true"); } // Timeout settings props.put("mail." + protocol + ".connectiontimeout", String.valueOf(connectionTimeout)); props.put("mail." + protocol + ".timeout", String.valueOf(readTimeout)); // IMAP-specific properties if (protocol.startsWith("imap")) { props.put("mail.imap.partialfetch", "false"); props.put("mail.imap.fetchsize", "8192"); props.put("mail.imap.peek", "true"); // Don't set seen flag when reading } // POP3-specific properties if (protocol.startsWith("pop3")) { props.put("mail.pop3.rsetbeforequit", "true"); } return props; } // Getters and setters public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public boolean isUseSSL() { return useSSL; } public void setUseSSL(boolean useSSL) { this.useSSL = useSSL; } public boolean isUseTLS() { return useTLS; } public void setUseTLS(boolean useTLS) { this.useTLS = useTLS; } public int getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } public int getReadTimeout() { return readTimeout; } public void setReadTimeout(int readTimeout) { this.readTimeout = readTimeout; } public int getBatchSize() { return batchSize; } public void setBatchSize(int batchSize) { this.batchSize = batchSize; } @Override public String toString() { return String.format( "EmailConfig{protocol='%s', host='%s', port=%d, username='%s', useSSL=%s, useTLS=%s}", protocol, host, port, username, useSSL, useTLS ); } }

2. Email Message Model

import javax.mail.Address; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import java.io.IOException; import java.util.*; import java.util.regex.Pattern; public class EmailMessage { private String messageId; private String subject; private String from; private List<String> to; private List<String> cc; private List<String> bcc; private Date sentDate; private Date receivedDate; private String plainText; private String htmlText; private List<EmailAttachment> attachments; private Map<String, String> headers; private int messageNumber; private long size; private boolean seen; private boolean replied; private boolean deleted; private List<String> flags; public EmailMessage() { this.to = new ArrayList<>(); this.cc = new ArrayList<>(); this.bcc = new ArrayList<>(); this.attachments = new ArrayList<>(); this.headers = new HashMap<>(); this.flags = new ArrayList<>(); } public static EmailMessage fromMimeMessage(Message message) throws EmailProcessingException { EmailMessage email = new EmailMessage(); try { // Basic message info email.setMessageNumber(message.getMessageNumber()); email.setMessageId(getMessageId(message)); email.setSubject(decodeText(message.getSubject())); email.setSentDate(message.getSentDate()); email.setReceivedDate(message.getReceivedDate()); email.setSize(message.getSize()); // Address information email.setFrom(extractAddress(message.getFrom())); email.setTo(extractAddresses(message.getRecipients(Message.RecipientType.TO))); email.setCc(extractAddresses(message.getRecipients(Message.RecipientType.CC))); email.setBcc(extractAddresses(message.getRecipients(Message.RecipientType.BCC))); // Flags email.setSeen(message.isSet(Flags.Flag.SEEN)); email.setReplied(message.isSet(Flags.Flag.ANSWERED)); email.setDeleted(message.isSet(Flags.Flag.DELETED)); email.setFlags(extractFlags(message.getFlags())); // Headers email.setHeaders(extractHeaders(message)); // Content extractContent(message, email); } catch (Exception e) { throw new EmailProcessingException("Failed to parse email message", e); } return email; } private static void extractContent(Part part, EmailMessage email) throws MessagingException, IOException { Object content = part.getContent(); if (content instanceof String) { // Simple text content String text = (String) content; if (part.isMimeType("text/plain")) { email.setPlainText(text); } else if (part.isMimeType("text/html")) { email.setHtmlText(text); } } else if (content instanceof Multipart) { // Multipart content Multipart multipart = (Multipart) content; for (int i = 0; i < multipart.getCount(); i++) { BodyPart bodyPart = multipart.getBodyPart(i); // Check for attachments if (Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()) || bodyPart.getFileName() != null) { EmailAttachment attachment = extractAttachment(bodyPart); if (attachment != null) { email.getAttachments().add(attachment); } } else { // Recursively process nested parts extractContent(bodyPart, email); } } } } private static EmailAttachment extractAttachment(BodyPart bodyPart) throws MessagingException, IOException { String fileName = bodyPart.getFileName(); if (fileName == null) return null; fileName = decodeText(fileName); EmailAttachment attachment = new EmailAttachment(); attachment.setFileName(fileName); attachment.setContentType(bodyPart.getContentType()); attachment.setSize(bodyPart.getSize()); // For large attachments, we might want to stream instead of loading into memory if (bodyPart.getSize() < 10 * 1024 * 1024) { // 10MB limit attachment.setData(bodyPart.getInputStream().readAllBytes()); } return attachment; } private static String extractAddress(Address[] addresses) { if (addresses == null || addresses.length == 0) return null; return decodeText(addresses[0].toString()); } private static List<String> extractAddresses(Address[] addresses) { List<String> result = new ArrayList<>(); if (addresses != null) { for (Address address : addresses) { result.add(decodeText(address.toString())); } } return result; } private static List<String> extractFlags(Flags flags) { List<String> result = new ArrayList<>(); if (flags != null) { Flags.Flag[] systemFlags = flags.getSystemFlags(); for (Flags.Flag flag : systemFlags) { if (flag == Flags.Flag.ANSWERED) result.add("ANSWERED"); else if (flag == Flags.Flag.DELETED) result.add("DELETED"); else if (flag == Flags.Flag.DRAFT) result.add("DRAFT"); else if (flag == Flags.Flag.FLAGGED) result.add("FLAGGED"); else if (flag == Flags.Flag.RECENT) result.add("RECENT"); else if (flag == Flags.Flag.SEEN) result.add("SEEN"); } String[] userFlags = flags.getUserFlags(); Collections.addAll(result, userFlags); } return result; } private static Map<String, String> extractHeaders(Message message) throws MessagingException { Map<String, String> headers = new HashMap<>(); Enumeration<?> headerEnum = message.getAllHeaderLines(); while (headerEnum.hasMoreElements()) { String header = (String) headerEnum.nextElement(); int colonIndex = header.indexOf(':'); if (colonIndex > 0) { String key = header.substring(0, colonIndex).trim(); String value = header.substring(colonIndex + 1).trim(); headers.put(key, value); } } return headers; } private static String getMessageId(Message message) throws MessagingException { String[] messageIds = message.getHeader("Message-ID"); return (messageIds != null && messageIds.length > 0) ? messageIds[0] : null; } private static String decodeText(String text) { if (text == null) return null; try { return MimeUtility.decodeText(MimeUtility.unfold(text)); } catch (Exception e) { return text; // Return original if decoding fails } } // Getters and setters public String getMessageId() { return messageId; } public void setMessageId(String messageId) { this.messageId = messageId; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public List<String> getTo() { return to; } public void setTo(List<String> to) { this.to = to; } public List<String> getCc() { return cc; } public void setCc(List<String> cc) { this.cc = cc; } public List<String> getBcc() { return bcc; } public void setBcc(List<String> bcc) { this.bcc = bcc; } public Date getSentDate() { return sentDate; } public void setSentDate(Date sentDate) { this.sentDate = sentDate; } public Date getReceivedDate() { return receivedDate; } public void setReceivedDate(Date receivedDate) { this.receivedDate = receivedDate; } public String getPlainText() { return plainText; } public void setPlainText(String plainText) { this.plainText = plainText; } public String getHtmlText() { return htmlText; } public void setHtmlText(String htmlText) { this.htmlText = htmlText; } public List<EmailAttachment> getAttachments() { return attachments; } public void setAttachments(List<EmailAttachment> attachments) { this.attachments = attachments; } public Map<String, String> getHeaders() { return headers; } public void setHeaders(Map<String, String> headers) { this.headers = headers; } public int getMessageNumber() { return messageNumber; } public void setMessageNumber(int messageNumber) { this.messageNumber = messageNumber; } public long getSize() { return size; } public void setSize(long size) { this.size = size; } public boolean isSeen() { return seen; } public void setSeen(boolean seen) { this.seen = seen; } public boolean isReplied() { return replied; } public void setReplied(boolean replied) { this.replied = replied; } public boolean isDeleted() { return deleted; } public void setDeleted(boolean deleted) { this.deleted = deleted; } public List<String> getFlags() { return flags; } public void setFlags(List<String> flags) { this.flags = flags; } public String getTextContent() { return plainText != null ? plainText : (htmlText != null ? stripHtml(htmlText) : null); } private String stripHtml(String html) { return html.replaceAll("<[^>]+>", "").trim(); } public boolean hasAttachments() { return attachments != null && !attachments.isEmpty(); } @Override public String toString() { return String.format( "EmailMessage{from='%s', subject='%s', date=%s, attachments=%d}", from, subject, sentDate, attachments.size() ); } } public class EmailAttachment { private String fileName; private String contentType; private byte[] data; private long size; // Getters and setters public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } public byte[] getData() { return data; } public void setData(byte[] data) { this.data = data; } public long getSize() { return size; } public void setSize(long size) { this.size = size; } public String getFileExtension() { if (fileName == null) return ""; int dotIndex = fileName.lastIndexOf('.'); return dotIndex > 0 ? fileName.substring(dotIndex + 1).toLowerCase() : ""; } public boolean isImage() { return contentType != null && contentType.startsWith("image/"); } public boolean isPdf() { return "application/pdf".equals(contentType) || fileName.toLowerCase().endsWith(".pdf"); } }

3. Email Reader Interface and Implementation

import javax.mail.*; import javax.mail.search.SearchTerm; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public interface EmailReader { // Connection management void connect() throws EmailException; void disconnect() throws EmailException; boolean isConnected(); // Folder operations List<String> getFolders() throws EmailException; void openFolder(String folderName) throws EmailException; void openFolder(String folderName, int mode) throws EmailException; void closeFolder() throws EmailException; // Email retrieval List<EmailMessage> getMessages() throws EmailException; List<EmailMessage> getMessages(int start, int end) throws EmailException; EmailMessage getMessage(int messageNumber) throws EmailException; List<EmailMessage> searchMessages(SearchTerm searchTerm) throws EmailException; // Email operations void markAsRead(int messageNumber) throws EmailException; void markAsUnread(int messageNumber) throws EmailException; void deleteMessage(int messageNumber) throws EmailException; void moveMessage(int messageNumber, String targetFolder) throws EmailException; // Batch operations int getMessageCount() throws EmailException; int getUnreadCount() throws EmailException; // Async operations CompletableFuture<List<EmailMessage>> getMessagesAsync(); CompletableFuture<Integer> getMessageCountAsync(); } public class JavaMailEmailReader implements EmailReader { private final EmailConfig config; private Session session; private Store store; private Folder folder; private final ExecutorService executor; public JavaMailEmailReader(EmailConfig config) { this.config = config; this.executor = Executors.newCachedThreadPool(); } @Override public void connect() throws EmailException { try { Properties props = config.toJavaMailProperties(); session = Session.getInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(config.getUsername(), config.getPassword()); } }); session.setDebug(false); // Set to true for debugging store = session.getStore(config.getProtocol()); store.connect(config.getHost(), config.getPort(), config.getUsername(), config.getPassword()); } catch (Exception e) { throw new EmailException("Failed to connect to email server", e); } } @Override public void disconnect() throws EmailException { try { if (folder != null && folder.isOpen()) { folder.close(false); } if (store != null && store.isConnected()) { store.close(); } } catch (Exception e) { throw new EmailException("Failed to disconnect from email server", e); } } @Override public boolean isConnected() { return store != null && store.isConnected(); } @Override public List<String> getFolders() throws EmailException { checkConnection(); try { List<String> folderNames = new ArrayList<>(); Folder[] folders = store.getDefaultFolder().list(); for (Folder f : folders) { folderNames.add(f.getName()); // Recursively get subfolders for IMAP if (f.getType() == Folder.HOLDS_FOLDERS) { addSubfolders(f, folderNames, ""); } } return folderNames; } catch (Exception e) { throw new EmailException("Failed to get folder list", e); } } private void addSubfolders(Folder parent, List<String> folderNames, String prefix) throws MessagingException { Folder[] subfolders = parent.list(); for (Folder subfolder : subfolders) { String fullName = prefix + "/" + subfolder.getName(); folderNames.add(fullName); if (subfolder.getType() == Folder.HOLDS_FOLDERS) { addSubfolders(subfolder, folderNames, fullName); } } } @Override public void openFolder(String folderName) throws EmailException { openFolder(folderName, Folder.READ_ONLY); } @Override public void openFolder(String folderName, int mode) throws EmailException { checkConnection(); try { if (folder != null && folder.isOpen()) { folder.close(false); } folder = store.getFolder(folderName); if (!folder.exists()) { throw new EmailException("Folder does not exist: " + folderName); } folder.open(mode); } catch (Exception e) { throw new EmailException("Failed to open folder: " + folderName, e); } } @Override public void closeFolder() throws EmailException { try { if (folder != null && folder.isOpen()) { folder.close(false); } folder = null; } catch (Exception e) { throw new EmailException("Failed to close folder", e); } } @Override public List<EmailMessage> getMessages() throws EmailException { return getMessages(1, getMessageCount()); } @Override public List<EmailMessage> getMessages(int start, int end) throws EmailException { checkFolderOpen(); try { int totalMessages = folder.getMessageCount(); if (totalMessages == 0) { return Collections.emptyList(); } // Adjust bounds start = Math.max(1, start); end = Math.min(totalMessages, end); List<EmailMessage> messages = new ArrayList<>(); Message[] javaMessages = folder.getMessages(start, end); // Fetch envelope data first for better performance folder.fetch(javaMessages, new FetchProfile[] { FetchProfile.Item.ENVELOPE, FetchProfile.Item.FLAGS, FetchProfile.Item.CONTENT_INFO }); for (Message javaMessage : javaMessages) { try { EmailMessage emailMessage = EmailMessage.fromMimeMessage(javaMessage); messages.add(emailMessage); } catch (EmailProcessingException e) { System.err.println("Failed to process message " + javaMessage.getMessageNumber() + ": " + e.getMessage()); // Continue with next message } } return messages; } catch (Exception e) { throw new EmailException("Failed to get messages", e); } } @Override public EmailMessage getMessage(int messageNumber) throws EmailException { checkFolderOpen(); try { Message javaMessage = folder.getMessage(messageNumber); if (javaMessage == null) { throw new EmailException("Message not found: " + messageNumber); } // Fetch full content folder.fetch(new Message[]{javaMessage}, new FetchProfile[] { FetchProfile.Item.ENVELOPE, FetchProfile.Item.CONTENT_INFO, FetchProfile.Item.FLAGS }); return EmailMessage.fromMimeMessage(javaMessage); } catch (Exception e) { throw new EmailException("Failed to get message: " + messageNumber, e); } } @Override public List<EmailMessage> searchMessages(SearchTerm searchTerm) throws EmailException { checkFolderOpen(); try { Message[] javaMessages = folder.search(searchTerm); List<EmailMessage> messages = new ArrayList<>(); for (Message javaMessage : javaMessages) { try { EmailMessage emailMessage = EmailMessage.fromMimeMessage(javaMessage); messages.add(emailMessage); } catch (EmailProcessingException e) { System.err.println("Failed to process search result: " + e.getMessage()); } } return messages; } catch (Exception e) { throw new EmailException("Failed to search messages", e); } } @Override public void markAsRead(int messageNumber) throws EmailException { setMessageFlags(messageNumber, new Flags(Flags.Flag.SEEN), true); } @Override public void markAsUnread(int messageNumber) throws EmailException { setMessageFlags(messageNumber, new Flags(Flags.Flag.SEEN), false); } @Override public void deleteMessage(int messageNumber) throws EmailException { setMessageFlags(messageNumber, new Flags(Flags.Flag.DELETED), true); } @Override public void moveMessage(int messageNumber, String targetFolder) throws EmailException { checkFolderOpen(); try { Message message = folder.getMessage(messageNumber); if (message == null) { throw new EmailException("Message not found: " + messageNumber); } Folder target = store.getFolder(targetFolder); if (!target.exists()) { throw new EmailException("Target folder does not exist: " + targetFolder); } folder.copyMessages(new Message[]{message}, target); } catch (Exception e) { throw new EmailException("Failed to move message to: " + targetFolder, e); } } @Override public int getMessageCount() throws EmailException { checkFolderOpen(); try { return folder.getMessageCount(); } catch (Exception e) { throw new EmailException("Failed to get message count", e); } } @Override public int getUnreadCount() throws EmailException { checkFolderOpen(); try { return folder.getUnreadMessageCount(); } catch (Exception e) { throw new EmailException("Failed to get unread count", e); } } @Override public CompletableFuture<List<EmailMessage>> getMessagesAsync() { return CompletableFuture.supplyAsync(() -> { try { return getMessages(); } catch (EmailException e) { throw new RuntimeException(e); } }, executor); } @Override public CompletableFuture<Integer> getMessageCountAsync() { return CompletableFuture.supplyAsync(() -> { try { return getMessageCount(); } catch (EmailException e) { throw new RuntimeException(e); } }, executor); } private void setMessageFlags(int messageNumber, Flags flags, boolean set) throws EmailException { checkFolderOpen(); try { Message message = folder.getMessage(messageNumber); if (message == null) { throw new EmailException("Message not found: " + messageNumber); } message.setFlags(flags, set); } catch (Exception e) { throw new EmailException("Failed to set message flags", e); } } private void checkConnection() throws EmailException { if (!isConnected()) { throw new EmailException("Not connected to email server"); } } private void checkFolderOpen() throws EmailException { checkConnection(); if (folder == null || !folder.isOpen()) { throw new EmailException("No folder open"); } } public void setSessionDebug(boolean debug) { if (session != null) { session.setDebug(debug); } } @Override protected void finalize() throws Throwable { try { disconnect(); executor.shutdown(); } finally { super.finalize(); } } }

4. Search Utilities

import javax.mail.search.*; public class EmailSearchBuilder { public static SearchTerm from(String fromAddress) { return new FromStringTerm(fromAddress); } public static SearchTerm subjectContains(String text) { return new SubjectTerm(text); } public static SearchTerm bodyContains(String text) { return new BodyTerm(text); } public static SearchTerm recipientContains(String address, Message.RecipientType type) { return new RecipientStringTerm(type, address); } public static SearchTerm hasAttachment() { return new FlagTerm(new Flags(Flags.Flag.FLAGGED), false); // Custom implementation needed } public static SearchTerm isUnread() { return new FlagTerm(new Flags(Flags.Flag.SEEN), false); } public static SearchTerm isRead() { return new FlagTerm(new Flags(Flags.Flag.SEEN), true); } public static SearchTerm isFlagged() { return new FlagTerm(new Flags(Flags.Flag.FLAGGED), true); } public static SearchTerm receivedAfter(Date date) { return new ReceivedDateTerm(ComparisonTerm.GT, date); } public static SearchTerm receivedBefore(Date date) { return new ReceivedDateTerm(ComparisonTerm.LT, date); } public static SearchTerm sentAfter(Date date) { return new SentDateTerm(ComparisonTerm.GT, date); } public static SearchTerm sentBefore(Date date) { return new SentDateTerm(ComparisonTerm.LT, date); } public static SearchTerm and(SearchTerm... terms) { return new AndTerm(terms); } public static SearchTerm or(SearchTerm... terms) { return new OrTerm(terms); } public static SearchTerm not(SearchTerm term) { return new NotTerm(term); } // Complex search examples public static SearchTerm unreadFromImportantSenders() { return and( isUnread(), or( from("[email protected]"), from("[email protected]"), subjectContains("URGENT"), subjectContains("IMPORTANT") ) ); } public static SearchTerm recentPromotions() { Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, -7); // Last 7 days Date weekAgo = cal.getTime(); return and( receivedAfter(weekAgo), or( from("@amazon.com"), from("@ebay.com"), subjectContains("sale"), subjectContains("discount"), subjectContains("promotion") ) ); } }

5. Exception Classes

public class EmailException extends Exception { public EmailException(String message) { super(message); } public EmailException(String message, Throwable cause) { super(message, cause); } } public class EmailProcessingException extends EmailException { public EmailProcessingException(String message) { super(message); } public EmailProcessingException(String message, Throwable cause) { super(message, cause); } }

6. Advanced Email Client with Management Features

import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; public class AdvancedEmailClient { private final EmailReader emailReader; private final EmailConfig config; private final ObjectMapper objectMapper; private final Map<String, List<EmailMessage>> cache; private final EmailFilterManager filterManager; public AdvancedEmailClient(EmailConfig config) throws EmailException { this.config = config; this.emailReader = new JavaMailEmailReader(config); this.objectMapper = new ObjectMapper(); this.cache = new ConcurrentHashMap<>(); this.filterManager = new EmailFilterManager(); } public void connect() throws EmailException { emailReader.connect(); } public void disconnect() throws EmailException { emailReader.disconnect(); cache.clear(); } public EmailStatistics getStatistics(String folderName) throws EmailException { emailReader.openFolder(folderName); int total = emailReader.getMessageCount(); int unread = emailReader.getUnreadCount(); List<EmailMessage> recentMessages = emailReader.getMessages( Math.max(1, total - 100), total); // Last 100 messages EmailStatistics stats = new EmailStatistics(); stats.setTotalMessages(total); stats.setUnreadMessages(unread); stats.setRecentMessageCount(recentMessages.size()); // Analyze senders Map<String, Long> senderCounts = recentMessages.stream() .filter(msg -> msg.getFrom() != null) .collect(Collectors.groupingBy(EmailMessage::getFrom, Collectors.counting())); stats.setTopSenders(senderCounts.entrySet().stream() .sorted(Map.Entry.<String, Long>comparingByValue().reversed()) .limit(10) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); // Analyze subjects for common topics stats.setCommonTopics(analyzeTopics(recentMessages)); return stats; } public List<EmailMessage> getFilteredMessages(String folderName, EmailFilter filter) throws EmailException { emailReader.openFolder(folderName); List<EmailMessage> messages; if (cache.containsKey(folderName)) { messages = cache.get(folderName); } else { messages = emailReader.getMessages(); cache.put(folderName, messages); } return filterManager.applyFilter(messages, filter); } public void exportMessages(String folderName, String outputPath, ExportFormat format) throws EmailException, IOException { emailReader.openFolder(folderName); List<EmailMessage> messages = emailReader.getMessages(); switch (format) { case JSON: exportToJson(messages, outputPath); break; case CSV: exportToCsv(messages, outputPath); break; case TEXT: exportToText(messages, outputPath); break; } } public void processEmails(String folderName, EmailProcessor processor) throws EmailException { emailReader.openFolder(folderName); List<EmailMessage> messages = emailReader.getMessages(); for (EmailMessage message : messages) { try { processor.process(message); } catch (Exception e) { System.err.println("Failed to process message " + message.getMessageNumber() + ": " + e.getMessage()); } } } public CompletableFuture<Void> processEmailsAsync(String folderName, EmailProcessor processor) { return CompletableFuture.runAsync(() -> { try { processEmails(folderName, processor); } catch (EmailException e) { throw new RuntimeException(e); } }); } private void exportToJson(List<EmailMessage> messages, String outputPath) throws IOException { List<Map<String, Object>> exportData = messages.stream() .map(this::messageToMap) .collect(Collectors.toList()); objectMapper.writerWithDefaultPrettyPrinter() .writeValue(new File(outputPath), exportData); } private void exportToCsv(List<EmailMessage> messages, String outputPath) throws IOException { // Implementation for CSV export // Would use a CSV library like OpenCSV } private void exportToText(List<EmailMessage> messages, String outputPath) throws IOException { StringBuilder sb = new StringBuilder(); for (EmailMessage message : messages) { sb.append("From: ").append(message.getFrom()).append("\n"); sb.append("Subject: ").append(message.getSubject()).append("\n"); sb.append("Date: ").append(message.getSentDate()).append("\n"); sb.append("Body: ").append(message.getTextContent()).append("\n"); sb.append("---\n\n"); } Files.write(Paths.get(outputPath), sb.toString().getBytes()); } private Map<String, Object> messageToMap(EmailMessage message) { Map<String, Object> map = new HashMap<>(); map.put("messageId", message.getMessageId()); map.put("from", message.getFrom()); map.put("subject", message.getSubject()); map.put("sentDate", message.getSentDate()); map.put("receivedDate", message.getReceivedDate()); map.put("hasAttachments", message.hasAttachments()); map.put("isRead", message.isSeen()); map.put("bodyPreview", message.getTextContent() != null ? message.getTextContent().substring(0, Math.min(200, message.getTextContent().length())) : ""); return map; } private Set<String> analyzeTopics(List<EmailMessage> messages) { // Simple topic analysis based on common words in subjects Map<String, Integer> wordCounts = new HashMap<>(); for (EmailMessage message : messages) { if (message.getSubject() != null) { String[] words = message.getSubject().toLowerCase().split("\\s+"); for (String word : words) { if (word.length() > 3 && !isCommonWord(word)) { wordCounts.put(word, wordCounts.getOrDefault(word, 0) + 1); } } } } return wordCounts.entrySet().stream() .sorted(Map.Entry.<String, Integer>comparingByValue().reversed()) .limit(5) .map(Map.Entry::getKey) .collect(Collectors.toSet()); } private boolean isCommonWord(String word) { Set<String> commonWords = Set.of("the", "and", "for", "with", "this", "that", "your", "have"); return commonWords.contains(word); } // Getters public EmailReader getEmailReader() { return emailReader; } public EmailFilterManager getFilterManager() { return filterManager; } } // Supporting classes public class EmailStatistics { private int totalMessages; private int unreadMessages; private int recentMessageCount; private Map<String, Long> topSenders; private Set<String> commonTopics; // Getters and setters public int getTotalMessages() { return totalMessages; } public void setTotalMessages(int totalMessages) { this.totalMessages = totalMessages; } public int getUnreadMessages() { return unreadMessages; } public void setUnreadMessages(int unreadMessages) { this.unreadMessages = unreadMessages; } public int getRecentMessageCount() { return recentMessageCount; } public void setRecentMessageCount(int recentMessageCount) { this.recentMessageCount = recentMessageCount; } public Map<String, Long> getTopSenders() { return topSenders; } public void setTopSenders(Map<String, Long> topSenders) { this.topSenders = topSenders; } public Set<String> getCommonTopics() { return commonTopics; } public void setCommonTopics(Set<String> commonTopics) { this.commonTopics = commonTopics; } @Override public String toString() { return String.format( "EmailStatistics{total=%d, unread=%d, recent=%d, topSenders=%d, topics=%s}", totalMessages, unreadMessages, recentMessageCount, topSenders.size(), commonTopics ); } } public class EmailFilter { private String from; private String subjectContains; private String bodyContains; private Boolean hasAttachments; private Boolean isRead; private Date receivedAfter; private Date receivedBefore; // Getters and setters public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public String getSubjectContains() { return subjectContains; } public void setSubjectContains(String subjectContains) { this.subjectContains = subjectContains; } public String getBodyContains() { return bodyContains; } public void setBodyContains(String bodyContains) { this.bodyContains = bodyContains; } public Boolean getHasAttachments() { return hasAttachments; } public void setHasAttachments(Boolean hasAttachments) { this.hasAttachments = hasAttachments; } public Boolean getIsRead() { return isRead; } public void setIsRead(Boolean isRead) { this.isRead = isRead; } public Date getReceivedAfter() { return receivedAfter; } public void setReceivedAfter(Date receivedAfter) { this.receivedAfter = receivedAfter; } public Date getReceivedBefore() { return receivedBefore; } public void setReceivedBefore(Date receivedBefore) { this.receivedBefore = receivedBefore; } } public class EmailFilterManager { public List<EmailMessage> applyFilter(List<EmailMessage> messages, EmailFilter filter) { return messages.stream() .filter(msg -> filter.getFrom() == null || (msg.getFrom() != null && msg.getFrom().contains(filter.getFrom()))) .filter(msg -> filter.getSubjectContains() == null || (msg.getSubject() != null && msg.getSubject().toLowerCase() .contains(filter.getSubjectContains().toLowerCase()))) .filter(msg -> filter.getBodyContains() == null || (msg.getTextContent() != null && msg.getTextContent().toLowerCase() .contains(filter.getBodyContains().toLowerCase()))) .filter(msg -> filter.getHasAttachments() == null || msg.hasAttachments() == filter.getHasAttachments()) .filter(msg -> filter.getIsRead() == null || msg.isSeen() == filter.getIsRead()) .filter(msg -> filter.getReceivedAfter() == null || (msg.getReceivedDate() != null && !msg.getReceivedDate().before(filter.getReceivedAfter()))) .filter(msg -> filter.getReceivedBefore() == null || (msg.getReceivedDate() != null && !msg.getReceivedDate().after(filter.getReceivedBefore()))) .collect(Collectors.toList()); } } public interface EmailProcessor { void process(EmailMessage message) throws Exception; } public enum ExportFormat { JSON, CSV, TEXT }

7. Usage Examples and Demo

import java.util.*; import java.util.concurrent.CompletableFuture; public class EmailReaderDemo { public static void main(String[] args) { try { // Configuration for Gmail EmailConfig config = EmailConfig.gmailImapConfig( "[email protected]", "your-app-password" // Use app password, not regular password ); AdvancedEmailClient client = new AdvancedEmailClient(config); client.connect(); System.out.println("Connected to email server"); // Get folder list List<String> folders = client.getEmailReader().getFolders(); System.out.println("Available folders: " + folders); // Open INBOX client.getEmailReader().openFolder("INBOX"); // Get statistics EmailStatistics stats = client.getStatistics("INBOX"); System.out.println("Email Statistics: " + stats); // Example 1: Read recent emails System.out.println("\n--- Recent Emails ---"); List<EmailMessage> recentEmails = client.getEmailReader().getMessages( Math.max(1, stats.getTotalMessages() - 10), stats.getTotalMessages()); for (EmailMessage email : recentEmails) { System.out.printf("From: %s\nSubject: %s\nDate: %s\nUnread: %s\n\n", email.getFrom(), email.getSubject(), email.getSentDate(), !email.isSeen()); } // Example 2: Search for specific emails System.out.println("--- Searching for Unread Emails ---"); SearchTerm unreadSearch = EmailSearchBuilder.isUnread(); List<EmailMessage> unreadEmails = client.getEmailReader().searchMessages(unreadSearch); System.out.println("Found " + unreadEmails.size() + " unread emails"); // Example 3: Filter emails System.out.println("\n--- Filtered Emails ---"); EmailFilter filter = new EmailFilter(); filter.setHasAttachments(true); filter.setIsRead(false); List<EmailMessage> filtered = client.getFilteredMessages("INBOX", filter); System.out.println("Found " + filtered.size() + " unread emails with attachments"); // Example 4: Process emails with custom processor System.out.println("\n--- Processing Emails ---"); client.processEmails("INBOX", new EmailProcessor() { @Override public void process(EmailMessage message) { System.out.printf("Processing: %s - %s\n", message.getFrom(), message.getSubject()); if (message.hasAttachments()) { System.out.println(" Has " + message.getAttachments().size() + " attachments"); } } }); // Example 5: Async processing System.out.println("\n--- Async Processing ---"); CompletableFuture<Void> asyncProcess = client.processEmailsAsync("INBOX", message -> { if (message.getSubject() != null && message.getSubject().contains("newsletter")) { System.out.println("Newsletter: " + message.getSubject()); // Mark newsletter as read client.getEmailReader().markAsRead(message.getMessageNumber()); } }); asyncProcess.thenRun(() -> System.out.println("Async processing completed")); // Wait for async completion asyncProcess.get(); // Example 6: Export emails System.out.println("\n--- Exporting Emails ---"); client.exportMessages("INBOX", "emails_export.json", ExportFormat.JSON); System.out.println("Emails exported to emails_export.json"); client.disconnect(); System.out.println("Disconnected from email server"); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); e.printStackTrace(); } } }

Key Features

  1. Dual Protocol Support: POP3 and IMAP with SSL/TLS
  2. Comprehensive Email Processing: Full MIME message parsing
  3. Advanced Search: Built-in search utilities with complex query support
  4. Batch Operations: Efficient processing of multiple emails
  5. Async Support: Non-blocking email operations
  6. Export Capabilities: JSON, CSV, and text exports
  7. Filtering System: Flexible email filtering and categorization
  8. Statistics: Email analytics and insights
  9. Attachment Handling: Automatic attachment detection and processing
  10. Error Handling: Comprehensive exception handling and recovery

Security Notes

  • Use app passwords for Gmail instead of regular passwords
  • Enable 2-factor authentication for email accounts
  • Store credentials securely (not in code)
  • Use SSL/TLS connections for security
  • Consider implementing connection pooling for production use

This implementation provides a robust foundation for building email client applications, automation tools, or email processing systems in Java.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper