| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.geronimo.javamail.store.pop3; |
| |
| import java.io.PrintStream; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import javax.mail.AuthenticationFailedException; |
| import javax.mail.Folder; |
| import javax.mail.MessagingException; |
| import javax.mail.Session; |
| import javax.mail.Store; |
| import javax.mail.URLName; |
| |
| import org.apache.geronimo.javamail.store.pop3.connection.POP3Connection; |
| import org.apache.geronimo.javamail.store.pop3.connection.POP3ConnectionPool; |
| import org.apache.geronimo.javamail.util.ProtocolProperties; |
| |
| /** |
| * POP3 implementation of javax.mail.Store POP protocol spec is implemented in |
| * org.apache.geronimo.javamail.store.pop3.POP3Connection |
| * |
| * @version $Rev$ $Date$ |
| */ |
| |
| public class POP3Store extends Store { |
| protected static final int DEFAULT_POP3_PORT = 110; |
| protected static final int DEFAULT_POP3_SSL_PORT = 995; |
| |
| |
| // our accessor for protocol properties and the holder of |
| // protocol-specific information |
| protected ProtocolProperties props; |
| // our connection object |
| protected POP3ConnectionPool connectionPool; |
| // our session provided debug output stream. |
| protected PrintStream debugStream; |
| // the debug flag |
| protected boolean debug; |
| // the root folder |
| protected POP3RootFolder root; |
| // until we're connected, we're closed |
| boolean closedForBusiness = true; |
| protected LinkedList openFolders = new LinkedList(); |
| |
| |
| public POP3Store(Session session, URLName name) { |
| this(session, name, "pop3", DEFAULT_POP3_PORT, false); |
| } |
| |
| /** |
| * Common constructor used by the POP3Store and POP3SSLStore classes |
| * to do common initialization of defaults. |
| * |
| * @param session |
| * The host session instance. |
| * @param name |
| * The URLName of the target. |
| * @param protocol |
| * The protocol type ("pop3"). This helps us in |
| * retrieving protocol-specific session properties. |
| * @param defaultPort |
| * The default port used by this protocol. For pop3, this will |
| * be 110. The default for pop3 with ssl is 995. |
| * @param sslConnection |
| * Indicates whether an SSL connection should be used to initial |
| * contact the server. This is different from the STARTTLS |
| * support, which switches the connection to SSL after the |
| * initial startup. |
| */ |
| protected POP3Store(Session session, URLName name, String protocol, int defaultPort, boolean sslConnection) { |
| super(session, name); |
| |
| // create the protocol property holder. This gives an abstraction over the different |
| // flavors of the protocol. |
| props = new ProtocolProperties(session, protocol, sslConnection, defaultPort); |
| |
| // get our debug settings |
| debugStream = session.getDebugOut(); |
| debug = session.getDebug(); |
| // the connection pool manages connections for the stores, folder, and message usage. |
| connectionPool = new POP3ConnectionPool(this, props); |
| } |
| |
| |
| /** |
| * Return a Folder object that represents the root of the namespace for the current user. |
| * |
| * Note that in some store configurations (such as IMAP4) the root folder might |
| * not be the INBOX folder. |
| * |
| * @return the root Folder |
| * @throws MessagingException if there was a problem accessing the store |
| */ |
| public Folder getDefaultFolder() throws MessagingException { |
| checkConnectionStatus(); |
| // if no root yet, create a root folder instance. |
| if (root == null) { |
| return new POP3RootFolder(this); |
| } |
| return root; |
| } |
| |
| /** |
| * Return the Folder corresponding to the given name. |
| * The folder might not physically exist; the {@link Folder#exists()} method can be used |
| * to determine if it is real. |
| * |
| * @param name the name of the Folder to return |
| * |
| * @return the corresponding folder |
| * @throws MessagingException |
| * if there was a problem accessing the store |
| */ |
| public Folder getFolder(String name) throws MessagingException { |
| return getDefaultFolder().getFolder(name); |
| } |
| |
| |
| /** |
| * Return the folder identified by the URLName; the URLName must refer to this Store. |
| * Implementations may use the {@link URLName#getFile()} method to determined the folder name. |
| * |
| * @param url |
| * |
| * @return the corresponding folder |
| * @throws MessagingException |
| * if there was a problem accessing the store |
| */ |
| public Folder getFolder(URLName url) throws MessagingException { |
| return getDefaultFolder().getFolder(url.getFile()); |
| } |
| |
| |
| /** |
| * @see javax.mail.Service#protocolConnect(java.lang.String, int, |
| * java.lang.String, java.lang.String) |
| */ |
| protected synchronized boolean protocolConnect(String host, int port, String username, String password) throws MessagingException { |
| |
| if (debug) { |
| debugOut("Connecting to server " + host + ":" + port + " for user " + username); |
| } |
| |
| // the connection pool handles all of the details here. |
| if (connectionPool.protocolConnect(host, port, username, password)) |
| { |
| // the store is now open |
| closedForBusiness = false; |
| return true; |
| } |
| return false; |
| } |
| |
| |
| /** |
| * Get a connection for the store. |
| * |
| * @return The request connection object. |
| * @exception MessagingException |
| */ |
| protected POP3Connection getConnection() throws MessagingException { |
| return connectionPool.getConnection(); |
| } |
| |
| /** |
| * Return a connection back to the connection pool after |
| * it has been used for a request. |
| * |
| * @param connection The return connection. |
| * |
| * @exception MessagingException |
| */ |
| protected void releaseConnection(POP3Connection connection) throws MessagingException { |
| connectionPool.releaseConnection(connection); |
| } |
| |
| /** |
| * Get a connection object for a folder to use. |
| * |
| * @param folder The requesting folder (always the inbox for POP3). |
| * |
| * @return An active POP3Connection. |
| * @exception MessagingException |
| */ |
| synchronized POP3Connection getFolderConnection(POP3Folder folder) throws MessagingException { |
| POP3Connection connection = connectionPool.getConnection(); |
| openFolders.add(folder); |
| return connection; |
| } |
| |
| /** |
| * Release a connection object after a folder is |
| * finished with a request. |
| * |
| * @param folder The requesting folder. |
| * @param connection |
| * |
| * @exception MessagingException |
| */ |
| synchronized void releaseFolderConnection(POP3Folder folder, POP3Connection connection) throws MessagingException { |
| openFolders.remove(folder); |
| // return this back to the pool |
| connectionPool.releaseConnection(connection); |
| } |
| |
| /** |
| * Close all open folders. We have a small problem here with a race condition. There's no safe, single |
| * synchronization point for us to block creation of new folders while we're closing. So we make a copy of |
| * the folders list, close all of those folders, and keep repeating until we're done. |
| */ |
| protected void closeOpenFolders() { |
| // we're no longer accepting additional opens. Any folders that open after this point will get an |
| // exception trying to get a connection. |
| closedForBusiness = true; |
| |
| while (true) { |
| List folders = null; |
| |
| // grab our lock, copy the open folders reference, and null this out. Once we see a null |
| // open folders ref, we're done closing. |
| synchronized(connectionPool) { |
| folders = openFolders; |
| openFolders = new LinkedList(); |
| } |
| |
| // null folder, we're done |
| if (folders.isEmpty()) { |
| return; |
| } |
| // now close each of the open folders. |
| for (int i = 0; i < folders.size(); i++) { |
| POP3Folder folder = (POP3Folder)folders.get(i); |
| try { |
| folder.close(false); |
| } catch (MessagingException e) { |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * @see javax.mail.Service#isConnected() |
| */ |
| public boolean isConnected() { |
| // the connect() method of the super class checks here first. If the connected flag |
| // is off, then it's too early for use to try to get a connection and verify we still |
| // have a live one. |
| if (!super.isConnected()) { |
| return false; |
| } |
| try { |
| POP3Connection connection = getConnection(); |
| // a null connection likely means we had a failure establishing a |
| // new connection to the POP3 server. |
| if (connection == null) { |
| return false; |
| } |
| try { |
| // make sure the server is really there |
| connection.pingServer(); |
| return true; |
| } |
| finally { |
| // return the connection to the pool when finished |
| if (connection != null) { |
| releaseConnection(connection); |
| } |
| } |
| } catch (MessagingException e) { |
| } |
| return false; |
| } |
| |
| /** |
| * Close the store, and any open folders associated with the |
| * store. |
| * |
| * @exception MessagingException |
| */ |
| public synchronized void close() throws MessagingException{ |
| // if already closed, nothing to do. |
| if (closedForBusiness) { |
| return; |
| } |
| |
| // close the folders first, then shut down the Store. |
| closeOpenFolders(); |
| |
| connectionPool.close(); |
| connectionPool = null; |
| |
| // make sure we do the superclass close operation first so |
| // notification events get broadcast properly. |
| super.close(); |
| } |
| |
| /** |
| * Check the status of our connection. |
| * |
| * @exception MessagingException |
| */ |
| private void checkConnectionStatus() throws MessagingException { |
| if (!this.isConnected()) { |
| throw new MessagingException("Not connected "); |
| } |
| } |
| |
| /** |
| * Internal debug output routine. |
| * |
| * @param value The string value to output. |
| */ |
| void debugOut(String message) { |
| debugStream.println("POP3Store DEBUG: " + message); |
| } |
| |
| /** |
| * Internal debugging routine for reporting exceptions. |
| * |
| * @param message A message associated with the exception context. |
| * @param e The received exception. |
| */ |
| void debugOut(String message, Throwable e) { |
| debugOut("Received exception -> " + message); |
| debugOut("Exception message -> " + e.getMessage()); |
| e.printStackTrace(debugStream); |
| } |
| |
| /** |
| * Finalizer to perform IMAPStore() cleanup when |
| * no longer in use. |
| * |
| * @exception Throwable |
| */ |
| protected void finalize() throws Throwable { |
| super.finalize(); |
| close(); |
| } |
| } |