----- Original Message ----- From: "Stefano Bagnara" <[EMAIL PROTECTED]>
To: <[email protected]>
Cc: <[EMAIL PROTECTED]>
Sent: Monday, January 23, 2006 12:09 PM
Subject: Re: Memory, Large messages and SharedInputStreams (Was: Apache James / JAMES-134)


Ok, that's clear, we must remove calls to that constructor from our code, and we should use new MimeMessage(Session s, InputStream is).
Furthermore we should pass a SharedInputStream to that constructor.

How is it done technically? How to do the conversion from MimeMessage(MimeMessage source) to MimeMessage(Session s, InputStream is) ?

This is what we're trying to find out.

This are my constructors. The entire class is attached with some modifications regarding packages, imports, comments and code modifications enabling file-move-operations. So it can not be used directly, but should be enough for guidance.

Markus

////////////////////////////////////////////////////
////////////////////////////////////////////////////

public MimeMessageWrapper(MimeMessageSource source) throws IOException,
  MessagingException {
 this(Session.getDefaultInstance(System.getProperties(), null), source
   .getInputStream());
 this.source = source;
}

private MimeMessageWrapper(Session session, InputStream is)
  throws MessagingException {
 super(session, is);
 inStream = is;
}
package whatever;

/**
 * ...
 */

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import javax.mail.internet.NewsAddress;

import xyz.util.IOUtil;
import xyz.util.InternetPrintWriter;
import xyz.util.RFC2822Headers;

/**
 * ...
 */
public class MimeMessageWrapper extends MimeMessage {

	/**
	 * Can provide an input stream to the data
	 */
	MimeMessageSource source = null;

	/**
	 * The Internet headers in memory
	 */
	MailHeaders headers = null;

	/**
	 * Records whether a change was made to this message
	 */
	boolean modified = false;

	/**
	 * The source inputstream.
	 */
	private InputStream inStream;

	/**
	 * A constructor that instantiates a MimeMessageWrapper based on a
	 * MimeMessageSource
	 * 
	 * @todo DSN PROPS in session setzen
	 * 
	 * @param source
	 *            the MimeMessageSource
	 */
	public MimeMessageWrapper(MimeMessageSource source) throws IOException,
			MessagingException {

		this(Session.getDefaultInstance(System.getProperties(), null), source
				.getInputStream());

		this.source = source;
	}

	private MimeMessageWrapper(Session session, InputStream is)
			throws MessagingException {
		super(session, is);
		inStream = is;
	}

	/**
	 * Returns the source ID of the MimeMessageSource that is supplying this
	 * with data.
	 * 
	 * @see MimeMessageSource
	 */
	public String getSourceId() {
		return source.getSourceId();
	}

	public MimeMessageSource getSource() {
		return source;
	}

	/**
	 * Internal implementation to get InternetAddress headers
	 */
	private Address[] getAddressHeader(String name) throws MessagingException {
		String addr = getHeader(name, ",");
		if (addr == null) {
			return null;
		} else {
			return InternetAddress.parse(addr);
		}
	}

	/**
	 * Internal implementation to find headers
	 */
	private String getHeaderName(Message.RecipientType recipienttype)
			throws MessagingException {
		String s;
		if (recipienttype == Message.RecipientType.TO) {
			s = RFC2822Headers.TO;
		} else if (recipienttype == Message.RecipientType.CC) {
			s = RFC2822Headers.CC;
		} else if (recipienttype == Message.RecipientType.BCC) {
			s = RFC2822Headers.BCC;
		} else if (recipienttype == RecipientType.NEWSGROUPS) {
			s = "Newsgroups";
		} else {
			throw new MessagingException("Invalid Recipient Type");
		}
		return s;
	}

	/**
	 * Gets whether the message has been modified.
	 * 
	 * @return whether the message has been modified
	 */
	public boolean isModified() {
		return modified;
	}

	/**
	 * Rewritten for optimization purposes
	 */
	public void writeTo(OutputStream os) throws IOException, MessagingException {
		if (!isModified()) {
			// We do not want to instantiate the message... just read from
			// source
			// and write to this outputstream
			InputStream in = source.getInputStream();
			try {
				copyStream(in, os);
			} finally {
				IOUtil.shutdownStream(in);
			}
		} else {
			writeTo(os, os);
		}
	}

	/**
	 * Rewritten for optimization purposes
	 */
	public void writeTo(OutputStream os, String[] ignoreList)
			throws IOException, MessagingException {
		writeTo(os, os, ignoreList);
	}

	/**
	 * Write
	 */
	public void writeTo(OutputStream headerOs, OutputStream bodyOs)
			throws IOException, MessagingException {
		writeTo(headerOs, bodyOs, new String[0]);
	}

