FileDocCategorySizeDatePackage
MimeMessageWrapper.javaAPI DocApache James 2.3.119029Fri Jan 12 12:56:24 GMT 2007org.apache.james.core

MimeMessageWrapper

public class MimeMessageWrapper extends MimeMessage implements org.apache.avalon.framework.activity.Disposable
This object wraps a MimeMessage, only loading the underlying MimeMessage object when needed. Also tracks if changes were made to reduce unnecessary saves.

Fields Summary
protected MimeMessageSource
source
Can provide an input stream to the data
protected boolean
messageParsed
This is false until we parse the message
protected boolean
headersModified
This is false until we parse the message
protected boolean
bodyModified
This is false until we parse the message
private InputStream
sourceIn
Keep a reference to the sourceIn so we can close it only when we dispose the message.
Constructors Summary
private MimeMessageWrapper(Session session)


         
        super(session);
        this.headers = null;
        this.modified = false;
        this.headersModified = false;
        this.bodyModified = false;
    
public MimeMessageWrapper(Session session, MimeMessageSource source)
A constructor that instantiates a MimeMessageWrapper based on a MimeMessageSource

param
source the MimeMessageSource
throws
MessagingException

        this(session);
        this.source = source;
    
public MimeMessageWrapper(MimeMessageSource source)
A constructor that instantiates a MimeMessageWrapper based on a MimeMessageSource

param
source the MimeMessageSource
throws
MessagingException
throws
MessagingException

        this(Session.getDefaultInstance(System.getProperties()),source);
    
public MimeMessageWrapper(MimeMessage original)

        this(Session.getDefaultInstance(System.getProperties()));
        flags = original.getFlags();
        
        // if the original is an unmodified MimeMessageWrapped we clone the headers and
        // take its source.
        /* Temporary commented out because of JAMES-474
        if (original instanceof MimeMessageWrapper && !((MimeMessageWrapper) original).bodyModified) {
            source = ((MimeMessageWrapper) original).source;
            // this probably speed up things
            if (((MimeMessageWrapper) original).headers != null) {
                ByteArrayOutputStream temp = new ByteArrayOutputStream();
                InternetHeaders ih = ((MimeMessageWrapper) original).headers;
                MimeMessageUtil.writeHeadersTo(ih.getAllHeaderLines(),temp);
                headers = createInternetHeaders(new ByteArrayInputStream(temp.toByteArray()));
                headersModified = ((MimeMessageWrapper) original).headersModified;
            }
        }
        */
        
        if (source == null) {
            ByteArrayOutputStream bos;
            int size = original.getSize();
            if (size > 0)
                bos = new ByteArrayOutputStream(size);
            else
                bos = new ByteArrayOutputStream();
            try {
                original.writeTo(bos);
                bos.close();
                SharedByteArrayInputStream bis =
                        new SharedByteArrayInputStream(bos.toByteArray());
                parse(bis);
                bis.close();
                saved = true;
            } catch (IOException ex) {
                // should never happen, but just in case...
                throw new MessagingException("IOException while copying message",
                                ex);
            }
        }
    
Methods Summary
public voidaddHeader(java.lang.String name, java.lang.String value)

        checkModifyHeaders();
        super.addHeader(name, value);
    
public voidaddHeaderLine(java.lang.String line)

        checkModifyHeaders();
        super.addHeaderLine(line);
    
private synchronized voidcheckModifyHeaders()

        // Disable only-header loading optimizations for JAMES-559
        if (!messageParsed) {
            loadMessage();
        }
        // End JAMES-559
        if (headers == null) {
            loadHeaders();
        }
        modified = true;
        saved = false;
        headersModified = true;
    
protected synchronized javax.mail.internet.InternetHeaderscreateInternetHeaders(java.io.InputStream is)
If we already parsed the headers then we simply return the updated ones. Otherwise we parse

