Jakarta Mail defines a platform-independent and protocol-independent framework to build mail and messaging applications.
—
Store and folder management provides comprehensive capabilities for accessing mail stores, managing folders, and organizing messages within mailboxes.
The Store abstract class provides access to message repositories such as IMAP servers, POP3 servers, or local mailboxes.
public abstract class Store extends Service {
// Folder access
public abstract Folder getDefaultFolder() throws MessagingException;
public abstract Folder getFolder(String name) throws MessagingException;
public abstract Folder getFolder(URLName url) throws MessagingException;
// Event handling
public void addStoreListener(StoreListener l);
public void removeStoreListener(StoreListener l);
// Store information
public Folder[] getPersonalNamespaces() throws MessagingException;
public Folder[] getUserNamespaces(String user) throws MessagingException;
public Folder[] getSharedNamespaces() throws MessagingException;
}import jakarta.mail.*;
import java.util.Properties;
// Configure properties for IMAP store
Properties props = new Properties();
props.put("mail.store.protocol", "imaps");
props.put("mail.imaps.host", "imap.gmail.com");
props.put("mail.imaps.port", "993");
props.put("mail.imaps.ssl.enable", "true");
Session session = Session.getInstance(props);
// Connect to store
Store store = session.getStore("imaps");
store.connect("imap.gmail.com", "username@gmail.com", "password");
try {
// Get default folder (usually root)
Folder defaultFolder = store.getDefaultFolder();
System.out.println("Default folder: " + defaultFolder.getFullName());
// Get inbox
Folder inbox = store.getFolder("INBOX");
// List all folders
Folder[] folders = defaultFolder.list("*");
for (Folder folder : folders) {
System.out.println("Folder: " + folder.getFullName());
}
} finally {
store.close();
}The Folder abstract class represents a container for messages and provides comprehensive folder management capabilities.
public abstract class Folder implements AutoCloseable {
// Folder constants
public static final int HOLDS_MESSAGES = 0x01;
public static final int HOLDS_FOLDERS = 0x02;
public static final int READ_ONLY = 1;
public static final int READ_WRITE = 2;
// Basic folder properties
public abstract String getName();
public abstract String getFullName();
public abstract Folder getParent() throws MessagingException;
public abstract int getType() throws MessagingException;
public abstract boolean exists() throws MessagingException;
// Folder operations
public abstract boolean create(int type) throws MessagingException;
public abstract boolean delete(boolean recurse) throws MessagingException;
public abstract boolean renameTo(Folder f) throws MessagingException;
// Folder access
public abstract void open(int mode) throws MessagingException;
public abstract void close(boolean expunge) throws MessagingException;
public abstract boolean isOpen();
public int getMode();
// Message access
public abstract int getMessageCount() throws MessagingException;
public abstract Message getMessage(int msgnum) throws MessagingException;
public abstract Message[] getMessages() throws MessagingException;
public abstract Message[] getMessages(int start, int end) throws MessagingException;
public abstract Message[] getMessages(int[] msgnums) throws MessagingException;
// New/unread message counts
public int getNewMessageCount() throws MessagingException;
public int getUnreadMessageCount() throws MessagingException;
public int getDeletedMessageCount() throws MessagingException;
// Message operations
public abstract void appendMessages(Message[] msgs) throws MessagingException;
public abstract Message[] expunge() throws MessagingException;
public abstract Message[] search(SearchTerm term) throws MessagingException;
public abstract Message[] search(SearchTerm term, Message[] msgs) throws MessagingException;
// Folder hierarchy
public abstract Folder[] list() throws MessagingException;
public abstract Folder[] list(String pattern) throws MessagingException;
public abstract Folder[] listSubscribed() throws MessagingException;
public abstract Folder[] listSubscribed(String pattern) throws MessagingException;
// Subscription management
public abstract boolean isSubscribed() throws MessagingException;
public abstract void setSubscribed(boolean subscribe) throws MessagingException;
// Event handling
public void addConnectionListener(ConnectionListener l);
public void removeConnectionListener(ConnectionListener l);
public void addFolderListener(FolderListener l);
public void removeFolderListener(FolderListener l);
public void addMessageChangedListener(MessageChangedListener l);
public void removeMessageChangedListener(MessageChangedListener l);
public void addMessageCountListener(MessageCountListener l);
public void removeMessageCountListener(MessageCountListener l);
// Store reference
public Store getStore();
// String representation
public String toString();
}import jakarta.mail.*;
import jakarta.mail.search.*;
// Open inbox for reading
Folder inbox = store.getFolder("INBOX");
inbox.open(Folder.READ_ONLY);
try {
// Get folder information
System.out.println("Folder name: " + inbox.getName());
System.out.println("Full name: " + inbox.getFullName());
System.out.println("Message count: " + inbox.getMessageCount());
System.out.println("New messages: " + inbox.getNewMessageCount());
System.out.println("Unread messages: " + inbox.getUnreadMessageCount());
// Check folder capabilities
if ((inbox.getType() & Folder.HOLDS_MESSAGES) != 0) {
System.out.println("Folder can hold messages");
}
if ((inbox.getType() & Folder.HOLDS_FOLDERS) != 0) {
System.out.println("Folder can hold subfolders");
}
// Get all messages
Message[] messages = inbox.getMessages();
// Get recent messages only
Message[] recentMessages = inbox.search(new FlagTerm(new Flags(Flags.Flag.RECENT), true));
// Get messages from specific range
if (messages.length > 10) {
Message[] lastTen = inbox.getMessages(messages.length - 9, messages.length);
}
} finally {
inbox.close(false); // Don't expunge on close
}// Create new folder
Folder newFolder = store.getFolder("Archive");
if (!newFolder.exists()) {
boolean created = newFolder.create(Folder.HOLDS_MESSAGES);
if (created) {
System.out.println("Folder created successfully");
}
}
// Create folder hierarchy
Folder projectFolder = store.getFolder("Projects");
if (!projectFolder.exists()) {
projectFolder.create(Folder.HOLDS_FOLDERS);
}
Folder subFolder = store.getFolder("Projects/Project1");
if (!subFolder.exists()) {
subFolder.create(Folder.HOLDS_MESSAGES);
}
// Rename folder
Folder oldFolder = store.getFolder("OldName");
Folder newFolderName = store.getFolder("NewName");
if (oldFolder.exists()) {
boolean renamed = oldFolder.renameTo(newFolderName);
if (renamed) {
System.out.println("Folder renamed successfully");
}
}
// Delete folder
Folder folderToDelete = store.getFolder("Temporary");
if (folderToDelete.exists()) {
// Close if open
if (folderToDelete.isOpen()) {
folderToDelete.close(false);
}
boolean deleted = folderToDelete.delete(false); // Don't delete recursively
if (deleted) {
System.out.println("Folder deleted successfully");
}
}// List all folders
Folder rootFolder = store.getDefaultFolder();
Folder[] allFolders = rootFolder.list("*");
for (Folder folder : allFolders) {
System.out.println("Folder: " + folder.getFullName());
// Get parent
Folder parent = folder.getParent();
if (parent != null) {
System.out.println(" Parent: " + parent.getFullName());
}
// Check if it has subfolders
if ((folder.getType() & Folder.HOLDS_FOLDERS) != 0) {
Folder[] subfolders = folder.list();
for (Folder subfolder : subfolders) {
System.out.println(" Subfolder: " + subfolder.getName());
}
}
}
// List only subscribed folders
Folder[] subscribedFolders = rootFolder.listSubscribed("*");
for (Folder folder : subscribedFolders) {
System.out.println("Subscribed: " + folder.getFullName());
}
// Pattern matching for folder listing
Folder[] inboxFolders = rootFolder.list("INBOX*");
Folder[] sentFolders = rootFolder.list("Sent*");// Open source and destination folders
Folder inbox = store.getFolder("INBOX");
Folder archive = store.getFolder("Archive");
inbox.open(Folder.READ_WRITE);
if (!archive.exists()) {
archive.create(Folder.HOLDS_MESSAGES);
}
try {
// Get messages to move
Message[] messages = inbox.getMessages();
Message[] oldMessages = Arrays.stream(messages)
.filter(msg -> {
try {
Date sentDate = msg.getSentDate();
if (sentDate != null) {
long daysDiff = (System.currentTimeMillis() - sentDate.getTime()) / (1000 * 60 * 60 * 24);
return daysDiff > 30; // Older than 30 days
}
return false;
} catch (MessagingException e) {
return false;
}
})
.toArray(Message[]::new);
if (oldMessages.length > 0) {
// Copy messages to archive
archive.appendMessages(oldMessages);
// Mark original messages as deleted
for (Message msg : oldMessages) {
msg.setFlag(Flags.Flag.DELETED, true);
}
// Expunge to actually remove deleted messages
inbox.expunge();
System.out.println("Moved " + oldMessages.length + " messages to archive");
}
} finally {
inbox.close(false);
}// Use FetchProfile for efficient bulk operations
FetchProfile fetchProfile = new FetchProfile();
fetchProfile.add(FetchProfile.Item.ENVELOPE);
fetchProfile.add(FetchProfile.Item.FLAGS);
fetchProfile.add("X-mailer");
// Fetch specified attributes for all messages
Message[] messages = inbox.getMessages();
inbox.fetch(messages, fetchProfile);
// Now access is more efficient
for (Message message : messages) {
System.out.println("From: " + Arrays.toString(message.getFrom()));
System.out.println("Subject: " + message.getSubject());
System.out.println("Flags: " + message.getFlags());
}For stores that support UIDs (like IMAP), you can use UID-based operations for more reliable message tracking.
// UID folder interface
public interface UIDFolder {
public static final class FetchProfileItem extends FetchProfile.Item {
public static final FetchProfileItem UID;
}
public long getUID(Message message) throws MessagingException;
public Message getMessageByUID(long uid) throws MessagingException;
public Message[] getMessagesByUID(long start, long end) throws MessagingException;
public Message[] getMessagesByUID(long[] uids) throws MessagingException;
public long getUIDValidity() throws MessagingException;
public long getUIDNext() throws MessagingException;
}if (inbox instanceof UIDFolder) {
UIDFolder uidFolder = (UIDFolder) inbox;
// Get message by UID (more reliable than message number)
Message message = uidFolder.getMessageByUID(12345L);
// Get UID for a message
long uid = uidFolder.getUID(message);
System.out.println("Message UID: " + uid);
// Get messages by UID range
Message[] messages = uidFolder.getMessagesByUID(1000L, 2000L);
// Get folder UID validity (changes when folder is rebuilt)
long uidValidity = uidFolder.getUIDValidity();
System.out.println("UID Validity: " + uidValidity);
}For stores that support quotas, you can monitor and manage storage limits.
// Quota-aware store interface
public interface QuotaAwareStore {
public Quota[] getQuota(String quotaRoot) throws MessagingException;
public void setQuota(Quota quota) throws MessagingException;
}
// Quota information
public class Quota {
public static final class Resource {
public static final Resource STORAGE;
public static final Resource MESSAGE;
public String name;
public long usage;
public long limit;
}
public String quotaRoot;
public Resource[] resources;
}if (store instanceof QuotaAwareStore) {
QuotaAwareStore quotaStore = (QuotaAwareStore) store;
// Get quota information
Quota[] quotas = quotaStore.getQuota("INBOX");
for (Quota quota : quotas) {
System.out.println("Quota root: " + quota.quotaRoot);
for (Quota.Resource resource : quota.resources) {
System.out.println("Resource: " + resource.name);
System.out.println("Usage: " + resource.usage);
System.out.println("Limit: " + resource.limit);
if (resource.limit > 0) {
double percentage = (double) resource.usage / resource.limit * 100;
System.out.println("Usage: " + String.format("%.1f%%", percentage));
}
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-jakarta-mail--jakarta-mail-api