CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-sun-mail--jakarta-mail-api

Jakarta Mail API provides a platform-independent and protocol-independent framework to build mail and messaging applications

Pending
Overview
Eval results
Files

event-handling.mddocs/

Event Handling

This document covers the comprehensive event-driven programming capabilities of the Jakarta Mail API, including connection events, message changes, folder operations, and transport notifications.

Event System Overview

The Jakarta Mail API provides an event-driven architecture for monitoring mail operations. Events are fired for connections, folder changes, message modifications, and transport operations.

Base Event Class

abstract class MailEvent extends EventObject {
    public MailEvent(Object source);
    
    public abstract void dispatch(Object listener);
}

All mail events extend this base class and implement the dispatch method to deliver themselves to appropriate listener methods.

Connection Events

Connection events are fired when mail service connections are opened, closed, or disconnected unexpectedly.

ConnectionEvent Class

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

ConnectionListener Interface

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

ConnectionAdapter Class

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

Connection Event Usage

public class ConnectionMonitor extends ConnectionAdapter {
    
    @Override
    public void opened(ConnectionEvent e) {
        System.out.println("Connection opened: " + e.getSource());
    }
    
    @Override
    public void disconnected(ConnectionEvent e) {
        System.err.println("Connection lost: " + e.getSource());
        handleDisconnection();
    }
    
    @Override
    public void closed(ConnectionEvent e) {
        System.out.println("Connection closed: " + e.getSource());
    }
    
    private void handleDisconnection() {
        // Implement reconnection logic
        System.out.println("Attempting to reconnect...");
    }
}

// Register connection listener
Store store = session.getStore("imap");
store.addConnectionListener(new ConnectionMonitor());
store.connect();

Folder Events

Folder events are fired when folders are created, deleted, or renamed in the message store.

FolderEvent Class

class FolderEvent extends MailEvent {
    public static final int CREATED = 1;
    public static final int DELETED = 2;
    public static final int RENAMED = 3;
    
    protected Folder folder;
    protected Folder newFolder;
    
    public FolderEvent(Object source, Folder folder, int type);
    public FolderEvent(Object source, Folder oldFolder, Folder newFolder, int type);
    
    public int getType();
    public Folder getFolder();
    public Folder getNewFolder();
    public void dispatch(Object listener);
}

FolderListener Interface

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

FolderAdapter Class

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

Folder Event Usage

public class FolderMonitor extends FolderAdapter {
    
    @Override
    public void folderCreated(FolderEvent e) {
        Folder newFolder = e.getFolder();
        System.out.println("New folder created: " + newFolder.getFullName());
        
        // Update folder cache or UI
        updateFolderList();
    }
    
    @Override
    public void folderDeleted(FolderEvent e) {
        Folder deletedFolder = e.getFolder();
        System.out.println("Folder deleted: " + deletedFolder.getFullName());
        
        // Clean up references
        removeFolderFromCache(deletedFolder);
    }
    
    @Override
    public void folderRenamed(FolderEvent e) {
        Folder oldFolder = e.getFolder();
        Folder newFolder = e.getNewFolder();
        System.out.println("Folder renamed: " + oldFolder.getFullName() + 
                          " -> " + newFolder.getFullName());
        
        // Update references
        updateFolderReferences(oldFolder, newFolder);
    }
    
    private void updateFolderList() { /* Implementation */ }
    private void removeFolderFromCache(Folder folder) { /* Implementation */ }
    private void updateFolderReferences(Folder old, Folder newFolder) { /* Implementation */ }
}

// Register folder listener
Store store = session.getStore("imap");
store.addFolderListener(new FolderMonitor());

Message Count Events

Message count events are fired when messages are added to or removed from folders.

MessageCountEvent Class

class MessageCountEvent extends MailEvent {
    public static final int ADDED = 1;
    public static final int REMOVED = 2;
    
    protected int type;
    protected boolean removed;
    protected Message[] msgs;
    
    public MessageCountEvent(Folder folder, int type, boolean removed, Message[] msgs);
    
    public int getType();
    public boolean isRemoved();
    public Message[] getMessages();
    public void dispatch(Object listener);
}

MessageCountListener Interface

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

MessageCountAdapter Class

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

Message Count Event Usage

public class MessageMonitor extends MessageCountAdapter {
    private int totalMessages = 0;
    
    @Override
    public void messagesAdded(MessageCountEvent e) {
        Message[] newMessages = e.getMessages();
        totalMessages += newMessages.length;
        
        System.out.println(newMessages.length + " new messages arrived");
        System.out.println("Total messages: " + totalMessages);
        
        // Process new messages
        for (Message msg : newMessages) {
            try {
                processNewMessage(msg);
            } catch (MessagingException ex) {
                System.err.println("Error processing message: " + ex.getMessage());
            }
        }
    }
    
