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

utility-classes-streams.mddocs/

Utility Classes and Streams

Jakarta Mail utility classes provide helper functionality for data sources, shared streams, stream providers, and mail-specific utilities that support the core mail operations.

Data Source Classes

Byte Array Data Source

The ByteArrayDataSource class provides a DataSource implementation backed by a byte array, commonly used for in-memory content.

public class ByteArrayDataSource implements DataSource {
    // Constructors
    public ByteArrayDataSource(byte[] data, String type);
    public ByteArrayDataSource(InputStream is, String type) throws IOException;
    public ByteArrayDataSource(String data, String type);
    
    // DataSource interface methods
    public InputStream getInputStream() throws IOException;
    public OutputStream getOutputStream() throws IOException;
    public String getContentType();
    public String getName();
    
    // Additional methods
    public void setName(String name);
}

Byte Array Data Source Usage Example

import jakarta.mail.util.ByteArrayDataSource;
import jakarta.mail.internet.*;
import jakarta.mail.*;

// Create data source from byte array
byte[] pdfData = loadPdfFile(); // Your PDF data
ByteArrayDataSource dataSource = new ByteArrayDataSource(pdfData, "application/pdf");
dataSource.setName("document.pdf");

// Use in MIME body part
MimeBodyPart attachment = new MimeBodyPart();
attachment.setDataHandler(new DataHandler(dataSource));
attachment.setFileName("document.pdf");
attachment.setDisposition(Part.ATTACHMENT);

// Create from string content
String htmlContent = "<html><body><h1>Hello World</h1></body></html>";
ByteArrayDataSource htmlSource = new ByteArrayDataSource(htmlContent, "text/html");

MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setDataHandler(new DataHandler(htmlSource));

// Create from input stream
InputStream imageStream = getImageInputStream();
ByteArrayDataSource imageSource = new ByteArrayDataSource(imageStream, "image/png");
imageSource.setName("logo.png");

MimeBodyPart imagePart = new MimeBodyPart();
imagePart.setDataHandler(new DataHandler(imageSource));
imagePart.setDisposition(Part.INLINE);
imagePart.setHeader("Content-ID", "<logo>");

Shared Input Stream Classes

Shared input streams allow multiple concurrent readers to access the same underlying data source efficiently.

Shared Input Stream Interface

public interface SharedInputStream {
    // Create new stream for specified range
    public InputStream newStream(long start, long end);
    
    // Get current position in stream
    public long getPosition();
}

Shared File Input Stream

public class SharedFileInputStream extends BufferedInputStream implements SharedInputStream {
    // Constructors
    public SharedFileInputStream(File file) throws IOException;
    public SharedFileInputStream(File file, int size) throws IOException;
    public SharedFileInputStream(String file) throws IOException;
    public SharedFileInputStream(String file, int size) throws IOException;
    
    // SharedInputStream interface methods
    public InputStream newStream(long start, long end);
    public long getPosition();
    
    // File access methods
    protected void finalize() throws Throwable;
}

Shared Byte Array Input Stream

public class SharedByteArrayInputStream extends ByteArrayInputStream implements SharedInputStream {
    // Constructors
    public SharedByteArrayInputStream(byte[] buf);
    public SharedByteArrayInputStream(byte[] buf, int offset, int length);
    
    // SharedInputStream interface methods
    public InputStream newStream(long start, long end);
    public long getPosition();
}

Shared Stream Usage Example

import jakarta.mail.util.*;
import java.io.*;

// Create shared file input stream for large files
File largeFile = new File("large-attachment.zip");
SharedFileInputStream sharedFile = new SharedFileInputStream(largeFile);

// Multiple readers can access different parts concurrently
InputStream reader1 = sharedFile.newStream(0, 1024); // First 1KB
InputStream reader2 = sharedFile.newStream(1024, 2048); // Second 1KB
InputStream reader3 = sharedFile.newStream(2048, -1); // Rest of file

// Read concurrently without blocking
CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
    try {
        processStream(reader1);
    } catch (IOException e) {
        e.printStackTrace();
    }
});

CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
    try {
        processStream(reader2);
    } catch (IOException e) {
        e.printStackTrace();
    }
});

// Shared byte array for in-memory data
byte[] data = loadLargeData();
SharedByteArrayInputStream sharedBytes = new SharedByteArrayInputStream(data);

// Create multiple streams from same data
InputStream stream1 = sharedBytes.newStream(0, 100);
InputStream stream2 = sharedBytes.newStream(100, 200);

// Each stream is independent
int data1 = stream1.read(); // Reads from position 0
int data2 = stream2.read(); // Reads from position 100

Line-Oriented Stream Interfaces

Line Input Stream Interface

