0
# Event Handling
1
2
This document covers the comprehensive event-driven programming capabilities of the Jakarta Mail API, including connection events, message changes, folder operations, and transport notifications.
3
4
## Event System Overview
5
6
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.
7
8
### Base Event Class
9
10
```java { .api }
11
abstract class MailEvent extends EventObject {
12
public MailEvent(Object source);
13
14
public abstract void dispatch(Object listener);
15
}
16
```
17
18
All mail events extend this base class and implement the `dispatch` method to deliver themselves to appropriate listener methods.
19
20
## Connection Events
21
22
Connection events are fired when mail service connections are opened, closed, or disconnected unexpectedly.
23
24
### ConnectionEvent Class
25
26
```java { .api }
27
class ConnectionEvent extends MailEvent {
28
public static final int OPENED = 1;
29
public static final int DISCONNECTED = 2;
30
public static final int CLOSED = 3;
31
32
public ConnectionEvent(Object source, int type);
33
34
public int getType();
35
public void dispatch(Object listener);
36
}
37
```
38
39
### ConnectionListener Interface
40
41
```java { .api }
42
interface ConnectionListener extends EventListener {
43
public void opened(ConnectionEvent e);
44
public void disconnected(ConnectionEvent e);
45
public void closed(ConnectionEvent e);
46
}
47
```
48
49
### ConnectionAdapter Class
50
51
```java { .api }
52
abstract class ConnectionAdapter implements ConnectionListener {
53
public void opened(ConnectionEvent e) {}
54
public void disconnected(ConnectionEvent e) {}
55
public void closed(ConnectionEvent e) {}
56
}
57
```
58
59
### Connection Event Usage
60
61
```java
62
public class ConnectionMonitor extends ConnectionAdapter {
63
64
@Override
65
public void opened(ConnectionEvent e) {
66
System.out.println("Connection opened: " + e.getSource());
67
}
68
69
@Override
70
public void disconnected(ConnectionEvent e) {
71
System.err.println("Connection lost: " + e.getSource());
72
handleDisconnection();
73
}
74
75
@Override
76
public void closed(ConnectionEvent e) {
77
System.out.println("Connection closed: " + e.getSource());
78
}
79
80
private void handleDisconnection() {
81
// Implement reconnection logic
82
System.out.println("Attempting to reconnect...");
83
}
84
}
85
86
// Register connection listener
87
Store store = session.getStore("imap");
88
store.addConnectionListener(new ConnectionMonitor());
89
store.connect();
90
```
91
92
## Folder Events
93
94
Folder events are fired when folders are created, deleted, or renamed in the message store.
95
96
### FolderEvent Class
97
98
```java { .api }
99
class FolderEvent extends MailEvent {
100
public static final int CREATED = 1;
101
public static final int DELETED = 2;
102
public static final int RENAMED = 3;
103
104
protected Folder folder;
105
protected Folder newFolder;
106
107
public FolderEvent(Object source, Folder folder, int type);
108
public FolderEvent(Object source, Folder oldFolder, Folder newFolder, int type);
109
110
public int getType();
111
public Folder getFolder();
112
public Folder getNewFolder();
113
public void dispatch(Object listener);
114
}
115
```
116
117
### FolderListener Interface
118
119
```java { .api }
120
interface FolderListener extends EventListener {
121
public void folderCreated(FolderEvent e);
122
public void folderDeleted(FolderEvent e);
123
public void folderRenamed(FolderEvent e);
124
}
125
```
126
127
### FolderAdapter Class
128
129
```java { .api }
130
abstract class FolderAdapter implements FolderListener {
131
public void folderCreated(FolderEvent e) {}
132
public void folderDeleted(FolderEvent e) {}
133
public void folderRenamed(FolderEvent e) {}
134
}
135
```
136
137
### Folder Event Usage
138
139
```java
140
public class FolderMonitor extends FolderAdapter {
141
142
@Override
143
public void folderCreated(FolderEvent e) {
144
Folder newFolder = e.getFolder();
145
System.out.println("New folder created: " + newFolder.getFullName());
146
147
// Update folder cache or UI
148
updateFolderList();
149
}
150
151
@Override
152
public void folderDeleted(FolderEvent e) {
153
Folder deletedFolder = e.getFolder();
154
System.out.println("Folder deleted: " + deletedFolder.getFullName());
155
156
// Clean up references
157
removeFolderFromCache(deletedFolder);
158
}
159
160
@Override
161
public void folderRenamed(FolderEvent e) {
162
Folder oldFolder = e.getFolder();
163
Folder newFolder = e.getNewFolder();
164
System.out.println("Folder renamed: " + oldFolder.getFullName() +
165
" -> " + newFolder.getFullName());
166
167
// Update references
168
updateFolderReferences(oldFolder, newFolder);
169
}
170
171
private void updateFolderList() { /* Implementation */ }
172
private void removeFolderFromCache(Folder folder) { /* Implementation */ }
173
private void updateFolderReferences(Folder old, Folder newFolder) { /* Implementation */ }
174
}
175
176
// Register folder listener
177
Store store = session.getStore("imap");
178
store.addFolderListener(new FolderMonitor());
179
```
180
181
## Message Count Events
182
183
Message count events are fired when messages are added to or removed from folders.
184
185
### MessageCountEvent Class
186
187
```java { .api }
188
class MessageCountEvent extends MailEvent {
189
public static final int ADDED = 1;
190
public static final int REMOVED = 2;
191
192
protected int type;
193
protected boolean removed;
194
protected Message[] msgs;
195
196
public MessageCountEvent(Folder folder, int type, boolean removed, Message[] msgs);
197
198
public int getType();
199
public boolean isRemoved();
200
public Message[] getMessages();
201
public void dispatch(Object listener);
202
}
203
```
204
205
### MessageCountListener Interface
206
207
```java { .api }
208
interface MessageCountListener extends EventListener {
209
public void messagesAdded(MessageCountEvent e);
210
public void messagesRemoved(MessageCountEvent e);
211
}
212
```
213
214
### MessageCountAdapter Class
215
216
```java { .api }
217
abstract class MessageCountAdapter implements MessageCountListener {
218
public void messagesAdded(MessageCountEvent e) {}
219
public void messagesRemoved(MessageCountEvent e) {}
220
}
221
```
222
223
### Message Count Event Usage
224
225
```java
226
public class MessageMonitor extends MessageCountAdapter {
227
private int totalMessages = 0;
228
229
@Override
230
public void messagesAdded(MessageCountEvent e) {
231
Message[] newMessages = e.getMessages();
232
totalMessages += newMessages.length;
233
234
System.out.println(newMessages.length + " new messages arrived");
235
System.out.println("Total messages: " + totalMessages);
236
237
// Process new messages
238
for (Message msg : newMessages) {
239
try {
240
processNewMessage(msg);
241
} catch (MessagingException ex) {
242
System.err.println("Error processing message: " + ex.getMessage());
243
}
244
}
245
}
246
247
@Override
248
public void messagesRemoved(MessageCountEvent e) {
249
Message[] removedMessages = e.getMessages();
250
totalMessages -= removedMessages.length;
251
252
System.out.println(removedMessages.length + " messages removed");
253
System.out.println("Total messages: " + totalMessages);
254
255
// Clean up references
256
for (Message msg : removedMessages) {
257
cleanupMessage(msg);
258
}
259
}
260
261
private void processNewMessage(Message msg) throws MessagingException {
262
// Handle new message arrival
263
System.out.println("New message: " + msg.getSubject());
264
265
// Check for important messages
266
if (isImportant(msg)) {
267
notifyUser(msg);
268
}
269
}
270
271
private boolean isImportant(Message msg) throws MessagingException {
272
return msg.isSet(Flags.Flag.FLAGGED) ||
273
(msg.getSubject() != null && msg.getSubject().contains("URGENT"));
274
}
275
276
private void notifyUser(Message msg) {
277
// Implementation for user notification
278
System.out.println("IMPORTANT MESSAGE RECEIVED!");
279
}
280
281
private void cleanupMessage(Message msg) {
282
// Clean up message references
283
System.out.println("Cleaning up message references");
284
}
285
}
286
287
// Register message count listener
288
Folder folder = store.getFolder("INBOX");
289
folder.addMessageCountListener(new MessageMonitor());
290
folder.open(Folder.READ_WRITE);
291
```
292
293
## Message Changed Events
294
295
Message changed events are fired when message properties (flags, headers, content) are modified.
296
297
### MessageChangedEvent Class
298
299
```java { .api }
300
class MessageChangedEvent extends MailEvent {
301
protected int type;
302
protected Message msg;
303
304
public MessageChangedEvent(Object source, int type, Message message);
305
306
public int getType();
307
public Message getMessage();
308
public void dispatch(Object listener);
309
}
310
```
311
312
### MessageChangedListener Interface
313
314
```java { .api }
315
interface MessageChangedListener extends EventListener {
316
public void messageChanged(MessageChangedEvent e);
317
}
318
```
319
320
### Message Changed Event Usage
321
322
```java
323
public class MessageChangeMonitor implements MessageChangedListener {
324
325
@Override
326
public void messageChanged(MessageChangedEvent e) {
327
Message msg = e.getMessage();
328
329
try {
330
System.out.println("Message changed: " + msg.getSubject());
331
332
// Check what changed
333
if (msg.isSet(Flags.Flag.SEEN)) {
334
System.out.println("Message marked as read");
335
}
336
337
if (msg.isSet(Flags.Flag.FLAGGED)) {
338
System.out.println("Message flagged as important");
339
}
340
341
if (msg.isSet(Flags.Flag.DELETED)) {
342
System.out.println("Message marked for deletion");
343
}
344
345
// Update UI or cache
346
updateMessageDisplay(msg);
347
348
} catch (MessagingException ex) {
349
System.err.println("Error handling message change: " + ex.getMessage());
350
}
351
}
352
353
private void updateMessageDisplay(Message msg) {
354
// Update UI representation of the message
355
System.out.println("Updating message display");
356
}
357
}
358
359
// Register message changed listener
360
folder.addMessageChangedListener(new MessageChangeMonitor());
361
```
362
363
## Store Events
364
365
Store events provide general notifications about store-level operations.
366
367
### StoreEvent Class
368
369
```java { .api }
370
class StoreEvent extends MailEvent {
371
protected int type;
372
protected String message;
373
374
public StoreEvent(Store store, int type, String message);
375
376
public int getType();
377
public String getMessage();
378
public void dispatch(Object listener);
379
}
380
```
381
382
### StoreListener Interface
383
384
```java { .api }
385
interface StoreListener extends EventListener {
386
public void notification(StoreEvent e);
387
}
388
```
389
390
### Store Event Usage
391
392
```java
393
public class StoreMonitor implements StoreListener {
394
395
@Override
396
public void notification(StoreEvent e) {
397
System.out.println("Store notification: " + e.getMessage());
398
399
// Handle different types of store events
400
int eventType = e.getType();
401
switch (eventType) {
402
case StoreEvent.ALERT:
403
handleAlert(e.getMessage());
404
break;
405
case StoreEvent.NOTICE:
406
handleNotice(e.getMessage());
407
break;
408
default:
409
handleGenericEvent(e.getMessage());
410
break;
411
}
412
}
413
414
private void handleAlert(String message) {
415
System.err.println("STORE ALERT: " + message);
416
// Handle critical store alerts
417
}
418
419
private void handleNotice(String message) {
420
System.out.println("Store notice: " + message);
421
// Handle informational notices
422
}
423
424
private void handleGenericEvent(String message) {
425
System.out.println("Store event: " + message);
426
}
427
}
428
429
// Register store listener
430
store.addStoreListener(new StoreMonitor());
431
```
432
433
## Transport Events
434
435
Transport events are fired during message sending operations to report delivery status.
436
437
### TransportEvent Class
438
439
```java { .api }
440
class TransportEvent extends MailEvent {
441
public static final int MESSAGE_DELIVERED = 1;
442
public static final int MESSAGE_NOT_DELIVERED = 2;
443
public static final int MESSAGE_PARTIALLY_DELIVERED = 3;
444
445
protected int type;
446
protected Address[] validSent;
447
protected Address[] validUnsent;
448
protected Address[] invalid;
449
protected Message msg;
450
451
public TransportEvent(Transport transport, int type, Address[] validSent,
452
Address[] validUnsent, Address[] invalid, Message msg);
453
454
public int getType();
455
public Address[] getValidSentAddresses();
456
public Address[] getValidUnsentAddresses();
457
public Address[] getInvalidAddresses();
458
public Message getMessage();
459
public void dispatch(Object listener);
460
}
461
```
462
463
### TransportListener Interface
464
465
```java { .api }
466
interface TransportListener extends EventListener {
467
public void messageDelivered(TransportEvent e);
468
public void messageNotDelivered(TransportEvent e);
469
public void messagePartiallyDelivered(TransportEvent e);
470
}
471
```
472
473
### TransportAdapter Class
474
475
```java { .api }
476
abstract class TransportAdapter implements TransportListener {
477
public void messageDelivered(TransportEvent e) {}
478
public void messageNotDelivered(TransportEvent e) {}
479
public void messagePartiallyDelivered(TransportEvent e) {}
480
}
481
```
482
483
### Transport Event Usage
484
485
```java
486
public class DeliveryMonitor extends TransportAdapter {
487
488
@Override
489
public void messageDelivered(TransportEvent e) {
490
Message msg = e.getMessage();
491
Address[] delivered = e.getValidSentAddresses();
492
493
try {
494
System.out.println("Message delivered successfully: " + msg.getSubject());
495
System.out.println("Delivered to " + delivered.length + " recipients:");
496
497
for (Address addr : delivered) {
498
System.out.println(" - " + addr.toString());
499
}
500
501
// Log successful delivery
502
logDelivery(msg, delivered, true);
503
504
} catch (MessagingException ex) {
505
System.err.println("Error processing delivery confirmation: " + ex.getMessage());
506
}
507
}
508
509
@Override
510
public void messageNotDelivered(TransportEvent e) {
511
Message msg = e.getMessage();
512
Address[] failed = e.getValidUnsentAddresses();
513
Address[] invalid = e.getInvalidAddresses();
514
515
try {
516
System.err.println("Message delivery failed: " + msg.getSubject());
517
518
if (failed != null && failed.length > 0) {
519
System.err.println("Failed addresses:");
520
for (Address addr : failed) {
521
System.err.println(" - " + addr.toString());
522
}
523
}
524
525
if (invalid != null && invalid.length > 0) {
526
System.err.println("Invalid addresses:");
527
for (Address addr : invalid) {
528
System.err.println(" - " + addr.toString());
529
}
530
}
531
532
// Log failed delivery and schedule retry
533
logDelivery(msg, failed, false);
534
scheduleRetry(msg, failed);
535
536
} catch (MessagingException ex) {
537
System.err.println("Error processing delivery failure: " + ex.getMessage());
538
}
539
}
540
541
@Override
542
public void messagePartiallyDelivered(TransportEvent e) {
543
Message msg = e.getMessage();
544
Address[] delivered = e.getValidSentAddresses();
545
Address[] failed = e.getValidUnsentAddresses();
546
547
try {
548
System.out.println("Message partially delivered: " + msg.getSubject());
549
550
if (delivered != null && delivered.length > 0) {
551
System.out.println("Successfully delivered to:");
552
for (Address addr : delivered) {
553
System.out.println(" - " + addr.toString());
554
}
555
}
556
557
if (failed != null && failed.length > 0) {
558
System.err.println("Failed to deliver to:");
559
for (Address addr : failed) {
560
System.err.println(" - " + addr.toString());
561
}
562
// Retry failed addresses
563
scheduleRetry(msg, failed);
564
}
565
566
} catch (MessagingException ex) {
567
System.err.println("Error processing partial delivery: " + ex.getMessage());
568
}
569
}
570
571
private void logDelivery(Message msg, Address[] addresses, boolean success) {
572
// Log delivery status to database or file
573
String status = success ? "DELIVERED" : "FAILED";
574
System.out.println("Logging delivery: " + status);
575
}
576
577
private void scheduleRetry(Message msg, Address[] failedAddresses) {
578
// Schedule retry for failed addresses
579
System.out.println("Scheduling retry for " + failedAddresses.length + " addresses");
580
}
581
}
582
583
// Register transport listener
584
Transport transport = session.getTransport("smtp");
585
transport.addTransportListener(new DeliveryMonitor());
586
```
587
588
## Complete Event Monitoring Example
589
590
```java
591
public class ComprehensiveMailMonitor {
592
593
public void setupEventMonitoring(Session session) throws MessagingException {
594
// Setup store monitoring
595
Store store = session.getStore("imap");
596
597
// Add connection monitoring
598
store.addConnectionListener(new ConnectionMonitor());
599
600
// Add folder monitoring
601
store.addFolderListener(new FolderMonitor());
602
603
// Add store-level monitoring
604
store.addStoreListener(new StoreMonitor());
605
606
store.connect();
607
608
// Setup folder-specific monitoring
609
Folder inbox = store.getFolder("INBOX");
610
611
// Add message count monitoring
612
inbox.addMessageCountListener(new MessageMonitor());
613
614
// Add message change monitoring
615
inbox.addMessageChangedListener(new MessageChangeMonitor());
616
617
inbox.open(Folder.READ_WRITE);
618
619
// Setup transport monitoring
620
Transport transport = session.getTransport("smtp");
621
transport.addTransportListener(new DeliveryMonitor());
622
623
// Keep connection alive and monitor events
624
monitorEvents(store, inbox, transport);
625
}
626
627
private void monitorEvents(Store store, Folder folder, Transport transport) {
628
// Keep the application running to receive events
629
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
630
try {
631
System.out.println("Shutting down mail monitoring...");
632
folder.close();
633
store.close();
634
transport.close();
635
} catch (MessagingException e) {
636
System.err.println("Error during shutdown: " + e.getMessage());
637
}
638
}));
639
640
// Keep alive with periodic operations
641
Timer keepAliveTimer = new Timer(true);
642
keepAliveTimer.scheduleAtFixedRate(new TimerTask() {
643
@Override
644
public void run() {
645
try {
646
// Periodic check to keep connection alive
647
if (store.isConnected()) {
648
folder.getMessageCount(); // No-op to keep connection active
649
} else {
650
System.out.println("Reconnecting to store...");
651
store.connect();
652
folder.open(Folder.READ_WRITE);
653
}
654
} catch (MessagingException e) {
655
System.err.println("Keep-alive check failed: " + e.getMessage());
656
}
657
}
658
}, 60000, 60000); // Check every minute
659
660
// Main event loop
661
try {
662
System.out.println("Mail monitoring started. Press Ctrl+C to stop.");
663
Thread.currentThread().join(); // Keep main thread alive
664
} catch (InterruptedException e) {
665
Thread.currentThread().interrupt();
666
}
667
}
668
}
669
```
670
671
## Event-Driven Mail Client Example
672
673
```java
674
public class EventDrivenMailClient {
675
private Store store;
676
private Folder inbox;
677
private List<Message> pendingMessages = new ArrayList<>();
678
679
public void startClient(Session session) throws MessagingException {
680
setupConnection(session);
681
setupEventHandlers();
682
startMessageProcessing();
683
}
684
685
private void setupConnection(Session session) throws MessagingException {
686
store = session.getStore("imap");
687
store.connect();
688
689
inbox = store.getFolder("INBOX");
690
inbox.open(Folder.READ_WRITE);
691
}
692
693
private void setupEventHandlers() {
694
// Handle new messages
695
inbox.addMessageCountListener(new MessageCountAdapter() {
696
@Override
697
public void messagesAdded(MessageCountEvent e) {
698
synchronized (pendingMessages) {
699
Collections.addAll(pendingMessages, e.getMessages());
700
pendingMessages.notifyAll();
701
}
702
}
703
});
704
705
// Handle connection issues
706
store.addConnectionListener(new ConnectionAdapter() {
707
@Override
708
public void disconnected(ConnectionEvent e) {
709
System.err.println("Connection lost! Attempting to reconnect...");
710
reconnect();
711
}
712
});
713
714
// Handle message changes
715
inbox.addMessageChangedListener(e -> {
716
try {
717
Message msg = e.getMessage();
718
System.out.println("Message updated: " + msg.getSubject());
719
} catch (MessagingException ex) {
720
System.err.println("Error handling message change: " + ex.getMessage());
721
}
722
});
723
}
724
725
private void startMessageProcessing() {
726
Thread processingThread = new Thread(() -> {
727
while (!Thread.currentThread().isInterrupted()) {
728
try {
729
synchronized (pendingMessages) {
730
while (pendingMessages.isEmpty()) {
731
pendingMessages.wait();
732
}
733
734
Message msg = pendingMessages.remove(0);
735
processMessage(msg);
736
}
737
} catch (InterruptedException e) {
738
Thread.currentThread().interrupt();
739
break;
740
} catch (Exception e) {
741
System.err.println("Error processing message: " + e.getMessage());
742
}
743
}
744
});
745
746
processingThread.setDaemon(true);
747
processingThread.start();
748
}
749
750
private void processMessage(Message msg) throws MessagingException {
751
System.out.println("Processing new message: " + msg.getSubject());
752
753
// Auto-reply to urgent messages
754
if (isUrgent(msg)) {
755
sendAutoReply(msg);
756
}
757
758
// Archive old messages automatically
759
if (isOld(msg)) {
760
archiveMessage(msg);
761
}
762
763
// Mark as processed
764
msg.setFlag(Flags.Flag.SEEN, true);
765
}
766
767
private boolean isUrgent(Message msg) throws MessagingException {
768
String subject = msg.getSubject();
769
return subject != null && (subject.contains("URGENT") || subject.contains("ASAP"));
770
}
771
772
private boolean isOld(Message msg) throws MessagingException {
773
Date received = msg.getReceivedDate();
774
if (received == null) return false;
775
776
Calendar cal = Calendar.getInstance();
777
cal.add(Calendar.MONTH, -6);
778
return received.before(cal.getTime());
779
}
780
781
private void sendAutoReply(Message msg) {
782
System.out.println("Sending auto-reply to urgent message");
783
// Implementation for auto-reply
784
}
785
786
private void archiveMessage(Message msg) throws MessagingException {
787
System.out.println("Archiving old message");
788
// Move to archive folder
789
Folder archive = store.getFolder("Archive");
790
if (!archive.exists()) {
791
archive.create(Folder.HOLDS_MESSAGES);
792
}
793
archive.open(Folder.READ_WRITE);
794
795
Message[] msgs = {msg};
796
inbox.copyMessages(msgs, archive);
797
msg.setFlag(Flags.Flag.DELETED, true);
798
799
archive.close(false);
800
}
801
802
private void reconnect() {
803
try {
804
Thread.sleep(5000); // Wait before reconnecting
805
if (!store.isConnected()) {
806
store.connect();
807
}
808
if (!inbox.isOpen()) {
809
inbox.open(Folder.READ_WRITE);
810
}
811
System.out.println("Reconnected successfully");
812
} catch (Exception e) {
813
System.err.println("Reconnection failed: " + e.getMessage());
814
}
815
}
816
817
public void shutdown() {
818
try {
819
if (inbox != null && inbox.isOpen()) {
820
inbox.close();
821
}
822
if (store != null && store.isConnected()) {
823
store.close();
824
}
825
} catch (MessagingException e) {
826
System.err.println("Error during shutdown: " + e.getMessage());
827
}
828
}
829
}
830
```
831
832
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.