----- 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]