FileDocCategorySizeDatePackage
IMAPStore.javaAPI DocJavaMail 1.4.360208Tue Nov 17 10:38:10 GMT 2009com.sun.mail.imap

IMAPStore

public class IMAPStore extends Store implements QuotaAwareStore, ResponseHandler
This class provides access to an IMAP message store.

Applications that need to make use of IMAP-specific features may cast a Store object to an IMAPStore object and use the methods on this class. The {@link #getQuota getQuota} and {@link #setQuota setQuota} methods support the IMAP QUOTA extension. Refer to RFC 2087 for more information.

See the com.sun.mail.imap package documentation for further information on the IMAP protocol provider.

WARNING: The APIs unique to this class should be considered EXPERIMENTAL. They may be changed in the future in ways that are incompatible with applications using the current APIs.

author
John Mani
author
Bill Shannon
author
Jim Glennon

Fields Summary
public static final int
RESPONSE
A special event type for a StoreEvent to indicate an IMAP response, if the mail.imap.enableimapevents property is set.
private final String
name
private final int
defaultPort
private final boolean
isSSL
private final int
blksize
private final int
statusCacheTimeout
private final int
appendBufferSize
private final int
minIdleTime
private int
port
private String
host
private String
user
private String
password
private String
proxyAuthUser
private String
authorizationID
private String
saslRealm
private Namespaces
namespaces
private boolean
disableAuthLogin
private boolean
disableAuthPlain
private boolean
disableAuthNtlm
private boolean
enableStartTLS
private boolean
requireStartTLS
private boolean
enableSASL
private String[]
saslMechanisms
private boolean
forcePasswordRefresh
private boolean
enableImapEvents
private volatile boolean
connectionFailed
private volatile boolean
forceClose
private final Object
connectionFailedLock
private PrintStream
out
private boolean
messageCacheDebug
private final ConnectionPool
pool
private ResponseHandler
nonStoreResponseHandler
A special response handler for connections that are being used to perform operations on behalf of an object other than the Store. It DOESN'T cause the Store to be cleaned up if a BYE is seen. The BYE may be real or synthetic and in either case just indicates that the connection is dead.
Constructors Summary
public IMAPStore(Session session, URLName url)
Constructor that takes a Session object and a URLName that represents a specific IMAP server.

 
                        
         
	this(session, url, "imap", false);
    
protected IMAPStore(Session session, URLName url, String name, boolean isSSL)
Constructor used by this class and by IMAPSSLStore subclass.

	super(session, url); // call super constructor
	if (url != null)
	    name = url.getProtocol();
	this.name = name;
	if (!isSSL)
	    isSSL = PropUtil.getBooleanSessionProperty(session,
				"mail." + name + ".ssl.enable", false);
	if (isSSL)
	    this.defaultPort = 993;
	else
	    this.defaultPort = 143;
	this.isSSL = isSSL;

        debug = session.getDebug();
	out = session.getDebugOut();
	if (out == null)	// should never happen
	    out = System.out;

	boolean partialFetch = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".partialfetch", true);
	if (!partialFetch) {
	    blksize = -1;
	    if (debug)
		out.println("DEBUG: mail.imap.partialfetch: false");
	} else {
	    blksize = PropUtil.getIntSessionProperty(session,
		"mail." + name +".fetchsize", 1024 * 16);
	    if (debug)
		out.println("DEBUG: mail.imap.fetchsize: " + blksize);
	}

	statusCacheTimeout = PropUtil.getIntSessionProperty(session,
	    "mail." + name + ".statuscachetimeout", 1000);
	if (debug)
	    out.println("DEBUG: mail.imap.statuscachetimeout: " +
						statusCacheTimeout);

	appendBufferSize = PropUtil.getIntSessionProperty(session,
	    "mail." + name + ".appendbuffersize", -1);
	if (debug)
	    out.println("DEBUG: mail.imap.appendbuffersize: " +
						appendBufferSize);

	minIdleTime = PropUtil.getIntSessionProperty(session,
	    "mail." + name + ".minidletime", 10);
	if (debug)
	    out.println("DEBUG: mail.imap.minidletime: " + minIdleTime);

	// check if we should do a PROXYAUTH login
	String s = session.getProperty("mail." + name + ".proxyauth.user");
	if (s != null) {
	    proxyAuthUser = s;
	    if (debug)
		out.println("DEBUG: mail.imap.proxyauth.user: " +
						proxyAuthUser);
	}

	// check if AUTH=LOGIN is disabled
	disableAuthLogin = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".auth.login.disable", false);
	if (debug && disableAuthLogin)
	    out.println("DEBUG: disable AUTH=LOGIN");

	// check if AUTH=PLAIN is disabled
	disableAuthPlain = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".auth.plain.disable", false);
	if (debug && disableAuthPlain)
	    out.println("DEBUG: disable AUTH=PLAIN");

	// check if AUTH=NTLM is disabled
	disableAuthNtlm = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".auth.ntlm.disable", false);
	if (debug && disableAuthNtlm)
	    out.println("DEBUG: disable AUTH=NTLM");

	// check if STARTTLS is enabled
	enableStartTLS = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".starttls.enable", false);
	if (debug && enableStartTLS)
	    out.println("DEBUG: enable STARTTLS");

	// check if STARTTLS is required
	requireStartTLS = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".starttls.required", false);
	if (debug && requireStartTLS)
	    out.println("DEBUG: require STARTTLS");

	// check if SASL is enabled
	enableSASL = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".sasl.enable", false);
	if (debug && enableSASL)
	    out.println("DEBUG: enable SASL");

	// check if SASL mechanisms are specified
	if (enableSASL) {
	    s = session.getProperty("mail." + name + ".sasl.mechanisms");
	    if (s != null && s.length() > 0) {
		if (debug)
		    out.println("DEBUG: SASL mechanisms allowed: " + s);
		Vector v = new Vector(5);
		StringTokenizer st = new StringTokenizer(s, " ,");
		while (st.hasMoreTokens()) {
		    String m = st.nextToken();
		    if (m.length() > 0)
			v.addElement(m);
		}
		saslMechanisms = new String[v.size()];
		v.copyInto(saslMechanisms);
	    }
	}

	// check if an authorization ID has been specified
	s = session.getProperty("mail." + name + ".sasl.authorizationid");
	if (s != null) {
	    authorizationID = s;
	    if (debug)
		out.println("DEBUG: mail.imap.sasl.authorizationid: " +
						authorizationID);
	}

	// check if a SASL realm has been specified
	s = session.getProperty("mail." + name + ".sasl.realm");
	if (s != null) {
	    saslRealm = s;
	    if (debug)
		out.println("DEBUG: mail.imap.sasl.realm: " + saslRealm);
	}

	// check if forcePasswordRefresh is enabled
	forcePasswordRefresh = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".forcepasswordrefresh", false);
	if (debug && forcePasswordRefresh)
	    out.println("DEBUG: enable forcePasswordRefresh");

	// check if enableimapevents is enabled
	enableImapEvents = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".enableimapevents", false);
	if (debug && enableImapEvents)
	    out.println("DEBUG: enable IMAP events");

	// check if message cache debugging set
	messageCacheDebug = PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".messagecache.debug", false);

	pool = new ConnectionPool(name, session);
    
