blob: f5d2d73391c0808065245f33e7a8e02d44a6e65d [file] [log] [blame]
/*
* 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();
}
}