see
javax.mail.internet.MimeMessage#createInternetHeaders(java.io.InputStream)

        /* This code is no more needed: see JAMES-570 and new tests
           
         * InternetHeaders can be a bit awkward to work with due to
         * its own internal handling of header order.  This hack may
         * not always be necessary, but for now we are trying to
         * ensure that there is a Return-Path header, even if just a
         * placeholder, so that later, e.g., in LocalDelivery, when we
         * call setHeader, it will remove any other Return-Path
         * headers, and ensure that ours is on the top. addHeader
         * handles header order, but not setHeader. This may change in
         * future JavaMail.  But if there are other Return-Path header
         * values, let's drop our placeholder.

        MailHeaders newHeaders = new MailHeaders(new ByteArrayInputStream((RFC2822Headers.RETURN_PATH + ": placeholder").getBytes()));
        newHeaders.setHeader(RFC2822Headers.RETURN_PATH, null);
        newHeaders.load(is);
        String[] returnPathHeaders = newHeaders.getHeader(RFC2822Headers.RETURN_PATH);
        if (returnPathHeaders.length > 1) newHeaders.setHeader(RFC2822Headers.RETURN_PATH, returnPathHeaders[1]);
        */
        
        // Keep this: skip the headers from the stream
        // we could put that code in the else and simple write an "header" skipping
        // reader for the others.
        MailHeaders newHeaders = new MailHeaders(is);
        
        if (headers != null) {
            return headers;
        } else {
            return newHeaders;
        }
    
public voiddispose()

see
org.apache.avalon.framework.activity.Disposable#dispose()

        if (sourceIn != null) {
            IOUtil.shutdownStream(sourceIn);
        }
        if (source != null) {
            ContainerUtil.dispose(source);
        }
    
public java.util.EnumerationgetAllHeaderLines()

        if (headers == null) {
            loadHeaders();
        }
        return headers.getAllHeaderLines();
    
public java.util.EnumerationgetAllHeaders()

        if (headers == null) {
            loadHeaders();
        }
        return headers.getAllHeaders();
    
protected java.io.InputStreamgetContentStream()

see
javax.mail.internet.MimeMessage#getContentStream()

        if (!messageParsed) {
            loadMessage();
        }
        return super.getContentStream();
    
public java.lang.String[]getHeader(java.lang.String name)
We override all the "headers" access methods to be sure that we loaded the headers

        if (headers == null) {
            loadHeaders();
        }
        return headers.getHeader(name);
    
public java.lang.StringgetHeader(java.lang.String name, java.lang.String delimiter)

        if (headers == null) {
            loadHeaders();
        }
        return headers.getHeader(name, delimiter);
    
public intgetLineCount()
Corrects JavaMail 1.1 version which always returns -1. Only corrected for content less than 5000 bytes, to avoid memory hogging.

            InputStream in=null;
        try{
            in = getContentStream();
        }catch(Exception e){
            return -1;
        }
        if (in == null) {
            return -1;
        }
        //Wrap input stream in LineNumberReader
        //Not sure what encoding to use really...
        try {
            LineNumberReader counter;
            if (getEncoding() != null) {
                counter = new LineNumberReader(new InputStreamReader(in, getEncoding()));
            } else {
                counter = new LineNumberReader(new InputStreamReader(in));
            }
            //Read through all the data
            char[] block = new char[4096];
            while (counter.read(block) > -1) {
                //Just keep reading
            }
            return counter.getLineNumber();
        } catch (IOException ioe) {
            return -1;
        } finally {
            IOUtil.shutdownStream(in);
        }
    
public java.util.EnumerationgetMatchingHeaderLines(java.lang.String[] names)

        if (headers == null) {
            loadHeaders();
        }
        return headers.getMatchingHeaderLines(names);
    
public java.util.EnumerationgetMatchingHeaders(java.lang.String[] names)

        if (headers == null) {
            loadHeaders();
        }
        return headers.getMatchingHeaders(names);
    
public longgetMessageSize()
Returns size of message, ie headers and content

        if (source != null && !isModified()) {
            try {
                return source.getMessageSize();
            } catch (IOException ioe) {
                throw new MessagingException("Error retrieving message size", ioe);
            }
        } else {
            return MimeMessageUtil.calculateMessageSize(this);
        }
    
public java.util.EnumerationgetNonMatchingHeaderLines(java.lang.String[] names)

        if (headers == null) {
            loadHeaders();
        }
        return headers.getNonMatchingHeaderLines(names);
    
public java.util.EnumerationgetNonMatchingHeaders(java.lang.String[] names)

        if (headers == null) {
            loadHeaders();
        }
        return headers.getNonMatchingHeaders(names);
    
public java.io.InputStreamgetRawInputStream()

see
javax.mail.internet.MimeMessage#getRawInputStream()

        if (!messageParsed && !isModified() && source != null) {
            InputStream is;
            try {
                is = source.getInputStream();
                // skip the headers.
                new MailHeaders(is);
                return is;
            } catch (IOException e) {
                throw new MessagingException("Unable to read the stream: " + e.getMessage(), e);
            }
        } else return super.getRawInputStream();
    