Methods Summary
booleanallowReadOnlySelect()
If a SELECT succeeds, but indicates that the folder is READ-ONLY, and the user asked to open the folder READ_WRITE, do we allow the open to succeed?

	return PropUtil.getBooleanSessionProperty(session,
	    "mail." + name + ".allowreadonlyselect", false);
    
private voidcheckConnected()

	assert Thread.holdsLock(this);
	if (!super.isConnected())
	    throw new IllegalStateException("Not connected");
    
private synchronized voidcleanup()
Cleanup before dying.

	// if we're not connected, someone beat us to it
	if (!super.isConnected()) {
	    if (debug)
		out.println("DEBUG: IMAPStore cleanup, not connected");
	    return;
	}

	/*
	 * If forceClose is true, some thread ran into an error that suggests
	 * the server might be dead, so we force the folders to close
	 * abruptly without waiting for the server.  Used when
	 * the store connection times out, for example.
	 */
	boolean force;
	synchronized (connectionFailedLock) {
	    force = forceClose;
	    forceClose = false;
	    connectionFailed = false;
	}
	if (debug)
	    out.println("DEBUG: IMAPStore cleanup, force " + force);

        Vector foldersCopy = null;
        boolean done = true;

	// To avoid violating the locking hierarchy, there's no lock we
	// can hold that prevents another thread from trying to open a
	// folder at the same time we're trying to close all the folders.
	// Thus, there's an inherent race condition here.  We close all
	// the folders we know about and then check whether any new folders
	// have been opened in the mean time.  We keep trying until we're
	// successful in closing all the folders.
	for (;;) {
	    // Make a copy of the folders list so we do not violate the
	    // folder-connection pool locking hierarchy.
	    synchronized (pool) {
		if (pool.folders != null) {
		    done = false;
		    foldersCopy = pool.folders;
		    pool.folders = null;
		} else {
                    done = true;
                }
	    }
	    if (done)
		break;

	    // Close and remove any open folders under this Store.
	    for (int i = 0, fsize = foldersCopy.size(); i < fsize; i++) {
		IMAPFolder f = (IMAPFolder)foldersCopy.elementAt(i);

		try {
		    if (force) {
			if (debug)
			    out.println("DEBUG: force folder to close");
			// Don't want to wait for folder connection to timeout
			// (if, for example, the server is down) so we close
			// folders abruptly.
			f.forceClose();
		    } else {
			if (debug)
			    out.println("DEBUG: close folder");
			f.close(false);
		    }
		} catch (MessagingException mex) {
		    // Who cares ?! Ignore 'em.
		} catch (IllegalStateException ex) {
		    // Ditto
		}
	    }

	}

        synchronized (pool) {
	    emptyConnectionPool(force);
	}

	// to set the state and send the closed connection event
	try {
	    super.close();
	} catch (MessagingException mex) { }
	if (debug)
	    out.println("DEBUG: IMAPStore cleanup done");
    