public interface LineInputStream {
    // Read a line of text, null if end of stream
    public String readLine() throws IOException;
}

Line Output Stream Interface

public interface LineOutputStream {
    // Write a line of text followed by line terminator
    public void writeln(String s) throws IOException;
    
    // Write line terminator only
    public void writeln() throws IOException;
}

Line Stream Usage Example

import jakarta.mail.util.*;
import java.io.*;

// Implement line-oriented processing
public class MailHeaderProcessor {
    
    public void processHeaders(InputStream input) throws IOException {
        if (input instanceof LineInputStream) {
            LineInputStream lineInput = (LineInputStream) input;
            String line;
            
            while ((line = lineInput.readLine()) != null) {
                if (line.trim().isEmpty()) {
                    break; // End of headers
                }
                processHeaderLine(line);
            }
        } else {
            // Wrap in BufferedReader for line reading
            BufferedReader reader = new BufferedReader(new InputStreamReader(input));
            String line;
            
            while ((line = reader.readLine()) != null) {
                if (line.trim().isEmpty()) {
                    break;
                }
                processHeaderLine(line);
            }
        }
    }
    
    public void writeHeaders(OutputStream output, Map<String, String> headers) throws IOException {
        if (output instanceof LineOutputStream) {
            LineOutputStream lineOutput = (LineOutputStream) output;
            
            for (Map.Entry<String, String> header : headers.entrySet()) {
                lineOutput.writeln(header.getKey() + ": " + header.getValue());
            }
            lineOutput.writeln(); // Empty line to end headers
        } else {
            PrintWriter writer = new PrintWriter(output);
            
            for (Map.Entry<String, String> header : headers.entrySet()) {
                writer.println(header.getKey() + ": " + header.getValue());
            }
            writer.println();
            writer.flush();
        }
    }
    
    private void processHeaderLine(String line) {
        System.out.println("Processing header: " + line);
    }
}

Stream Provider Interface

The StreamProvider interface allows for pluggable stream implementations.

public interface StreamProvider {
    // Get input stream for reading
    public InputStream getInputStream() throws IOException;
    
    // Get output stream for writing
    public OutputStream getOutputStream() throws IOException;
}

Stream Provider Usage Example

import jakarta.mail.util.StreamProvider;
import java.io.*;
import java.util.ServiceLoader;

// Custom stream provider implementation
public class CustomStreamProvider implements StreamProvider {
    private final File dataFile;
    
    public CustomStreamProvider(File dataFile) {
        this.dataFile = dataFile;
    }
    
    @Override
    public InputStream getInputStream() throws IOException {
        return new BufferedInputStream(new FileInputStream(dataFile));
    }
    
    @Override
    public OutputStream getOutputStream() throws IOException {
        return new BufferedOutputStream(new FileOutputStream(dataFile));
    }
}

// Load stream providers via ServiceLoader
ServiceLoader<StreamProvider> providers = ServiceLoader.load(StreamProvider.class);
for (StreamProvider provider : providers) {
    try (InputStream is = provider.getInputStream()) {
        // Use stream from provider
        processStream(is);
    }
}

// Use custom provider
StreamProvider customProvider = new CustomStreamProvider(new File("data.txt"));
try (OutputStream os = customProvider.getOutputStream()) {
    os.write("Hello World".getBytes());
}

Factory Finder Utility

The FactoryFinder class provides service loading capabilities for mail-related factories.

public class FactoryFinder {
    // Find factory implementation by factory ID
    public static Object find(String factoryId) throws FactoryConfigurationError;
    public static Object find(String factoryId, String fallbackClassName) throws FactoryConfigurationError;
    
    // Find factory with specific class loader
    public static Object find(String factoryId, String fallbackClassName, ClassLoader cl) throws FactoryConfigurationError;
    
    // Create new instance of factory
    public static Object newInstance(String className) throws FactoryConfigurationError;
    public static Object newInstance(String className, ClassLoader cl) throws FactoryConfigurationError;
}

Factory Finder Usage Example

import jakarta.mail.util.FactoryFinder;

// Find mail transport factory
try {
    Object transportFactory = FactoryFinder.find("jakarta.mail.Transport", 
                                                 "com.sun.mail.smtp.SMTPTransport");
    System.out.println("Found transport factory: " + transportFactory.getClass().getName());
} catch (FactoryConfigurationError e) {
    System.err.println("Failed to find transport factory: " + e.getMessage());
}

// Find store factory with fallback
try {
    Object storeFactory = FactoryFinder.find("jakarta.mail.Store", 
                                            "com.sun.mail.imap.IMAPStore");
    System.out.println("Found store factory: " + storeFactory.getClass().getName());
} catch (FactoryConfigurationError e) {
    System.err.println("Failed to find store factory: " + e.getMessage());
}

