View Javadoc

1   /*
2    * $Id: RetrieveMessageReceiver.java 20799 2010-12-19 05:56:37Z dirk.olmes $
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 
160                         routeMessage(message);
161                     }
162                 }
163                 catch (MuleException e)
164                 {
165                     getConnector().getMuleContext().getExceptionListener().handleException(e);
166                 }
167                 catch (Exception e)
168                 {
169                     Exception forwarded;
170 
171                     if (message != null)
172                     {
173                         forwarded = new org.mule.api.MessagingException(EmailMessages.routingError(), message, e);
174                     }
175                     else
176                     {
177                         forwarded = new ReceiveException(endpoint, -1, e);
178                     }
179 
180                     getConnector().getMuleContext().getExceptionListener().handleException(forwarded);
181                 }
182             }
183             // Lets move all messages in one go
184             if (moveToFolder != null)
185             {
186                 try
187                 {
188                     folder.copyMessages(messages, moveToFolder);
189                 }
190                 catch (MessagingException e)
191                 {
192                     getConnector().getMuleContext().getExceptionListener().handleException(e);
193                 }
194             }
195         }
196     }
197 
198     public void messagesRemoved(MessageCountEvent event)
199     {
200         if (logger.isDebugEnabled())
201         {
202             Message messages[] = event.getMessages();
203             for (int i = 0; i < messages.length; i++)
204             {
205                 try
206                 {
207                     logger.debug("Message removed: " + messages[i].getSubject());
208                 }
209                 catch (MessagingException ignore)
210                 {
211                     logger.debug("ignoring exception: " + ignore.getMessage());
212                 }
213             }
214         }
215     }
216 
217     /** @return the current Mail folder */
218     public Folder getFolder()
219     {
220         return folder;
221     }
222 
223     /** @param folder */
224     public synchronized void setFolder(Folder folder)
225     {
226         if (folder == null)
227         {
228             throw new IllegalArgumentException("Mail folder cannot be null");
229         }
230         this.folder = folder;
231         synchronized (this.folder)
232         {
233             if (!this.folder.isOpen())
234             {
235                 try
236                 {
237                     this.folder.open(Folder.READ_WRITE);
238                 }
239                 catch (MessagingException e)
240                 {
241                     logger.warn("Failed to open folder: " + folder.getFullName(), e);
242                 }
243             }
244         }
245     }
246 
247     /**
248      * Helper method for testing which stores a copy of the message locally as the
249      * POP3
250      * <p/>
251      * message will be deleted from the server
252      *
253      * @param msg the message to store
254      * @throws IOException If a failure happens writing the message
255      * @throws MessagingException If a failure happens reading the message
256      */
257     protected void storeMessage(Message msg) throws IOException, MessagingException
258     {
259         if (backupEnabled)
260         {
261             String filename = msg.getFileName();
262             if (filename == null)
263             {
264                 Address[] from = msg.getFrom();
265                 if (from != null && from.length > 0)
266                 {
267                     filename = from[0] instanceof InternetAddress
268                                                                  ? ((InternetAddress) from[0]).getAddress()
269                                                                  : from[0].toString();
270                 }
271                 else
272                 {
273                     filename = "(no from address)";
274                 }
275                 filename += "[" + UUID.getUUID() + "]";
276             }
277             filename = FileUtils.prepareWinFilename(filename);
278             filename = backupFolder + filename + ".msg";
279             if (logger.isDebugEnabled())
280             {
281                 logger.debug("Writing message to: " + filename);
282             }
283             File f = FileUtils.createFile(filename);
284             FileOutputStream fos = new FileOutputStream(f);
285             msg.writeTo(fos);
286         }
287     }
288 
289     @Override
290     public synchronized void poll()
291     {
292         try
293         {
294             try
295             {
296                 if (!folder.isOpen())
297                 {
298                     folder.open(Folder.READ_WRITE);
299                 }
300             }
301             catch (Exception e)
302             {
303                 if (logger.isDebugEnabled())
304                 {
305                     logger.debug("ignoring exception: " + e.getMessage());
306                 }
307             }
308 
309             int count = folder.getMessageCount();
310             if (count > 0)
311             {
312                 Message[] messages = folder.getMessages();
313                 MessageCountEvent event = new MessageCountEvent(folder, MessageCountEvent.ADDED, true,
314                     messages);
315                 messagesAdded(event);
316             }
317             else if (count == -1)
318             {
319                 throw new MessagingException("Cannot monitor folder: " + folder.getFullName()
320                                              + " as folder is closed");
321             }
322         }
323         catch (MessagingException e)
324         {
325             getConnector().getMuleContext().getExceptionListener().handleException(e);
326         }
327         finally
328         {
329             try
330             {
331                 folder.close(true); // close and expunge deleted messages
332             }
333             catch (Exception e)
334             {
335                 logger.error("Failed to close pop3  inbox: " + e.getMessage());
336             }
337         }
338     }
339 
340     @Override
341     protected void doDispose()
342     {
343         if (null != folder)
344         {
345             folder.removeMessageCountListener(this);
346             if (folder.isOpen())
347             {
348                 try
349                 {
350 
351                     folder.close(true);
352                 }
353                 catch (Exception e)
354                 {
355                     logger.debug("ignoring exception: " + e.getMessage(), e);
356                 }
357             }
358         }
359     }
360 
361     @Override
362     protected MuleMessage handleUnacceptedFilter(MuleMessage message)
363     {
364         super.handleUnacceptedFilter(message);
365         if (message.getPayload() instanceof Message)
366         {
367             Message msg = (Message) message.getPayload();
368             try
369             {
370                 msg.setFlag(Flags.Flag.DELETED, endpoint.isDeleteUnacceptedMessages());
371             }
372             catch (MessagingException e)
373             {
374                 logger.error("failed to set message deleted: " + e.getMessage(), e);
375             }
376         }
377         return message;
378     }
379 }