public synchronized voidclose()
Close this Store.

	if (!super.isConnected()) // Already closed.
	    return;

        IMAPProtocol protocol = null;
	try {
	    boolean isEmpty;
	    synchronized (pool) {
		// If there's no authenticated connections available
		// don't create a new one
		isEmpty = pool.authenticatedConnections.isEmpty();
	    }
	    /*
	     * Have to drop the lock before calling cleanup.
	     * Yes, there's a potential race here.  The pool could
	     * become empty after we check, in which case we'll just
	     * waste time getting a new connection and closing it.
	     * Or, the pool could be empty now and not empty by the
	     * time we get into cleanup, but that's ok because cleanup
	     * will just close the connection.
	     */
	    if (isEmpty) {
		if (pool.debug)
		    out.println("DEBUG: close() - no connections ");
		cleanup();
		return;
	    }

            protocol = getStoreProtocol();
	    /*
	     * We have to remove the protocol from the pool so that,
	     * when our response handler processes the BYE response
	     * and calls cleanup, which calls emptyConnection, that
	     * we don't try to log out this connection twice.
	     */
	    synchronized (pool) {
                pool.authenticatedConnections.removeElement(protocol);
	    }

	    /*
	     * LOGOUT. 
	     *
	     * Note that protocol.logout() closes the server socket
	     * connection, regardless of what happens ..
	     *
	     * Also note that protocol.logout() results in a BYE
	     * response (As per rfc 2060, BYE is a *required* response
	     * to LOGOUT). In fact, even if protocol.logout() fails
	     * with an IOException (if the server connection is dead),
	     * iap.Protocol.command() converts that exception into a 
	     * BYE response. So, I depend on my BYE handler to set the
	     * flag that causes releaseStoreProtocol to do the
	     * Store cleanup.
	     */
	    protocol.logout();
	} catch (ProtocolException pex) { 
	    // Hmm .. will this ever happen ?
	    throw new MessagingException(pex.getMessage(), pex);
        } finally {
            releaseStoreProtocol(protocol);
        }
    
private voidemptyConnectionPool(boolean force)
Empty the connection pool.


        synchronized (pool) {
            for (int index = pool.authenticatedConnections.size() - 1;
		    index >= 0; --index) {
                try {
		    IMAPProtocol p = (IMAPProtocol)
			pool.authenticatedConnections.elementAt(index);
		    p.removeResponseHandler(this);
		    if (force)
			p.disconnect();
		    else
			p.logout();
                } catch (ProtocolException pex) {};
            }

            pool.authenticatedConnections.removeAllElements();
        }
        
        if (pool.debug)
            out.println("DEBUG: removed all authenticated connections");
    
protected voidfinalize()

	super.finalize();
	close();
    
intgetAppendBufferSize()
Get the maximum size of a message to buffer for append.

	return appendBufferSize;
    
booleangetConnectionPoolDebug()
Report whether connection pool debugging is enabled.

        return pool.debug; 
    
public synchronized javax.mail.FoldergetDefaultFolder()
Get the default folder, representing the root of this user's namespace. Returns a closed DefaultFolder object.

	checkConnected();
	return new DefaultFolder(this);
    
intgetFetchBlockSize()
Get the block size to use for fetch requests on this Store.

	return blksize;
    
public synchronized javax.mail.FoldergetFolder(java.lang.String name)
Get named folder. Returns a new, closed IMAPFolder.

	checkConnected();
	return new IMAPFolder(name, IMAPFolder.UNKNOWN_SEPARATOR, this);
    
public synchronized javax.mail.FoldergetFolder(javax.mail.URLName url)
Get named folder. Returns a new, closed IMAPFolder.

	checkConnected();
	return new IMAPFolder(url.getFile(), 
			      IMAPFolder.UNKNOWN_SEPARATOR, 
			      this);
    
com.sun.mail.imap.protocol.IMAPProtocolgetFolderStoreProtocol()
Get a store protocol object for use by a folder.

	IMAPProtocol p = getStoreProtocol();
	p.removeResponseHandler(this);
	p.addResponseHandler(nonStoreResponseHandler);
	return p;
    
booleangetMessageCacheDebug()
Report whether message cache debugging is enabled.

        return messageCacheDebug; 
    
intgetMinIdleTime()
Get the minimum amount of time to delay when returning from idle.

	return minIdleTime;
    
private synchronized com.sun.mail.imap.protocol.NamespacesgetNamespaces()

	checkConnected();

        IMAPProtocol p = null;

	if (namespaces == null) {
	    try {
                p = getStoreProtocol();
		namespaces = p.namespace();
	    } catch (BadCommandException bex) { 
		// NAMESPACE not supported, ignore it
	    } catch (ConnectionException cex) {
		throw new StoreClosedException(this, cex.getMessage());
	    } catch (ProtocolException pex) { 
		throw new MessagingException(pex.getMessage(), pex);
	    } finally {
		releaseStoreProtocol(p);
	    }
	}
	return namespaces;
    
public javax.mail.Folder[]getPersonalNamespaces()
Using the IMAP NAMESPACE command (RFC 2342), return a set of folders representing the Personal namespaces.

	Namespaces ns = getNamespaces();
	if (ns == null || ns.personal == null)
	    return super.getPersonalNamespaces();
	return namespaceToFolders(ns.personal, null);
    