// Create instance directly
try {
    Object instance = FactoryFinder.newInstance("com.example.CustomMailProcessor");
    if (instance instanceof MailProcessor) {
        MailProcessor processor = (MailProcessor) instance;
        processor.process();
    }
} catch (FactoryConfigurationError e) {
    System.err.println("Failed to create instance: " + e.getMessage());
}

Mail Logger Utility

The MailLogger class provides logging functionality specifically designed for mail operations.

public class MailLogger {
    // Constructors
    public MailLogger(String name, String defaultDebug, boolean debug, PrintStream out);
    public MailLogger(String name, String defaultDebug, Session session);
    
    // Logging methods
    public void log(Level level, String msg);
    public void log(Level level, String msg, Object param1);
    public void log(Level level, String msg, Object... params);
    
    // Configuration access
    public boolean isLoggable(Level level);
    public void config(String msg);
    public void fine(String msg);
    public void finer(String msg);
    public void finest(String msg);
    
    // Session-based logging
    public static MailLogger getLogger(String name, String defaultDebug, Session session);
    public static MailLogger getLogger(String name, String defaultDebug, String debug, boolean debugOut, PrintStream out);
}

Mail Logger Usage Example

import jakarta.mail.MailLogger;
import jakarta.mail.Session;
import java.util.logging.Level;

// Create logger for mail session
Properties props = new Properties();
props.put("mail.debug", "true");
Session session = Session.getInstance(props);

MailLogger logger = MailLogger.getLogger("jakarta.mail.transport", "mail.debug", session);

// Log mail operations
logger.fine("Connecting to SMTP server");
logger.config("Using SSL connection");
logger.log(Level.INFO, "Message sent successfully to {0} recipients", 5);

// Check if logging is enabled
if (logger.isLoggable(Level.FINE)) {
    logger.fine("Detailed connection information: " + getConnectionDetails());
}

// Different log levels
logger.finest("Trace level message");
logger.finer("Debug level message");
logger.fine("Fine level message");
logger.config("Configuration message");
logger.log(Level.INFO, "Info level message");
logger.log(Level.WARNING, "Warning message");
logger.log(Level.SEVERE, "Error message");

MIME Utilities (Additional Functions)

Beyond the core MimeUtility class, there are additional utility functions for MIME handling.

MIME Utility Extensions

public class MimeUtilityExtensions {
    
    // Content type utilities
    public static boolean isMultipart(String contentType) {
        return contentType != null && contentType.toLowerCase().startsWith("multipart/");
    }
    
    public static boolean isText(String contentType) {
        return contentType != null && contentType.toLowerCase().startsWith("text/");
    }
    
    public static String extractBoundary(String contentType) {
        if (contentType == null) return null;
        
        int boundaryIndex = contentType.toLowerCase().indexOf("boundary=");
        if (boundaryIndex == -1) return null;
        
        String boundary = contentType.substring(boundaryIndex + 9);
        if (boundary.startsWith("\"") && boundary.endsWith("\"")) {
            boundary = boundary.substring(1, boundary.length() - 1);
        }
        
        return boundary;
    }
    
    // Charset utilities
    public static String getCharset(String contentType, String defaultCharset) {
        if (contentType == null) return defaultCharset;
        
        int charsetIndex = contentType.toLowerCase().indexOf("charset=");
        if (charsetIndex == -1) return defaultCharset;
        
        String charset = contentType.substring(charsetIndex + 8);
        int semicolonIndex = charset.indexOf(';');
        if (semicolonIndex != -1) {
            charset = charset.substring(0, semicolonIndex);
        }
        
        charset = charset.trim();
        if (charset.startsWith("\"") && charset.endsWith("\"")) {
            charset = charset.substring(1, charset.length() - 1);
        }
        
        return charset;
    }
    
    // File extension to MIME type mapping
    public static String getContentTypeForFile(String filename) {
        if (filename == null) return "application/octet-stream";
        
        String extension = getFileExtension(filename).toLowerCase();
        
        switch (extension) {
            case "txt": return "text/plain";
            case "html": case "htm": return "text/html";
            case "pdf": return "application/pdf";
            case "doc": return "application/msword";
            case "docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case "xls": return "application/vnd.ms-excel";
            case "xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "jpg": case "jpeg": return "image/jpeg";
            case "png": return "image/png";
            case "gif": return "image/gif";
            case "zip": return "application/zip";
            case "json": return "application/json";
            case "xml": return "application/xml";
            default: return "application/octet-stream";
        }
    }
    
    private static String getFileExtension(String filename) {
        int lastDot = filename.lastIndexOf('.');
        return lastDot == -1 ? "" : filename.substring(lastDot + 1);
    }
}

