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 }