View Javadoc

1   /**
2    *
3    * Copyright 2003-2005 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You 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 implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.apache.geronimo.javamail.store.pop3;
19  
20  import java.util.Vector;
21  
22  import javax.mail.FetchProfile;
23  import javax.mail.Flags;
24  import javax.mail.Folder;
25  import javax.mail.Message;
26  import javax.mail.MessagingException;
27  import javax.mail.MethodNotSupportedException;
28  import javax.mail.Session;
29  import javax.mail.Store;
30  import javax.mail.URLName;
31  import javax.mail.event.ConnectionEvent;
32  
33  import org.apache.geronimo.javamail.store.pop3.message.POP3Message;
34  import org.apache.geronimo.javamail.store.pop3.message.POP3MessageFactory;
35  import org.apache.geronimo.javamail.store.pop3.response.POP3ResponseFactory;
36  import org.apache.geronimo.javamail.store.pop3.response.POP3StatusResponse;
37  
38  /**
39   * The POP3 implementation of the javax.mail.Folder Note that only INBOX is
40   * supported in POP3
41   * <p>
42   * <url>http://www.faqs.org/rfcs/rfc1939.html</url>
43   * </p>
44   * 
45   * @see javax.mail.Folder
46   * 
47   * @version $Rev: 432884 $ $Date: 2006-08-19 14:53:20 -0700 (Sat, 19 Aug 2006) $
48   */
49  public class POP3Folder extends Folder {
50  
51      private boolean isFolderOpen = false;
52  
53      private int mode;
54  
55      private POP3Connection pop3Con;
56  
57      private int msgCount;
58  
59      private Session session;
60  
61      /**
62       * Vector is synchronized so choose over the other Collection impls This is
63       * initialized on open A chache will save the expensive operation of
64       * retrieving the message again from the server.
65       */
66      private Vector msgCache;
67  
68      protected POP3Folder(Store store, URLName url) {
69          super(store);
70      }
71  
72      protected POP3Folder(Store store, Session session, POP3Connection pop3Con) {
73          super(store);
74          this.pop3Con = pop3Con;
75          this.session = session;
76      }
77  
78      public String getName() {
79          return "INBOX";
80      }
81  
82      public String getFullName() {
83          return "INBOX";
84      }
85  
86      /**
87       * Never return "this" as the parent folder. Somebody not familliar with
88       * POP3 may do something like while(getParent() != null) or something
89       * simmilar which will result in an infinte loop
90       */
91      public Folder getParent() throws MessagingException {
92          throw new MethodNotSupportedException("INBOX is the root folder");
93      }
94  
95      public boolean exists() throws MessagingException {
96          // INBOX always exists at the backend
97          return true;
98      }
99  
100     public Folder[] list(String pattern) throws MessagingException {
101         throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
102     }
103 
104     /**
105      * No sub folders, hence there is no notion of a seperator
106      */
107     public char getSeparator() throws MessagingException {
108         throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
109     }
110 
111     public int getType() throws MessagingException {
112         return HOLDS_MESSAGES;
113     }
114 
115     public boolean create(int type) throws MessagingException {
116         throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
117     }
118 
119     public boolean hasNewMessages() throws MessagingException {
120         throw new MethodNotSupportedException("POP3 doesn't support this operation");
121     }
122 
123     public Folder getFolder(String name) throws MessagingException {
124         throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
125     }
126 
127     public boolean delete(boolean recurse) throws MessagingException {
128         throw new MethodNotSupportedException("Only INBOX is supported in POP3 and INBOX cannot be deleted");
129     }
130 
131     public boolean renameTo(Folder f) throws MessagingException {
132         throw new MethodNotSupportedException("Only INBOX is supported in POP3 and INBOX cannot be renamed");
133     }
134 
135     /**
136      * @see javax.mail.Folder#open(int)
137      */
138     public void open(int mode) throws MessagingException {
139         // Can only be performed on a closed folder
140         checkClosed();
141 
142         try {
143 
144             POP3StatusResponse res = (POP3StatusResponse) POP3ResponseFactory.getStatusResponse(pop3Con
145                     .sendCommand(POP3CommandFactory.getCOMMAND_STAT()));
146 
147             // I am not checking for the res == null condition as the
148             // try catch block will handle it.
149 
150             this.mode = mode;
151             this.isFolderOpen = true;
152             this.msgCount = res.getNumMessages();
153             // JavaMail API has no method in Folder to expose the total
154             // size (no of bytes) of the mail drop;
155 
156             msgCache = new Vector(msgCount);
157             msgCache.setSize(msgCount);
158 
159         } catch (Exception e) {
160             throw new MessagingException("Unable to execute STAT command", e);
161         }
162 
163         notifyConnectionListeners(ConnectionEvent.OPENED);
164     }
165 
166     public void close(boolean expunge) throws MessagingException {
167         // Can only be performed on an open folder
168         checkOpen();
169 
170         try {
171             if (mode == READ_WRITE) {
172                 // find all messages marked deleted and issue DELE commands
173                 POP3Message m;
174                 for (int i = 0; i < msgCache.size(); i++) {
175                     if ((m = (POP3Message) msgCache.elementAt(i)) != null) {
176                         if (m.isSet(Flags.Flag.DELETED)) {
177                             try {
178                                 pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_DELE(i + 1));
179                             } catch (Exception e) {
180                                 throw new MessagingException("Exception deleting message no [" + (i + 1)
181                                         + "] during close", e);
182                             }
183                         }
184                     }
185                 }
186             }
187 
188             try {
189                 pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_QUIT());
190             } catch (Exception e) {
191                 // doesn't really care about the response
192             }
193             // dosn't need a catch block here, but added incase something goes
194             // wrong
195             // so that the finnaly is garunteed to execute in such a case.
196         } finally {
197             try {
198                 pop3Con.close();
199             } catch (Exception e) {
200                 // doesn't really care about the response
201                 // all we can do is to set the reference explicitly to null
202                 pop3Con = null;
203             }
204 
205             /*
206              * The message numbers depend on the mail drop if the connection is
207              * closed, then purge the cache
208              */
209             msgCache = null;
210             isFolderOpen = false;
211             notifyConnectionListeners(ConnectionEvent.CLOSED);
212         }
213     }
214 
215     public boolean isOpen() {
216         return isFolderOpen;
217     }
218 
219     public Flags getPermanentFlags() {
220         // unfortunately doesn't have a throws clause for this method
221         // throw new MethodNotSupportedException("POP3 doesn't support permanent
222         // flags");
223 
224         // Better than returning null, save the extra condition from a user to
225         // check for null
226         // and avoids a NullPointerException for the careless.
227         return new Flags();
228     }
229 
230     public int getMessageCount() throws MessagingException {
231         return msgCount;
232     }
233 
234     /**
235      * Checks wether the message is in cache, if not will create a new message
236      * object and return it.
237      * 
238      * @see javax.mail.Folder#getMessage(int)
239      */
240     public Message getMessage(int msgNum) throws MessagingException {
241         // Can only be performed on an Open folder
242         checkOpen();
243         if (msgNum < 1 || msgNum > getMessageCount()) {
244             throw new MessagingException("Invalid Message number");
245         }
246 
247         Message msg = null;
248         try {
249             msg = (Message) msgCache.elementAt(msgNum);
250         } catch (RuntimeException e) {
251             session.getDebugOut().println("Message not in cache");
252         }
253         if (msg == null) {
254             msg = POP3MessageFactory.createMessage(this, session, pop3Con, msgNum);
255             msgCache.setElementAt(msg, msgNum);
256         }
257 
258         return msg;
259     }
260 
261     public void appendMessages(Message[] msgs) throws MessagingException {
262         throw new MethodNotSupportedException("Message appending is not supported in POP3");
263 
264     }
265 
266     public Message[] expunge() throws MessagingException {
267         throw new MethodNotSupportedException("Expunge is not supported in POP3");
268     }
269 
270     public int getMode() throws IllegalStateException {
271         // Can only be performed on an Open folder
272         checkOpen();
273         return mode;
274     }
275 
276     /**
277      * @see javax.mail.Folder#fetch(javax.mail.Message[],
278      *      javax.mail.FetchProfile)
279      * 
280      * The JavaMail API recommends that this method be overrident to provide a
281      * meaningfull implementation.
282      */
283     public void fetch(Message[] msgs, FetchProfile fp) throws MessagingException {
284         // Can only be performed on an Open folder
285         checkOpen();
286         for (int i = 0; i < msgs.length; i++) {
287             Message msg = msgs[i];
288             if (msg == null) {
289                 msg = POP3MessageFactory.createMessage(this, session, pop3Con, i);
290             }
291             if (fp.contains(FetchProfile.Item.ENVELOPE)) {
292                 msg = POP3MessageFactory.createMessageWithEvelope((POP3Message) msg);
293             }
294 
295             if (fp.contains(FetchProfile.Item.CONTENT_INFO)) {
296                 msg = POP3MessageFactory.createMessageWithContentInfo((POP3Message) msg);
297             }
298 
299             if (fp.contains(FetchProfile.Item.FLAGS)) {
300                 msg = POP3MessageFactory.createMessageWithFlags((POP3Message) msg);
301             }
302 
303             msgs[i] = msg;
304         }
305     }
306 
307     /**
308      * Below is a list of covinience methods that avoid repeated checking for a
309      * value and throwing an exception
310      */
311 
312     /** Ensure the folder is open */
313     private void checkOpen() throws IllegalStateException {
314         if (!isFolderOpen) {
315             throw new IllegalStateException("Folder is not Open");
316         }
317     }
318 
319     /** Ensure the folder is not open */
320     private void checkClosed() throws IllegalStateException {
321         if (isFolderOpen) {
322             throw new IllegalStateException("Folder is Open");
323         }
324     }
325 
326     /**
327      * @see javax.mail.Folder#notifyMessageChangedListeners(int,
328      *      javax.mail.Message)
329      * 
330      * this method is protected and cannot be used outside of Folder, therefore
331      * had to explicitly expose it via a method in POP3Folder, so that
332      * POP3Message has access to it
333      * 
334      * Bad design on the part of the Java Mail API.
335      */
336     public void notifyMessageChangedListeners(int type, Message m) {
337         super.notifyMessageChangedListeners(type, m);
338     }
339 
340 }