commits@javamail.java.net

[javamail~mercurial:640] add IdleManager to monitor multiple folders for new messages - bug 6352

From: <shannon_at_java.net>
Date: Sat, 5 Apr 2014 00:17:54 +0000

Project: javamail
Repository: mercurial
Revision: 640
Author: shannon
Date: 2014-04-04 22:41:36 UTC
Link:

Log Message:
------------
Improve exception messages.
Fix documentation to properly indicate which are Session vs. System properties.
add ability to fetch and cache entire IMAP message - bug 6324
add ability to return server-specific STATUS responses - bug 6325
add ability to set "peek" flag for all IMAP messages - bug 6326
add ability to specify scope of event queue - bug 6327
add ability to specify an Executor to process events - bug 6328
handle multiple IMAP BODY elements in a single FETCH response - bug 6336
add IdleManager to monitor multiple folders for new messages - bug 6352


Revisions:
----------
635
636
637
638
639
640


Modified Paths:
---------------
mail/src/main/java/javax/mail/internet/ContentType.java
mail/src/main/java/javax/mail/internet/ParameterList.java
mail/src/main/java/javax/mail/internet/package.html
doc/release/CHANGES.txt
mail/src/main/java/com/sun/mail/imap/IMAPFolder.java
mail/src/main/java/com/sun/mail/imap/IMAPMessage.java
mail/src/main/java/com/sun/mail/imap/IMAPStore.java
mail/src/main/java/com/sun/mail/imap/package.html
mail/src/main/java/com/sun/mail/imap/protocol/BODY.java
mail/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java
mail/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java
mail/src/main/java/com/sun/mail/imap/protocol/Status.java
mail/src/main/java/javax/mail/EventQueue.java
mail/src/main/java/javax/mail/Folder.java
mail/src/main/java/javax/mail/Service.java
mail/src/main/java/javax/mail/Session.java
mail/src/main/java/javax/mail/package.html
mail/src/main/java/com/sun/mail/iap/Protocol.java
mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java
javadoc/pom.xml
mail/src/main/java/com/sun/mail/util/SocketFetcher.java
mail/src/main/java/com/sun/mail/util/WriteTimeoutSocket.java


Added Paths:
------------
mail/src/test/java/com/sun/mail/imap/protocol/IMAPProtocolTest.java
mail/src/main/java/com/sun/mail/imap/IdleManager.java


Diffs:
------
diff -r bb4b8c17762e -r 849c84b027fb mail/src/main/java/javax/mail/internet/ContentType.java
--- a/mail/src/main/java/javax/mail/internet/ContentType.java Fri Feb 07 16:35:37 2014 -0800
+++ b/mail/src/main/java/javax/mail/internet/ContentType.java Wed Mar 05 11:39:28 2014 -0800
@@ -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
@@ -92,19 +92,22 @@
         // First "type" ..
         tk = h.next();
         if (tk.getType() != HeaderTokenizer.Token.ATOM)
