001: /*
002: * $Id: SimpleMailMessageAdapter.java 10489 2008-01-23 17:53:38Z dfeist $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.transport.email;
012:
013: import org.mule.api.MessagingException;
014: import org.mule.api.ThreadSafeAccess;
015: import org.mule.api.transport.MessageTypeNotSupportedException;
016: import org.mule.config.i18n.CoreMessages;
017: import org.mule.transport.AbstractMessageAdapter;
018: import org.mule.util.IOUtils;
019: import org.mule.util.StringUtils;
020: import org.mule.util.SystemUtils;
021:
022: import java.io.BufferedInputStream;
023: import java.io.BufferedReader;
024: import java.io.ByteArrayOutputStream;
025: import java.io.InputStream;
026: import java.io.InputStreamReader;
027: import java.util.Date;
028: import java.util.Enumeration;
029: import java.util.LinkedList;
030: import java.util.List;
031:
032: import javax.mail.Header;
033: import javax.mail.Message;
034: import javax.mail.Part;
035:
036: /**
037: * <code>SimpleMailMessageAdapter</code> is an adapter for mail messages.
038: * Unlike {@link MailMessageAdapter} this preserves the message intact in its original
039: * form.
040: *
041: * <p>Header values are stored in two formats. First, as historically used by
042: * {@link MailMessageAdapter}, a single String value is stored for each distinct
043: * header name (if a header is repeated only one value is stored).
044: * Secondly, a list of values for each distinct header is stored in a property name
045: * prefixed by HEADER_LIST_PREFIX
046: * (which produces an invalid header name according to RFC 822 and so (i) avoids
047: * conflict with the first property type and (ii) will cause current applications
048: * that wrongly assume all properties are simple header values to fail fast).
049: * The utility methods
050: * {@link #isListHeader(String)}, {@link #toHeader(String)} and
051: * {@link #toListHeader(String)} identify and convert between property and
052: * header names as required.
053: */
054: public class SimpleMailMessageAdapter extends AbstractMessageAdapter {
055:
056: private static final long serialVersionUID = 8002607243523460556L;
057: public static final String HEADER_LIST_PREFIX = "List:";
058: private Part message;
059: private byte[] cache = null;
060:
061: public SimpleMailMessageAdapter(Object object)
062: throws MessagingException {
063: Message message = assertMessageType(object);
064:
065: try {
066: setMessageDetails(message);
067: handleMessage(message);
068: } catch (Exception e) {
069: throw new MessagingException(CoreMessages
070: .failedToCreate("Message Adapter"), e);
071: }
072: }
073:
074: protected SimpleMailMessageAdapter(SimpleMailMessageAdapter template) {
075: super (template);
076: message = template.message;
077: cache = template.cache;
078: }
079:
080: /**
081: * By default, this simply stores the entire message as a single message.
082: * Sub-classes may override with more complex processing.
083: */
084: protected void handleMessage(Message message) throws Exception {
085: setMessage(message);
086: }
087:
088: protected void setMessage(Part message) {
089: this .message = message;
090: }
091:
092: public Object getPayload() {
093: return message;
094: }
095:
096: public byte[] getPayloadAsBytes() throws Exception {
097: return buildCache(getEncoding());
098: }
099:
100: public String getPayloadAsString(String encoding) throws Exception {
101: // TODO - i don't understand how encoding is used here
102: // could this method be called with various encodings?
103: // does that invalidate the cache?
104: // (ie there are two encodings -- one used to generate the cache from
105: // the mail message, and one used to generate the string from the cache)
106: return new String(buildCache(encoding), encoding);
107: }
108:
109: private byte[] buildCache(String encoding) throws Exception {
110: if (null == cache) {
111: if (message.getContentType().startsWith("text/")) {
112: cache = textPayload(encoding);
113: } else {
114: cache = binaryPayload();
115: }
116: }
117: return cache;
118: }
119:
120: private static Message assertMessageType(Object message)
121: throws MessageTypeNotSupportedException {
122: if (message instanceof Message) {
123: return (Message) message;
124: } else {
125: throw new MessageTypeNotSupportedException(message,
126: MailMessageAdapter.class);
127: }
128: }
129:
130: private void setMessageDetails(Message message)
131: throws javax.mail.MessagingException {
132: setProperty(MailProperties.INBOUND_TO_ADDRESSES_PROPERTY,
133: MailUtils.mailAddressesToString(message
134: .getRecipients(Message.RecipientType.TO)));
135: setProperty(MailProperties.INBOUND_CC_ADDRESSES_PROPERTY,
136: MailUtils.mailAddressesToString(message
137: .getRecipients(Message.RecipientType.CC)));
138: setProperty(MailProperties.INBOUND_BCC_ADDRESSES_PROPERTY,
139: MailUtils.mailAddressesToString(message
140: .getRecipients(Message.RecipientType.BCC)));
141: try {
142: setProperty(
143: MailProperties.INBOUND_REPLY_TO_ADDRESSES_PROPERTY,
144: MailUtils.mailAddressesToString(message
145: .getReplyTo()));
146: } catch (javax.mail.MessagingException me) {
147: logger.warn("Invalid address found in ReplyTo header:", me);
148: }
149:
150: try {
151: setProperty(MailProperties.INBOUND_FROM_ADDRESS_PROPERTY,
152: MailUtils.mailAddressesToString(message.getFrom()));
153: } catch (javax.mail.MessagingException me) {
154: logger.warn("Invalid address found in From header:", me);
155: }
156:
157: setProperty(MailProperties.INBOUND_SUBJECT_PROPERTY,
158: StringUtils.defaultIfEmpty(message.getSubject(),
159: "(no subject)"));
160: setProperty(MailProperties.INBOUND_CONTENT_TYPE_PROPERTY,
161: StringUtils.defaultIfEmpty(message.getContentType(),
162: "text/plain"));
163:
164: Date sentDate = message.getSentDate();
165: if (sentDate == null) {
166: sentDate = new Date();
167: }
168: setProperty(MailProperties.SENT_DATE_PROPERTY, sentDate);
169:
170: for (Enumeration e = message.getAllHeaders(); e
171: .hasMoreElements();) {
172: Header header = (Header) e.nextElement();
173: String name = header.getName();
174: String listName = toListHeader(name);
175: String value = header.getValue();
176:
177: if (null == getProperty(name)) {
178: setProperty(name, value);
179: }
180:
181: if (null == getProperty(listName)) {
182: setProperty(listName, new LinkedList());
183: }
184: if (getProperty(listName) instanceof List) {
185: ((List) getProperty(listName)).add(header.getValue());
186: }
187: }
188: }
189:
190: /**
191: * Check whether a property name has the format associated with a list
192: * of header values
193: * @param name A property name
194: * @return true if the name is associated with a list of header values
195: * (more exactly, if it starts with HEADER_LIST_PREFIX, which gives an
196: * invalid header name according to RFC822).
197: */
198: public static boolean isListHeader(String name) {
199: return null != name && name.startsWith(HEADER_LIST_PREFIX);
200: }
201:
202: /**
203: * Convert a property name associated with a list of header values to
204: * the relevant header name (ie drop the prefix)
205: * @param name A property name
206: * @return The associated header name (ie with HEADER_LIST_PREFIX removed)
207: */
208: public static String toHeader(String name) {
209: if (isListHeader(name)) {
210: return name.substring(HEADER_LIST_PREFIX.length());
211: } else {
212: return name;
213: }
214: }
215:
216: /**
217: * Convert a header name to the property name associated with a list of
218: * header values (ie prepend the prefix)
219: * @param header A header name
220: * @return The associated list property name (ie with HEADER_LIST_PREFIX prepended)
221: */
222: public static String toListHeader(String header) {
223: if (isListHeader(header)) {
224: return header;
225: } else {
226: return HEADER_LIST_PREFIX + header;
227: }
228: }
229:
230: private static InputStream addBuffer(InputStream stream) {
231: if (!(stream instanceof BufferedInputStream)) {
232: stream = new BufferedInputStream(stream);
233: }
234: return stream;
235: }
236:
237: private byte[] binaryPayload() throws Exception {
238: InputStream stream = addBuffer(message.getInputStream());
239: ByteArrayOutputStream baos = new ByteArrayOutputStream(32768);
240: IOUtils.copy(stream, baos);
241: return baos.toByteArray();
242: }
243:
244: private byte[] textPayload(String encoding) throws Exception {
245: InputStream stream = addBuffer(message.getInputStream());
246: BufferedReader reader = new BufferedReader(
247: new InputStreamReader(stream));
248: StringBuffer buffer = new StringBuffer(32768);
249:
250: String line;
251: while ((line = reader.readLine()) != null) {
252: buffer.append(line).append(SystemUtils.LINE_SEPARATOR);
253: }
254:
255: return buffer.toString().getBytes(encoding);
256: }
257:
258: public ThreadSafeAccess newThreadCopy() {
259: return new SimpleMailMessageAdapter(this);
260: }
261: }
|