View Javadoc

1   /*
2    * $Id: RetrieveMessageReceiver.java 11343 2008-03-13 10:58:26Z tcarlson $
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.transport.email;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.MuleServer;
15  import org.mule.api.MuleException;
16  import org.mule.api.MuleMessage;
17  import org.mule.api.endpoint.InboundEndpoint;
18  import org.mule.api.lifecycle.CreateException;
19  import org.mule.api.lifecycle.Startable;
20  import org.mule.api.lifecycle.Stoppable;
21  import org.mule.api.routing.RoutingException;
22  import org.mule.api.service.Service;
23  import org.mule.api.transport.Connector;
24  import org.mule.api.transport.ReceiveException;
25  import org.mule.transport.AbstractPollingMessageReceiver;
26  import org.mule.transport.email.i18n.EmailMessages;
27  import org.mule.util.FileUtils;
28  import org.mule.util.StringUtils;
29  import org.mule.util.UUID;
30  
31  import java.io.File;
32  import java.io.FileOutputStream;
33  import java.io.IOException;
34  
35  import javax.mail.Address;
36  import javax.mail.Flags;
37  import javax.mail.Folder;
38  import javax.mail.Message;
39  import javax.mail.MessagingException;
40  import javax.mail.Store;
41  import javax.mail.event.MessageCountEvent;
42  import javax.mail.event.MessageCountListener;
43  import javax.mail.internet.InternetAddress;
44  import javax.mail.internet.MimeMessage;
45  
46  /**
47   * Poll a mailbox for messages, remove the messages and route them as events into Mule.
48   * <p/>
49   * This contains a reference to a mail folder (and also the endpoint and connector, via superclasses)
50   */
51  
52  public class RetrieveMessageReceiver extends AbstractPollingMessageReceiver
53          implements MessageCountListener, Startable, Stoppable
54  {
55      private Folder folder = null;
56      private boolean backupEnabled;
57      private String backupFolder = null;
58  
59      public RetrieveMessageReceiver(Connector connector,
60                                          Service service,
61                                          InboundEndpoint endpoint,
62                                          long checkFrequency,
63                                          boolean backupEnabled,
64                                          String backupFolder)
65                   throws CreateException
66      {
67          super(connector, service, endpoint);
68               this.backupFolder = backupFolder;
69               this.backupEnabled = backupEnabled;
70               this.setFrequency(checkFrequency);
71           }
72  
73      private AbstractRetrieveMailConnector castConnector()
74      {
75          return (AbstractRetrieveMailConnector) getConnector();
76      }
77  
78      protected void doConnect() throws Exception
79      {
80          SessionDetails session = castConnector().getSessionDetails(endpoint);
81  
82          Store store = session.newStore();
83          store.connect();
84          folder = store.getFolder(castConnector().getMailboxFolder());
85  
86          // set default value if empty/null
87          if (StringUtils.isEmpty(backupFolder))
88          {
89              this.backupFolder =
90                      MuleServer.getMuleContext().getConfiguration().getWorkingDirectory() + "/mail/" + folder.getName();
91          }
92  
93          if (backupFolder != null && !this.backupFolder.endsWith(File.separator))
94          {
95              this.backupFolder += File.separator;
96          }
97      }
98  
99      protected void doDisconnect() throws Exception
100     {
101         // nothing to do here
102     }
103 
104     protected void doStop()
105     {
106         if (folder != null)
107         {
108             folder.removeMessageCountListener(this);
109         }
110     }
111 
112     protected void doStart() throws MuleException
113     {
114         super.doStart();
115         folder.addMessageCountListener(this);
116     }
117 
118     public void messagesAdded(MessageCountEvent event)
119     {
120         Message messages[] = event.getMessages();
121         if (messages != null)
122         {
123             MuleMessage message = null;
124             for (int i = 0; i < messages.length; i++)
125             {
126                 try
127                 {
128                     if (!messages[i].getFlags().contains(Flags.Flag.DELETED))
129                     {
130                         MimeMessage mimeMessage = new MimeMessage((MimeMessage) messages[i]);
131                         storeMessage(mimeMessage);
132                         message = new DefaultMuleMessage(castConnector().getMessageAdapter(mimeMessage));
133 
134                         if (castConnector().isDeleteReadMessages())
135                         {
136                             // Mark as deleted
137                             messages[i].setFlag(Flags.Flag.DELETED, true);
138                         }
139                         else
140                         {
141                             messages[i].setFlag(Flags.Flag.SEEN, true);
142                         }
143                         routeMessage(message, endpoint.isSynchronous());
144                     }
145                 }
146                 catch (MuleException e)
147                 {
148                     handleException(e);
149                 }
150                 catch (Exception e)
151                 {
152                     Exception forwarded;
153 
154                     if (message != null)
155                     {
156                         forwarded = new RoutingException(EmailMessages.routingError(), message, endpoint, e);
157                     }
158                     else
159                     {
160                         forwarded = new ReceiveException(endpoint, -1, e);
161                     }
162 
163                     handleException(forwarded);
164                 }
165             }
166         }
167     }
168 
169     // @Override
170     protected MuleMessage handleUnacceptedFilter(MuleMessage message)
171     {
172         super.handleUnacceptedFilter(message);
173         if (message.getPayload() instanceof Message)
174         {
175             Message msg = (Message) message.getPayload();
176             try
177             {
178                 msg.setFlag(Flags.Flag.DELETED, endpoint.isDeleteUnacceptedMessages());
179             }
180             catch (MessagingException e)
181             {
182                 logger.error("failed to set message deleted: " + e.getMessage(), e);
183             }
184         }
185         return message;
186     }
187 
188     public void messagesRemoved(MessageCountEvent event)
189     {
190         if (logger.isDebugEnabled())
191         {
192             Message messages[] = event.getMessages();
193             for (int i = 0; i < messages.length; i++)
194             {
195                 try
196                 {
197                     logger.debug("Message removed: " + messages[i].getSubject());
198                 }
199                 catch (MessagingException ignore)
200                 {
201                     logger.debug("ignoring exception: " + ignore.getMessage());
202                 }
203             }
204         }
205     }
206 
207     /** @return the current Mail folder */
208     public Folder getFolder()
209     {
210         return folder;
211     }
212 
213     /** @param folder  */
214     public synchronized void setFolder(Folder folder)
215     {
216         if (folder == null)
217         {
218             throw new IllegalArgumentException("Mail folder cannot be null");
219         }
220         this.folder = folder;
221         synchronized (this.folder)
222         {
223             if (!this.folder.isOpen())
224             {
225                 try
226                 {
227                     this.folder.open(Folder.READ_WRITE);
228                 }
229                 catch (MessagingException e)
230                 {
231                     logger.warn("Failed to open folder: " + folder.getFullName(), e);
232                 }
233             }
234         }
235     }
236 
237     /**
238      * Helper method for testing which stores a copy of the message locally as the
239      * POP3 <p/> message will be deleted from the server
240      *
241      * @param msg the message to store
242      * @throws IOException        If a failure happens writing the message
243      * @throws MessagingException If a failure happens reading the message
244      */
245     protected void storeMessage(Message msg) throws IOException, MessagingException
246     {
247         if (backupEnabled)
248         {
249             String filename = msg.getFileName();
250             if (filename == null)
251             {
252                 Address[] from = msg.getFrom();
253                 if (from != null && from.length > 0)
254                 {
255                     filename = from[0] instanceof InternetAddress
256                             ? ((InternetAddress) from[0]).getAddress()
257                             : from[0].toString();
258                 }
259                 else
260                 {
261                     filename = "(no from address)";
262                 }
263                 filename += "[" + UUID.getUUID() + "]";
264             }
265             filename = FileUtils.prepareWinFilename(filename);
266             filename = backupFolder + filename + ".msg";
267             if (logger.isDebugEnabled())
268             {
269                 logger.debug("Writing message to: " + filename);
270             }
271             File f = FileUtils.createFile(filename);
272             FileOutputStream fos = new FileOutputStream(f);
273             msg.writeTo(fos);
274         }
275     }
276 
277     public synchronized void poll()
278     {
279         try
280         {
281             try
282             {
283                 if (!folder.isOpen())
284                 {
285                     folder.open(Folder.READ_WRITE);
286                 }
287             }
288             catch (Exception e)
289             {
290                 if (logger.isDebugEnabled())
291                 {
292                     logger.debug("ignoring exception: " + e.getMessage());
293                 }
294             }
295 
296             int count = folder.getMessageCount();
297             if (count > 0)
298             {
299                 Message[] messages = folder.getMessages();
300                 MessageCountEvent event = new MessageCountEvent(folder, MessageCountEvent.ADDED, true,
301                         messages);
302                 messagesAdded(event);
303             }
304             else if (count == -1)
305             {
306                 throw new MessagingException("Cannot monitor folder: " + folder.getFullName()
307                         + " as folder is closed");
308             }
309         }
310         catch (MessagingException e)
311         {
312             handleException(e);
313         }
314         finally
315         {
316             try
317             {
318                 folder.close(true); // close and expunge deleted messages
319             }
320             catch (Exception e)
321             {
322                 logger.error("Failed to close pop3  inbox: " + e.getMessage());
323             }
324         }
325     }
326 
327     protected void doDispose()
328     {
329         if (null != folder)
330         {
331             folder.removeMessageCountListener(this);
332             if (folder.isOpen())
333             {
334                 try
335                 {
336 
337                     folder.close(true);
338                 }
339                 catch (Exception e)
340                 {
341                     logger.debug("ignoring exception: " + e.getMessage(), e);
342                 }
343             }
344         }
345     }
346 
347 }