	public void writeTo(OutputStream headerOs, OutputStream bodyOs,
			String[] ignoreList) throws IOException, MessagingException {
		if (!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 {
				if (headerOs != null) {
					InternetHeaders iheaders = new InternetHeaders(in);

					// InternetPrintWriter fügt immer "\r\n" jedem print(...)
					// hinzu daher wird aus print - println.
					PrintWriter pos = new InternetPrintWriter(
							new BufferedWriter(
									new OutputStreamWriter(headerOs), 512),
							true);
					for (Enumeration e = iheaders
							.getNonMatchingHeaderLines(ignoreList); e
							.hasMoreElements();) {
						String header = (String) e.nextElement();
						pos.println(header);
					}
					pos.println();
					pos.flush();
				}
				if (bodyOs != null)
					copyStream(in, bodyOs);
			} finally {
				IOUtil.shutdownStream(in);
			}
		}
	}

	/**
	 * Convenience method to take any MimeMessage and write the headers and body
	 * to two different output streams
	 */
	public static void writeTo(MimeMessage message, OutputStream headerOs,
			OutputStream bodyOs) throws IOException, MessagingException {
		writeTo(message, headerOs, bodyOs, null);
	}

	/**
	 * Convenience method to take any MimeMessage and write the headers and body
	 * to two different output streams, with an ignore list
	 */
	public static void writeTo(MimeMessage message, OutputStream headerOs,
			OutputStream bodyOs, String[] ignoreList) throws IOException,
			MessagingException {
		if (message instanceof MimeMessageWrapper) {
			MimeMessageWrapper wrapper = (MimeMessageWrapper) message;
			wrapper.writeTo(headerOs, bodyOs, ignoreList);
		} else {
			if (message.getMessageID() == null) {
				message.saveChanges(); // siehe localdelivery for delails
			}
			if (headerOs != null) {
				// Write the headers (minus ignored ones)
				Enumeration headers = message
						.getNonMatchingHeaderLines(ignoreList);
				PrintWriter hos = new InternetPrintWriter(new BufferedWriter(
						new OutputStreamWriter(headerOs), 512), true);
				while (headers.hasMoreElements()) {
					hos.println((String) headers.nextElement());
				}
				// Print header/data separator
				hos.println();
				hos.flush();
			}
			if (bodyOs != null) {
				InputStream bis = null;
				OutputStream bos = null;
				// Write the body to the output stream

				/*
				 * try { bis = message.getRawInputStream(); bos = bodyOs; }
				 * catch(javax.mail.MessagingException me) { // we may get a "No
				 * content" exception // if that happens, try it the hard way
				 *  // Why, you ask? In JavaMail v1.3, when you initially //
				 * create a message using MimeMessage APIs, there is no // raw
				 * content available. getInputStream() works, but //
				 * getRawInputStream() throws an exception.
				 * 
				 * bos = MimeUtility.encode(bodyOs, message.getEncoding()); bis =
				 * message.getInputStream(); }
				 */

				try {
					// Get the message as a stream. This will encode
					// objects as necessary, and we have some input from
					// decoding an re-encoding the stream. I'd prefer the
					// raw stream, but see
					bos = MimeUtility.encode(bodyOs, message.getEncoding());
					bis = message.getInputStream();
				} catch (javax.activation.UnsupportedDataTypeException udte) {
					/*
					 * If we get an UnsupportedDataTypeException try using the
					 * raw input stream as a "best attempt" at rendering a
					 * message.
					 * 
					 * WARNING: JavaMail v1.3 getRawInputStream() returns
					 * INVALID (unchanged) content for a changed message.
					 * getInputStream() works properly, but in this case has
					 * failed due to a missing DataHandler.
					 * 
					 * MimeMessage.getRawInputStream() may throw a "no content"
					 * MessagingException. In JavaMail v1.3, when you initially
					 * create a message using MimeMessage APIs, there is no raw
					 * content available. getInputStream() works, but
					 * getRawInputStream() throws an exception. If we catch that
					 * exception, throw the UDTE. It should mean that someone
					 * has locally constructed a message part for which JavaMail
					 * doesn't have a DataHandler.
					 */

					try {
						bis = message.getRawInputStream();
						bos = bodyOs;
					} catch (javax.mail.MessagingException _) {
						throw udte;
					}
				} catch (javax.mail.MessagingException me) {
					/*
					 * This could be another kind of MessagingException thrown
					 * by MimeMessage.getInputStream(), such as a
					 * javax.mail.internet.ParseException.
					 * 
					 * The ParseException is precisely one of the reasons why
					 * the getRawInputStream() method exists, so that we can
					 * continue to stream the content, even if we cannot handle
					 * it. Again, if we get an exception, we throw the one that
					 * caused us to call getRawInputStream().
					 */
					try {
						bis = message.getRawInputStream();
						bos = bodyOs;
					} catch (javax.mail.MessagingException _) {
						throw me;
					}
				}

				try {
					copyStream(bis, bos);
				} finally {
					IOUtil.shutdownStream(bis);
				}
			}
		}
	}