    @Override
    public void messagesRemoved(MessageCountEvent e) {
        Message[] removedMessages = e.getMessages();
        totalMessages -= removedMessages.length;
        
        System.out.println(removedMessages.length + " messages removed");
        System.out.println("Total messages: " + totalMessages);
        
        // Clean up references
        for (Message msg : removedMessages) {
            cleanupMessage(msg);
        }
    }
    
    private void processNewMessage(Message msg) throws MessagingException {
        // Handle new message arrival
        System.out.println("New message: " + msg.getSubject());
        
        // Check for important messages
        if (isImportant(msg)) {
            notifyUser(msg);
        }
    }
    
    private boolean isImportant(Message msg) throws MessagingException {
        return msg.isSet(Flags.Flag.FLAGGED) || 
               (msg.getSubject() != null && msg.getSubject().contains("URGENT"));
    }
    
    private void notifyUser(Message msg) {
        // Implementation for user notification
        System.out.println("IMPORTANT MESSAGE RECEIVED!");
    }
    
    private void cleanupMessage(Message msg) {
        // Clean up message references
        System.out.println("Cleaning up message references");
    }
}

// Register message count listener
Folder folder = store.getFolder("INBOX");
folder.addMessageCountListener(new MessageMonitor());
folder.open(Folder.READ_WRITE);

Message Changed Events

Message changed events are fired when message properties (flags, headers, content) are modified.

MessageChangedEvent Class

class MessageChangedEvent extends MailEvent {
    protected int type;
    protected Message msg;
    
    public MessageChangedEvent(Object source, int type, Message message);
    
    public int getType();
    public Message getMessage();
    public void dispatch(Object listener);
}

MessageChangedListener Interface

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

Message Changed Event Usage

public class MessageChangeMonitor implements MessageChangedListener {
    
    @Override
    public void messageChanged(MessageChangedEvent e) {
        Message msg = e.getMessage();
        
        try {
            System.out.println("Message changed: " + msg.getSubject());
            
            // Check what changed
            if (msg.isSet(Flags.Flag.SEEN)) {
                System.out.println("Message marked as read");
            }
            
            if (msg.isSet(Flags.Flag.FLAGGED)) {
                System.out.println("Message flagged as important");
            }
            
            if (msg.isSet(Flags.Flag.DELETED)) {
                System.out.println("Message marked for deletion");
            }
            
            // Update UI or cache
            updateMessageDisplay(msg);
            
        } catch (MessagingException ex) {
            System.err.println("Error handling message change: " + ex.getMessage());
        }
    }
    
    private void updateMessageDisplay(Message msg) {
        // Update UI representation of the message
        System.out.println("Updating message display");
    }
}

// Register message changed listener
folder.addMessageChangedListener(new MessageChangeMonitor());

Store Events

Store events provide general notifications about store-level operations.

StoreEvent Class

class StoreEvent extends MailEvent {
    protected int type;
    protected String message;
    
    public StoreEvent(Store store, int type, String message);
    
    public int getType();
    public String getMessage();
    public void dispatch(Object listener);
}

StoreListener Interface

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

Store Event Usage

public class StoreMonitor implements StoreListener {
    
    @Override
    public void notification(StoreEvent e) {
        System.out.println("Store notification: " + e.getMessage());
        
        // Handle different types of store events
        int eventType = e.getType();
        switch (eventType) {
            case StoreEvent.ALERT:
                handleAlert(e.getMessage());
                break;
            case StoreEvent.NOTICE:
                handleNotice(e.getMessage());
                break;
            default:
                handleGenericEvent(e.getMessage());
                break;
        }
    }
    
    private void handleAlert(String message) {
        System.err.println("STORE ALERT: " + message);
        // Handle critical store alerts
    }
    
    private void handleNotice(String message) {
        System.out.println("Store notice: " + message);
        // Handle informational notices
    }
    
    private void handleGenericEvent(String message) {
        System.out.println("Store event: " + message);
    }
}

// Register store listener
store.addStoreListener(new StoreMonitor());

Transport Events

Transport events are fired during message sending operations to report delivery status.

TransportEvent Class

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

TransportListener Interface

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

TransportAdapter Class

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

Transport Event Usage

public class DeliveryMonitor extends TransportAdapter {
    
