View Javadoc

1   /************************************************************************
2    * Copyright (c) 2003-2006 The Apache Software Foundation.             *
3    * All rights reserved.                                                *
4    * ------------------------------------------------------------------- *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you *
6    * may not use this file except in compliance with the License. You    *
7    * may obtain a copy of the License at:                                *
8    *                                                                     *
9    *     http://www.apache.org/licenses/LICENSE-2.0                      *
10   *                                                                     *
11   * Unless required by applicable law or agreed to in writing, software *
12   * distributed under the License is distributed on an "AS IS" BASIS,   *
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
14   * implied.  See the License for the specific language governing       *
15   * permissions and limitations under the License.                      *
16   ***********************************************************************/
17   
18  package org.apache.james.fetchmail;
19  
20  import javax.mail.FetchProfile;
21  import javax.mail.Flags;
22  import javax.mail.Folder;
23  import javax.mail.Message;
24  import javax.mail.MessagingException;
25  import javax.mail.internet.MimeMessage;
26  
27  /***
28   * <p>Class <code>FolderProcessor</code> opens a Folder and iterates over all
29   * of the Messages, delegating their processing to 
30   * <code>MessageProcessor</code>.</p>
31   * 
32   * <p>If isRecurse(), all subfolders are fetched recursively.</p>
33   * 
34   * <p>Creation Date: 25-May-03</p>
35   *
36   */
37  public class FolderProcessor extends ProcessorAbstract
38  {
39      /***
40       * The fetched folder
41       */ 
42      private Folder fieldFolder;
43      
44      private Boolean fieldMarkSeenPermanent;
45  
46      /***
47       * Constructor for FolderProcessor.
48       * @param folder The folder to be fetched
49       * @param account The account being processed
50       */
51      protected FolderProcessor(Folder folder, Account account)
52      {
53          super(account);
54          setFolder(folder);
55      }
56      
57      /***
58       * Method process opens a Folder, fetches the Envelopes for all of its 
59       * Messages, creates a <code>MessageProcessor</code> and runs it to process
60       * each message.
61       * 
62       * @see org.apache.james.fetchmail.ProcessorAbstract#process()
63       */
64      public void process() throws MessagingException
65      {
66          int messagesProcessed = 0;
67          int messageCount = 0;
68          try
69          {
70              // open the folder            
71              try
72              {
73                  open();
74              }
75              catch (MessagingException ex)
76              {
77                  getLogger().error(
78                      getFetchTaskName() + " Failed to open folder!");
79                  throw ex;
80              }
81  
82              // Lock the folder while processing each message
83              synchronized (getFolder())
84              {
85                  messageCount = getFolder().getMessageCount();
86                  for (int i = 1; i <= messageCount; i++)
87                  {
88                      MimeMessage message =
89                          (MimeMessage) getFolder().getMessage(i);
90                      if (isFetchAll() || !isSeen(message))
91                      {
92                          try
93                          {
94                              new MessageProcessor(message, getAccount())
95                                  .process();
96                              messagesProcessed++;
97                          }
98                          // Catch and report an exception but don't rethrow it, 
99                          // allowing subsequent messages to be processed.                    
100                         catch (Exception ex)
101                         {
102                             StringBuffer logMessageBuffer =
103                                 new StringBuffer("Exception processing message ID: ");
104                             logMessageBuffer.append(message.getMessageID());
105                             getLogger().error(logMessageBuffer.toString(), ex);
106                         }
107                     }
108                 }
109             }
110         }
111         catch (MessagingException mex)
112         {
113             getLogger().error(
114                 "A MessagingException has terminated fetching messages for this folder",
115                 mex);
116         }
117         finally
118         {
119             // Close the folder
120             try
121             {
122                 close();
123             }
124             catch (MessagingException ex)
125             {
126                 // No-op
127             }
128             StringBuffer logMessageBuffer = new StringBuffer("Processed ");
129             logMessageBuffer.append(messagesProcessed);
130             logMessageBuffer.append(" messages of ");
131             logMessageBuffer.append(messageCount);
132             logMessageBuffer.append(" in folder '");
133             logMessageBuffer.append(getFolder().getName());
134             logMessageBuffer.append("'");
135             getLogger().info(logMessageBuffer.toString());
136         }
137 
138         // Recurse through sub-folders if required
139         try
140         {
141             if (isRecurse())
142                 recurse();
143         }
144         catch (MessagingException mex)
145         {
146             getLogger().error(
147                 "A MessagingException has terminated recursing through sub-folders",
148                 mex);
149         }
150 
151         return;
152     }
153     
154     /***
155      * Method close.
156      * @throws MessagingException
157      */
158     protected void close() throws MessagingException
159     {
160         if (null != getFolder() && getFolder().isOpen())
161             getFolder().close(true);
162     }   
163     
164     /***
165      * Method recurse.
166      * @throws MessagingException
167      */
168     protected void recurse() throws MessagingException
169     {
170         if ((getFolder().getType() & Folder.HOLDS_FOLDERS)
171             == Folder.HOLDS_FOLDERS)
172         {
173             // folder contains subfolders...
174             Folder folders[] = getFolder().list();
175 
176             for (int i = 0; i < folders.length; i++)
177             {
178                 new FolderProcessor(folders[i], getAccount()).process();
179             }
180 
181         }
182     }   
183     
184     /***
185      * Method open.
186      * @throws MessagingException
187      */
188     protected void open() throws MessagingException
189     {
190         int openFlag = Folder.READ_WRITE;
191         
192         if (isOpenReadOnly())
193             openFlag = Folder.READ_ONLY;
194 
195         getFolder().open(openFlag);                 
196     }           
197 
198     /***
199      * Returns the folder.
200      * @return Folder
201      */
202     protected Folder getFolder()
203     {
204         return fieldFolder;
205     }
206     
207     /***
208      * Answer if <code>aMessage</code> has been SEEN.
209      * @param aMessage
210      * @return boolean
211      * @throws MessagingException
212      */
213     protected boolean isSeen(MimeMessage aMessage) throws MessagingException
214     {
215         boolean isSeen = false;
216         if (isMarkSeenPermanent().booleanValue())
217             isSeen = aMessage.isSet(Flags.Flag.SEEN);
218         else
219             isSeen = handleMarkSeenNotPermanent(aMessage);
220         return isSeen;
221     }
222 
223     /***
224      * Answer the result of computing markSeenPermanent.
225      * @return Boolean
226      */
227     protected Boolean computeMarkSeenPermanent()
228     {
229         return new Boolean(
230             getFolder().getPermanentFlags().contains(Flags.Flag.SEEN));
231     }
232 
233     /***
234      * <p>Handler for when the folder does not support the SEEN flag.
235      * The default behaviour implemented here is to answer the value of the
236      * SEEN flag anyway.</p>
237      * 
238      * <p>Subclasses may choose to override this method and implement their own
239      *  solutions.</p>
240      *
241      * @param aMessage
242      * @return boolean 
243      * @throws MessagingException
244      */
245     protected boolean handleMarkSeenNotPermanent(MimeMessage aMessage)
246         throws MessagingException
247     {
248         return aMessage.isSet(Flags.Flag.SEEN);
249     }    
250 
251     /***
252      * Sets the folder.
253      * @param folder The folder to set
254      */
255     protected void setFolder(Folder folder)
256     {
257         fieldFolder = folder;
258     }
259     
260     /***
261      * Returns the isMarkSeenPermanent.
262      * @return Boolean
263      */
264     protected Boolean isMarkSeenPermanent()
265     {
266         Boolean markSeenPermanent = null;
267         if (null == (markSeenPermanent = isMarkSeenPermanentBasic()))
268         {
269             updateMarkSeenPermanent();
270             return isMarkSeenPermanent();
271         }    
272         return markSeenPermanent;
273     }
274     
275     /***
276      * Returns the markSeenPermanent.
277      * @return Boolean
278      */
279     private Boolean isMarkSeenPermanentBasic()
280     {
281         return fieldMarkSeenPermanent;
282     }    
283 
284     /***
285      * Sets the markSeenPermanent.
286      * @param markSeenPermanent The isMarkSeenPermanent to set
287      */
288     protected void setMarkSeenPermanent(Boolean markSeenPermanent)
289     {
290         fieldMarkSeenPermanent = markSeenPermanent;
291     }
292     
293     /***
294      * Updates the markSeenPermanent.
295      */
296     protected void updateMarkSeenPermanent()
297     {
298         setMarkSeenPermanent(computeMarkSeenPermanent());
299     }    
300 
301 }