com.sun.mail.imap.protocol.IMAPProtocolgetProtocol(com.sun.mail.imap.IMAPFolder folder)

	IMAPProtocol p = null;

	// keep looking for a connection until we get a good one
	while (p == null) {
 
        // New authenticated protocol objects are either acquired
        // from the connection pool, or created when the pool is
        // empty or no connections are available. None are available
        // if the current pool size is one and the separate store
        // property is set or the connection is in use.

        synchronized (pool) {

            // If there's none available in the pool,
            // create a new one.
            if (pool.authenticatedConnections.isEmpty() ||
                (pool.authenticatedConnections.size() == 1 &&
                (pool.separateStoreConnection || pool.storeConnectionInUse))) {

                if (debug)
                    out.println("DEBUG: no connections in the pool, " +
                                       "creating a new one");
                try {
		    if (forcePasswordRefresh)
			refreshPassword();
                    // Use cached host, port and timeout values.
                    p = new IMAPProtocol(name, host, port,
                                         session.getDebug(),
                                         session.getDebugOut(),
                                         session.getProperties(),
					 isSSL
                                        );
                    // Use cached auth info
                    login(p, user, password);
                } catch(Exception ex1) {
                    if (p != null)
                        try {
                            p.disconnect();
                        } catch (Exception ex2) { }
                    p = null;
                }
                 
                if (p == null)
                    throw new MessagingException("connection failure");
            } else {
                if (debug)
                    out.println("DEBUG: connection available -- size: " +
                        pool.authenticatedConnections.size());

                // remove the available connection from the Authenticated queue
                p = (IMAPProtocol)pool.authenticatedConnections.lastElement();
                pool.authenticatedConnections.removeElement(p);

		// check if the connection is still live
		long lastUsed = System.currentTimeMillis() - p.getTimestamp();
		if (lastUsed > pool.serverTimeoutInterval) {
		    try {
			/*
			 * Swap in a special response handler that will handle
			 * alerts, but won't cause the store to be closed and
			 * cleaned up if the connection is dead.
			 */
			p.removeResponseHandler(this);
			p.addResponseHandler(nonStoreResponseHandler);
			p.noop();
			p.removeResponseHandler(nonStoreResponseHandler);
			p.addResponseHandler(this);
		    } catch (ProtocolException pex) {
			try {
			    p.removeResponseHandler(nonStoreResponseHandler);
			    p.disconnect();
			} finally {
			    // don't let any exception stop us
			    p = null;
			    continue;	// try again, from the top
			}
		    }
		}

                // remove the store as a response handler.
                p.removeResponseHandler(this);
	    }

            // check if we need to look for client-side timeouts
            timeoutConnections();

	    // Add folder to folder-list
	    if (folder != null) {
                if (pool.folders == null)
                    pool.folders = new Vector();
		pool.folders.addElement(folder);
	    }
        }

	}
	
	return p;
    
public synchronized javax.mail.Quota[]getQuota(java.lang.String root)
Get the quotas for the named quota root. Quotas are controlled on the basis of a quota root, not (necessarily) a folder. The relationship between folders and quota roots depends on the IMAP server. Some servers might implement a single quota root for all folders owned by a user. Other servers might implement a separate quota root for each folder. A single folder can even have multiple quota roots, perhaps controlling quotas for different resources.

param
root the name of the quota root
return
array of Quota objects
exception
MessagingException if the server doesn't support the QUOTA extension

	checkConnected();
	Quota[] qa = null;

        IMAPProtocol p = null;
	try {
	    p = getStoreProtocol();
	    qa = p.getQuotaRoot(root);
	} catch (BadCommandException bex) {
	    throw new MessagingException("QUOTA not supported", bex);
	} catch (ConnectionException cex) {
	    throw new StoreClosedException(this, cex.getMessage());
	} catch (ProtocolException pex) {
	    throw new MessagingException(pex.getMessage(), pex);
	} finally {
	    releaseStoreProtocol(p);
	}
	return qa;
    
javax.mail.SessiongetSession()
Get a reference to the session.

        return session;
    
public javax.mail.Folder[]getSharedNamespaces()
Using the IMAP NAMESPACE command (RFC 2342), return a set of folders representing the Shared namespaces.

	Namespaces ns = getNamespaces();
	if (ns == null || ns.shared == null)
	    return super.getSharedNamespaces();
	return namespaceToFolders(ns.shared, null);
    
intgetStatusCacheTimeout()
Get the number of milliseconds to cache STATUS response.

	return statusCacheTimeout;
    