- throw new ParseException("Expected MIME type, got " +
+ throw new ParseException("In Content-Type string <" + s + ">" +
+ ", expected MIME type, got " +
                                         tk.getValue());
         primaryType = tk.getValue();
 
         // The '/' separator ..
         tk = h.next();
         if ((char)tk.getType() != '/')
- throw new ParseException("Expected '/', got " + tk.getValue());
+ throw new ParseException("In Content-Type string <" + s + ">" +
+ ", expected '/', got " + tk.getValue());
 
         // Then "subType" ..
         tk = h.next();
         if (tk.getType() != HeaderTokenizer.Token.ATOM)
- throw new ParseException("Expected MIME subtype, got " +
+ throw new ParseException("In Content-Type string <" + s + ">" +
+ ", expected MIME subtype, got " +
                                         tk.getValue());
         subType = tk.getValue();
 

diff -r bb4b8c17762e -r 849c84b027fb mail/src/main/java/javax/mail/internet/ParameterList.java
--- a/mail/src/main/java/javax/mail/internet/ParameterList.java Fri Feb 07 16:35:37 2014 -0800
+++ b/mail/src/main/java/javax/mail/internet/ParameterList.java Wed Mar 05 11:39:28 2014 -0800
@@ -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
@@ -240,14 +240,16 @@
                     break;
                 // parameter name must be a MIME Atom
                 if (tk.getType() != HeaderTokenizer.Token.ATOM)
- throw new ParseException("Expected parameter name, " +
+ throw new ParseException("In parameter list <" + s + ">" +
+ ", expected parameter name, " +
                                             "got \"" + tk.getValue() + "\"");
                 name = tk.getValue().toLowerCase(Locale.ENGLISH);
 
                 // expect '='
                 tk = h.next();
                 if ((char)tk.getType() != '=')
- throw new ParseException("Expected '=', " +
+ throw new ParseException("In parameter list <" + s + ">" +
+ ", expected '=', " +
                                             "got \"" + tk.getValue() + "\"");
 
                 // expect parameter value
@@ -262,7 +264,8 @@
                 // parameter value must be a MIME Atom or Quoted String
                 if (type != HeaderTokenizer.Token.ATOM &&
                     type != HeaderTokenizer.Token.QUOTEDSTRING)
- throw new ParseException("Expected parameter value, " +
+ throw new ParseException("In parameter list <" + s + ">" +
+ ", expected parameter value, " +
                                             "got \"" + tk.getValue() + "\"");
 
                 value = tk.getValue();
@@ -289,8 +292,9 @@
                     value = lastValue + " " + tk.getValue();
                     list.put(lastName, value);
                 } else {
- throw new ParseException("Expected ';', " +
- "got \"" + tk.getValue() + "\"");
+ throw new ParseException("In parameter list <" + s + ">" +
+ ", expected ';', got \"" +
+ tk.getValue() + "\"");
                 }
             }
         }


diff -r 849c84b027fb -r b40cf4793195 mail/src/main/java/javax/mail/internet/package.html
--- a/mail/src/main/java/javax/mail/internet/package.html Wed Mar 05 11:39:28 2014 -0800
+++ b/mail/src/main/java/javax/mail/internet/package.html Fri Mar 07 16:48:40 2014 -0800
@@ -5,7 +5,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
@@ -55,12 +55,13 @@
 The IMAP, SMTP, and POP3 protocols use
 {_at_link javax.mail.internet.MimeMessage MimeMessages}.
 <P>
-The JavaMail API specification requires support for the following properties,
-which must be set in the <code>System</code> properties.
+The JavaMail API supports the following standard properties,
+which may be set in the <code>Session</code> object, or in the
+<code>Properties</code> object used to create the <code>Session</code> object.
 The properties are always set as strings; the Type column describes
 how the string is interpreted. For example, use
 <PRE>
- System.setProperty("mail.mime.address.strict", "false");
+ session.setProperty("mail.mime.address.strict", "false");
 </PRE>
 to set the <CODE>mail.mime.address.strict</CODE> property,
 which is of type boolean.
@@ -84,6 +85,24 @@
 class for details.
 </TD>
 </TR>
+</TABLE>
+<P>
+The JavaMail API specification requires support for the following properties,
+which must be set in the <code>System</code> properties.
+The properties are always set as strings; the Type column describes
+how the string is interpreted. For example, use
+<PRE>
+ System.setProperty("mail.mime.decodetext.strict", "false");
+</PRE>
+to set the <CODE>mail.mime.decodetext.strict</CODE> property,
+which is of type boolean.
+<P>
+<TABLE BORDER>
+<TR>
+<TH>Name</TH>
+<TH>Type</TH>
+<TH>Description</TH>
+</TR>
 
 <TR>
 <TD>mail.mime.charset</TD>
@@ -248,11 +267,49 @@
 </TABLE>
 
 
+<P>
+The following properties are supported by the reference implementation (RI) of
+JavaMail, but are not currently a required part of the specification.
+These must be set as <CODE>Session</CODE> properties.
+The names, types, defaults, and semantics of these properties may
+change in future releases.
+<P>
+<TABLE BORDER>
+<TR>
+<TH>Name</TH>
+<TH>Type</TH>
+<TH>Description</TH>
+</TR>
+
+<TR>
+<TD>mail.alternates</TD>
+<TD>String</TD>
+<TD>
+A string containing other email addresses that the current user is known by.
+The <code>MimeMessage</code> <code>reply</code> method will eliminate any
+of these addresses from the recipient list in the message it constructs,
+to avoid sending the reply back to the sender.
+</TD>
+</TR>
+
+<TR>
+<TD>mail.replyallcc</TD>
+<TD>boolean</TD>
+<TD>
+If set to <code>"true"</code>, the <code>MimeMessage</code>
+<code>reply</code> method will put all recipients except the original
+sender in the <code>Cc</code> list of the newly constructed message.
+Normally, recipients in the <code>To</code> header of the original
+message will also appear in the <code>To</code> list of the newly
+constructed message.
+</TD>
+</TR>
+</TABLE>
 
 <P>
 The following properties are supported by the reference implementation (RI) of
 JavaMail, but are not currently a required part of the specification.
-As above, these must be set as <CODE>System</CODE> properties.
+These must be set as <CODE>System</CODE> properties.
 The names, types, defaults, and semantics of these properties may
 change in future releases.
 <P>
@@ -454,30 +511,6 @@
 </TD>
 </TR>
 
-<TR>
-<TD>mail.alternates</TD>
-<TD>String</TD>
-<TD>
-A string containing other email addresses that the current user is known by.
-The <code>MimeMessage</code> <code>reply</code> method will eliminate any
-of these addresses from the recipient list in the message it constructs,
-to avoid sending the reply back to the sender.
-</TD>
-</TR>
-
-<TR>
-<TD>mail.replyallcc</TD>
-<TD>boolean</TD>
-<TD>
-If set to <code>"true"</code>, the <code>MimeMessage</code>
-<code>reply</code> method will put all recipients except the original
-sender in the <code>Cc</code> list of the newly constructed message.
-Normally, recipients in the <code>To</code> header of the original
-message will also appear in the <code>To</code> list of the newly
-constructed message.
-</TD>
-</TR>
-
 </TABLE>
 <P>
 The current


diff -r b40cf4793195 -r 7f54d1e8f995 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Mar 07 16:48:40 2014 -0800
+++ b/doc/release/CHANGES.txt Mon Mar 17 13:26:23 2014 -0700
@@ -29,6 +29,9 @@
 K 6261 need way to monitor IMAP responses
 K 6274 MimeUtility.encodeText() does not work with Unicode surrogate pairs
 K 6283 IMAP alerts and notifications are not sent during authentication
+K 6324 add ability to fetch and cache entire IMAP message
+K 6325 add ability to return server-specific STATUS responses
+K 6326 add ability to set "peek" flag for all IMAP messages
 
 
                   CHANGES IN THE 1.5.1 RELEASE

diff -r b40cf4793195 -r 7f54d1e8f995 mail/src/main/java/com/sun/mail/imap/IMAPFolder.java
--- a/mail/src/main/java/com/sun/mail/imap/IMAPFolder.java Fri Mar 07 16:48:40 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/IMAPFolder.java Mon Mar 17 13:26:23 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
@@ -47,6 +47,7 @@
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Locale;
 import java.util.logging.Level;
 import java.io.*;
 
@@ -325,6 +326,30 @@
          */
         public static final FetchProfileItem SIZE =
                 new FetchProfileItem("SIZE");
+
+ /**
+ * MESSAGE is a fetch profile item that can be included in a
+ * <code>FetchProfile</code> during a fetch request to a Folder.
+ * This item indicates that the entire messages (headers and body,
+ * including all "attachments") in the specified
+ * range are desired to be prefetched. Note that the entire message
+ * content is cached in memory while the Folder is open. The cached
+ * message will be parsed locally to return header information and
+ * message content. <p>
+ *
+ * An example of how a client uses this is below:
+ * <blockquote><pre>
+ *
+ * FetchProfile fp = new FetchProfile();
+ * fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
+ * folder.fetch(msgs, fp);
+ *
+ * </pre></blockquote>
+ *
+ * @since JavaMail 1.5.2
+ */
+ public static final FetchProfileItem MESSAGE =
+ new FetchProfileItem("MESSAGE");
     }
 
     /**
@@ -1131,6 +1156,14 @@
                 command.append(first ? "RFC822.HEADER" : " RFC822.HEADER");
             first = false;
         }
+ if (fp.contains(IMAPFolder.FetchProfileItem.MESSAGE)) {
+ allHeaders = true;
+ if (protocol.isREV1())
+ command.append(first ? "BODY.PEEK[]" : " BODY.PEEK[]");
+ else
+ command.append(first ? "RFC822" : " RFC822");
+ first = false;
+ }
         if (fp.contains(FetchProfile.Item.SIZE) ||
                 fp.contains(IMAPFolder.FetchProfileItem.SIZE)) {
             command.append(first ? "RFC822.SIZE" : " RFC822.SIZE");
@@ -2988,6 +3021,45 @@
     }
 
     /**
+ * Use the IMAP STATUS command to get the indicated item.
+ * The STATUS item may be a standard item such as "RECENT" or "UNSEEN",
+ * or may be a server-specific item.
+ * The folder must be closed. If the item is not found, or the
+ * folder is open, -1 is returned.
+ *
+ * @param item the STATUS item to fetch
+ * @return the value of the STATUS item, or -1
+ * @exception MessagingException for errors
+ * @since JavaMail 1.5.2
+ */
+ public long getStatusItem(String item) throws MessagingException {
+ if (!opened) {
+ checkExists();
+
+ IMAPProtocol p = null;
+ Status status = null;
+ try {
+ p = getStoreProtocol(); // XXX
+ String[] items = { item };
+ status = p.status(fullName, items);
+ return status.getItem(item);
+ } catch (BadCommandException bex) {
+ // doesn't support STATUS, probably vanilla IMAP4 ..
+ // Could EXAMINE, SEARCH for UNREAD messages and
+ // return the count .. bah, not worth it.
+ return -1;
+ } catch (ConnectionException cex) {
+ throw new StoreClosedException(store, cex.getMessage());
+ } catch (ProtocolException pex) {
+ throw new MessagingException(pex.getMessage(), pex);
+ } finally {
+ releaseStoreProtocol(p);
+ }
+ }
+ return -1;
+ }
+
+ /**
      * The response handler. This is the callback routine that is
      * invoked by the protocol layer.
      */

diff -r b40cf4793195 -r 7f54d1e8f995 mail/src/main/java/com/sun/mail/imap/IMAPMessage.java
--- a/mail/src/main/java/com/sun/mail/imap/IMAPMessage.java Fri Mar 07 16:48:40 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/IMAPMessage.java Mon Mar 17 13:26:23 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
@@ -93,7 +93,7 @@
     private Date receivedDate; // INTERNALDATE
     private int size = -1; // RFC822.SIZE
 
- private boolean peek; // use BODY.PEEK when fetching content?
+ private Boolean peek; // use BODY.PEEK when fetching content?
 
     // this message's IMAP UID
     private volatile long uid = -1;
@@ -113,6 +113,9 @@
     // Indicates that we've loaded *all* headers for this message
     private volatile boolean headersLoaded = false;
 
+ // Indicates that we've cached the body of this message
+ private volatile boolean bodyLoaded = false;
+
     /* Hashtable of names of headers we've loaded from the server.
      * Used in isHeaderLoaded() and getHeaderLoaded() to keep track
      * of those headers we've attempted to load from the server. We
@@ -302,7 +305,7 @@
         return ((IMAPStore)folder.getStore()).getFetchBlockSize();
     }
 
- // Return the block size for FETCH requests
+ // Should we ignore the size in the BODYSTRUCTURE?
     // MUST be overridden by IMAPNestedMessage
     protected boolean ignoreBodyStructureSize() {
         return ((IMAPStore)folder.getStore()).ignoreBodyStructureSize();
@@ -313,6 +316,8 @@
      */
     public Address[] getFrom() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getFrom();
         loadEnvelope();
         InternetAddress[] a = envelope.from;
         /*
@@ -341,6 +346,8 @@
      */
     public Address getSender() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getSender();
         loadEnvelope();
         if (envelope.sender != null && envelope.sender.length > 0)
                 return (envelope.sender)[0]; // there can be only one sender
@@ -359,6 +366,8 @@
     public Address[] getRecipients(Message.RecipientType type)
                                 throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getRecipients(type);
         loadEnvelope();
 
         if (type == Message.RecipientType.TO)
@@ -386,6 +395,8 @@
      */
     public Address[] getReplyTo() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getReplyTo();
         loadEnvelope();
         /*
          * The IMAP spec requires that the Reply-To field never be
@@ -407,6 +418,8 @@
      */
     public String getSubject() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getSubject();
 
         if (subject != null) // already cached ?
             return subject;
@@ -438,6 +451,8 @@
      */
     public Date getSentDate() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getSentDate();
         loadEnvelope();
         if (envelope.date == null)
             return null;
@@ -454,6 +469,7 @@
      */
     public Date getReceivedDate() throws MessagingException {
         checkExpunged();
+ // XXX - have to go to the server for this
         loadEnvelope();
         if (receivedDate == null)
             return null;
@@ -469,6 +485,7 @@
      */
     public int getSize() throws MessagingException {
         checkExpunged();
+ // if bodyLoaded, size is already set
         if (size == -1)
             loadEnvelope(); // XXX - could just fetch the size
         return size;
@@ -483,6 +500,7 @@
      */
     public int getLineCount() throws MessagingException {
         checkExpunged();
+ // XXX - superclass doesn't implement this
         loadBODYSTRUCTURE();
         return bs.lines;
     }
@@ -492,6 +510,8 @@
      */
     public String[] getContentLanguage() throws MessagingException {
             checkExpunged();
+ if (bodyLoaded)
+ return super.getContentLanguage();
             loadBODYSTRUCTURE();
             if (bs.language != null)
             return (String[])(bs.language).clone();
@@ -513,6 +533,8 @@
      */
     public String getInReplyTo() throws MessagingException {
             checkExpunged();
+ if (bodyLoaded)
+ return super.getHeader("In-Reply-To", " ");
             loadEnvelope();
             return envelope.inReplyTo;
     }
@@ -525,6 +547,8 @@
      */
     public synchronized String getContentType() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getContentType();
 
         // If we haven't cached the type yet ..
         if (type == null) {
@@ -541,6 +565,8 @@
      */
     public String getDisposition() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getDisposition();
         loadBODYSTRUCTURE();
         return bs.disposition;
     }
@@ -554,6 +580,8 @@
      */
     public String getEncoding() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getEncoding();
         loadBODYSTRUCTURE();
         return bs.encoding;
     }
@@ -563,6 +591,8 @@
      */
     public String getContentID() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getContentID();
         loadBODYSTRUCTURE();
         return bs.id;
     }
@@ -576,6 +606,8 @@
      */
     public String getContentMD5() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getContentMD5();
         loadBODYSTRUCTURE();
         return bs.md5;
     }
@@ -589,6 +621,8 @@
      */
     public String getDescription() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getDescription();
 
         if (description != null) // cached value ?
             return description;
@@ -616,6 +650,8 @@
      */
     public String getMessageID() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getMessageID();
         loadEnvelope();
         return envelope.messageId;
     }
