View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.geronimo.javamail.store.pop3;
21   
22  import java.io.PrintStream; 
23  import java.util.LinkedList;
24  import java.util.List;
25  
26  import javax.mail.AuthenticationFailedException;
27  import javax.mail.Folder;
28  import javax.mail.MessagingException;
29  import javax.mail.Session;
30  import javax.mail.Store;
31  import javax.mail.URLName;
32  
33  import org.apache.geronimo.javamail.store.pop3.connection.POP3Connection; 
34  import org.apache.geronimo.javamail.store.pop3.connection.POP3ConnectionPool; 
35  import org.apache.geronimo.javamail.util.ProtocolProperties;
36  
37  /**
38   * POP3 implementation of javax.mail.Store POP protocol spec is implemented in
39   * org.apache.geronimo.javamail.store.pop3.POP3Connection
40   * 
41   * @version $Rev: 693530 $ $Date: 2008-09-09 13:57:23 -0400 (Tue, 09 Sep 2008) $
42   */
43  
44  public class POP3Store extends Store {
45      protected static final int DEFAULT_POP3_PORT = 110;
46      protected static final int DEFAULT_POP3_SSL_PORT = 995;
47      
48      
49      // our accessor for protocol properties and the holder of 
50      // protocol-specific information 
51      protected ProtocolProperties props; 
52      // our connection object    
53      protected POP3ConnectionPool connectionPool; 
54      // our session provided debug output stream.
55      protected PrintStream debugStream;
56      // the debug flag 
57      protected boolean debug; 
58      // the root folder 
59      protected POP3RootFolder root; 
60      // until we're connected, we're closed 
61      boolean closedForBusiness = true; 
62      protected LinkedList openFolders = new LinkedList(); 
63      
64      
65      public POP3Store(Session session, URLName name) {
66          this(session, name, "pop3", DEFAULT_POP3_PORT, false);
67      }
68  
69      /**
70       * Common constructor used by the POP3Store and POP3SSLStore classes
71       * to do common initialization of defaults.
72       *
73       * @param session
74       *            The host session instance.
75       * @param name
76       *            The URLName of the target.
77       * @param protocol
78       *            The protocol type ("pop3"). This helps us in
79       *            retrieving protocol-specific session properties.
80       * @param defaultPort
81       *            The default port used by this protocol. For pop3, this will
82       *            be 110. The default for pop3 with ssl is 995.
83       * @param sslConnection
84       *            Indicates whether an SSL connection should be used to initial
85       *            contact the server. This is different from the STARTTLS
86       *            support, which switches the connection to SSL after the
87       *            initial startup.
88       */
89      protected POP3Store(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) {
90          super(session, name);
91          
92          // create the protocol property holder.  This gives an abstraction over the different 
93          // flavors of the protocol. 
94          props = new ProtocolProperties(session, protocol, sslConnection, defaultPort); 
95  
96          // get our debug settings
97          debugStream = session.getDebugOut();
98          debug = session.getDebug(); 
99          // the connection pool manages connections for the stores, folder, and message usage. 
100         connectionPool = new POP3ConnectionPool(this, props); 
101     }
102 
103 
104     /**
105      * Return a Folder object that represents the root of the namespace for the current user.
106      *
107      * Note that in some store configurations (such as IMAP4) the root folder might
108      * not be the INBOX folder.
109      *
110      * @return the root Folder
111      * @throws MessagingException if there was a problem accessing the store
112      */
113 	public Folder getDefaultFolder() throws MessagingException {
114 		checkConnectionStatus();
115         // if no root yet, create a root folder instance. 
116         if (root == null) {
117             return new POP3RootFolder(this);
118         }
119         return root;
120 	}
121 
122     /**
123      * Return the Folder corresponding to the given name.
124      * The folder might not physically exist; the {@link Folder#exists()} method can be used
125      * to determine if it is real.
126      * 
127      * @param name   the name of the Folder to return
128      * 
129      * @return the corresponding folder
130      * @throws MessagingException
131      *                if there was a problem accessing the store
132      */
133 	public Folder getFolder(String name) throws MessagingException {
134         return getDefaultFolder().getFolder(name);
135 	}
136 
137     
138     /**
139      * Return the folder identified by the URLName; the URLName must refer to this Store.
140      * Implementations may use the {@link URLName#getFile()} method to determined the folder name.
141      * 
142      * @param url
143      * 
144      * @return the corresponding folder
145      * @throws MessagingException
146      *                if there was a problem accessing the store
147      */
148 	public Folder getFolder(URLName url) throws MessagingException {
149         return getDefaultFolder().getFolder(url.getFile());
150 	}
151 
152     
153     /**
154      * @see javax.mail.Service#protocolConnect(java.lang.String, int,
155      *      java.lang.String, java.lang.String)
156      */
157     protected synchronized boolean protocolConnect(String host, int port, String username, String password) throws MessagingException {
158         
159         if (debug) {
160             debugOut("Connecting to server " + host + ":" + port + " for user " + username);
161         }
162 
163         // the connection pool handles all of the details here. 
164         if (connectionPool.protocolConnect(host, port, username, password)) 
165         {
166             // the store is now open 
167             closedForBusiness = false; 
168             return true; 
169         }
170         return false; 
171     }
172     
173     
174     /**
175      * Get a connection for the store. 
176      * 
177      * @return The request connection object. 
178      * @exception MessagingException
179      */
180     protected POP3Connection getConnection() throws MessagingException {
181         return connectionPool.getConnection(); 
182     }
183     
184     /**
185      * Return a connection back to the connection pool after 
186      * it has been used for a request. 
187      * 
188      * @param connection The return connection.
189      * 
190      * @exception MessagingException
191      */
192     protected void releaseConnection(POP3Connection connection) throws MessagingException {
193         connectionPool.releaseConnection(connection); 
194     }
195     
196     /**
197      * Get a connection object for a folder to use. 
198      * 
199      * @param folder The requesting folder (always the inbox for POP3).
200      * 
201      * @return An active POP3Connection. 
202      * @exception MessagingException
203      */
204     synchronized POP3Connection getFolderConnection(POP3Folder folder) throws MessagingException {
205         POP3Connection connection = connectionPool.getConnection(); 
206         openFolders.add(folder);
207         return connection; 
208     }
209     
210     /**
211      * Release a connection object after a folder is 
212      * finished with a request. 
213      * 
214      * @param folder     The requesting folder.
215      * @param connection
216      * 
217      * @exception MessagingException
218      */
219     synchronized void releaseFolderConnection(POP3Folder folder, POP3Connection connection) throws MessagingException {
220         openFolders.remove(folder); 
221         // return this back to the pool 
222         connectionPool.releaseConnection(connection); 
223     }
224     
225     /**
226      * Close all open folders.  We have a small problem here with a race condition.  There's no safe, single
227      * synchronization point for us to block creation of new folders while we're closing.  So we make a copy of
228      * the folders list, close all of those folders, and keep repeating until we're done.
229      */
230     protected void closeOpenFolders() {
231         // we're no longer accepting additional opens.  Any folders that open after this point will get an
232         // exception trying to get a connection.
233         closedForBusiness = true;
234 
235         while (true) {
236             List folders = null;
237 
238             // grab our lock, copy the open folders reference, and null this out.  Once we see a null
239             // open folders ref, we're done closing.
240             synchronized(connectionPool) {
241                 folders = openFolders;
242                 openFolders = new LinkedList();
243             }
244 
245             // null folder, we're done
246             if (folders.isEmpty()) {
247                 return;
248             }
249             // now close each of the open folders.
250             for (int i = 0; i < folders.size(); i++) {
251                 POP3Folder folder = (POP3Folder)folders.get(i);
252                 try {
253                     folder.close(false);
254                 } catch (MessagingException e) {
255                 }
256             }
257         }
258     }
259     
260 
261     /**
262      * @see javax.mail.Service#isConnected()
263      */
264     public boolean isConnected() {
265         // the connect() method of the super class checks here first.  If the connected flag 
266         // is off, then it's too early for use to try to get a connection and verify we still 
267         // have a live one.  
268         if (!super.isConnected()) {
269             return false; 
270         }
271         try {
272             POP3Connection connection = getConnection(); 
273             // a null connection likely means we had a failure establishing a 
274             // new connection to the POP3 server.  
275             if (connection == null) {
276                 return false; 
277             }
278             try {
279                 // make sure the server is really there 
280                 connection.pingServer(); 
281                 return true; 
282             }
283             finally {
284                 // return the connection to the pool when finished 
285                 if (connection != null) {
286                     releaseConnection(connection); 
287                 }
288             }
289         } catch (MessagingException e) {
290         }
291         return false; 
292     }
293 
294     /**
295      * Close the store, and any open folders associated with the 
296      * store. 
297      * 
298      * @exception MessagingException
299      */
300 	public synchronized void close() throws MessagingException{
301         // if already closed, nothing to do. 
302         if (closedForBusiness) {
303             return; 
304         }
305         
306         // close the folders first, then shut down the Store. 
307         closeOpenFolders();
308         
309         connectionPool.close(); 
310         connectionPool = null; 
311 
312 		// make sure we do the superclass close operation first so 
313         // notification events get broadcast properly. 
314 		super.close();
315 	}
316 
317     /**
318      * Check the status of our connection. 
319      * 
320      * @exception MessagingException
321      */
322     private void checkConnectionStatus() throws MessagingException {
323         if (!this.isConnected()) {
324             throw new MessagingException("Not connected ");
325         }
326     }
327 
328     /**
329      * Internal debug output routine.
330      *
331      * @param value  The string value to output.
332      */
333     void debugOut(String message) {
334         debugStream.println("POP3Store DEBUG: " + message);
335     }
336 
337     /**
338      * Internal debugging routine for reporting exceptions.
339      *
340      * @param message A message associated with the exception context.
341      * @param e       The received exception.
342      */
343     void debugOut(String message, Throwable e) {
344         debugOut("Received exception -> " + message);
345         debugOut("Exception message -> " + e.getMessage());
346         e.printStackTrace(debugStream);
347     }
348     
349     /**
350      * Finalizer to perform IMAPStore() cleanup when 
351      * no longer in use. 
352      * 
353      * @exception Throwable
354      */
355     protected void finalize() throws Throwable {
356         super.finalize(); 
357         close(); 
358     }
359 }