Stream Processing Utilities

Stream Copying and Processing

public class StreamUtils {
    
    // Copy stream with progress tracking
    public static long copyStream(InputStream input, OutputStream output, 
                                 ProgressCallback callback) throws IOException {
        byte[] buffer = new byte[8192];
        long totalBytes = 0;
        int bytesRead;
        
        while ((bytesRead = input.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);
            totalBytes += bytesRead;
            
            if (callback != null) {
                callback.onProgress(totalBytes);
            }
        }
        
        return totalBytes;
    }
    
    // Read stream to byte array with size limit
    public static byte[] readStreamToBytes(InputStream input, int maxSize) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        byte[] temp = new byte[1024];
        int totalRead = 0;
        int bytesRead;
        
        while ((bytesRead = input.read(temp)) != -1) {
            if (totalRead + bytesRead > maxSize) {
                throw new IOException("Stream size exceeds maximum allowed: " + maxSize);
            }
            
            buffer.write(temp, 0, bytesRead);
            totalRead += bytesRead;
        }
        
        return buffer.toByteArray();
    }
    
    // Create tee stream that writes to multiple outputs
    public static OutputStream createTeeStream(OutputStream... outputs) {
        return new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                for (OutputStream output : outputs) {
                    output.write(b);
                }
            }
            
            @Override
            public void write(byte[] b, int off, int len) throws IOException {
                for (OutputStream output : outputs) {
                    output.write(b, off, len);
                }
            }
            
            @Override
            public void flush() throws IOException {
                for (OutputStream output : outputs) {
                    output.flush();
                }
            }
            
            @Override
            public void close() throws IOException {
                for (OutputStream output : outputs) {
                    output.close();
                }
            }
        };
    }
    
    public interface ProgressCallback {
        void onProgress(long bytesProcessed);
    }
}

Stream Usage Example

import java.io.*;

// Copy large file with progress tracking
FileInputStream input = new FileInputStream("large-file.dat");
FileOutputStream output = new FileOutputStream("copy.dat");

StreamUtils.copyStream(input, output, bytesProcessed -> {
    System.out.println("Copied " + bytesProcessed + " bytes");
});

// Read attachment with size limit (10MB max)
try {
    byte[] attachmentData = StreamUtils.readStreamToBytes(attachmentStream, 10 * 1024 * 1024);
    // Process attachment data
} catch (IOException e) {
    System.err.println("Attachment too large: " + e.getMessage());
}

// Write to multiple destinations simultaneously
FileOutputStream file1 = new FileOutputStream("backup1.dat");
FileOutputStream file2 = new FileOutputStream("backup2.dat");
OutputStream teeStream = StreamUtils.createTeeStream(file1, file2);

// Data written to teeStream goes to both files
teeStream.write("Hello World".getBytes());
teeStream.close(); // Closes both underlying streams

Exception Handling for Utilities

Utility Exception Types

// Configuration errors in factory finding
public class FactoryConfigurationError extends Error {
    public FactoryConfigurationError();
    public FactoryConfigurationError(String msg);
    public FactoryConfigurationError(Exception e);
    public FactoryConfigurationError(String msg, Exception e);
    
    public Exception getException();
}

Error Handling Example

import jakarta.mail.util.*;

public class SafeUtilityUsage {
    
    public void safeFactoryLookup() {
        try {
            Object factory = FactoryFinder.find("com.example.MailFactory");
            // Use factory
        } catch (FactoryConfigurationError e) {
            System.err.println("Factory configuration error: " + e.getMessage());
            if (e.getException() != null) {
                System.err.println("Underlying cause: " + e.getException().getMessage());
            }
            // Use fallback implementation
            useDefaultFactory();
        }
    }
    
    public void safeStreamOperations() {
        SharedFileInputStream sharedStream = null;
        try {
            sharedStream = new SharedFileInputStream("data.txt");
            
            // Create multiple readers safely
            InputStream reader1 = sharedStream.newStream(0, 1024);
            processStreamSafely(reader1);
            
        } catch (IOException e) {
            System.err.println("Stream operation failed: " + e.getMessage());
        } finally {
            if (sharedStream != null) {
                try {
                    sharedStream.close();
                } catch (IOException e) {
                    System.err.println("Error closing stream: " + e.getMessage());
                }
            }
        }
    }
    
    private void processStreamSafely(InputStream stream) {
        try (stream) { // Auto-close
            byte[] buffer = new byte[1024];
            int bytesRead = stream.read(buffer);
            // Process data
        } catch (IOException e) {
            System.err.println("Error processing stream: " + e.getMessage());
        }
    }
    
    private void useDefaultFactory() {
        // Fallback implementation
        System.out.println("Using default factory implementation");
    }
}

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