CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-jakarta-mail--jakarta-mail-api

Jakarta Mail defines a platform-independent and protocol-independent framework to build mail and messaging applications.

Pending
Overview
Eval results
Files

event-handling-system.mddocs/

Event Handling System

Jakarta Mail's event handling system provides comprehensive asynchronous notifications for mail operations including connection changes, folder operations, message modifications, and transport events.

Event Foundation

All mail events extend the base MailEvent class which provides common event functionality.

public abstract class MailEvent extends EventObject {
    // Constructor
    protected MailEvent(Object source);
    
    // Source access (inherited from EventObject)
    public Object getSource();
    
    // String representation
    public String toString();
}

Connection Events

Connection events track the state of connections to mail stores and transports.

public final class ConnectionEvent extends MailEvent {
    // Event type constants
    public static final int OPENED = 1;
    public static final int DISCONNECTED = 2;
    public static final int CLOSED = 3;
    
    // Constructors
    public ConnectionEvent(Object source, int type);
    
    // Event type access
    public int getType();
    
    // Utility methods
    public void dispatch(Object listener);
}

Connection Listener Interface

public interface ConnectionListener extends EventListener {
    public void opened(ConnectionEvent e);
    public void disconnected(ConnectionEvent e);
    public void closed(ConnectionEvent e);
}

Connection Adapter

public abstract class ConnectionAdapter implements ConnectionListener {
    public void opened(ConnectionEvent e) {}
    public void disconnected(ConnectionEvent e) {}
    public void closed(ConnectionEvent e) {}
}

Connection Event Usage Example

import jakarta.mail.event.*;
import jakarta.mail.*;

// Create connection listener
ConnectionListener connectionListener = new ConnectionListener() {
    @Override
    public void opened(ConnectionEvent e) {
        System.out.println("Connection opened to: " + e.getSource());
    }
    
    @Override
    public void disconnected(ConnectionEvent e) {
        System.out.println("Connection lost to: " + e.getSource());
        // Could implement reconnection logic here
    }
    
    @Override
    public void closed(ConnectionEvent e) {
        System.out.println("Connection closed to: " + e.getSource());
    }
};

// Add listener to store
Store store = session.getStore("imaps");
store.addConnectionListener(connectionListener);

// Add listener to transport
Transport transport = session.getTransport("smtp");
transport.addConnectionListener(connectionListener);

// Connections now generate events
store.connect("imap.example.com", "user", "password"); // Fires opened event
transport.connect(); // Fires opened event
store.close(); // Fires closed event

// Using adapter for selective handling
store.addConnectionListener(new ConnectionAdapter() {
    @Override
    public void disconnected(ConnectionEvent e) {
        // Only handle disconnection events
        System.err.println("Store connection lost - attempting reconnect");
        reconnectStore((Store) e.getSource());
    }
});

Folder Events

Folder events track structural changes to folders including creation, deletion, and renaming.

public final class FolderEvent extends MailEvent {
    // Event type constants
    public static final int CREATED = 1;
    public static final int DELETED = 2;
    public static final int RENAMED = 3;
    
    // Constructors
    public FolderEvent(Object source, Folder folder, int type);
    public FolderEvent(Object source, Folder folder, Folder newFolder, int type);
    
    // Event access
    public int getType();
    public Folder getFolder();
    public Folder getNewFolder(); // For rename events
    
    // Utility methods
    public void dispatch(Object listener);
}

Folder Listener Interface

public interface FolderListener extends EventListener {
    public void folderCreated(FolderEvent e);
    public void folderDeleted(FolderEvent e);
    public void folderRenamed(FolderEvent e);
}

Folder Adapter

public abstract class FolderAdapter implements FolderListener {
    public void folderCreated(FolderEvent e) {}
    public void folderDeleted(FolderEvent e) {}
    public void folderRenamed(FolderEvent e) {}
}

Folder Event Usage Example

import jakarta.mail.event.*;
import jakarta.mail.*;

// Create folder listener
FolderListener folderListener = new FolderListener() {
    @Override
    public void folderCreated(FolderEvent e) {
        System.out.println("Folder created: " + e.getFolder().getFullName());
    }
    
    @Override
    public void folderDeleted(FolderEvent e) {
        System.out.println("Folder deleted: " + e.getFolder().getFullName());
    }
    
    @Override
    public void folderRenamed(FolderEvent e) {
        System.out.println("Folder renamed from " + e.getFolder().getFullName() + 
                          " to " + e.getNewFolder().getFullName());
    }
};

// Add listener to store
Store store = session.getStore("imaps");
store.addFolderListener(folderListener);

// Folder operations now generate events
Folder newFolder = store.getFolder("NewFolder");
newFolder.create(Folder.HOLDS_MESSAGES); // Fires created event