@@ -627,6 +663,8 @@
      */
     public String getFileName() throws MessagingException {
         checkExpunged();
+ if (bodyLoaded)
+ return super.getFileName();
 
         String filename = null;
         loadBODYSTRUCTURE();
@@ -650,6 +688,8 @@
      * @see javax.mail.internet.MimeMessage#getContentStream
      */
     protected InputStream getContentStream() throws MessagingException {
+ if (bodyLoaded)
+ return super.getContentStream();
         InputStream is = null;
         boolean pk = getPeek(); // get before acquiring message cache lock
 
@@ -701,7 +741,7 @@
                 throws MessagingException {
         checkExpunged();
 
- if (dh == null) {
+ if (dh == null && !bodyLoaded) {
             loadBODYSTRUCTURE();
             if (type == null) { // type not yet computed
                 // generate content-type from BODYSTRUCTURE
@@ -747,6 +787,7 @@
      * @since JavaMail 1.4.5
      */
     public InputStream getMimeStream() throws MessagingException {
+ // XXX - need an "if (bodyLoaded)" version
         InputStream is = null;
         boolean pk = getPeek(); // get before acquiring message cache lock
 
@@ -795,6 +836,10 @@
      */
     public void writeTo(OutputStream os)
                                 throws IOException, MessagingException {
+ if (bodyLoaded) {
+ super.writeTo(os);
+ return;
+ }
         InputStream is = getMimeStream();
         try {
             // write out the bytes
@@ -991,13 +1036,14 @@
 
     /**
      * Set whether or not to use the PEEK variant of FETCH when
- * fetching message content.
+ * fetching message content. This overrides the default
+ * value from the "mail.imap.peek" property.
      *
      * @param peek the peek flag
      * @since JavaMail 1.3.3
      */
     public synchronized void setPeek(boolean peek) {
- this.peek = peek;
+ this.peek = Boolean.valueOf(peek);
     }
 
     /**
@@ -1008,7 +1054,10 @@
      * @since JavaMail 1.3.3
      */
     public synchronized boolean getPeek() {
- return peek;
+ if (peek == null)
+ return ((IMAPStore)folder.getStore()).getPeek();
+ else
+ return peek.booleanValue();
     }
 
     /**
@@ -1030,6 +1079,9 @@
         subject = null;
         description = null;
         flags = null;
+ content = null;
+ contentStream = null;
+ bodyLoaded = false;
     }
 
     /**
@@ -1046,6 +1098,7 @@
         private boolean needUID = false;
         private boolean needHeaders = false;
         private boolean needSize = false;
+ private boolean needMessage = false;
         private String[] hdrs = null;
         private Set need = new HashSet(); // Set<FetchItem>
 
@@ -1071,6 +1124,8 @@
                 needHeaders = true;
             if (fp.contains(IMAPFolder.FetchProfileItem.SIZE))
                 needSize = true;
+ if (fp.contains(IMAPFolder.FetchProfileItem.MESSAGE))
+ needMessage = true;
             hdrs = fp.getHeaderNames();
             for (int i = 0; i < fitems.length; i++) {
                 if (fp.contains(fitems[i].getFetchProfileItem()))
@@ -1083,17 +1138,20 @@
          * for the specified message.
          */
         public boolean test(IMAPMessage m) {
- if (needEnvelope && m._getEnvelope() == null)
+ if (needEnvelope && m._getEnvelope() == null && !m.bodyLoaded)
                 return true; // no envelope
             if (needFlags && m._getFlags() == null)
                 return true; // no flags
- if (needBodyStructure && m._getBodyStructure() == null)
+ if (needBodyStructure && m._getBodyStructure() == null &&
+ !m.bodyLoaded)
                 return true; // no BODYSTRUCTURE
             if (needUID && m.getUID() == -1) // no UID
                 return true;
             if (needHeaders && !m.areHeadersLoaded()) // no headers
                 return true;
- if (needSize && m.size == -1) // no size
+ if (needSize && m.size == -1 && !m.bodyLoaded) // no size
+ return true;
+ if (needMessage && !m.bodyLoaded) // no message body
                 return true;
 
             // Is the desired header present ?
@@ -1155,53 +1213,71 @@
         else if (item instanceof RFC822DATA ||
                  item instanceof BODY) {
             InputStream headerStream;
- if (item instanceof RFC822DATA) // IMAP4
+ boolean isHeader;
+ if (item instanceof RFC822DATA) { // IMAP4
                 headerStream =
                     ((RFC822DATA)item).getByteArrayInputStream();
- else // IMAP4rev1
+ isHeader = ((RFC822DATA)item).isHeader();
+ } else { // IMAP4rev1
                 headerStream =
                     ((BODY)item).getByteArrayInputStream();
-
- // Load the obtained headers.
- InternetHeaders h = new InternetHeaders();
- // Some IMAP servers (e.g., gmx.net) return NIL
- // instead of a string just containing a CR/LF
- // when the header list is empty.
- if (headerStream != null)
- h.load(headerStream);
- if (headers == null || allHeaders)
- headers = h;
- else {
- /*
- * This is really painful. A second fetch
- * of the same headers (which might occur because
- * a new header was added to the set requested)
- * will return headers we already know about.
- * In this case, only load the headers we haven't
- * seen before to avoid adding duplicates of
- * headers we already have.
- *
- * XXX - There's a race condition here if another
- * thread is reading headers in the same message
- * object, because InternetHeaders is not thread
- * safe.
- */
- Enumeration e = h.getAllHeaders();
- while (e.hasMoreElements()) {
- Header he = (Header)e.nextElement();
- if (!isHeaderLoaded(he.getName()))
- headers.addHeader(
- he.getName(), he.getValue());
- }
+ isHeader = ((BODY)item).isHeader();
             }
 
- // if we asked for all headers, assume we got them
- if (allHeaders)
+ if (!isHeader) {
+ // load the entire message by using the superclass
+ // MimeMessage.parse method
+ // first, save the size of the message
+ try {
+ size = headerStream.available();
+ } catch (IOException ex) {
+ // should never occur
+ }
+ parse(headerStream);
+ bodyLoaded = true;
                 setHeadersLoaded(true);
- else {
- // Mark all headers we asked for as 'loaded'
- for (int k = 0; k < hdrs.length; k++)
- setHeaderLoaded(hdrs[k]);
+ } else {
+ // Load the obtained headers.
+ InternetHeaders h = new InternetHeaders();
+ // Some IMAP servers (e.g., gmx.net) return NIL
+ // instead of a string just containing a CR/LF
+ // when the header list is empty.
+ if (headerStream != null)
+ h.load(headerStream);
+ if (headers == null || allHeaders)
+ headers = h;
+ else {
+ /*
+ * This is really painful. A second fetch
+ * of the same headers (which might occur because
+ * a new header was added to the set requested)
+ * will return headers we already know about.
+ * In this case, only load the headers we haven't
+ * seen before to avoid adding duplicates of
+ * headers we already have.
+ *
+ * XXX - There's a race condition here if another
+ * thread is reading headers in the same message
+ * object, because InternetHeaders is not thread
+ * safe.
+ */
+ Enumeration e = h.getAllHeaders();
+ while (e.hasMoreElements()) {
+ Header he = (Header)e.nextElement();
+ if (!isHeaderLoaded(he.getName()))
+ headers.addHeader(
+ he.getName(), he.getValue());
+ }
+ }
+
+ // if we asked for all headers, assume we got them
+ if (allHeaders)
+ setHeadersLoaded(true);
+ else {
+ // Mark all headers we asked for as 'loaded'
+ for (int k = 0; k < hdrs.length; k++)
+ setHeaderLoaded(hdrs[k]);
+ }
             }
         } else
             return false; // not handled

diff -r b40cf4793195 -r 7f54d1e8f995 mail/src/main/java/com/sun/mail/imap/IMAPStore.java
--- a/mail/src/main/java/com/sun/mail/imap/IMAPStore.java Fri Mar 07 16:48:40 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/IMAPStore.java Mon Mar 17 13:26:23 2014 -0700
@@ -224,6 +224,7 @@
     private boolean enableImapEvents = false;
     private String guid; // for Yahoo! Mail IMAP
     private boolean throwSearchException = false;
+ private boolean peek = false;
 
     /*
      * This field is set in the Store's response handler if we see
@@ -596,6 +597,12 @@
         if (throwSearchException)
             logger.config("throw SearchException");
 
+ // check if peek is set
+ peek = PropUtil.getBooleanSessionProperty(session,
+ "mail." + name + ".peek", false);
+ if (peek)
+ logger.config("peek");
+
         s = session.getProperty("mail." + name + ".folder.class");
         if (s != null) {
             logger.log(Level.CONFIG, "IMAP: folder class: {0}", s);
@@ -1413,6 +1420,13 @@
     }
 
     /**
+ * Get the default "peek" value.
+ */
+ boolean getPeek() {
+ return peek;
+ }
+
+ /**
      * Return true if the specified capability string is in the list
      * of capabilities the server announced.
      *

diff -r b40cf4793195 -r 7f54d1e8f995 mail/src/main/java/com/sun/mail/imap/package.html
--- a/mail/src/main/java/com/sun/mail/imap/package.html Fri Mar 07 16:48:40 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/package.html Mon Mar 17 13:26:23 2014 -0700
@@ -245,6 +245,19 @@
 </TR>
 
 <TR>
+<TD>mail.imap.peek</TD>
+<TD>boolean</TD>
+<TD>
+If set to true, use the IMAP PEEK option when fetching body parts,
+to avoid setting the SEEN flag on messages.
+Defaults to false.
+Can be overridden on a per-message basis by the
+{_at_link com.sun.mail.imap.IMAPMessage#setPeek setPeek}
+method on IMAPMessage.
+</TD>
+</TR>
+
+<TR>
 <TD>mail.imap.ignorebodystructuresize</TD>
 <TD>boolean</TD>
 <TD>The IMAP BODYSTRUCTURE response includes the exact size of each body part.
@@ -694,7 +707,7 @@
 {_at_link javax.mail.search.SearchException SearchException}.
 For example, the IMAP protocol only supports less-than and greater-than
 comparisons for a {_at_link javax.mail.search.SizeTerm SizeTerm}.
-Otherwise, the search will be done locally by fetching the required
+If false, the search will be done locally by fetching the required
 message data and comparing it locally.
 Defaults to false.
 </TD>

diff -r b40cf4793195 -r 7f54d1e8f995 mail/src/main/java/com/sun/mail/imap/protocol/BODY.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/BODY.java Fri Mar 07 16:48:40 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/BODY.java Mon Mar 17 13:26:23 2014 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 1997-2010 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
@@ -48,16 +48,18 @@
  * The BODY fetch response item.
  *
  * @author John Mani
+ * @author Bill Shannon
  */
 
 public class BODY implements Item {
     
     static final char[] name = {'B','O','D','Y'};
 
- public int msgno;
- public ByteArray data;
- public String section;
- public int origin = 0;
+ private final int msgno;
+ private final ByteArray data;
+ private final String section;
+ private final int origin;
+ private final boolean isHeader;
 
     /**
      * Constructor
@@ -67,18 +69,20 @@
 
         r.skipSpaces();
 
- int b;
- while ((b = r.readByte()) != ']') { // skip section
- if (b == 0)
- throw new ParsingException(
- "BODY parse error: missing ``]'' at section end");
- }
+ if (r.readByte() != '[')
+ throw new ParsingException(
+ "BODY parse error: missing ``['' at section start");
+ section = r.readString(']');
+ if (r.readByte() != ']')
+ throw new ParsingException(
+ "BODY parse error: missing ``]'' at section end");
+ isHeader = section.regionMatches(true, 0, "HEADER", 0, 6);
 
-
         if (r.readByte() == '<') { // origin
             origin = r.readNumber();
             r.skip(1); // skip '>';
- }
+ } else
+ origin = 0;
 
         data = r.readByteArray();
     }
@@ -93,4 +97,12 @@
         else
             return null;
     }
+
+ public boolean isHeader() {
+ return isHeader;
+ }
+
+ public String getSection() {
+ return section;
+ }
 }

diff -r b40cf4793195 -r 7f54d1e8f995 mail/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java Fri Mar 07 16:48:40 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/FetchResponse.java Mon Mar 17 13:26:23 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
@@ -207,11 +207,12 @@
             if (match(RFC822SIZE.name))
                 return new RFC822SIZE(this);
             else if (match(RFC822DATA.name)) {
+ boolean isHeader = false;
                 if (match(HEADER))
- ; // skip ".HEADER"
+ isHeader = true; // skip ".HEADER"
                 else if (match(TEXT))
                     ; // skip ".TEXT"
- return new RFC822DATA(this);
+ return new RFC822DATA(this, isHeader);
             }
             break;
         case 'U': case 'u':

diff -r b40cf4793195 -r 7f54d1e8f995 mail/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java Fri Mar 07 16:48:40 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/RFC822DATA.java Mon Mar 17 13:26:23 2014 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 1997-2011 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
@@ -45,21 +45,32 @@
 import com.sun.mail.util.ASCIIUtility;
 
 /**
- * This class
+ * The RFC822 response data item.
  *
  * @author John Mani
+ * @author Bill Shannon
  */
 
 public class RFC822DATA implements Item {
    
     static final char[] name = {'R','F','C','8','2','2'};
- public int msgno;
- public ByteArray data;
+ private final int msgno;
+ private final ByteArray data;
+ private final boolean isHeader;
 
     /**
      * Constructor
      */
     public RFC822DATA(FetchResponse r) throws ParsingException {
+ this(r, false);
+ }
+
+ /**
+ * Constructor, specifying header flag.
+ */
+ public RFC822DATA(FetchResponse r, boolean isHeader)
+ throws ParsingException {
+ this.isHeader = isHeader;
         msgno = r.getNumber();
         r.skipSpaces();
         data = r.readByteArray();
@@ -75,4 +86,8 @@
         else
             return null;
     }
+
+ public boolean isHeader() {
+ return isHeader;
+ }
 }

diff -r b40cf4793195 -r 7f54d1e8f995 mail/src/main/java/com/sun/mail/imap/protocol/Status.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/Status.java Fri Mar 07 16:48:40 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/Status.java Mon Mar 17 13:26:23 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
@@ -40,12 +40,17 @@
 
 package com.sun.mail.imap.protocol;
 
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Locale;
+
 import com.sun.mail.iap.*;
 
 /**
  * STATUS response.
  *
  * @author John Mani
+ * @author Bill Shannon
  */
 
 public class Status {
@@ -56,6 +61,7 @@
     public long uidvalidity = -1;
     public int unseen = -1;
     public long highestmodseq = -1;
+ public Map<String,Long> items; // any unknown items
 
     static final String[] standardItems =
         { "MESSAGES", "RECENT", "UNSEEN", "UIDNEXT", "UIDVALIDITY" };
@@ -99,9 +105,41 @@
                 unseen = r.readNumber();
             else if (attr.equalsIgnoreCase("HIGHESTMODSEQ"))
                 highestmodseq = r.readLong();
+ else {
+ if (items == null)
+ items = new HashMap<String,Long>();
+ items.put(attr.toUpperCase(Locale.ENGLISH),
+ Long.valueOf(r.readLong()));
+ }
         } while (r.readByte() != ')');
     }
 
+ /**
+ * Get the value for the STATUS item.
+ *
+ * @since JavaMail 1.5.2
+ */
+ public long getItem(String item) {
+ item = item.toUpperCase(Locale.ENGLISH);
+ Long v;
+ long ret = -1;
+ if (items != null && (v = items.get(item)) != null)
+ ret = v.longValue();
+ else if (item.equals("MESSAGES"))
+ ret = total;
+ else if (item.equals("RECENT"))
+ ret = recent;
+ else if (item.equals("UIDNEXT"))
+ ret = uidnext;
+ else if (item.equals("UIDVALIDITY"))
+ ret = uidvalidity;
+ else if (item.equals("UNSEEN"))
+ ret = unseen;
+ else if (item.equals("HIGHESTMODSEQ"))
+ ret = highestmodseq;
+ return ret;
+ }
+
     public static void add(Status s1, Status s2) {
         if (s2.total != -1)
             s1.total = s2.total;
@@ -115,5 +153,9 @@
             s1.unseen = s2.unseen;
         if (s2.highestmodseq != -1)
             s1.highestmodseq = s2.highestmodseq;
+ if (s1.items == null)
+ s1.items = s2.items;
+ else if (s2.items != null)
+ s1.items.putAll(s2.items);
     }
 }


diff -r 7f54d1e8f995 -r 893cd9d57c40 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Mon Mar 17 13:26:23 2014 -0700
+++ b/doc/release/CHANGES.txt Mon Mar 17 13:57:41 2014 -0700
@@ -32,6 +32,8 @@
 K 6324 add ability to fetch and cache entire IMAP message
 K 6325 add ability to return server-specific STATUS responses
 K 6326 add ability to set "peek" flag for all IMAP messages
+K 6327 add ability to specify scope of event queue
+K 6328 add ability to specify an Executor to process events
 
 
                   CHANGES IN THE 1.5.1 RELEASE

diff -r 7f54d1e8f995 -r 893cd9d57c40 mail/src/main/java/javax/mail/EventQueue.java
--- a/mail/src/main/java/javax/mail/EventQueue.java Mon Mar 17 13:26:23 2014 -0700
+++ b/mail/src/main/java/javax/mail/EventQueue.java Mon Mar 17 13:57:41 2014 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 1997-2012 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
@@ -40,8 +40,12 @@
 
 package javax.mail;
 
-import java.io.*;
 import java.util.Vector;
+import java.util.Queue;
+import java.util.WeakHashMap;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Executor;
 import javax.mail.event.MailEvent;
 
 /**
@@ -49,15 +53,35 @@
  * This class implements an event queue, and a dispatcher thread that
  * dequeues and dispatches events from the queue.
  *
- * Pieces stolen from sun.misc.Queue.
- *
  * @author Bill Shannon
  */
 class EventQueue implements Runnable {
 
+ private volatile BlockingQueue<QueueElement> q;
+ private Executor executor;
+
+ private static WeakHashMap<ClassLoader,EventQueue> appq;
+
+ /**
+ * A special event that causes the queue processing task to terminate.
+ */
+ static class TerminatorEvent extends MailEvent {
+ //private static final long serialVersionUID = 5542172141759168416L;
+
+ TerminatorEvent() {
+ super(new Object());
+ }
+
+ public void dispatch(Object listener) {
+ // Kill the event dispatching thread.
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * A "struct" to put on the queue.
+ */
     static class QueueElement {
- QueueElement next = null;
- QueueElement prev = null;
         MailEvent event = null;
         Vector vector = null;
 
@@ -67,66 +91,73 @@
         }
     }
 
- private QueueElement head = null;
- private QueueElement tail = null;
- private Thread qThread;
-
- public EventQueue() {
- qThread = new Thread(this, "JavaMail-EventQueue");
- qThread.setDaemon(true); // not a user thread
- qThread.start();
+ /**
+ * Construct an EventQueue using the specified Executor.
+ * If the Executor is null, threads will be created as needed.
+ */
+ EventQueue(Executor ex) {
+ this.executor = ex;
     }
 
     /**
      * Enqueue an event.
      */
- public synchronized void enqueue(MailEvent event, Vector vector) {
- QueueElement newElt = new QueueElement(event, vector);
-
- if (head == null) {
- head = newElt;
- tail = newElt;
- } else {
- newElt.next = head;
- head.prev = newElt;
- head = newElt;
+ synchronized void enqueue(MailEvent event, Vector vector) {
+ // if this is the first event, create the queue and start the event task
+ if (q == null) {
+ q = new LinkedBlockingQueue<QueueElement>();
+ if (executor != null) {
+ executor.execute(this);
+ } else {
+ Thread qThread = new Thread(this, "JavaMail-EventQueue");
+ qThread.setDaemon(true); // not a user thread
+ qThread.start();
+ }
         }
- notifyAll();
+ q.add(new QueueElement(event, vector));
     }
 
     /**
- * Dequeue the oldest object on the queue.
- * Used only by the run() method.
- *
- * @return the oldest object on the queue.
- * @exception java.lang.InterruptedException if another thread has
- * interrupted this thread.
+ * Terminate the task running the queue, but only if there is a queue.
      */
- private synchronized QueueElement dequeue()
- throws InterruptedException {
- while (tail == null)
- wait();
- QueueElement elt = tail;
- tail = elt.prev;
- if (tail == null) {
- head = null;
- } else {
- tail.next = null;
+ synchronized void terminateQueue() {
+ if (q != null) {
+ Vector dummyListeners = new Vector();
+ dummyListeners.setSize(1); // need atleast one listener
+ q.add(new QueueElement(new TerminatorEvent(), dummyListeners));
+ q = null;
         }
- elt.prev = elt.next = null;
- return elt;
+ }
+
+ /**
+ * Create (if necessary) an application-scoped event queue.
+ * Application scoping is based on the thread's context class loader.
+ */
+ static synchronized EventQueue getApplicationEventQueue(Executor ex) {
+ ClassLoader cl = Session.getContextClassLoader();
+ if (appq == null)
+ appq = new WeakHashMap<ClassLoader,EventQueue>();
+ EventQueue q = appq.get(cl);
+ if (q == null) {
+ q = new EventQueue(ex);
+ appq.put(cl, q);
+ }
+ return q;
     }
 
     /**
      * Pull events off the queue and dispatch them.
      */
     public void run() {
- QueueElement qe;
 
+ BlockingQueue<QueueElement> bq = q;
+ if (bq == null)
+ return;
         try {
             loop:
             for (;;) {
- qe = dequeue(); // blocks until an item is available
+ // block until an item is available
+ QueueElement qe = bq.take();
                 MailEvent e = qe.event;
                 Vector v = qe.vector;
 
@@ -145,14 +176,4 @@
             // just die
         }
     }
-
- /**
- * Stop the dispatcher so we can be destroyed.
- */
- void stop() {
- if (qThread != null) {
- qThread.interrupt(); // kill our thread
- qThread = null;
- }
- }
 }

diff -r 7f54d1e8f995 -r 893cd9d57c40 mail/src/main/java/javax/mail/Folder.java
--- a/mail/src/main/java/javax/mail/Folder.java Mon Mar 17 13:26:23 2014 -0700
+++ b/mail/src/main/java/javax/mail/Folder.java Mon Mar 17 13:57: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
@@ -44,6 +44,7 @@
 import java.lang.*;
 import java.util.Vector;
 import java.util.StringTokenizer;
+import java.util.concurrent.Executor;
 import javax.mail.search.SearchTerm;
 import javax.mail.event.*;
 
@@ -129,6 +130,11 @@
      */
     protected int mode = -1;
 
+ /*
+ * The queue of events to be delivered.
+ */
+ private final EventQueue q;
+
     /**
      * Constructor that takes a Store object.
      *
@@ -136,6 +142,21 @@
      */
     protected Folder(Store store) {
         this.store = store;
+
+ // create or choose the appropriate event queue
+ Session session = store.getSession();
+ String scope =
+ session.getProperties().getProperty("mail.event.scope", "folder");
+ Executor executor =
+ (Executor)session.getProperties().get("mail.event.executor");
+ if (scope.equalsIgnoreCase("application"))
+ q = EventQueue.getApplicationEventQueue(executor);
+ else if (scope.equalsIgnoreCase("session"))
+ q = session.getEventQueue();
+ else if (scope.equalsIgnoreCase("store"))
+ q = store.getEventQueue();
+ else // if (scope.equalsIgnoreCase("folder"))
+ q = new EventQueue(executor);
     }
 
     /**
@@ -1366,7 +1387,7 @@
          * self destruct.
          */
         if (type == ConnectionEvent.CLOSED)
- terminateQueue();
+ q.terminateQueue();
     }
 
     // Vector of folder listeners
@@ -1594,28 +1615,9 @@
     }
 
     /*
- * The queue of events to be delivered.
- */
- private EventQueue q;
-
- /*
- * A lock for creating the EventQueue object. Only one thread should
- * create an EventQueue for this folder. We can't synchronize on the
- * folder's lock because that would violate the locking hierarchy in
- * some cases. For details, see the IMAP provider.
- */
- private Object qLock = new Object();
-
- /*
      * Add the event and vector of listeners to the queue to be delivered.
      */
     private void queueEvent(MailEvent event, Vector vector) {
- // synchronize creation of the event queue
- synchronized (qLock) {
- if (q == null)
- q = new EventQueue();
- }
-
         /*
          * Copy the vector in order to freeze the state of the set
          * of EventListeners the event should be delivered to prior
@@ -1628,34 +1630,9 @@
         q.enqueue(event, v);
     }
 
- static class TerminatorEvent extends MailEvent {
- private static final long serialVersionUID = 3765761925441296565L;
-
- TerminatorEvent() {
- super(new Object());
- }
-
- public void dispatch(Object listener) {
- // Kill the event dispatching thread.
- Thread.currentThread().interrupt();
- }
- }
-
- // Dispatch the terminator
- private void terminateQueue() {
- synchronized (qLock) {
- if (q != null) {
- Vector dummyListeners = new Vector();
- dummyListeners.setSize(1); // need atleast one listener
- q.enqueue(new TerminatorEvent(), dummyListeners);
- q = null;
- }
- }
- }
-
     protected void finalize() throws Throwable {
         super.finalize();
- terminateQueue();
+ q.terminateQueue();
     }
 
     /**

diff -r 7f54d1e8f995 -r 893cd9d57c40 mail/src/main/java/javax/mail/Service.java
--- a/mail/src/main/java/javax/mail/Service.java Mon Mar 17 13:26:23 2014 -0700
+++ b/mail/src/main/java/javax/mail/Service.java Mon Mar 17 13:57: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
@@ -43,6 +43,7 @@
 import java.io.*;
 import java.net.*;
 import java.util.*;
+import java.util.concurrent.Executor;
 import javax.mail.event.*;
 
 /**
@@ -88,6 +89,11 @@
     private final Vector connectionListeners = new Vector();
 
     /**
+ * The queue of events to be delivered.
+ */
+ private final EventQueue q;
+
+ /**
      * Constructor.
      *
      * @param session Session object for this service
@@ -147,6 +153,19 @@
         }
 
         url = new URLName(protocol, host, port, file, user, password);
+
+ // create or choose the appropriate event queue
+ String scope =
+ session.getProperties().getProperty("mail.event.scope", "folder");
+ Executor executor =
+ (Executor)session.getProperties().get("mail.event.executor");
+ if (scope.equalsIgnoreCase("application"))
+ q = EventQueue.getApplicationEventQueue(executor);
+ else if (scope.equalsIgnoreCase("session"))
+ q = session.getEventQueue();
+ else // if (scope.equalsIgnoreCase("store") ||
+ // scope.equalsIgnoreCase("folder"))
+ q = new EventQueue(executor);
     }
 
     /**
@@ -594,7 +613,7 @@
          * self destruct.
          */
         if (type == ConnectionEvent.CLOSED)
- terminateQueue();
+ q.terminateQueue();
     }
 
     /**
@@ -609,19 +628,6 @@
             return super.toString();
     }
 
- /*
- * The queue of events to be delivered.
- */
- private EventQueue q;
-
- /*
- * A lock for creating the EventQueue object. Only one thread should
- * create an EventQueue for this service. We can't synchronize on the
- * service's lock because that might violate the locking hierarchy in
- * some cases.
- */
- private Object qLock = new Object();
-
     /**
      * Add the event and vector of listeners to the queue to be delivered.
      *
@@ -629,12 +635,6 @@
      * @param vector the vector of listeners
      */
     protected void queueEvent(MailEvent event, Vector vector) {
- // synchronize creation of the event queue
- synchronized (qLock) {
- if (q == null)
- q = new EventQueue();
- }
-
         /*
          * Copy the vector in order to freeze the state of the set
          * of EventListeners the event should be delivered to prior
@@ -647,36 +647,25 @@
         q.enqueue(event, v);
     }
 
- static class TerminatorEvent extends MailEvent {
- private static final long serialVersionUID = 5542172141759168416L;
-
- TerminatorEvent() {
- super(new Object());
- }
-
- public void dispatch(Object listener) {
- // Kill the event dispatching thread.
- Thread.currentThread().interrupt();
- }
- }
-
- // Dispatch the terminator
- private void terminateQueue() {
- synchronized (qLock) {
- if (q != null) {
- Vector dummyListeners = new Vector();
- dummyListeners.setSize(1); // need atleast one listener
- q.enqueue(new TerminatorEvent(), dummyListeners);
- q = null;
- }
- }
- }
-
     /**
      * Stop the event dispatcher thread so the queue can be garbage collected.
      */
     protected void finalize() throws Throwable {
         super.finalize();
- terminateQueue();
+ q.terminateQueue();
+ }
+
+ /**
+ * Package private method to allow Folder to get the Session for a Store.
+ */
+ Session getSession() {
+ return session;
+ }
+
+ /**
+ * Package private method to allow Folder to get the EventQueue for a Store.
+ */
+ EventQueue getEventQueue() {
+ return q;
     }
 }

diff -r 7f54d1e8f995 -r 893cd9d57c40 mail/src/main/java/javax/mail/Session.java
--- a/mail/src/main/java/javax/mail/Session.java Mon Mar 17 13:26:23 2014 -0700
+++ b/mail/src/main/java/javax/mail/Session.java Mon Mar 17 13:57: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
@@ -50,6 +50,7 @@
 import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.logging.Level;
+import java.util.concurrent.Executor;
 
 import javax.activation.*;
 
@@ -192,6 +193,9 @@
     private final Hashtable providersByClassName = new Hashtable();
     private final Properties addressMap = new Properties();
                                                 // maps type to protocol
+ // the queue of events to be delivered, if mail.event.scope===session
+ private final EventQueue q;
+
     // The default session.
     private static Session defaultSession = null;
 
@@ -215,6 +219,7 @@
         // load the resources
         loadProviders(cl);
         loadAddressMap(cl);
+ q = new EventQueue((Executor)props.get("mail.event.executor"));
     }
 
     private final synchronized void initLogger() {
@@ -1197,7 +1202,7 @@
      * Following are security related methods that work on JDK 1.2 or newer.
      */
 
- private static ClassLoader getContextClassLoader() {
+ static ClassLoader getContextClassLoader() {
         return (
[truncated due to length]