	public Address[] getRecipients(Message.RecipientType type)
			throws MessagingException {

		if (type == RecipientType.NEWSGROUPS) {
			String s = headers.getHeader("Newsgroups", ",");
			if (s == null) {
				return null;
			} else {
				return NewsAddress.parse(s);
			}
		} else {
			return getAddressHeader(getHeaderName(type));
		}
	}

	public Address[] getAllRecipients() throws MessagingException {

		Address toAddresses[] = getRecipients(RecipientType.TO);
		Address ccAddresses[] = getRecipients(RecipientType.CC);
		Address bccAddresses[] = getRecipients(RecipientType.BCC);
		Address newsAddresses[] = getRecipients(RecipientType.NEWSGROUPS);
		if (ccAddresses == null && bccAddresses == null
				&& newsAddresses == null) {
			return toAddresses;
		}
		int i = (toAddresses == null ? 0 : toAddresses.length)
				+ (ccAddresses == null ? 0 : ccAddresses.length)
				+ (bccAddresses == null ? 0 : bccAddresses.length)
				+ (newsAddresses == null ? 0 : newsAddresses.length);
		Address allAddresses[] = new Address[i];
		int j = 0;
		if (toAddresses != null) {
			System.arraycopy(toAddresses, 0, allAddresses, j,
					toAddresses.length);
			j += toAddresses.length;
		}
		if (ccAddresses != null) {
			System.arraycopy(ccAddresses, 0, allAddresses, j,
					ccAddresses.length);
			j += ccAddresses.length;
		}
		if (bccAddresses != null) {
			System.arraycopy(bccAddresses, 0, allAddresses, j,
					bccAddresses.length);
			j += bccAddresses.length;
		}
		if (newsAddresses != null) {
			System.arraycopy(newsAddresses, 0, allAddresses, j,
					newsAddresses.length);
			j += newsAddresses.length;
		}
		return allAddresses;
	}

	/**
	 * Corrects JavaMail 1.1 version which always returns -1. Only corrected for
	 * content less than 5000 bytes, to avoid memory hogging.
	 */
	public int getLineCount() throws MessagingException {
		/**
		 * @todo why would some one wants to know this??
		 * 
		 * 
		 * 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 = new LineNumberReader(new
		 * InputStreamReader(in, getEncoding())); //Read through all the data
		 * char[] block = new char[4096]; while (counter.read(block) > -1) {
		 * //Just keep reading } return counter.getLineNumber(); } catch
		 * (IOException ioe) { ioe.printStackTrace(); return -1; } finally {
		 * IOUtil.shutdownStream(in); }
		 */
		return -1;
	}

	/**
	 * Returns size of message, ie headers and content. Current implementation
	 * actually returns number of characters in headers plus number of bytes in
	 * the internal content byte array.
	 */
	public long getMessageSize() throws MessagingException {
		try {
			return source.getMessageSize();
		} catch (IOException ioe) {
			throw new MessagingException("Error retrieving message size", ioe);
		}
	}

	public InputStream getSourceInputStream() throws IOException,
			MessagingException {
		return source.getInputStream();
	}

	/**
	 * Writes content only, ie not headers, to the specified OutputStream.
	 * 
	 * @param outs
	 *            the OutputStream to which the content is written
	 */
	public void writeContentTo(OutputStream outs) throws java.io.IOException,
			MessagingException {

		InputStream in = getContentStream();
		try {
			copyStream(in, outs);
		} finally {
			IOUtil.shutdownStream(in);
		}
	}

	/**
	 * Convenience method to copy streams
	 */
	private static void copyStream(InputStream in, OutputStream out)
			throws IOException {
		// TODO: This is really a bad way to do this sort of thing. A shared
		// buffer to
		// allow simultaneous read/writes would be a substantial improvement
		byte[] block = new byte[1024];
		int read = 0;
		while ((read = in.read(block)) > -1) {
			out.write(block, 0, read);
		}
		out.flush();
	}

	// called by constructor
	protected InternetHeaders createInternetHeaders(InputStream is)
			throws MessagingException {
		headers = new MailHeaders(is);
		return headers; // new InternetHeaders(is);
	}

	// public byte[] getbytes() {
	// return this.content;
	// }

	/**
	 * Closes all open streams pointing to this MimeMessageSource. Further
	 * operations on this message require reopenStreams() to be called.
	 */
	public void closeStreams() {
		try {
			inStream.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Opens an InputStream after it has been closed by closeStreams().
	 */
	public void reopenStreams() {
		if (source != null) {
			try {
				inStream = source.getInputStream();
				parse(inStream);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	}
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to