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 */
019
020 package org.apache.geronimo.javamail.store.pop3;
021
022 import java.io.PrintStream;
023 import java.util.LinkedList;
024 import java.util.List;
025
026 import javax.mail.AuthenticationFailedException;
027 import javax.mail.Folder;
028 import javax.mail.MessagingException;
029 import javax.mail.Session;
030 import javax.mail.Store;
031 import javax.mail.URLName;
032
033 import org.apache.geronimo.javamail.store.pop3.connection.POP3Connection;
034 import org.apache.geronimo.javamail.store.pop3.connection.POP3ConnectionPool;
035 import org.apache.geronimo.javamail.util.ProtocolProperties;
036
037 /**
038 * POP3 implementation of javax.mail.Store POP protocol spec is implemented in
039 * org.apache.geronimo.javamail.store.pop3.POP3Connection
040 *
041 * @version $Rev: 693530 $ $Date: 2008-09-09 13:57:23 -0400 (Tue, 09 Sep 2008) $
042 */
043
044 public class POP3Store extends Store {
045 protected static final int DEFAULT_POP3_PORT = 110;
046 protected static final int DEFAULT_POP3_SSL_PORT = 995;
047
048
049 // our accessor for protocol properties and the holder of
050 // protocol-specific information
051 protected ProtocolProperties props;
052 // our connection object
053 protected POP3ConnectionPool connectionPool;
054 // our session provided debug output stream.
055 protected PrintStream debugStream;
056 // the debug flag
057 protected boolean debug;
058 // the root folder
059 protected POP3RootFolder root;
060 // until we're connected, we're closed
061 boolean closedForBusiness = true;
062 protected LinkedList openFolders = new LinkedList();
063
064
065 public POP3Store(Session session, URLName name) {
066 this(session, name, "pop3", DEFAULT_POP3_PORT, false);
067 }
068
069 /**
070 * Common constructor used by the POP3Store and POP3SSLStore classes
071 * to do common initialization of defaults.
072 *
073 * @param session
074 * The host session instance.
075 * @param name
076 * The URLName of the target.
077 * @param protocol
078 * The protocol type ("pop3"). This helps us in
079 * retrieving protocol-specific session properties.
080 * @param defaultPort
081 * The default port used by this protocol. For pop3, this will
082 * be 110. The default for pop3 with ssl is 995.
083 * @param sslConnection
084 * Indicates whether an SSL connection should be used to initial
085 * contact the server. This is different from the STARTTLS
086 * support, which switches the connection to SSL after the
087 * initial startup.
088 */
089 protected POP3Store(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) {
090 super(session, name);
091
092 // create the protocol property holder. This gives an abstraction over the different
093 // flavors of the protocol.
094 props = new ProtocolProperties(session, protocol, sslConnection, defaultPort);
095
096 // get our debug settings
097 debugStream = session.getDebugOut();
098 debug = session.getDebug();
099 // 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 }