0
# Utility Classes and Streams
1
2
Jakarta Mail utility classes provide helper functionality for data sources, shared streams, stream providers, and mail-specific utilities that support the core mail operations.
3
4
## Data Source Classes
5
6
### Byte Array Data Source
7
8
The ByteArrayDataSource class provides a DataSource implementation backed by a byte array, commonly used for in-memory content.
9
10
```java { .api }
11
public class ByteArrayDataSource implements DataSource {
12
// Constructors
13
public ByteArrayDataSource(byte[] data, String type);
14
public ByteArrayDataSource(InputStream is, String type) throws IOException;
15
public ByteArrayDataSource(String data, String type);
16
17
// DataSource interface methods
18
public InputStream getInputStream() throws IOException;
19
public OutputStream getOutputStream() throws IOException;
20
public String getContentType();
21
public String getName();
22
23
// Additional methods
24
public void setName(String name);
25
}
26
```
27
28
### Byte Array Data Source Usage Example
29
30
```java
31
import jakarta.mail.util.ByteArrayDataSource;
32
import jakarta.mail.internet.*;
33
import jakarta.mail.*;
34
35
// Create data source from byte array
36
byte[] pdfData = loadPdfFile(); // Your PDF data
37
ByteArrayDataSource dataSource = new ByteArrayDataSource(pdfData, "application/pdf");
38
dataSource.setName("document.pdf");
39
40
// Use in MIME body part
41
MimeBodyPart attachment = new MimeBodyPart();
42
attachment.setDataHandler(new DataHandler(dataSource));
43
attachment.setFileName("document.pdf");
44
attachment.setDisposition(Part.ATTACHMENT);
45
46
// Create from string content
47
String htmlContent = "<html><body><h1>Hello World</h1></body></html>";
48
ByteArrayDataSource htmlSource = new ByteArrayDataSource(htmlContent, "text/html");
49
50
MimeBodyPart htmlPart = new MimeBodyPart();
51
htmlPart.setDataHandler(new DataHandler(htmlSource));
52
53
// Create from input stream
54
InputStream imageStream = getImageInputStream();
55
ByteArrayDataSource imageSource = new ByteArrayDataSource(imageStream, "image/png");
56
imageSource.setName("logo.png");
57
58
MimeBodyPart imagePart = new MimeBodyPart();
59
imagePart.setDataHandler(new DataHandler(imageSource));
60
imagePart.setDisposition(Part.INLINE);
61
imagePart.setHeader("Content-ID", "<logo>");
62
```
63
64
## Shared Input Stream Classes
65
66
Shared input streams allow multiple concurrent readers to access the same underlying data source efficiently.
67
68
### Shared Input Stream Interface
69
70
```java { .api }
71
public interface SharedInputStream {
72
// Create new stream for specified range
73
public InputStream newStream(long start, long end);
74
75
// Get current position in stream
76
public long getPosition();
77
}
78
```
79
80
### Shared File Input Stream
81
82
```java { .api }
83
public class SharedFileInputStream extends BufferedInputStream implements SharedInputStream {
84
// Constructors
85
public SharedFileInputStream(File file) throws IOException;
86
public SharedFileInputStream(File file, int size) throws IOException;
87
public SharedFileInputStream(String file) throws IOException;
88
public SharedFileInputStream(String file, int size) throws IOException;
89
90
// SharedInputStream interface methods
91
public InputStream newStream(long start, long end);
92
public long getPosition();
93
94
// File access methods
95
protected void finalize() throws Throwable;
96
}
97
```
98
99
### Shared Byte Array Input Stream
100
101
```java { .api }
102
public class SharedByteArrayInputStream extends ByteArrayInputStream implements SharedInputStream {
103
// Constructors
104
public SharedByteArrayInputStream(byte[] buf);
105
public SharedByteArrayInputStream(byte[] buf, int offset, int length);
106
107
// SharedInputStream interface methods
108
public InputStream newStream(long start, long end);
109
public long getPosition();
110
}
111
```
112
113
### Shared Stream Usage Example
114
115
```java
116
import jakarta.mail.util.*;
117
import java.io.*;
118
119
// Create shared file input stream for large files
120
File largeFile = new File("large-attachment.zip");
121
SharedFileInputStream sharedFile = new SharedFileInputStream(largeFile);
122
123
// Multiple readers can access different parts concurrently
124
InputStream reader1 = sharedFile.newStream(0, 1024); // First 1KB
125
InputStream reader2 = sharedFile.newStream(1024, 2048); // Second 1KB
126
InputStream reader3 = sharedFile.newStream(2048, -1); // Rest of file
127
128
// Read concurrently without blocking
129
CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
130
try {
131
processStream(reader1);
132
} catch (IOException e) {
133
e.printStackTrace();
134
}
135
});
136
137
CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
138
try {
139
processStream(reader2);
140
} catch (IOException e) {
141
e.printStackTrace();
142
}
143
});
144
145
// Shared byte array for in-memory data
146
byte[] data = loadLargeData();
147
SharedByteArrayInputStream sharedBytes = new SharedByteArrayInputStream(data);
148
149
// Create multiple streams from same data
150
InputStream stream1 = sharedBytes.newStream(0, 100);
151
InputStream stream2 = sharedBytes.newStream(100, 200);
152
153
// Each stream is independent
154
int data1 = stream1.read(); // Reads from position 0
155
int data2 = stream2.read(); // Reads from position 100
156
```
157
158
## Line-Oriented Stream Interfaces
159
160
### Line Input Stream Interface
161
162
```java { .api }
163
public interface LineInputStream {
164
// Read a line of text, null if end of stream
165
public String readLine() throws IOException;
166
}
167
```
168
169
### Line Output Stream Interface
170
171
```java { .api }
172
public interface LineOutputStream {
173
// Write a line of text followed by line terminator
174
public void writeln(String s) throws IOException;
175
176
// Write line terminator only
177
public void writeln() throws IOException;
178
}
179
```
180
181
### Line Stream Usage Example
182
183
```java
184
import jakarta.mail.util.*;
185
import java.io.*;
186
187
// Implement line-oriented processing
188
public class MailHeaderProcessor {
189
190
public void processHeaders(InputStream input) throws IOException {
191
if (input instanceof LineInputStream) {
192
LineInputStream lineInput = (LineInputStream) input;
193
String line;
194
195
while ((line = lineInput.readLine()) != null) {
196
if (line.trim().isEmpty()) {
197
break; // End of headers
198
}
199
processHeaderLine(line);
200
}
201
} else {
202
// Wrap in BufferedReader for line reading
203
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
204
String line;
205
206
while ((line = reader.readLine()) != null) {
207
if (line.trim().isEmpty()) {
208
break;
209
}
210
processHeaderLine(line);
211
}
212
}
213
}
214
215
public void writeHeaders(OutputStream output, Map<String, String> headers) throws IOException {
216
if (output instanceof LineOutputStream) {
217
LineOutputStream lineOutput = (LineOutputStream) output;
218
219
for (Map.Entry<String, String> header : headers.entrySet()) {
220
lineOutput.writeln(header.getKey() + ": " + header.getValue());
221
}
222
lineOutput.writeln(); // Empty line to end headers
223
} else {
224
PrintWriter writer = new PrintWriter(output);
225
226
for (Map.Entry<String, String> header : headers.entrySet()) {
227
writer.println(header.getKey() + ": " + header.getValue());
228
}
229
writer.println();
230
writer.flush();
231
}
232
}
233
234
private void processHeaderLine(String line) {
235
System.out.println("Processing header: " + line);
236
}
237
}
238
```
239
240
## Stream Provider Interface
241
242
The StreamProvider interface allows for pluggable stream implementations.
243
244
```java { .api }
245
public interface StreamProvider {
246
// Get input stream for reading
247
public InputStream getInputStream() throws IOException;
248
249
// Get output stream for writing
250
public OutputStream getOutputStream() throws IOException;
251
}
252
```
253
254
### Stream Provider Usage Example
255
256
```java
257
import jakarta.mail.util.StreamProvider;
258
import java.io.*;
259
import java.util.ServiceLoader;
260
261
// Custom stream provider implementation
262
public class CustomStreamProvider implements StreamProvider {
263
private final File dataFile;
264
265
public CustomStreamProvider(File dataFile) {
266
this.dataFile = dataFile;
267
}
268
269
@Override
270
public InputStream getInputStream() throws IOException {
271
return new BufferedInputStream(new FileInputStream(dataFile));
272
}
273
274
@Override
275
public OutputStream getOutputStream() throws IOException {
276
return new BufferedOutputStream(new FileOutputStream(dataFile));
277
}
278
}
279
280
// Load stream providers via ServiceLoader
281
ServiceLoader<StreamProvider> providers = ServiceLoader.load(StreamProvider.class);
282
for (StreamProvider provider : providers) {
283
try (InputStream is = provider.getInputStream()) {
284
// Use stream from provider
285
processStream(is);
286
}
287
}
288
289
// Use custom provider
290
StreamProvider customProvider = new CustomStreamProvider(new File("data.txt"));
291
try (OutputStream os = customProvider.getOutputStream()) {
292
os.write("Hello World".getBytes());
293
}
294
```
295
296
## Factory Finder Utility
297
298
The FactoryFinder class provides service loading capabilities for mail-related factories.
299
300
```java { .api }
301
public class FactoryFinder {
302
// Find factory implementation by factory ID
303
public static Object find(String factoryId) throws FactoryConfigurationError;
304
public static Object find(String factoryId, String fallbackClassName) throws FactoryConfigurationError;
305
306
// Find factory with specific class loader
307
public static Object find(String factoryId, String fallbackClassName, ClassLoader cl) throws FactoryConfigurationError;
308
309
// Create new instance of factory
310
public static Object newInstance(String className) throws FactoryConfigurationError;
311
public static Object newInstance(String className, ClassLoader cl) throws FactoryConfigurationError;
312
}
313
```
314
315
### Factory Finder Usage Example
316
317
```java
318
import jakarta.mail.util.FactoryFinder;
319
320
// Find mail transport factory
321
try {
322
Object transportFactory = FactoryFinder.find("jakarta.mail.Transport",
323
"com.sun.mail.smtp.SMTPTransport");
324
System.out.println("Found transport factory: " + transportFactory.getClass().getName());
325
} catch (FactoryConfigurationError e) {
326
System.err.println("Failed to find transport factory: " + e.getMessage());
327
}
328
329
// Find store factory with fallback
330
try {
331
Object storeFactory = FactoryFinder.find("jakarta.mail.Store",
332
"com.sun.mail.imap.IMAPStore");
333
System.out.println("Found store factory: " + storeFactory.getClass().getName());
334
} catch (FactoryConfigurationError e) {
335
System.err.println("Failed to find store factory: " + e.getMessage());
336
}
337
338
// Create instance directly
339
try {
340
Object instance = FactoryFinder.newInstance("com.example.CustomMailProcessor");
341
if (instance instanceof MailProcessor) {
342
MailProcessor processor = (MailProcessor) instance;
343
processor.process();
344
}
345
} catch (FactoryConfigurationError e) {
346
System.err.println("Failed to create instance: " + e.getMessage());
347
}
348
```
349
350
## Mail Logger Utility
351
352
The MailLogger class provides logging functionality specifically designed for mail operations.
353
354
```java { .api }
355
public class MailLogger {
356
// Constructors
357
public MailLogger(String name, String defaultDebug, boolean debug, PrintStream out);
358
public MailLogger(String name, String defaultDebug, Session session);
359
360
// Logging methods
361
public void log(Level level, String msg);
362
public void log(Level level, String msg, Object param1);
363
public void log(Level level, String msg, Object... params);
364
365
// Configuration access
366
public boolean isLoggable(Level level);
367
public void config(String msg);
368
public void fine(String msg);
369
public void finer(String msg);
370
public void finest(String msg);
371
372
// Session-based logging
373
public static MailLogger getLogger(String name, String defaultDebug, Session session);
374
public static MailLogger getLogger(String name, String defaultDebug, String debug, boolean debugOut, PrintStream out);
375
}
376
```
377
378
### Mail Logger Usage Example
379
380
```java
381
import jakarta.mail.MailLogger;
382
import jakarta.mail.Session;
383
import java.util.logging.Level;
384
385
// Create logger for mail session
386
Properties props = new Properties();
387
props.put("mail.debug", "true");
388
Session session = Session.getInstance(props);
389
390
MailLogger logger = MailLogger.getLogger("jakarta.mail.transport", "mail.debug", session);
391
392
// Log mail operations
393
logger.fine("Connecting to SMTP server");
394
logger.config("Using SSL connection");
395
logger.log(Level.INFO, "Message sent successfully to {0} recipients", 5);
396
397
// Check if logging is enabled
398
if (logger.isLoggable(Level.FINE)) {
399
logger.fine("Detailed connection information: " + getConnectionDetails());
400
}
401
402
// Different log levels
403
logger.finest("Trace level message");
404
logger.finer("Debug level message");
405
logger.fine("Fine level message");
406
logger.config("Configuration message");
407
logger.log(Level.INFO, "Info level message");
408
logger.log(Level.WARNING, "Warning message");
409
logger.log(Level.SEVERE, "Error message");
410
```
411
412
## MIME Utilities (Additional Functions)
413
414
Beyond the core MimeUtility class, there are additional utility functions for MIME handling.
415
416
### MIME Utility Extensions
417
418
```java
419
public class MimeUtilityExtensions {
420
421
// Content type utilities
422
public static boolean isMultipart(String contentType) {
423
return contentType != null && contentType.toLowerCase().startsWith("multipart/");
424
}
425
426
public static boolean isText(String contentType) {
427
return contentType != null && contentType.toLowerCase().startsWith("text/");
428
}
429
430
public static String extractBoundary(String contentType) {
431
if (contentType == null) return null;
432
433
int boundaryIndex = contentType.toLowerCase().indexOf("boundary=");
434
if (boundaryIndex == -1) return null;
435
436
String boundary = contentType.substring(boundaryIndex + 9);
437
if (boundary.startsWith("\"") && boundary.endsWith("\"")) {
438
boundary = boundary.substring(1, boundary.length() - 1);
439
}
440
441
return boundary;
442
}
443
444
// Charset utilities
445
public static String getCharset(String contentType, String defaultCharset) {
446
if (contentType == null) return defaultCharset;
447
448
int charsetIndex = contentType.toLowerCase().indexOf("charset=");
449
if (charsetIndex == -1) return defaultCharset;
450
451
String charset = contentType.substring(charsetIndex + 8);
452
int semicolonIndex = charset.indexOf(';');
453
if (semicolonIndex != -1) {
454
charset = charset.substring(0, semicolonIndex);
455
}
456
457
charset = charset.trim();
458
if (charset.startsWith("\"") && charset.endsWith("\"")) {
459
charset = charset.substring(1, charset.length() - 1);
460
}
461
462
return charset;
463
}
464
465
// File extension to MIME type mapping
466
public static String getContentTypeForFile(String filename) {
467
if (filename == null) return "application/octet-stream";
468
469
String extension = getFileExtension(filename).toLowerCase();
470
471
switch (extension) {
472
case "txt": return "text/plain";
473
case "html": case "htm": return "text/html";
474
case "pdf": return "application/pdf";
475
case "doc": return "application/msword";
476
case "docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
477
case "xls": return "application/vnd.ms-excel";
478
case "xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
479
case "jpg": case "jpeg": return "image/jpeg";
480
case "png": return "image/png";
481
case "gif": return "image/gif";
482
case "zip": return "application/zip";
483
case "json": return "application/json";
484
case "xml": return "application/xml";
485
default: return "application/octet-stream";
486
}
487
}
488
489
private static String getFileExtension(String filename) {
490
int lastDot = filename.lastIndexOf('.');
491
return lastDot == -1 ? "" : filename.substring(lastDot + 1);
492
}
493
}
494
```
495
496
## Stream Processing Utilities
497
498
### Stream Copying and Processing
499
500
```java
501
public class StreamUtils {
502
503
// Copy stream with progress tracking
504
public static long copyStream(InputStream input, OutputStream output,
505
ProgressCallback callback) throws IOException {
506
byte[] buffer = new byte[8192];
507
long totalBytes = 0;
508
int bytesRead;
509
510
while ((bytesRead = input.read(buffer)) != -1) {
511
output.write(buffer, 0, bytesRead);
512
totalBytes += bytesRead;
513
514
if (callback != null) {
515
callback.onProgress(totalBytes);
516
}
517
}
518
519
return totalBytes;
520
}
521
522
// Read stream to byte array with size limit
523
public static byte[] readStreamToBytes(InputStream input, int maxSize) throws IOException {
524
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
525
byte[] temp = new byte[1024];
526
int totalRead = 0;
527
int bytesRead;
528
529
while ((bytesRead = input.read(temp)) != -1) {
530
if (totalRead + bytesRead > maxSize) {
531
throw new IOException("Stream size exceeds maximum allowed: " + maxSize);
532
}
533
534
buffer.write(temp, 0, bytesRead);
535
totalRead += bytesRead;
536
}
537
538
return buffer.toByteArray();
539
}
540
541
// Create tee stream that writes to multiple outputs
542
public static OutputStream createTeeStream(OutputStream... outputs) {
543
return new OutputStream() {
544
@Override
545
public void write(int b) throws IOException {
546
for (OutputStream output : outputs) {
547
output.write(b);
548
}
549
}
550
551
@Override
552
public void write(byte[] b, int off, int len) throws IOException {
553
for (OutputStream output : outputs) {
554
output.write(b, off, len);
555
}
556
}
557
558
@Override
559
public void flush() throws IOException {
560
for (OutputStream output : outputs) {
561
output.flush();
562
}
563
}
564
565
@Override
566
public void close() throws IOException {
567
for (OutputStream output : outputs) {
568
output.close();
569
}
570
}
571
};
572
}
573
574
public interface ProgressCallback {
575
void onProgress(long bytesProcessed);
576
}
577
}
578
```
579
580
### Stream Usage Example
581
582
```java
583
import java.io.*;
584
585
// Copy large file with progress tracking
586
FileInputStream input = new FileInputStream("large-file.dat");
587
FileOutputStream output = new FileOutputStream("copy.dat");
588
589
StreamUtils.copyStream(input, output, bytesProcessed -> {
590
System.out.println("Copied " + bytesProcessed + " bytes");
591
});
592
593
// Read attachment with size limit (10MB max)
594
try {
595
byte[] attachmentData = StreamUtils.readStreamToBytes(attachmentStream, 10 * 1024 * 1024);
596
// Process attachment data
597
} catch (IOException e) {
598
System.err.println("Attachment too large: " + e.getMessage());
599
}
600
601
// Write to multiple destinations simultaneously
602
FileOutputStream file1 = new FileOutputStream("backup1.dat");
603
FileOutputStream file2 = new FileOutputStream("backup2.dat");
604
OutputStream teeStream = StreamUtils.createTeeStream(file1, file2);
605
606
// Data written to teeStream goes to both files
607
teeStream.write("Hello World".getBytes());
608
teeStream.close(); // Closes both underlying streams
609
```
610
611
## Exception Handling for Utilities
612
613
### Utility Exception Types
614
615
```java { .api }
616
// Configuration errors in factory finding
617
public class FactoryConfigurationError extends Error {
618
public FactoryConfigurationError();
619
public FactoryConfigurationError(String msg);
620
public FactoryConfigurationError(Exception e);
621
public FactoryConfigurationError(String msg, Exception e);
622
623
public Exception getException();
624
}
625
```
626
627
### Error Handling Example
628
629
```java
630
import jakarta.mail.util.*;
631
632
public class SafeUtilityUsage {
633
634
public void safeFactoryLookup() {
635
try {
636
Object factory = FactoryFinder.find("com.example.MailFactory");
637
// Use factory
638
} catch (FactoryConfigurationError e) {
639
System.err.println("Factory configuration error: " + e.getMessage());
640
if (e.getException() != null) {
641
System.err.println("Underlying cause: " + e.getException().getMessage());
642
}
643
// Use fallback implementation
644
useDefaultFactory();
645
}
646
}
647
648
public void safeStreamOperations() {
649
SharedFileInputStream sharedStream = null;
650
try {
651
sharedStream = new SharedFileInputStream("data.txt");
652
653
// Create multiple readers safely
654
InputStream reader1 = sharedStream.newStream(0, 1024);
655
processStreamSafely(reader1);
656
657
} catch (IOException e) {
658
System.err.println("Stream operation failed: " + e.getMessage());
659
} finally {
660
if (sharedStream != null) {
661
try {
662
sharedStream.close();
663
} catch (IOException e) {
664
System.err.println("Error closing stream: " + e.getMessage());
665
}
666
}
667
}
668
}
669
670
private void processStreamSafely(InputStream stream) {
671
try (stream) { // Auto-close
672
byte[] buffer = new byte[1024];
673
int bytesRead = stream.read(buffer);
674
// Process data
675
} catch (IOException e) {
676
System.err.println("Error processing stream: " + e.getMessage());
677
}
678
}
679
680
private void useDefaultFactory() {
681
// Fallback implementation
682
System.out.println("Using default factory implementation");
683
}
684
}
685
```