All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.mail.imap.IMAPFolder.idle Maven / Gradle / Ivy

There is a newer version: 1.6.2
Show newest version
diff -r bb4b8c17762e mail/src/main/java/com/sun/mail/imap/IMAPFolder.java
--- a/mail/src/main/java/com/sun/mail/imap/IMAPFolder.java	Fri Feb 07 16:35:37 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/IMAPFolder.java	Mon Mar 17 14:37:41 2014 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997-2014 Oracle and/or its affiliates. All rights reserved.
  *
  * The contents of this file are subject to the terms of either the GNU
  * General Public License Version 2 only ("GPL") or the Common Development
@@ -49,6 +49,7 @@
 import java.util.NoSuchElementException;
 import java.util.logging.Level;
 import java.io.*;
+import java.nio.channels.SocketChannel;
 
 import javax.mail.*;
 import javax.mail.event.*;
@@ -264,6 +265,8 @@
     private static final int IDLE = 1;		// IDLE command in effect
     private static final int ABORTING = 2;	// IDLE command aborting
     private int idleState = RUNNING;
+    private volatile IdleManager idleManager;
+    private volatile List idleEvents;
 
     private volatile int total = -1;	// total number of messages in the
 					// message cache
@@ -2259,6 +2262,86 @@
 	hasMessageCountListener = true;
     }
 