    @Override
    public void messageDelivered(TransportEvent e) {
        Message msg = e.getMessage();
        Address[] delivered = e.getValidSentAddresses();
        
        try {
            System.out.println("Message delivered successfully: " + msg.getSubject());
            System.out.println("Delivered to " + delivered.length + " recipients:");
            
            for (Address addr : delivered) {
                System.out.println("  - " + addr.toString());
            }
            
            // Log successful delivery
            logDelivery(msg, delivered, true);
            
        } catch (MessagingException ex) {
            System.err.println("Error processing delivery confirmation: " + ex.getMessage());
        }
    }
    
    @Override
    public void messageNotDelivered(TransportEvent e) {
        Message msg = e.getMessage();
        Address[] failed = e.getValidUnsentAddresses();
        Address[] invalid = e.getInvalidAddresses();
        
        try {
            System.err.println("Message delivery failed: " + msg.getSubject());
            
            if (failed != null && failed.length > 0) {
                System.err.println("Failed addresses:");
                for (Address addr : failed) {
                    System.err.println("  - " + addr.toString());
                }
            }
            
            if (invalid != null && invalid.length > 0) {
                System.err.println("Invalid addresses:");
                for (Address addr : invalid) {
                    System.err.println("  - " + addr.toString());
                }
            }
            
            // Log failed delivery and schedule retry
            logDelivery(msg, failed, false);
            scheduleRetry(msg, failed);
            
        } catch (MessagingException ex) {
            System.err.println("Error processing delivery failure: " + ex.getMessage());
        }
    }
    
    @Override
    public void messagePartiallyDelivered(TransportEvent e) {
        Message msg = e.getMessage();
        Address[] delivered = e.getValidSentAddresses();
        Address[] failed = e.getValidUnsentAddresses();
        
        try {
            System.out.println("Message partially delivered: " + msg.getSubject());
            
            if (delivered != null && delivered.length > 0) {
                System.out.println("Successfully delivered to:");
                for (Address addr : delivered) {
                    System.out.println("  - " + addr.toString());
                }
            }
            
            if (failed != null && failed.length > 0) {
                System.err.println("Failed to deliver to:");
                for (Address addr : failed) {
                    System.err.println("  - " + addr.toString());
                }
                // Retry failed addresses
                scheduleRetry(msg, failed);
            }
            
        } catch (MessagingException ex) {
            System.err.println("Error processing partial delivery: " + ex.getMessage());
        }
    }
    
    private void logDelivery(Message msg, Address[] addresses, boolean success) {
        // Log delivery status to database or file
        String status = success ? "DELIVERED" : "FAILED";
        System.out.println("Logging delivery: " + status);
    }
    
    private void scheduleRetry(Message msg, Address[] failedAddresses) {
        // Schedule retry for failed addresses
        System.out.println("Scheduling retry for " + failedAddresses.length + " addresses");
    }
}

// Register transport listener
Transport transport = session.getTransport("smtp");
transport.addTransportListener(new DeliveryMonitor());

Complete Event Monitoring Example

public class ComprehensiveMailMonitor {
    
    public void setupEventMonitoring(Session session) throws MessagingException {
        // Setup store monitoring
        Store store = session.getStore("imap");
        
        // Add connection monitoring
        store.addConnectionListener(new ConnectionMonitor());
        
        // Add folder monitoring
        store.addFolderListener(new FolderMonitor());
        
        // Add store-level monitoring
        store.addStoreListener(new StoreMonitor());
        
        store.connect();
        
        // Setup folder-specific monitoring
        Folder inbox = store.getFolder("INBOX");
        
        // Add message count monitoring
        inbox.addMessageCountListener(new MessageMonitor());
        
        // Add message change monitoring
        inbox.addMessageChangedListener(new MessageChangeMonitor());
        
        inbox.open(Folder.READ_WRITE);
        
        // Setup transport monitoring
        Transport transport = session.getTransport("smtp");
        transport.addTransportListener(new DeliveryMonitor());
        
        // Keep connection alive and monitor events
        monitorEvents(store, inbox, transport);
    }
    