private com.sun.mail.imap.protocol.IMAPProtocolgetStoreProtocol()
Get this Store's protocol connection. When acquiring a store protocol object, it is important to use the following steps: IMAPProtocol p = null; try { p = getStoreProtocol(); // perform the command } catch (ConnectionException cex) { throw new StoreClosedException(this, cex.getMessage()); } catch (WhateverException ex) { // handle it } finally { releaseStoreProtocol(p); }

        IMAPProtocol p = null;

	while (p == null) {
        synchronized (pool) {
	    waitIfIdle();

            // If there's no authenticated connections available create a 
            // new one and place it in the authenticated queue.
            if (pool.authenticatedConnections.isEmpty()) {
                if (pool.debug)
                    out.println("DEBUG: getStoreProtocol() - no connections " +
                        "in the pool, creating a new one");
                try {
		    if (forcePasswordRefresh)
			refreshPassword();
                    // Use cached host, port and timeout values.
                    p = new IMAPProtocol(name, host, port,
                                         session.getDebug(),
                                         session.getDebugOut(),
                                         session.getProperties(),
					 isSSL
                                        );
                    // Use cached auth info
                    login(p, user, password);
                } catch(Exception ex1) {
                    if (p != null)
                        try {
                            p.logout();
                        } catch (Exception ex2) { }
                    p = null;
                }
 
                if (p == null)
                    throw new ConnectionException(
				"failed to create new store connection");
             
	        p.addResponseHandler(this);
                pool.authenticatedConnections.addElement(p);
 
            } else {
                // Always use the first element in the Authenticated queue.
                if (pool.debug)
                    out.println("DEBUG: getStoreProtocol() - " +
                        "connection available -- size: " +
                        pool.authenticatedConnections.size());
                p = (IMAPProtocol)pool.authenticatedConnections.firstElement();
            }
 
	    if (pool.storeConnectionInUse) {
		try {
		    // someone else is using the connection, give up
		    // and wait until they're done
		    p = null;
		    pool.wait();
		} catch (InterruptedException ex) { }
	    } else {
		pool.storeConnectionInUse = true;

		if (pool.debug) 
		    out.println("DEBUG: getStoreProtocol() -- " +
			"storeConnectionInUse");
	    }
 
            timeoutConnections();
        }
	}
	return p;
    
public javax.mail.Folder[]getUserNamespaces(java.lang.String user)
Using the IMAP NAMESPACE command (RFC 2342), return a set of folders representing the User's namespaces.

	Namespaces ns = getNamespaces();
	if (ns == null || ns.otherUsers == null)
	    return super.getUserNamespaces(user);
	return namespaceToFolders(ns.otherUsers, user);
    
public voidhandleResponse(com.sun.mail.iap.Response r)
Response handler method.

	// Any of these responses may have a response code.
	if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE())
	    handleResponseCode(r);
	if (r.isBYE()) {
	    if (debug)
		out.println("DEBUG: IMAPStore connection dead");
	    // Store's IMAP connection is dead, save the response so that
	    // releaseStoreProtocol will cleanup later.
	    synchronized (connectionFailedLock) {
		connectionFailed = true;
		if (r.isSynthetic())
		    forceClose = true;
	    }
	    return;
	}
    
