📄 imapstore.java
字号:
// 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);
}
connected = false;
notifyConnectionListeners(ConnectionEvent.CLOSED);
if (debug)
out.println("DEBUG: IMAPStore cleanup done");
}
/**
* Get the default folder, representing the root of this user's
* namespace. Returns a closed DefaultFolder object.
*/
public synchronized Folder getDefaultFolder() throws MessagingException {
checkConnected();
return new DefaultFolder(this);
}
/**
* Get named folder. Returns a new, closed IMAPFolder.
*/
public synchronized Folder getFolder(String name)
throws MessagingException {
checkConnected();
return new IMAPFolder(name, IMAPFolder.UNKNOWN_SEPARATOR, this);
}
/**
* Get named folder. Returns a new, closed IMAPFolder.
*/
public synchronized Folder getFolder(URLName url)
throws MessagingException {
checkConnected();
return new IMAPFolder(url.getFile(),
IMAPFolder.UNKNOWN_SEPARATOR,
this);
}
/**
* Using the IMAP NAMESPACE command (RFC 2342), return a set
* of folders representing the Personal namespaces.
*/
public Folder[] getPersonalNamespaces() throws MessagingException {
Namespaces ns = getNamespaces();
if (ns == null || ns.personal == null)
return super.getPersonalNamespaces();
return namespaceToFolders(ns.personal, null);
}
/**
* Using the IMAP NAMESPACE command (RFC 2342), return a set
* of folders representing the User's namespaces.
*/
public Folder[] getUserNamespaces(String user)
throws MessagingException {
Namespaces ns = getNamespaces();
if (ns == null || ns.otherUsers == null)
return super.getUserNamespaces(user);
return namespaceToFolders(ns.otherUsers, user);
}
/**
* Using the IMAP NAMESPACE command (RFC 2342), return a set
* of folders representing the Shared namespaces.
*/
public Folder[] getSharedNamespaces() throws MessagingException {
Namespaces ns = getNamespaces();
if (ns == null || ns.shared == null)
return super.getSharedNamespaces();
return namespaceToFolders(ns.shared, null);
}
private synchronized Namespaces getNamespaces() throws MessagingException {
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);
if (p == null) { // failed to get a Store connection
// have to force Store to be closed
cleanup();
}
}
}
return namespaces;
}
private Folder[] namespaceToFolders(Namespaces.Namespace[] ns,
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;
}
/**
* 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
*/
public synchronized Quota[] getQuota(String root)
throws MessagingException {
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);
if (p == null) { // failed to get a Store connection
// have to force Store to be closed
cleanup();
}
}
return qa;
}
/**
* Set the quotas for the quota root specified in the quota argument.
* Typically this will be one of the quota roots obtained from the
* <code>getQuota</code> method, but it need not be.
*
* @param quota the quota to set
* @exception MessagingException if the server doesn't support the
* QUOTA extension
*/
public synchronized void setQuota(Quota quota) throws MessagingException {
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);
if (p == null) { // failed to get a Store connection
// have to force Store to be closed
cleanup();
}
}
}
private void checkConnected() {
assert Thread.holdsLock(this);
if (!connected) {
super.setConnected(false); // just in case
throw new IllegalStateException("Not connected");
}
}
/**
* Response handler method.
*/
public void handleResponse(Response r) {
// 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, cleanup.
if (connected) // Check if its already closed
cleanup(r.isSynthetic());
return;
}
}
/**
* Use the IMAP IDLE command (see
* <A HREF="http://www.ietf.org/rfc/rfc2177.txt">RFC 2177</A>),
* 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 <code>ConnectionListener</code> 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. <p>
*
* If the mail.imap.enableimapevents property is set, notifications
* received while the IDLE command is active will be delivered to
* <code>ConnectionListener</code>s as events with a type of
* <code>IMAPStore.RESPONSE</code>. 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
* <code>idle</code> method on <code>IMAPFolder</code>. <p>
*
* NOTE: This capability is highly experimental and likely will change
* in future releases. <p>
*
* 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
*/
public void idle() throws MessagingException {
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);
if (p == null) { // failed to get a Store connection
// have to force Store to be closed
cleanup();
}
}
}
/*
* If an IDLE command is in progress, abort it if necessary,
* and wait until it completes.
* ASSERT: Must be called with the pool's lock held.
*/
private void waitIfIdle() throws ProtocolException {
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) { }
}
}
/**
* Handle notifications and alerts.
* Response must be an OK, NO, BAD, or BYE response.
*/
void handleResponseCode(Response r) {
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);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -