001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.fetchmail;
019:
020: import javax.mail.FetchProfile;
021: import javax.mail.Flags;
022: import javax.mail.Folder;
023: import javax.mail.Message;
024: import javax.mail.MessagingException;
025: import javax.mail.internet.MimeMessage;
026:
027: /**
028: * <p>Class <code>FolderProcessor</code> opens a Folder and iterates over all
029: * of the Messages, delegating their processing to
030: * <code>MessageProcessor</code>.</p>
031: *
032: * <p>If isRecurse(), all subfolders are fetched recursively.</p>
033: *
034: * <p>Creation Date: 25-May-03</p>
035: *
036: */
037: public class FolderProcessor extends ProcessorAbstract {
038: /**
039: * The fetched folder
040: */
041: private Folder fieldFolder;
042:
043: private Boolean fieldMarkSeenPermanent;
044:
045: /**
046: * Constructor for FolderProcessor.
047: * @param folder The folder to be fetched
048: * @param account The account being processed
049: */
050: protected FolderProcessor(Folder folder, Account account) {
051: super (account);
052: setFolder(folder);
053: }
054:
055: /**
056: * Method process opens a Folder, fetches the Envelopes for all of its
057: * Messages, creates a <code>MessageProcessor</code> and runs it to process
058: * each message.
059: *
060: * @see org.apache.james.fetchmail.ProcessorAbstract#process()
061: */
062: public void process() throws MessagingException {
063: int messagesProcessed = 0;
064: int messageCount = 0;
065: try {
066: // open the folder
067: try {
068: open();
069: } catch (MessagingException ex) {
070: getLogger().error(
071: getFetchTaskName() + " Failed to open folder!");
072: throw ex;
073: }
074:
075: // Lock the folder while processing each message
076: synchronized (getFolder()) {
077: messageCount = getFolder().getMessageCount();
078: for (int i = 1; i <= messageCount; i++) {
079: MimeMessage message = (MimeMessage) getFolder()
080: .getMessage(i);
081: if (isFetchAll() || !isSeen(message)) {
082: try {
083: new MessageProcessor(message, getAccount())
084: .process();
085: messagesProcessed++;
086: }
087: // Catch and report an exception but don't rethrow it,
088: // allowing subsequent messages to be processed.
089: catch (Exception ex) {
090: StringBuffer logMessageBuffer = new StringBuffer(
091: "Exception processing message ID: ");
092: logMessageBuffer.append(message
093: .getMessageID());
094: getLogger().error(
095: logMessageBuffer.toString(), ex);
096: }
097: }
098: }
099: }
100: } catch (MessagingException mex) {
101: getLogger()
102: .error(
103: "A MessagingException has terminated fetching messages for this folder",
104: mex);
105: } finally {
106: // Close the folder
107: try {
108: close();
109: } catch (MessagingException ex) {
110: // No-op
111: }
112: StringBuffer logMessageBuffer = new StringBuffer(
113: "Processed ");
114: logMessageBuffer.append(messagesProcessed);
115: logMessageBuffer.append(" messages of ");
116: logMessageBuffer.append(messageCount);
117: logMessageBuffer.append(" in folder '");
118: logMessageBuffer.append(getFolder().getName());
119: logMessageBuffer.append("'");
120: getLogger().info(logMessageBuffer.toString());
121: }
122:
123: // Recurse through sub-folders if required
124: try {
125: if (isRecurse())
126: recurse();
127: } catch (MessagingException mex) {
128: getLogger()
129: .error(
130: "A MessagingException has terminated recursing through sub-folders",
131: mex);
132: }
133:
134: return;
135: }
136:
137: /**
138: * Method close.
139: * @throws MessagingException
140: */
141: protected void close() throws MessagingException {
142: if (null != getFolder() && getFolder().isOpen())
143: getFolder().close(true);
144: }
145:
146: /**
147: * Method recurse.
148: * @throws MessagingException
149: */
150: protected void recurse() throws MessagingException {
151: if ((getFolder().getType() & Folder.HOLDS_FOLDERS) == Folder.HOLDS_FOLDERS) {
152: // folder contains subfolders...
153: Folder folders[] = getFolder().list();
154:
155: for (int i = 0; i < folders.length; i++) {
156: new FolderProcessor(folders[i], getAccount()).process();
157: }
158:
159: }
160: }
161:
162: /**
163: * Method open.
164: * @throws MessagingException
165: */
166: protected void open() throws MessagingException {
167: int openFlag = Folder.READ_WRITE;
168:
169: if (isOpenReadOnly())
170: openFlag = Folder.READ_ONLY;
171:
172: getFolder().open(openFlag);
173: }
174:
175: /**
176: * Returns the folder.
177: * @return Folder
178: */
179: protected Folder getFolder() {
180: return fieldFolder;
181: }
182:
183: /**
184: * Answer if <code>aMessage</code> has been SEEN.
185: * @param aMessage
186: * @return boolean
187: * @throws MessagingException
188: */
189: protected boolean isSeen(MimeMessage aMessage)
190: throws MessagingException {
191: boolean isSeen = false;
192: if (isMarkSeenPermanent().booleanValue())
193: isSeen = aMessage.isSet(Flags.Flag.SEEN);
194: else
195: isSeen = handleMarkSeenNotPermanent(aMessage);
196: return isSeen;
197: }
198:
199: /**
200: * Answer the result of computing markSeenPermanent.
201: * @return Boolean
202: */
203: protected Boolean computeMarkSeenPermanent() {
204: return new Boolean(getFolder().getPermanentFlags().contains(
205: Flags.Flag.SEEN));
206: }
207:
208: /**
209: * <p>Handler for when the folder does not support the SEEN flag.
210: * The default behaviour implemented here is to answer the value of the
211: * SEEN flag anyway.</p>
212: *
213: * <p>Subclasses may choose to override this method and implement their own
214: * solutions.</p>
215: *
216: * @param aMessage
217: * @return boolean
218: * @throws MessagingException
219: */
220: protected boolean handleMarkSeenNotPermanent(MimeMessage aMessage)
221: throws MessagingException {
222: return aMessage.isSet(Flags.Flag.SEEN);
223: }
224:
225: /**
226: * Sets the folder.
227: * @param folder The folder to set
228: */
229: protected void setFolder(Folder folder) {
230: fieldFolder = folder;
231: }
232:
233: /**
234: * Returns the isMarkSeenPermanent.
235: * @return Boolean
236: */
237: protected Boolean isMarkSeenPermanent() {
238: Boolean markSeenPermanent = null;
239: if (null == (markSeenPermanent = isMarkSeenPermanentBasic())) {
240: updateMarkSeenPermanent();
241: return isMarkSeenPermanent();
242: }
243: return markSeenPermanent;
244: }
245:
246: /**
247: * Returns the markSeenPermanent.
248: * @return Boolean
249: */
250: private Boolean isMarkSeenPermanentBasic() {
251: return fieldMarkSeenPermanent;
252: }
253:
254: /**
255: * Sets the markSeenPermanent.
256: * @param markSeenPermanent The isMarkSeenPermanent to set
257: */
258: protected void setMarkSeenPermanent(Boolean markSeenPermanent) {
259: fieldMarkSeenPermanent = markSeenPermanent;
260: }
261:
262: /**
263: * Updates the markSeenPermanent.
264: */
265: protected void updateMarkSeenPermanent() {
266: setMarkSeenPermanent(computeMarkSeenPermanent());
267: }
268:
269: }
|