View Javadoc

1   /*
2    * $Id: SimpleMailMessageAdapter.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.providers.email;
12  
13  import org.mule.config.i18n.CoreMessages;
14  import org.mule.impl.ThreadSafeAccess;
15  import org.mule.providers.AbstractMessageAdapter;
16  import org.mule.umo.MessagingException;
17  import org.mule.umo.provider.MessageTypeNotSupportedException;
18  import org.mule.util.IOUtils;
19  import org.mule.util.StringUtils;
20  import org.mule.util.SystemUtils;
21  
22  import java.io.BufferedInputStream;
23  import java.io.BufferedReader;
24  import java.io.ByteArrayOutputStream;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.util.Date;
28  import java.util.Enumeration;
29  import java.util.LinkedList;
30  import java.util.List;
31  
32  import javax.mail.Header;
33  import javax.mail.Message;
34  import javax.mail.Part;
35  
36  /**
37   * <code>SimpleMailMessageAdapter</code> is an adapter for mail messages.  
38   * Unlike {@link MailMessageAdapter} this preserves the message intact in its original 
39   * form.
40   *
41   * <p>Header values are stored in two formats.  First, as historically used by
42   * {@link MailMessageAdapter}, a single String value is stored for each distinct
43   * header name (if a header is repeated only one value is stored).
44   * Secondly, a list of values for each distinct header is stored in a property name
45   * prefixed by HEADER_LIST_PREFIX
46   * (which produces an invalid header name according to RFC 822 and so (i) avoids
47   * conflict with the first property type and (ii) will cause current applications
48   * that wrongly assume all properties are simple header values to fail fast).
49   * The utility methods
50   * {@link #isListHeader(String)}, {@link #toHeader(String)} and
51   * {@link #toListHeader(String)} identify and convert between property and
52   * header names as required.
53   */
54  public class SimpleMailMessageAdapter extends AbstractMessageAdapter
55  {
56  
57      private static final long serialVersionUID = 8002607243523460556L;
58      public static final String HEADER_LIST_PREFIX = "List:";
59      private Part message;
60      private byte[] cache = null;
61  
62      public SimpleMailMessageAdapter(Object object) throws MessagingException 
63      {
64          Message message = assertMessageType(object);
65  
66          try 
67          {
68              setMessageDetails(message);
69              handleMessage(message);
70          } 
71          catch (Exception e) 
72          {
73              throw new MessagingException(CoreMessages.failedToCreate("Message Adapter"), e);
74          }
75      }
76  
77      protected SimpleMailMessageAdapter(SimpleMailMessageAdapter template)
78      {
79          super(template);
80          message = template.message;
81          cache = template.cache;
82      }
83  
84      /**
85       * By default, this simply stores the entire message as a single message.
86       * Sub-classes may override with more complex processing.
87       */
88      protected void handleMessage(Message message) throws Exception 
89      {
90          setMessage(message);
91      }
92  
93      protected void setMessage(Part message) 
94      {
95          this.message = message;
96      }
97  
98      public Object getPayload() {
99          return message;
100     }
101 
102     public byte[] getPayloadAsBytes() throws Exception 
103     {
104         return buildCache(getEncoding());
105     }
106 
107     public String getPayloadAsString(String encoding) throws Exception
108     {
109         // TODO - i don't understand how encoding is used here
110         // could this method be called with various encodings?
111         // does that invalidate the cache?
112         // (ie there are two encodings -- one used to generate the cache from
113         // the mail message, and one used to generate the string from the cache)
114         return new String(buildCache(encoding), encoding);
115     }
116 
117     private byte[] buildCache(String encoding) throws Exception
118     {
119         if (null == cache)
120         {
121             if (message.getContentType().startsWith("text/"))
122             {
123                 cache = textPayload(encoding);
124             }
125             else
126             {
127                 cache = binaryPayload();
128             }
129         }
130         return cache;
131     }
132 
133     private static Message assertMessageType(Object message) throws MessageTypeNotSupportedException
134     {
135         if (message instanceof Message)
136         {
137             return (Message)message;
138         }
139         else
140         {
141             throw new MessageTypeNotSupportedException(message, MailMessageAdapter.class);
142         }
143     }
144 
145     private void setMessageDetails(Message message) throws javax.mail.MessagingException 
146     {
147         setProperty(MailProperties.TO_ADDRESSES_PROPERTY,
148             MailUtils.mailAddressesToString(message.getRecipients(Message.RecipientType.TO)));
149         setProperty(MailProperties.CC_ADDRESSES_PROPERTY,
150             MailUtils.mailAddressesToString(message.getRecipients(Message.RecipientType.CC)));
151         setProperty(MailProperties.BCC_ADDRESSES_PROPERTY,
152             MailUtils.mailAddressesToString(message.getRecipients(Message.RecipientType.BCC)));
153         setProperty(MailProperties.REPLY_TO_ADDRESSES_PROPERTY,
154             MailUtils.mailAddressesToString(message.getReplyTo()));
155         setProperty(MailProperties.FROM_ADDRESS_PROPERTY, 
156             MailUtils.mailAddressesToString(message.getFrom()));
157         setProperty(MailProperties.SUBJECT_PROPERTY, 
158             StringUtils.defaultIfEmpty(message.getSubject(), "(no subject)"));
159         setProperty(MailProperties.CONTENT_TYPE_PROPERTY, 
160             StringUtils.defaultIfEmpty(message.getContentType(), "text/plain"));
161 
162         Date sentDate = message.getSentDate();
163         if (sentDate == null)
164         {
165             sentDate = new Date();
166         }
167         setProperty(MailProperties.SENT_DATE_PROPERTY, sentDate);
168 
169         for (Enumeration e = message.getAllHeaders(); e.hasMoreElements();)
170         {
171             Header header = (Header)e.nextElement();
172             String name = header.getName();
173             String listName = toListHeader(name);
174             String value = header.getValue();
175 
176             if (null == getProperty(name))
177             {
178                 setProperty(name, value);
179             }
180 
181             if (null == getProperty(listName))
182             {
183                 setProperty(listName, new LinkedList());
184             }
185             if (getProperty(listName) instanceof List)
186             {
187                 ((List) getProperty(listName)).add(header.getValue());
188             }
189        }
190     }
191 
192     /**
193      * Check whether a property name has the format associated with a list
194      * of header values
195      * @param name A property name
196      * @return true if the name is associated with a list of header values
197      * (more exactly, if it starts with HEADER_LIST_PREFIX, which gives an
198      * invalid header name according to RFC822).
199      */
200     public static boolean isListHeader(String name)
201     {
202         return null != name && name.startsWith(HEADER_LIST_PREFIX);
203     }
204 
205     /**
206      * Convert a property name associated with a list of header values to
207      * the relevant header name (ie drop the prefix)
208      * @param name A property name
209      * @return The associated header name (ie with HEADER_LIST_PREFIX removed)
210      */
211     public static String toHeader(String name)
212     {
213         if (isListHeader(name))
214         {
215             return name.substring(HEADER_LIST_PREFIX.length());
216         }
217         else
218         {
219             return name;
220         }
221     }
222 
223     /**
224      * Convert a header name to the property name associated with a list of
225      * header values (ie prepend the prefix)
226      * @param header A header name
227      * @return The associated list property name (ie with HEADER_LIST_PREFIX prepended)
228      */
229     public static String toListHeader(String header)
230     {
231         if (isListHeader(header))
232         {
233             return header;
234         }
235         else
236         {
237             return HEADER_LIST_PREFIX + header;
238         }
239     }
240 
241     private static InputStream addBuffer(InputStream stream)
242     {
243         if (!(stream instanceof BufferedInputStream))
244         {
245             stream = new BufferedInputStream(stream);
246         }
247         return stream;
248     }
249 
250     private byte[] binaryPayload() throws Exception 
251     {
252         InputStream stream = addBuffer(message.getInputStream());
253         ByteArrayOutputStream baos = new ByteArrayOutputStream(32768);
254         IOUtils.copy(stream, baos);
255         return baos.toByteArray();
256     }
257 
258     private byte[] textPayload(String encoding) throws Exception 
259     {
260         InputStream stream = addBuffer(message.getInputStream());
261         BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
262         StringBuffer buffer = new StringBuffer(32768);
263 
264         String line;
265         while ((line = reader.readLine()) != null)
266         {
267             buffer.append(line).append(SystemUtils.LINE_SEPARATOR);
268         }
269 
270         return buffer.toString().getBytes(encoding);
271     }
272 
273     public ThreadSafeAccess newThreadCopy()
274     {
275         return new SimpleMailMessageAdapter(this);
276     }
277     
278 }