    private void monitorEvents(Store store, Folder folder, Transport transport) {
        // Keep the application running to receive events
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                System.out.println("Shutting down mail monitoring...");
                folder.close();
                store.close();
                transport.close();
            } catch (MessagingException e) {
                System.err.println("Error during shutdown: " + e.getMessage());
            }
        }));
        
        // Keep alive with periodic operations
        Timer keepAliveTimer = new Timer(true);
        keepAliveTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    // Periodic check to keep connection alive
                    if (store.isConnected()) {
                        folder.getMessageCount(); // No-op to keep connection active
                    } else {
                        System.out.println("Reconnecting to store...");
                        store.connect();
                        folder.open(Folder.READ_WRITE);
                    }
                } catch (MessagingException e) {
                    System.err.println("Keep-alive check failed: " + e.getMessage());
                }
            }
        }, 60000, 60000); // Check every minute
        
        // Main event loop
        try {
            System.out.println("Mail monitoring started. Press Ctrl+C to stop.");
            Thread.currentThread().join(); // Keep main thread alive
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Event-Driven Mail Client Example

public class EventDrivenMailClient {
    private Store store;
    private Folder inbox;
    private List<Message> pendingMessages = new ArrayList<>();
    
    public void startClient(Session session) throws MessagingException {
        setupConnection(session);
        setupEventHandlers();
        startMessageProcessing();
    }
    
    private void setupConnection(Session session) throws MessagingException {
        store = session.getStore("imap");
        store.connect();
        
        inbox = store.getFolder("INBOX");
        inbox.open(Folder.READ_WRITE);
    }
    
    private void setupEventHandlers() {
        // Handle new messages
        inbox.addMessageCountListener(new MessageCountAdapter() {
            @Override
            public void messagesAdded(MessageCountEvent e) {
                synchronized (pendingMessages) {
                    Collections.addAll(pendingMessages, e.getMessages());
                    pendingMessages.notifyAll();
                }
            }
        });
        
        // Handle connection issues
        store.addConnectionListener(new ConnectionAdapter() {
            @Override
            public void disconnected(ConnectionEvent e) {
                System.err.println("Connection lost! Attempting to reconnect...");
                reconnect();
            }
        });
        
        // Handle message changes
        inbox.addMessageChangedListener(e -> {
            try {
                Message msg = e.getMessage();
                System.out.println("Message updated: " + msg.getSubject());
            } catch (MessagingException ex) {
                System.err.println("Error handling message change: " + ex.getMessage());
            }
        });
    }
    
    private void startMessageProcessing() {
        Thread processingThread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    synchronized (pendingMessages) {
                        while (pendingMessages.isEmpty()) {
                            pendingMessages.wait();
                        }
                        
                        Message msg = pendingMessages.remove(0);
                        processMessage(msg);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    System.err.println("Error processing message: " + e.getMessage());
                }
            }
        });
        
        processingThread.setDaemon(true);
        processingThread.start();
    }
    
    private void processMessage(Message msg) throws MessagingException {
        System.out.println("Processing new message: " + msg.getSubject());
        
        // Auto-reply to urgent messages
        if (isUrgent(msg)) {
            sendAutoReply(msg);
        }
        
        // Archive old messages automatically
        if (isOld(msg)) {
            archiveMessage(msg);
        }
        
        // Mark as processed
        msg.setFlag(Flags.Flag.SEEN, true);
    }
    
    private boolean isUrgent(Message msg) throws MessagingException {
        String subject = msg.getSubject();
        return subject != null && (subject.contains("URGENT") || subject.contains("ASAP"));
    }
    
    private boolean isOld(Message msg) throws MessagingException {
        Date received = msg.getReceivedDate();
        if (received == null) return false;
        
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MONTH, -6);
        return received.before(cal.getTime());
    }
    
    private void sendAutoReply(Message msg) {
        System.out.println("Sending auto-reply to urgent message");
        // Implementation for auto-reply
    }
    
    private void archiveMessage(Message msg) throws MessagingException {
        System.out.println("Archiving old message");
        // Move to archive folder
        Folder archive = store.getFolder("Archive");
        if (!archive.exists()) {
            archive.create(Folder.HOLDS_MESSAGES);
        }
        archive.open(Folder.READ_WRITE);
        
        Message[] msgs = {msg};
        inbox.copyMessages(msgs, archive);
        msg.setFlag(Flags.Flag.DELETED, true);
        
        archive.close(false);
    }
    
    private void reconnect() {
        try {
            Thread.sleep(5000); // Wait before reconnecting
            if (!store.isConnected()) {
                store.connect();
            }
            if (!inbox.isOpen()) {
                inbox.open(Folder.READ_WRITE);
            }
            System.out.println("Reconnected successfully");
        } catch (Exception e) {
            System.err.println("Reconnection failed: " + e.getMessage());
        }
    }
    
    public void shutdown() {
        try {
            if (inbox != null && inbox.isOpen()) {
                inbox.close();
            }
            if (store != null && store.isConnected()) {
                store.close();
            }
        } catch (MessagingException e) {
            System.err.println("Error during shutdown: " + e.getMessage());
        }
    }
}

This comprehensive event handling system allows for reactive, event-driven mail applications that can respond to real-time changes in mail stores, handle connection issues gracefully, and provide rich user experiences with immediate feedback on mail operations.

Install with Tessl CLI

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

docs

core-messaging.md

event-handling.md

folder-operations.md

index.md

internet-messaging.md

search-capabilities.md

tile.json