Jakarta Mail defines a platform-independent and protocol-independent framework to build mail and messaging applications.
—
Message search and filtering provides comprehensive capabilities for querying messages based on various criteria including content, headers, dates, flags, and addresses with support for logical operations.
The SearchTerm abstract class forms the foundation of Jakarta Mail's search system.
public abstract class SearchTerm implements Serializable {
// Core search method - must be implemented by all search terms
public abstract boolean match(Message msg);
// Object operations
public boolean equals(Object obj);
public int hashCode();
}All search operations are performed by implementing the match() method that tests whether a message satisfies the search criteria.
public abstract class StringTerm extends SearchTerm {
protected String pattern;
protected boolean ignoreCase;
// Constructors
protected StringTerm(String pattern);
protected StringTerm(String pattern, boolean ignoreCase);
// Accessors
public String getPattern();
public boolean getIgnoreCase();
}// Search message body content
public final class BodyTerm extends StringTerm {
public BodyTerm(String pattern);
public BodyTerm(String pattern, boolean ignoreCase);
}
// Search subject line
public final class SubjectTerm extends StringTerm {
public SubjectTerm(String pattern);
public SubjectTerm(String pattern, boolean ignoreCase);
}
// Search specific header
public final class HeaderTerm extends StringTerm {
public HeaderTerm(String headerName, String pattern);
public HeaderTerm(String headerName, String pattern, boolean ignoreCase);
public String getHeaderName();
}
// Search Message-ID header
public final class MessageIDTerm extends StringTerm {
public MessageIDTerm(String msgid);
}import jakarta.mail.search.*;
import jakarta.mail.*;
// Search for messages containing "urgent" in body (case-insensitive)
SearchTerm bodySearch = new BodyTerm("urgent", true);
Message[] urgentMessages = folder.search(bodySearch);
// Search by subject
SearchTerm subjectSearch = new SubjectTerm("Meeting");
Message[] meetingMessages = folder.search(subjectSearch);
// Search specific header
SearchTerm headerSearch = new HeaderTerm("X-Priority", "1");
Message[] highPriorityMessages = folder.search(headerSearch);
// Search by Message-ID
SearchTerm messageIdSearch = new MessageIDTerm("<12345@example.com>");
Message[] specificMessage = folder.search(messageIdSearch);
// Case-sensitive body search
SearchTerm caseSensitiveSearch = new BodyTerm("CONFIDENTIAL", false);
Message[] confidentialMessages = folder.search(caseSensitiveSearch);public abstract class AddressTerm extends SearchTerm {
protected Address address;
// Constructor
protected AddressTerm(Address address);
// Accessor
public Address getAddress();
}
// Search sender addresses (Address objects)
public final class FromTerm extends AddressTerm {
public FromTerm(Address address);
}
// Search recipient addresses (Address objects)
public final class RecipientTerm extends AddressTerm {
public RecipientTerm(Message.RecipientType type, Address address);
public Message.RecipientType getRecipientType();
}public abstract class AddressStringTerm extends StringTerm {
// Constructor
protected AddressStringTerm(String pattern);
protected AddressStringTerm(String pattern, boolean ignoreCase);
}
// Search sender addresses (string pattern)
public final class FromStringTerm extends AddressStringTerm {
public FromStringTerm(String pattern);
public FromStringTerm(String pattern, boolean ignoreCase);
}
// Search recipient addresses (string pattern)
public final class RecipientStringTerm extends AddressStringTerm {
public RecipientStringTerm(Message.RecipientType type, String pattern);
public RecipientStringTerm(Message.RecipientType type, String pattern, boolean ignoreCase);
public Message.RecipientType getRecipientType();
}import jakarta.mail.search.*;
import jakarta.mail.internet.*;
// Search by exact sender address
InternetAddress senderAddr = new InternetAddress("boss@company.com");
SearchTerm fromSearch = new FromTerm(senderAddr);
Message[] fromBoss = folder.search(fromSearch);
// Search by sender address pattern (string)
SearchTerm fromStringSearch = new FromStringTerm("@company.com");
Message[] fromCompany = folder.search(fromStringSearch);
// Search by recipient (TO addresses)
SearchTerm toSearch = new RecipientStringTerm(Message.RecipientType.TO, "team@company.com");
Message[] toTeam = folder.search(toSearch);
// Search by CC recipients
SearchTerm ccSearch = new RecipientStringTerm(Message.RecipientType.CC, "@department.com");
Message[] ccDepartment = folder.search(ccSearch);
// Case-insensitive address search
SearchTerm caseInsensitiveSearch = new FromStringTerm("MANAGER", true);
Message[] fromManager = folder.search(caseInsensitiveSearch);public abstract class DateTerm extends ComparisonTerm {
protected Date date;
// Constructor
protected DateTerm(int comparison, Date date);
// Accessor
public Date getDate();
}// Search by received date
public final class ReceivedDateTerm extends DateTerm {
public ReceivedDateTerm(int comparison, Date date);
}
// Search by sent date
public final class SentDateTerm extends DateTerm {
public SentDateTerm(int comparison, Date date);
}public abstract class ComparisonTerm extends SearchTerm {
// Comparison constants
public static final int LE = 1; // Less than or equal
public static final int LT = 2; // Less than
public static final int EQ = 3; // Equal
public static final int NE = 4; // Not equal
public static final int GT = 5; // Greater than
public static final int GE = 6; // Greater than or equal
protected int comparison;
// Constructor
protected ComparisonTerm(int comparison);
// Accessor
public int getComparison();
}import jakarta.mail.search.*;
import java.util.Date;
import java.util.Calendar;
// Search for messages received today
Calendar today = Calendar.getInstance();
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
today.set(Calendar.MILLISECOND, 0);
SearchTerm todaySearch = new ReceivedDateTerm(ComparisonTerm.GE, today.getTime());
Message[] todayMessages = folder.search(todaySearch);
// Search for messages sent last week
Calendar weekAgo = Calendar.getInstance();
weekAgo.add(Calendar.DAY_OF_YEAR, -7);
SearchTerm lastWeekSearch = new SentDateTerm(ComparisonTerm.GE, weekAgo.getTime());
Message[] lastWeekMessages = folder.search(lastWeekSearch);
// Search for messages received between two dates
Calendar startDate = Calendar.getInstance();
startDate.add(Calendar.DAY_OF_YEAR, -30);
Calendar endDate = Calendar.getInstance();
endDate.add(Calendar.DAY_OF_YEAR, -1);
SearchTerm dateRange = new AndTerm(
new ReceivedDateTerm(ComparisonTerm.GE, startDate.getTime()),
new ReceivedDateTerm(ComparisonTerm.LE, endDate.getTime())
);
Message[] rangeMessages = folder.search(dateRange);
// Search for messages sent exactly on a specific date
Calendar specificDate = Calendar.getInstance();
specificDate.set(2024, Calendar.JANUARY, 1);
SearchTerm exactDateSearch = new SentDateTerm(ComparisonTerm.EQ, specificDate.getTime());
Message[] exactDateMessages = folder.search(exactDateSearch);public abstract class IntegerComparisonTerm extends ComparisonTerm {
protected int number;
// Constructor
protected IntegerComparisonTerm(int comparison, int number);
// Accessor
public int getNumber();
}// Search by message number
public final class MessageNumberTerm extends IntegerComparisonTerm {
public MessageNumberTerm(int number);
public MessageNumberTerm(int comparison, int number);
}
// Search by message size
public final class SizeTerm extends IntegerComparisonTerm {
public SizeTerm(int comparison, int size);
}import jakarta.mail.search.*;
// Search for large messages (over 1MB)
SearchTerm largeMessages = new SizeTerm(ComparisonTerm.GT, 1024 * 1024);
Message[] bigMessages = folder.search(largeMessages);
// Search for small messages (under 10KB)
SearchTerm smallMessages = new SizeTerm(ComparisonTerm.LT, 10 * 1024);
Message[] tinyMessages = folder.search(smallMessages);
// Search for specific message number
SearchTerm messageNumber = new MessageNumberTerm(100);
Message[] specificNumber = folder.search(messageNumber);
// Search for messages in a range of numbers
SearchTerm numberRange = new AndTerm(
new MessageNumberTerm(ComparisonTerm.GE, 50),
new MessageNumberTerm(ComparisonTerm.LE, 100)
);
Message[] numberRangeMessages = folder.search(numberRange);public final class FlagTerm extends SearchTerm {
// Constructors
public FlagTerm(Flags flags, boolean set);
// Accessors
public Flags getFlags();
public boolean getTestSet();
}import jakarta.mail.search.*;
import jakarta.mail.*;
// Search for unread messages
Flags unreadFlags = new Flags(Flags.Flag.SEEN);
SearchTerm unreadSearch = new FlagTerm(unreadFlags, false);
Message[] unreadMessages = folder.search(unreadSearch);
// Search for flagged messages
Flags flaggedFlags = new Flags(Flags.Flag.FLAGGED);
SearchTerm flaggedSearch = new FlagTerm(flaggedFlags, true);
Message[] flaggedMessages = folder.search(flaggedSearch);
// Search for deleted messages
Flags deletedFlags = new Flags(Flags.Flag.DELETED);
SearchTerm deletedSearch = new FlagTerm(deletedFlags, true);
Message[] deletedMessages = folder.search(deletedSearch);
// Search for draft messages
Flags draftFlags = new Flags(Flags.Flag.DRAFT);
SearchTerm draftSearch = new FlagTerm(draftFlags, true);
Message[] draftMessages = folder.search(draftSearch);
// Search for answered messages
Flags answeredFlags = new Flags(Flags.Flag.ANSWERED);
SearchTerm answeredSearch = new FlagTerm(answeredFlags, true);
Message[] answeredMessages = folder.search(answeredSearch);
// Search for recent messages
Flags recentFlags = new Flags(Flags.Flag.RECENT);
SearchTerm recentSearch = new FlagTerm(recentFlags, true);
Message[] recentMessages = folder.search(recentSearch);
// Search for custom user flags
Flags customFlags = new Flags();
customFlags.add("Important");
SearchTerm customSearch = new FlagTerm(customFlags, true);
Message[] importantMessages = folder.search(customSearch);public final class AndTerm extends SearchTerm {
// Constructors
public AndTerm(SearchTerm t1, SearchTerm t2);
public AndTerm(SearchTerm[] terms);
// Accessor
public SearchTerm[] getTerms();
}public final class OrTerm extends SearchTerm {
// Constructors
public OrTerm(SearchTerm t1, SearchTerm t2);
public OrTerm(SearchTerm[] terms);
// Accessor
public SearchTerm[] getTerms();
}public final class NotTerm extends SearchTerm {
// Constructor
public NotTerm(SearchTerm t);
// Accessor
public SearchTerm getTerm();
}import jakarta.mail.search.*;
// Complex search: Unread messages from specific sender
SearchTerm complexSearch = new AndTerm(
new FlagTerm(new Flags(Flags.Flag.SEEN), false),
new FromStringTerm("important@company.com")
);
Message[] unreadFromImportant = folder.search(complexSearch);
// Multiple AND conditions
SearchTerm multipleAnd = new AndTerm(new SearchTerm[] {
new SubjectTerm("Meeting"),
new FromStringTerm("@company.com"),
new ReceivedDateTerm(ComparisonTerm.GE, weekAgo.getTime())
});
Message[] complexResults = folder.search(multipleAnd);
// OR search: Messages from multiple senders
SearchTerm multiSenderSearch = new OrTerm(
new FromStringTerm("boss@company.com"),
new FromStringTerm("manager@company.com")
);
Message[] fromBossOrManager = folder.search(multiSenderSearch);
// NOT search: Messages not from specific domain
SearchTerm notFromSpam = new NotTerm(
new FromStringTerm("@spam.com")
);
Message[] nonSpamMessages = folder.search(notFromSpam);
// Complex nested logic: (urgent OR important) AND not read AND from company
SearchTerm urgentOrImportant = new OrTerm(
new SubjectTerm("urgent"),
new SubjectTerm("important")
);
SearchTerm unreadAndFromCompany = new AndTerm(
new FlagTerm(new Flags(Flags.Flag.SEEN), false),
new FromStringTerm("@company.com")
);
SearchTerm finalSearch = new AndTerm(urgentOrImportant, unreadAndFromCompany);
Message[] complexNestedResults = folder.search(finalSearch);// Messages from last 24 hours
Calendar yesterday = Calendar.getInstance();
yesterday.add(Calendar.DAY_OF_YEAR, -1);
SearchTerm last24Hours = new ReceivedDateTerm(ComparisonTerm.GE, yesterday.getTime());
// Messages from this month
Calendar startOfMonth = Calendar.getInstance();
startOfMonth.set(Calendar.DAY_OF_MONTH, 1);
startOfMonth.set(Calendar.HOUR_OF_DAY, 0);
startOfMonth.set(Calendar.MINUTE, 0);
startOfMonth.set(Calendar.SECOND, 0);
SearchTerm thisMonth = new ReceivedDateTerm(ComparisonTerm.GE, startOfMonth.getTime());
// Messages older than 30 days
Calendar thirtyDaysAgo = Calendar.getInstance();
thirtyDaysAgo.add(Calendar.DAY_OF_YEAR, -30);
SearchTerm oldMessages = new ReceivedDateTerm(ComparisonTerm.LT, thirtyDaysAgo.getTime());// High priority messages
SearchTerm highPriority = new OrTerm(new SearchTerm[] {
new HeaderTerm("X-Priority", "1"),
new HeaderTerm("Priority", "urgent"),
new SubjectTerm("URGENT"),
new SubjectTerm("IMPORTANT")
});
// Attachment searches (size-based heuristic)
SearchTerm hasAttachments = new SizeTerm(ComparisonTerm.GT, 50 * 1024); // > 50KB
// Meeting-related messages
SearchTerm meetingMessages = new OrTerm(new SearchTerm[] {
new SubjectTerm("meeting"),
new SubjectTerm("conference"),
new SubjectTerm("call"),
new BodyTerm("calendar")
});// Messages to clean up (old, read, not flagged)
SearchTerm cleanupCandidates = new AndTerm(new SearchTerm[] {
new ReceivedDateTerm(ComparisonTerm.LT, thirtyDaysAgo.getTime()),
new FlagTerm(new Flags(Flags.Flag.SEEN), true),
new FlagTerm(new Flags(Flags.Flag.FLAGGED), false)
});
// Important messages to preserve
SearchTerm importantToKeep = new OrTerm(new SearchTerm[] {
new FlagTerm(new Flags(Flags.Flag.FLAGGED), true),
new SubjectTerm("contract"),
new SubjectTerm("invoice"),
new FromStringTerm("legal@")
});
// Spam-like messages
SearchTerm potentialSpam = new OrTerm(new SearchTerm[] {
new SubjectTerm("free"),
new SubjectTerm("urgent action required"),
new BodyTerm("click here"),
new FromStringTerm("noreply@")
});// Optimize search performance with fetch profiles
FetchProfile fetchProfile = new FetchProfile();
fetchProfile.add(FetchProfile.Item.ENVELOPE);
fetchProfile.add(FetchProfile.Item.FLAGS);
// Perform search
Message[] results = folder.search(searchTerm);
// Fetch needed data in bulk
folder.fetch(results, fetchProfile);
// Now access is optimized
for (Message message : results) {
// These accesses are now efficient
System.out.println("From: " + Arrays.toString(message.getFrom()));
System.out.println("Subject: " + message.getSubject());
System.out.println("Flags: " + message.getFlags());
}// Search within a specific range first
Message[] recentMessages = folder.getMessages(folder.getMessageCount() - 100, folder.getMessageCount());
// Then search within that subset
Message[] searchResults = folder.search(searchTerm, recentMessages);public class SearchException extends MessagingException {
public SearchException();
public SearchException(String message);
public SearchException(String message, Exception e);
}try {
Message[] results = folder.search(complexSearchTerm);
System.out.println("Found " + results.length + " messages");
} catch (SearchException e) {
System.err.println("Search failed: " + e.getMessage());
// Fallback to simpler search or manual filtering
} catch (MessagingException e) {
System.err.println("Messaging error during search: " + e.getMessage());
}Install with Tessl CLI
npx tessl i tessl/maven-jakarta-mail--jakarta-mail-api