Folder renamedFolder = store.getFolder("RenamedFolder");
newFolder.renameTo(renamedFolder); // Fires renamed event

// Using adapter for specific events only
store.addFolderListener(new FolderAdapter() {
    @Override
    public void folderDeleted(FolderEvent e) {
        // Log folder deletions for audit purposes
        auditLog("Folder deleted: " + e.getFolder().getFullName());
    }
});

Message Count Events

Message count events track additions and removals of messages from folders.

public final class MessageCountEvent extends MailEvent {
    // Event type constants
    public static final int ADDED = 1;
    public static final int REMOVED = 2;
    
    // Constructors
    public MessageCountEvent(Folder source, int type, boolean removed, Message[] msgs);
    
    // Event access
    public int getType();
    public boolean isRemoved();
    public Message[] getMessages();
    
    // Utility methods
    public void dispatch(Object listener);
}

Message Count Listener Interface

public interface MessageCountListener extends EventListener {
    public void messagesAdded(MessageCountEvent e);
    public void messagesRemoved(MessageCountEvent e);
}

Message Count Adapter

public abstract class MessageCountAdapter implements MessageCountListener {
    public void messagesAdded(MessageCountEvent e) {}
    public void messagesRemoved(MessageCountEvent e) {}
}

Message Count Event Usage Example

import jakarta.mail.event.*;
import jakarta.mail.*;

// Create message count listener
MessageCountListener messageCountListener = new MessageCountListener() {
    @Override
    public void messagesAdded(MessageCountEvent e) {
        Message[] newMessages = e.getMessages();
        System.out.println(newMessages.length + " new messages arrived");
        
        // Process new messages
        for (Message message : newMessages) {
            try {
                System.out.println("New message: " + message.getSubject());
                // Could trigger notifications, auto-processing, etc.
            } catch (MessagingException ex) {
                System.err.println("Error processing new message: " + ex.getMessage());
            }
        }
    }
    
    @Override
    public void messagesRemoved(MessageCountEvent e) {
        Message[] removedMessages = e.getMessages();
        System.out.println(removedMessages.length + " messages removed");
    }
};

// Add listener to folder
Folder inbox = store.getFolder("INBOX");
inbox.addMessageCountListener(messageCountListener);

// Open folder to enable event generation
inbox.open(Folder.READ_WRITE);

// Message operations now generate events
// (New messages arriving via IMAP IDLE or manual refresh will fire events)

// Using adapter for new message notifications only
inbox.addMessageCountListener(new MessageCountAdapter() {
    @Override
    public void messagesAdded(MessageCountEvent e) {
        // Send desktop notification for new messages
        showNotification(e.getMessages().length + " new messages");
        
        // Update UI badge count
        updateUnreadCount();
    }
});

Message Changed Events

Message changed events track modifications to message flags and headers.

public final class MessageChangedEvent extends MailEvent {
    // Event type constants
    public static final int FLAGS_CHANGED = 1;
    public static final int ENVELOPE_CHANGED = 2;
    
    // Constructors
    public MessageChangedEvent(Object source, int type, Message msg);
    
    // Event access
    public int getType();
    public Message getMessage();
    
    // Utility methods
    public void dispatch(Object listener);
}

Message Changed Listener Interface

public interface MessageChangedListener extends EventListener {
    public void messageChanged(MessageChangedEvent e);
}

Message Changed Event Usage Example

import jakarta.mail.event.*;
import jakarta.mail.*;

// Create message changed listener
MessageChangedListener messageChangedListener = new MessageChangedListener() {
    @Override
    public void messageChanged(MessageChangedEvent e) {
        Message changedMessage = e.getMessage();
        
        switch (e.getType()) {
            case MessageChangedEvent.FLAGS_CHANGED:
                try {
                    System.out.println("Flags changed for message: " + changedMessage.getSubject());
                    Flags flags = changedMessage.getFlags();
                    
                    if (flags.contains(Flags.Flag.SEEN)) {
                        System.out.println("Message marked as read");
                        updateReadCount();
                    }
                    
                    if (flags.contains(Flags.Flag.FLAGGED)) {
                        System.out.println("Message flagged");
                        addToImportantList(changedMessage);
                    }
                    
                    if (flags.contains(Flags.Flag.DELETED)) {
                        System.out.println("Message marked for deletion");
                    }
                    
                } catch (MessagingException ex) {
                    System.err.println("Error processing flag change: " + ex.getMessage());
                }
                break;
                
            case MessageChangedEvent.ENVELOPE_CHANGED:
                System.out.println("Envelope changed for message");
                // Headers like subject, from, to have changed
                refreshMessageDisplay(changedMessage);
                break;
        }
    }
};

// Add listener to folder
Folder folder = store.getFolder("INBOX");
folder.addMessageChangedListener(messageChangedListener);