+    // Override notification methods to accumulate IDLE events.
+
+    /**
+     * Notify all MessageCountListeners about the addition of messages
+     * into this folder. Folder implementations are expected to use this 
+     * method to broadcast MessageCount events for indicating arrival of
+     * new messages. 

+ * + * The provided implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * MessageCountListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param msgs the messages that were added + */ + @Override + protected void notifyMessageAddedListeners(Message[] msgs) { + if (idleEvents != null) { + MessageCountEvent e = new MessageCountEvent( + this, + MessageCountEvent.ADDED, + false, + msgs); + idleEvents.add(e); + } + super.notifyMessageAddedListeners(msgs); + } + + /** + * Notify all MessageCountListeners about the removal of messages + * from this Folder. Folder implementations are expected to use this + * method to broadcast MessageCount events indicating removal of + * messages.

+ * + * The provided implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * MessageCountListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param removed was the message removed by this client? + * @param msgs the messages that were removed + */ + @Override + protected void notifyMessageRemovedListeners(boolean removed, + Message[] msgs) { + if (idleEvents != null) { + MessageCountEvent e = new MessageCountEvent( + this, + MessageCountEvent.REMOVED, + removed, + msgs); + idleEvents.add(e); + } + super.notifyMessageRemovedListeners(removed, msgs); + } + + /** + * Notify all MessageChangedListeners. Folder implementations are + * expected to use this method to broadcast MessageChanged events.

+ * + * The provided implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to registered + * MessageChangedListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param type the MessageChangedEvent type + * @param msg the message that changed + */ + @Override + protected void notifyMessageChangedListeners(int type, Message msg) { + if (idleEvents != null) { + MessageChangedEvent e = new MessageChangedEvent(this, type, msg); + idleEvents.add(e); + } + super.notifyMessageChangedListeners(type, msg); + } + /*********************************************************** * UIDFolder interface methods **********************************************************/ @@ -2855,6 +2938,67 @@ * @since JavaMail 1.4.3 */ public void idle(boolean once) throws MessagingException { + synchronized (this) { + /* + * We can't support the idle method if we're using SocketChannels + * because SocketChannels don't allow simultaneous read and write. + * If we're blocked in a read waiting for IDLE responses, we can't + * send the DONE message to abort the IDLE. Sigh. + * XXX - We could do select here too, like IdleManager, instead + * of blocking in read, but that's more complicated. + */ + if (protocol != null && protocol.getChannel() != null) + throw new MessagingException( + "idle method not supported with SocketChannels"); + } + startIdle(null); + + /* + * We gave up the folder lock so that other threads + * can get into the folder 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 messageCacheLock while reading because + * it protects the idleState and other threads need to be + * able to examine the state. + * + * The messageCacheLock is held in handleIdle while processing + * the responses so that we can update the number of messages + * in the folder (for example). + */ + for (;;) { + if (!handleIdle(once)) + break; + } + + /* + * Enforce a minimum delay to give time to threads + * processing the responses that came in while we + * were idle. + */ + int minidle = ((IMAPStore)store).getMinIdleTime(); + if (minidle > 0) { + try { + Thread.sleep(minidle); + } catch (InterruptedException ex) { } + } + } + + /** + * Like {@link #idle}, but if once is true, abort the + * IDLE command after the first notification, to allow the caller + * to process any notification synchronously. + * + * @param once only do one notification? + * @exception MessagingException if the server doesn't support the + * IDLE extension + * @exception IllegalStateException if the folder isn't open + * + * @since JavaMail 1.4.3 + */ + private void XXXidle(boolean once) throws MessagingException { // ASSERT: Must NOT be called with this folder's // synchronization lock held. assert !Thread.holdsLock(this); @@ -2943,6 +3087,134 @@ } } + /** + * Start the IDLE command and put this folder into the IDLE state. + * IDLE processing is done later in handleIdle(), e.g., called from + * the IdleManager. + * + * @exception MessagingException if the server doesn't support the + * IDLE extension + * @exception IllegalStateException if the folder isn't open + * @since JavaMail 1.5.2 + */ + void startIdle(IdleManager im) throws MessagingException { + // ASSERT: Must NOT be called with this folder's + // synchronization lock held. + assert !Thread.holdsLock(this); + idleManager = im; + synchronized(this) { + checkOpened(); + Boolean started = (Boolean)doOptionalCommand("IDLE not supported", + new ProtocolCommand() { + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + if (idleState == RUNNING) { + p.idleStart(); + idleState = IDLE; + return Boolean.TRUE; + } 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 + messageCacheLock.wait(); + } catch (InterruptedException ex) { } + return Boolean.FALSE; + } + } + }); + if (!started.booleanValue()) + return; + } + } + + /** + * Read a response from the server while we're in the IDLE state. + * We hold the messageCacheLock while processing the + * responses so that we can update the number of messages + * in the folder (for example). + * + * @param once only do one notification? + * @return true if we should look for more IDLE responses, + * false if IDLE is done + * @exception MessagingException for errors + * @since JavaMail 1.5.2 + */ + boolean handleIdle(boolean once) throws MessagingException { + Response r = protocol.readIdleResponse(); + try { + synchronized (messageCacheLock) { + try { + if (r == null || protocol == null || + !protocol.processIdleResponse(r)) { + idleState = RUNNING; + messageCacheLock.notifyAll(); + return false; // done + } + } catch (ProtocolException pex) { + idleState = RUNNING; + messageCacheLock.notifyAll(); + throw pex; // also done + } + if (once) { + if (idleState == IDLE) { + protocol.idleAbort(); + idleState = ABORTING; + } + } + } + } catch (ConnectionException cex) { + // Oops, the store or folder died on us. + throwClosedException(cex); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + return true; + } + + /** + * Read a response from the server while we're in the IDLE state. + * We hold the messageCacheLock while processing the + * responses so that we can update the number of messages + * in the folder (for example). + * + * @return a list of events if we should look for more IDLE responses, + * null if IDLE is done + * @exception MessagingException for errors + * @since JavaMail 1.5.2 + */ + List handleIdleEvents() throws MessagingException { + List ret = null; + Response r = protocol.readIdleResponse(); + synchronized (messageCacheLock) { + try { + idleEvents = new ArrayList(); + try { + if (r == null || protocol == null || + !protocol.processIdleResponse(r)) { + idleState = RUNNING; + messageCacheLock.notifyAll(); + return null; // done + } + } catch (ProtocolException pex) { + idleState = RUNNING; + messageCacheLock.notifyAll(); + throw pex; // also done + } + ret = idleEvents; + } catch (ConnectionException cex) { + // Oops, the store or folder died on us. + throwClosedException(cex); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + idleEvents = null; + } + } + return ret; + } + /* * If an IDLE command is in progress, abort it if necessary, * and wait until it completes. @@ -2952,7 +3224,11 @@ assert Thread.holdsLock(messageCacheLock); while (idleState != RUNNING) { if (idleState == IDLE) { - protocol.idleAbort(); + IdleManager im = idleManager; + if (im != null) + im.requestAbort(this); + else + protocol.idleAbort(); idleState = ABORTING; } try { @@ -2962,6 +3238,23 @@ } } + /* + * Send the DONE command that aborts the IDLE; used by IdleManager. + */ + void idleAbort() { + idleManager = null; // don't need it anymore + if (protocol != null) // should always be true + protocol.idleAbort(); + } + + /** + * Return the SocketChannel for this connection, if any, for use + * in IdleManager. + */ + SocketChannel getChannel() { + return protocol != null ? protocol.getChannel() : null; + } + /** * Send the IMAP ID command (if supported by the server) and return * the result from the server. The ID command identfies the client @@ -3056,7 +3349,7 @@ total += count; // avoid instantiating Message objects if no listeners. - if (hasMessageCountListener) { + if (hasMessageCountListener || idleEvents != null) { for (int i = 0; i < count; i++) msgs[i] = messageCache.getMessage(++oldtotal); @@ -3069,7 +3362,8 @@ int seqnum = ir.getNumber(); Message[] msgs = null; - if (doExpungeNotification && hasMessageCountListener) { + if (doExpungeNotification && + (hasMessageCountListener || idleEvents != null)) { // save the Message object first; can't look it // up after it's expunged msgs = new Message[] { getMessageBySeqNumber(seqnum) }; @@ -3101,7 +3395,8 @@ if (m.getMessageNumber() > 0) messageCache.expungeMessage(m.getMessageNumber()); } - if (doExpungeNotification && hasMessageCountListener) { + if (doExpungeNotification && + (hasMessageCountListener || idleEvents != null)) { notifyMessageRemovedListeners(true, msgs); } } // else if (EARLIER), ignore





© 2015 - 2024 Weber Informatics LLC | Privacy Policy