public intgetSize()
This is the MimeMessage implementation - this should return ONLY the body, not the entire message (should not count headers). Will have to parse the message.

        if (!messageParsed) {
            loadMessage();
        }
        return super.getSize();
    
public synchronized java.lang.StringgetSourceId()
Returns the source ID of the MimeMessageSource that is supplying this with data.

see
MimeMessageSource

        return source != null ? source.getSourceId() : null;
    
public synchronized booleanisModified()
Get whether the message has been modified.

return
whether the message has been modified

        return headersModified || bodyModified || modified;
    
protected synchronized voidloadHeaders()
Load the message headers from the internal source.

throws
MessagingException if an error is encountered while loading the headers

        if (headers != null) {
            //Another thread has already loaded these headers
            return;
        } else if (source != null) { 
            try {
                InputStream in = source.getInputStream();
                try {
                    headers = createInternetHeaders(in);
                } finally {
                    IOUtil.shutdownStream(in);
                }
            } catch (IOException ioe) {
                throw new MessagingException("Unable to parse headers from stream: " + ioe.getMessage(), ioe);
            }
        } else {
            throw new MessagingException("loadHeaders called for a message with no source, contentStream or stream");
        }
    
protected synchronized voidloadMessage()
Load the complete MimeMessage from the internal source.

throws
MessagingException if an error is encountered while loading the message

        if (messageParsed) {
            //Another thread has already loaded this message
            return;
        } else if (source != null) {
            sourceIn = null;
            try {
                sourceIn = source.getInputStream();
    
                parse(sourceIn);
                // TODO is it ok?
                saved = true;
                
            } catch (IOException ioe) {
                IOUtil.shutdownStream(sourceIn);
                sourceIn = null;
                throw new MessagingException("Unable to parse stream: " + ioe.getMessage(), ioe);
            }
        } else {
            throw new MessagingException("loadHeaders called for an unparsed message with no source");
        }
    
protected synchronized voidparse(java.io.InputStream is)

see
javax.mail.internet.MimeMessage#parse(java.io.InputStream)

        // the super implementation calls
        // headers = createInternetHeaders(is);
        super.parse(is);
        messageParsed = true;
    
public voidremoveHeader(java.lang.String name)

        checkModifyHeaders();
        super.removeHeader(name);
    
public synchronized voidsetDataHandler(javax.activation.DataHandler arg0)
The message is changed when working with headers and when altering the content. Every method that alter the content will fallback to this one.

see
javax.mail.Part#setDataHandler(javax.activation.DataHandler)

        modified = true;
        saved = false;
        bodyModified = true;
        super.setDataHandler(arg0);
    
public voidsetHeader(java.lang.String name, java.lang.String value)

        checkModifyHeaders();
        super.setHeader(name, value);
    
public voidwriteTo(java.io.OutputStream os, java.lang.String[] ignoreList)
Rewritten for optimization purposes

        writeTo(os, os, ignoreList);
    
public voidwriteTo(java.io.OutputStream headerOs, java.io.OutputStream bodyOs)
Write

        writeTo(headerOs, bodyOs, new String[0]);
    
public synchronized voidwriteTo(java.io.OutputStream headerOs, java.io.OutputStream bodyOs, java.lang.String[] ignoreList)

        if (source != null && !isModified()) {
            //We do not want to instantiate the message... just read from source
            //  and write to this outputstream

            //First handle the headers
            InputStream in = source.getInputStream();
            try {
                InternetHeaders headers = new InternetHeaders(in);
                PrintWriter pos = new InternetPrintWriter(new BufferedWriter(new OutputStreamWriter(headerOs), 512), true);
                for (Enumeration e = headers.getNonMatchingHeaderLines(ignoreList); e.hasMoreElements(); ) {
                    String header = (String)e.nextElement();
                    pos.println(header);
                }
                pos.println();
                pos.flush();
                MimeMessageUtil.copyStream(in, bodyOs);
            } finally {
                IOUtil.shutdownStream(in);
            }
        } else {
            MimeMessageUtil.writeToInternal(this, headerOs, bodyOs, ignoreList);
        }
    
public synchronized voidwriteTo(java.io.OutputStream os)
Rewritten for optimization purposes

        if (source != null && !isModified()) {
            // We do not want to instantiate the message... just read from source
            // and write to this outputstream
            InputStream in = source.getInputStream();
            try {
                MimeMessageUtil.copyStream(in, os);
            } finally {
                IOUtil.shutdownStream(in);
            }
        } else {
            writeTo(os, os);
        }