// Message flag changes now generate events
Message message = folder.getMessage(1);
message.setFlag(Flags.Flag.SEEN, true); // Fires FLAGS_CHANGED event
message.setFlag(Flags.Flag.FLAGGED, true); // Fires FLAGS_CHANGED event

Store Events

Store events provide general notifications and alerts from mail stores.

public final class StoreEvent extends MailEvent {
    // Event type constants
    public static final int ALERT = 1;
    public static final int NOTICE = 2;
    
    // Constructors
    public StoreEvent(Store source, int type, String message);
    
    // Event access
    public int getType();
    public String getMessage();
    
    // Utility methods
    public void dispatch(Object listener);
}

Store Listener Interface

public interface StoreListener extends EventListener {
    public void notification(StoreEvent e);
}

Store Event Usage Example

import jakarta.mail.event.*;
import jakarta.mail.*;

// Create store listener
StoreListener storeListener = new StoreListener() {
    @Override
    public void notification(StoreEvent e) {
        switch (e.getType()) {
            case StoreEvent.ALERT:
                System.err.println("Store Alert: " + e.getMessage());
                // Handle critical store alerts
                handleStoreAlert(e.getMessage());
                break;
                
            case StoreEvent.NOTICE:
                System.out.println("Store Notice: " + e.getMessage());
                // Handle informational notices
                logStoreNotice(e.getMessage());
                break;
        }
    }
};

// Add listener to store
Store store = session.getStore("imaps");
store.addStoreListener(storeListener);

// Store operations may generate events
// Examples: quota warnings, maintenance notices, server messages

Transport Events

Transport events track message delivery status and transmission results.

public final class TransportEvent extends MailEvent {
    // Event type constants
    public static final int MESSAGE_DELIVERED = 1;
    public static final int MESSAGE_NOT_DELIVERED = 2;
    public static final int MESSAGE_PARTIALLY_DELIVERED = 3;
    
    // Constructors
    public TransportEvent(Transport source, int type, Address[] validSent, 
                         Address[] validUnsent, Address[] invalid, Message msg);
    
    // Event access
    public int getType();
    public Address[] getValidSentAddresses();
    public Address[] getValidUnsentAddresses();
    public Address[] getInvalidAddresses();
    public Message getMessage();
    
    // Utility methods
    public void dispatch(Object listener);
}

Transport Listener Interface

public interface TransportListener extends EventListener {
    public void messageDelivered(TransportEvent e);
    public void messageNotDelivered(TransportEvent e);
    public void messagePartiallyDelivered(TransportEvent e);
}

Transport Adapter

public abstract class TransportAdapter implements TransportListener {
    public void messageDelivered(TransportEvent e) {}
    public void messageNotDelivered(TransportEvent e) {}
    public void messagePartiallyDelivered(TransportEvent e) {}
}

Transport Event Usage Example

import jakarta.mail.event.*;
import jakarta.mail.*;
import jakarta.mail.internet.*;

// Create transport listener
TransportListener transportListener = new TransportListener() {
    @Override
    public void messageDelivered(TransportEvent e) {
        System.out.println("Message delivered successfully");
        Address[] delivered = e.getValidSentAddresses();
        for (Address addr : delivered) {
            System.out.println("Delivered to: " + addr.toString());
        }
        
        // Update message status, log delivery, etc.
        logDelivery(e.getMessage(), delivered);
    }
    
    @Override
    public void messageNotDelivered(TransportEvent e) {
        System.err.println("Message delivery failed");
        Address[] failed = e.getValidUnsentAddresses();
        Address[] invalid = e.getInvalidAddresses();
        
        for (Address addr : failed) {
            System.err.println("Failed to deliver to: " + addr.toString());
        }
        
        for (Address addr : invalid) {
            System.err.println("Invalid address: " + addr.toString());
        }
        
        // Handle delivery failure
        handleDeliveryFailure(e.getMessage(), failed, invalid);
    }
    
    @Override
    public void messagePartiallyDelivered(TransportEvent e) {
        System.out.println("Message partially delivered");
        
        Address[] delivered = e.getValidSentAddresses();
        Address[] failed = e.getValidUnsentAddresses();
        Address[] invalid = e.getInvalidAddresses();
        
        System.out.println("Delivered to " + delivered.length + " recipients");
        System.out.println("Failed to deliver to " + failed.length + " recipients");
        System.out.println("Invalid addresses: " + invalid.length);
        
        // Handle partial delivery
        handlePartialDelivery(e.getMessage(), delivered, failed, invalid);
    }
};

// Add listener to transport
Transport transport = session.getTransport("smtp");
transport.addTransportListener(transportListener);

// Send message - events will be generated
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("sender@example.com"));
message.setRecipients(Message.RecipientType.TO, 
    InternetAddress.parse("valid@example.com,invalid-address,another@example.com"));
