View Javadoc

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