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 }