View Javadoc

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