voidhandleResponseCode(com.sun.mail.iap.Response r)
Handle notifications and alerts. Response must be an OK, NO, BAD, or BYE response.

	String s = r.getRest();	// get the text after the response
	boolean isAlert = false;
	if (s.startsWith("[")) {	// a response code
	    int i = s.indexOf(']");
	    // remember if it's an alert
	    if (i > 0 && s.substring(0, i + 1).equalsIgnoreCase("[ALERT]"))
		isAlert = true;
	    // strip off the response code in any event
	    s = s.substring(i + 1).trim();
	}
	if (isAlert)
	    notifyStoreListeners(StoreEvent.ALERT, s);
	else if (r.isUnTagged() && s.length() > 0)
	    // Only send notifications that come with untagged
	    // responses, and only if there is actually some
	    // text there.
	    notifyStoreListeners(StoreEvent.NOTICE, s);
    
public synchronized booleanhasCapability(java.lang.String capability)
Return true if the specified capability string is in the list of capabilities the server announced.

since
JavaMail 1.3.3

        IMAPProtocol p = null;
	try {
	    p = getStoreProtocol();
            return p.hasCapability(capability);
	} catch (ProtocolException pex) {
	    throw new MessagingException(pex.getMessage(), pex);
        } finally {
            releaseStoreProtocol(p);
        }
    
booleanhasSeparateStoreConnection()
Report whether the separateStoreConnection is set.

        return pool.separateStoreConnection;
    
public voididle()
Use the IMAP IDLE command (see RFC 2177), if supported by the server, to enter idle mode so that the server can send unsolicited notifications without the need for the client to constantly poll the server. Use a ConnectionListener to be notified of events. When another thread (e.g., the listener thread) needs to issue an IMAP comand for this Store, the idle mode will be terminated and this method will return. Typically the caller will invoke this method in a loop.

If the mail.imap.enableimapevents property is set, notifications received while the IDLE command is active will be delivered to ConnectionListeners as events with a type of IMAPStore.RESPONSE. The event's message will be the raw IMAP response string. Note that most IMAP servers will not deliver any events when using the IDLE command on a connection with no mailbox selected (i.e., this method). In most cases you'll want to use the idle method on IMAPFolder.

NOTE: This capability is highly experimental and likely will change in future releases.

The mail.imap.minidletime property enforces a minimum delay before returning from this method, to ensure that other threads have a chance to issue commands before the caller invokes this method again. The default delay is 10 milliseconds.

exception
MessagingException if the server doesn't support the IDLE extension
exception
IllegalStateException if the store isn't connected
since
JavaMail 1.4.1

	IMAPProtocol p = null;
	// ASSERT: Must NOT be called with the connection pool
	// synchronization lock held.
	assert !Thread.holdsLock(pool);
	synchronized (this) {
	    checkConnected();
	}
	try {
	    synchronized (pool) {
		p = getStoreProtocol();
		if (pool.idleState == ConnectionPool.RUNNING) {
		    p.idleStart();
		    pool.idleState = ConnectionPool.IDLE;
		} else {
		    // some other thread must be running the IDLE
		    // command, we'll just wait for it to finish
		    // without aborting it ourselves
		    try {
			// give up lock and wait to be not idle
			pool.wait();
		    } catch (InterruptedException ex) { }
		    return;
		}
		pool.idleProtocol = p;
	    }

	    /*
	     * We gave up the pool lock so that other threads
	     * can get into the pool far enough to see that we're
	     * in IDLE and abort the IDLE.
	     *
	     * Now we read responses from the IDLE command, especially
	     * including unsolicited notifications from the server.
	     * We don't hold the pool lock while reading because
	     * it protects the idleState and other threads need to be
	     * able to examine the state.
	     *
	     * We hold the pool lock while processing the responses.
	     */
	    for (;;) {
		Response r = p.readIdleResponse();
		synchronized (pool) {
		    if (r == null || !p.processIdleResponse(r)) {
			pool.idleState = ConnectionPool.RUNNING;
			pool.notifyAll();
			break;
		    }
		}
		if (enableImapEvents && r.isUnTagged()) {
		    notifyStoreListeners(IMAPStore.RESPONSE, r.toString());
		}
	    }

	    /*
	     * Enforce a minimum delay to give time to threads
	     * processing the responses that came in while we
	     * were idle.
	     */
	    int minidle = getMinIdleTime();
	    if (minidle > 0) {
		try {
		    Thread.sleep(minidle);
		} catch (InterruptedException ex) { }
	    }

	} catch (BadCommandException bex) {
	    throw new MessagingException("IDLE not supported", bex);
	} catch (ConnectionException cex) {
	    throw new StoreClosedException(this, cex.getMessage());
	} catch (ProtocolException pex) {
	    throw new MessagingException(pex.getMessage(), pex);
	} finally {
	    synchronized (pool) {
		pool.idleProtocol = null;
	    }
	    releaseStoreProtocol(p);
	}
    
public synchronized booleanisConnected()
Check whether this store is connected. Override superclass method, to actually ping our server connection.

	if (!super.isConnected()) {
	    // if we haven't been connected at all, don't bother with
	    // the NOOP.
	    return false;
	}

	/*
	 * The below noop() request can:
	 * (1) succeed - in which case all is fine.
	 *
	 * (2) fail because the server returns NO or BAD, in which
	 * 	case we ignore it since we can't really do anything.
	 * (2) fail because a BYE response is obtained from the 
	 *	server
	 * (3) fail because the socket.write() to the server fails,
	 *	in which case the iap.protocol() code converts the
	 *	IOException into a BYE response.
	 *
	 * Thus, our BYE handler will take care of closing the Store
	 * in case our connection is really gone.
	 */
   
        IMAPProtocol p = null;
	try {
	    p = getStoreProtocol();
            p.noop();
	} catch (ProtocolException pex) {
	    // will return false below
        } finally {
            releaseStoreProtocol(p);
        }


	return super.isConnected();
    
booleanisConnectionPoolFull()
Report whether the connection pool is full.


        synchronized (pool) {
            if (pool.debug)
                out.println("DEBUG: current size: " +
                    pool.authenticatedConnections.size() + 
                    "   pool size: " + pool.poolSize);

            return (pool.authenticatedConnections.size() >= pool.poolSize);

        }
    
private voidlogin(com.sun.mail.imap.protocol.IMAPProtocol p, java.lang.String u, java.lang.String pw)

	// turn on TLS if it's been enabled or required and is supported
	if (enableStartTLS || requireStartTLS) {
	    if (p.hasCapability("STARTTLS")) {
		p.startTLS();
		// if startTLS succeeds, refresh capabilities
		p.capability();
	    } else if (requireStartTLS) {
		if (debug)
                    out.println("DEBUG: STARTTLS required but not supported");
		throw new ProtocolException(
		    "STARTTLS required but not supported by server");
	    }
	}
	if (p.isAuthenticated())
	    return;		// no need to login

	/*
	 * Put a special "marker" in the capabilities list so we can
	 * detect if the server refreshed the capabilities in the OK
	 * response.
	 */
	p.getCapabilities().put("__PRELOGIN__", "");
	String authzid;
	if (authorizationID != null)
	    authzid = authorizationID;
	else if (proxyAuthUser != null)
	    authzid = proxyAuthUser;
	else
	    authzid = u;

	if (enableSASL)
	    p.sasllogin(saslMechanisms, saslRealm, authzid, u, pw);

	if (p.isAuthenticated())
	    ;	// SASL login succeeded, go to bottom
	else if (p.hasCapability("AUTH=PLAIN") && !disableAuthPlain)
	    p.authplain(authzid, u, pw);
	else if ((p.hasCapability("AUTH-LOGIN") ||
		p.hasCapability("AUTH=LOGIN")) && !disableAuthLogin)
	    p.authlogin(u, pw);
	else if (p.hasCapability("AUTH=NTLM") && !disableAuthNtlm)
	    p.authntlm(authzid, u, pw);
	else if (!p.hasCapability("LOGINDISABLED"))
	    p.login(u, pw);
	else
	    throw new ProtocolException("No login methods supported!");

	if (proxyAuthUser != null)
	    p.proxyauth(proxyAuthUser);

	/*
	 * If marker is still there, capabilities haven't been refreshed,
	 * refresh them now.
	 */
	if (p.hasCapability("__PRELOGIN__")) {
	    try {
		p.capability();
	    } catch (ConnectionException cex) {
		throw cex;	// rethrow connection failures
		// XXX - assume connection has been closed
	    } catch (ProtocolException pex) {
		// ignore other exceptions that "should never happen"
	    }
	}
    
private javax.mail.Folder[]namespaceToFolders(com.sun.mail.imap.protocol.Namespaces$Namespace[] ns, java.lang.String user)

	Folder[] fa = new Folder[ns.length];
	for (int i = 0; i < fa.length; i++) {
	    String name = ns[i].prefix;
	    if (user == null) {
		// strip trailing delimiter
		int len = name.length();
		if ( len > 0 && name.charAt(len - 1) == ns[i].delimiter)
		    name = name.substring(0, len - 1);
	    } else {
		// add user
		name += user;
	    }
	    fa[i] = new IMAPFolder(name, ns[i].delimiter, this, user == null);
	}
	return fa;
    
protected synchronized booleanprotocolConnect(java.lang.String host, int pport, java.lang.String user, java.lang.String password)
Implementation of protocolConnect(). Will create a connection to the server and authenticate the user using the mechanisms specified by various properties.

The host, user, and password parameters must all be non-null. If the authentication mechanism being used does not require a password, an empty string or other suitable dummy password should be used.

        
        IMAPProtocol protocol = null;

	// check for non-null values of host, password, user
	if (host == null || password == null || user == null) {
	    if (debug)
		out.println("DEBUG: protocolConnect returning false" +
				", host=" + host +
				", user=" + user +
				", password=" + (password != null ?
					"<non-null>" : "<null>"));
	    return false;
	}

	// set the port correctly
	if (pport != -1) {
	    port = pport;
	} else {
	    port = PropUtil.getIntSessionProperty(session,
					"mail." + name + ".port", port);
	} 
	
	// use the default if needed
	if (port == -1) {
	    port = defaultPort;
	}
	
	try {
            boolean poolEmpty;
            synchronized (pool) {
                poolEmpty = pool.authenticatedConnections.isEmpty();
            }

            if (poolEmpty) {
		if (debug)
		    out.println("DEBUG: trying to connect to host \"" + host +
				"\", port " + port + ", isSSL " + isSSL);
                protocol = new IMAPProtocol(name, host, port, 
		    			    session.getDebug(),
		    			    session.getDebugOut(),
					    session.getProperties(),
					    isSSL
					   );
		if (debug)
		    out.println("DEBUG: protocolConnect login" +
				", host=" + host +
				", user=" + user +
				", password=<non-null>");
	        login(protocol, user, password);

	        protocol.addResponseHandler(this);

	        this.host = host;
	        this.user = user;
	        this.password = password;

                synchronized (pool) {
                    pool.authenticatedConnections.addElement(protocol);
                }
            }
	} catch (CommandFailedException cex) {
	    // login failure, close connection to server
	    if (protocol != null)
		protocol.disconnect();
	    protocol = null;
	    throw new AuthenticationFailedException(
					cex.getResponse().getRest());
	} catch (ProtocolException pex) { // any other exception
	    throw new MessagingException(pex.getMessage(), pex);
	} catch (IOException ioex) {
	    throw new MessagingException(ioex.getMessage(), ioex);
	} 

        return true;
    
private voidrefreshPassword()

	if (debug)
	    out.println("DEBUG: refresh password, user: " + user);
	InetAddress addr;
	try {
	    addr = InetAddress.getByName(host);
	} catch (UnknownHostException e) {
	    addr = null;
	}
	PasswordAuthentication pa =
	    session.requestPasswordAuthentication(addr, port,
					name, null, user);
	if (pa != null) {
	    user = pa.getUserName();
	    password = pa.getPassword();
	}
    
voidreleaseFolderStoreProtocol(com.sun.mail.imap.protocol.IMAPProtocol protocol)
Release a store protocol object that was being used by a folder.

	if (protocol == null)
	    return;		// should never happen
	protocol.removeResponseHandler(nonStoreResponseHandler);
	protocol.addResponseHandler(this);
        synchronized (pool) {
	    pool.storeConnectionInUse = false;
	    pool.notifyAll();	// in case anyone waiting

	    if (pool.debug)
		out.println("DEBUG: releaseFolderStoreProtocol()");

            timeoutConnections();
        }
    
voidreleaseProtocol(com.sun.mail.imap.IMAPFolder folder, com.sun.mail.imap.protocol.IMAPProtocol protocol)
Release the protocol object back to the connection pool.


        synchronized (pool) {
            if (protocol != null) {
                // If the pool is not full, add the store as a response handler
                // and return the protocol object to the connection pool.
                if (!isConnectionPoolFull()) {
                    protocol.addResponseHandler(this);
                    pool.authenticatedConnections.addElement(protocol);

                    if (debug)
                        out.println("DEBUG: added an " +
                            "Authenticated connection -- size: " +
                            pool.authenticatedConnections.size());
                } else {
                    if (debug)
                        out.println("DEBUG: pool is full, not adding " +
                            "an Authenticated connection");
                    try {
                        protocol.logout();
                    } catch (ProtocolException pex) {};
                }
            }

            if (pool.folders != null)
                pool.folders.removeElement(folder);

            timeoutConnections();
        }
    
private voidreleaseStoreProtocol(com.sun.mail.imap.protocol.IMAPProtocol protocol)
Release the store connection.


	// will be called from idle() without the Store lock held,
	// but cleanup is synchronized and will acquire the Store lock

	if (protocol == null) {
	    cleanup();		// failed to ever get the connection
	    return;		// nothing to release
	}

	/*
	 * Read out the flag that says whether this connection failed
	 * before releasing the protocol object for others to use.
	 */
	boolean failed;
	synchronized (connectionFailedLock) {
	    failed = connectionFailed;
	    connectionFailed = false;	// reset for next use
	}

	// now free the store connection
        synchronized (pool) {
	    pool.storeConnectionInUse = false;
	    pool.notifyAll();	// in case anyone waiting

	    if (pool.debug)
		out.println("DEBUG: releaseStoreProtocol()");

            timeoutConnections();
        }

	/*
	 * If the connection died while we were using it, clean up.
	 * It's critical that the store connection be freed and the
	 * connection pool not be locked while we do this.
	 */
	assert !Thread.holdsLock(pool);
	if (failed)
	    cleanup();
    
public synchronized voidsetPassword(java.lang.String password)
Set the password that will be used for subsequent connections after this Store is first connected (for example, when creating a connection to open a Folder). This value is overridden by any call to the Store's connect method.

Most applications will never need to use this method.

since
JavaMail 1.3.3

	this.password = password;
    
public synchronized voidsetQuota(javax.mail.Quota quota)
Set the quotas for the quota root specified in the quota argument. Typically this will be one of the quota roots obtained from the getQuota method, but it need not be.

param
quota the quota to set
exception
MessagingException if the server doesn't support the QUOTA extension

	checkConnected();
        IMAPProtocol p = null;
	try {
	    p = getStoreProtocol();
	    p.setQuota(quota);
	} catch (BadCommandException bex) {
	    throw new MessagingException("QUOTA not supported", bex);
	} catch (ConnectionException cex) {
	    throw new StoreClosedException(this, cex.getMessage());
	} catch (ProtocolException pex) {
	    throw new MessagingException(pex.getMessage(), pex);
	} finally {
	    releaseStoreProtocol(p);
	}
    
public synchronized voidsetUsername(java.lang.String user)
Set the user name that will be used for subsequent connections after this Store is first connected (for example, when creating a connection to open a Folder). This value is overridden by any call to the Store's connect method.

Some IMAP servers may provide an authentication ID that can be used for more efficient authentication for future connections. This authentication ID is provided in a server-specific manner not described here.

Most applications will never need to use this method.

since
JavaMail 1.3.3

	this.user = user;
    
private voidtimeoutConnections()
Check to see if it's time to shrink the connection pool.


        synchronized (pool) {

            // If we've exceeded the pruning interval, look for stale
            // connections to logout.
            if (System.currentTimeMillis() - pool.lastTimePruned > 
                pool.pruningInterval && 
                pool.authenticatedConnections.size() > 1) {

                if (pool.debug) {
                    out.println("DEBUG: checking for connections " +
                        "to prune: " +
                        (System.currentTimeMillis() - pool.lastTimePruned));
                    out.println("DEBUG: clientTimeoutInterval: " +
                        pool.clientTimeoutInterval);
                }   
 
                IMAPProtocol p;
 
                // Check the timestamp of the protocol objects in the pool and
                // logout if the interval exceeds the client timeout value
                // (leave the first connection).
                for (int index = pool.authenticatedConnections.size() - 1; 
                     index > 0; index--) {
                    p = (IMAPProtocol)pool.authenticatedConnections.
                        elementAt(index);
                    if (pool.debug) {
                        out.println("DEBUG: protocol last used: " +
                            (System.currentTimeMillis() - p.getTimestamp()));
                    }
                    if (System.currentTimeMillis() - p.getTimestamp() >
                        pool.clientTimeoutInterval) {
 
                        if (pool.debug) {
                            out.println("DEBUG: authenticated " +
                                "connection timed out");
                            out.println("DEBUG: logging out " + 
                                "the connection");
                        }
 
                        p.removeResponseHandler(this);
                        pool.authenticatedConnections.removeElementAt(index);

                        try {
                            p.logout();
                        } catch (ProtocolException pex) {}
                    }
                }
                pool.lastTimePruned = System.currentTimeMillis();
            }
        }
    
private voidwaitIfIdle()

	assert Thread.holdsLock(pool);
	while (pool.idleState != ConnectionPool.RUNNING) {
	    if (pool.idleState == ConnectionPool.IDLE) {
		pool.idleProtocol.idleAbort();
		pool.idleState = ConnectionPool.ABORTING;
	    }
	    try {
		// give up lock and wait to be not idle
		pool.wait();
	    } catch (InterruptedException ex) { }
	}