message.setSubject("Test Message");
message.setText("Test content");

transport.connect();
transport.sendMessage(message, message.getAllRecipients());
transport.close();

// Using adapter for delivery confirmation only
transport.addTransportListener(new TransportAdapter() {
    @Override
    public void messageDelivered(TransportEvent e) {
        // Send confirmation to sender
        sendDeliveryConfirmation(e.getMessage());
    }
});

Event Management Patterns

Centralized Event Handler

public class MailEventManager implements ConnectionListener, FolderListener, 
                                        MessageCountListener, MessageChangedListener, 
                                        StoreListener, TransportListener {
    
    private final List<MailEventObserver> observers = new ArrayList<>();
    
    public void addObserver(MailEventObserver observer) {
        observers.add(observer);
    }
    
    // Connection events
    @Override
    public void opened(ConnectionEvent e) {
        notifyObservers("Connection opened: " + e.getSource());
    }
    
    @Override
    public void disconnected(ConnectionEvent e) {
        notifyObservers("Connection lost: " + e.getSource());
        // Attempt reconnection
        scheduleReconnection((Service) e.getSource());
    }
    
    @Override
    public void closed(ConnectionEvent e) {
        notifyObservers("Connection closed: " + e.getSource());
    }
    
    // Message count events
    @Override
    public void messagesAdded(MessageCountEvent e) {
        notifyObservers(e.getMessages().length + " new messages");
        updateNotificationBadge();
    }
    
    @Override
    public void messagesRemoved(MessageCountEvent e) {
        notifyObservers(e.getMessages().length + " messages removed");
        updateNotificationBadge();
    }
    
    // Message changed events
    @Override
    public void messageChanged(MessageChangedEvent e) {
        if (e.getType() == MessageChangedEvent.FLAGS_CHANGED) {
            updateMessageFlags(e.getMessage());
        }
    }
    
    // Transport events
    @Override
    public void messageDelivered(TransportEvent e) {
        notifyObservers("Message delivered successfully");
    }
    
    @Override
    public void messageNotDelivered(TransportEvent e) {
        notifyObservers("Message delivery failed");
        handleDeliveryFailure(e);
    }
    
    @Override
    public void messagePartiallyDelivered(TransportEvent e) {
        notifyObservers("Message partially delivered");
        handlePartialDelivery(e);
    }
    
    // Other event implementations...
    
    private void notifyObservers(String message) {
        for (MailEventObserver observer : observers) {
            observer.onEvent(message);
        }
    }
}

// Register the centralized handler
MailEventManager eventManager = new MailEventManager();
store.addConnectionListener(eventManager);
folder.addMessageCountListener(eventManager);
transport.addTransportListener(eventManager);

Asynchronous Event Processing

public class AsyncEventProcessor {
    private final ExecutorService eventExecutor = Executors.newCachedThreadPool();
    
    public void processMessageCountEvent(MessageCountEvent e) {
        eventExecutor.submit(() -> {
            try {
                // Process new messages asynchronously
                for (Message message : e.getMessages()) {
                    processNewMessage(message);
                }
            } catch (Exception ex) {
                System.err.println("Error processing messages: " + ex.getMessage());
            }
        });
    }
    
    public void processTransportEvent(TransportEvent e) {
        eventExecutor.submit(() -> {
            // Update delivery status in database
            updateDeliveryStatus(e);
            
            // Send notifications
            sendDeliveryNotifications(e);
        });
    }
    
    public void shutdown() {
        eventExecutor.shutdown();
    }
}

Event Filtering and Routing

public class EventRouter {
    private final Map<Class<?>, List<Object>> listenerMap = new HashMap<>();
    
    public void addListener(Class<?> eventType, Object listener) {
        listenerMap.computeIfAbsent(eventType, k -> new ArrayList<>()).add(listener);
    }
    
    public void routeEvent(Object event) {
        Class<?> eventType = event.getClass();
        List<Object> listeners = listenerMap.get(eventType);
        
        if (listeners != null) {
            for (Object listener : listeners) {
                try {
                    if (event instanceof MessageCountEvent && listener instanceof MessageCountListener) {
                        MessageCountEvent mce = (MessageCountEvent) event;
                        MessageCountListener mcl = (MessageCountListener) listener;
                        
                        if (mce.getType() == MessageCountEvent.ADDED) {
                            mcl.messagesAdded(mce);
                        } else {
                            mcl.messagesRemoved(mce);
                        }
                    }
                    // Handle other event types...
                } catch (Exception e) {
                    System.err.println("Error in event listener: " + e.getMessage());
                }
            }
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-jakarta-mail--jakarta-mail-api

docs

core-mail-operations.md

event-handling-system.md

index.md

internet-mail-mime.md

message-search-filtering.md

store-folder-management.md

utility-classes-streams.md

tile.json