commits@javamail.java.net

[javamail~mercurial:706] fix javadocs and update copyright

From: <shannon_at_java.net>
Date: Mon, 13 Apr 2015 22:34:11 +0000

Project: javamail
Repository: mercurial
Revision: 706
Author: shannon
Date: 2015-04-13 22:33:28 UTC
Link:

Log Message:
------------
Handler state needs to be static because handler is cloned for each connection.
CollectorFormatter explain <formatter-name> in docs.
CompactFormatter explain <formatter-name> in docs.
CompactFormatter ignore frames from Throwables classname.
MailHandler use new API for creating Authenticator.
LogManagerProperties remove JavaMail class dependencies.
CompactFormatterTest add test for ignoring Throwables classname.
LogManagerPropertiesTest add tests for newObjectFrom.
LogManagerPropertiesTest add tests for JavaMail class dependencies.
MailHandlerTest add GAE header test.
Disable GAE tests since they don't always work with Hotspot.

(From Jason)
EXPUNGE response during UID FETCH breaks UID->seqnum mapping - bug 6755
ignore message numbers in IMAP responses that are larger than the number
of messages in the folder - bug 6762
add HK2 metadata to allow MailHandler to be used in GlassFish - bug 6552
can use @Override now
write timeouts don't work with a custom SSL socket factory - bug 6772
CompactFormatter workaround for JDK-8057919

(From Jason)
fix javadocs and update copyright


Revisions:
----------
698
699
700
701
702
703
704
705
706


Modified Paths:
---------------
mail/src/test/java/com/sun/mail/imap/IMAPIdleStateTest.java
mail/src/test/java/com/sun/mail/imap/IMAPIdleUntaggedResponseTest.java
mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java
mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java
mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java
mail/src/main/java/com/sun/mail/util/logging/MailHandler.java
mail/src/test/java/com/sun/mail/util/logging/CompactFormatterTest.java
mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java
mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
doc/release/CHANGES.txt
mail/src/main/java/com/sun/mail/imap/IMAPFolder.java
mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java
mail/src/test/java/com/sun/mail/imap/IMAPHandler.java
mail/src/main/java/com/sun/mail/util/MailSSLSocketFactory.java
mail/src/main/java/com/sun/mail/util/SocketFetcher.java
mail/src/test/java/com/sun/mail/test/TestServer.java


Added Paths:
------------
mail/src/test/java/com/sun/mail/imap/IMAPUidExpungeTest.java
mail/src/test/java/com/sun/mail/imap/IMAPMessageNumberOutOfRangeTest.java
mail/src/main/resources/META-INF/hk2-locator/default
mail/src/test/java/com/sun/mail/test/TestSSLSocketFactory.java
mail/src/test/java/com/sun/mail/test/TestSocketFactory.java
mail/src/test/java/com/sun/mail/util/WriteTimeoutSocketTest.java


Diffs:
------
diff -r 84348657bb8a -r d9c2380c7f1b mail/src/test/java/com/sun/mail/imap/IMAPIdleStateTest.java
--- a/mail/src/test/java/com/sun/mail/imap/IMAPIdleStateTest.java Wed Mar 18 15:15:16 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/imap/IMAPIdleStateTest.java Fri Mar 27 15:17:30 2015 -0700
@@ -122,7 +122,8 @@
      * to abort an IDLE.
      */
     private static final class IMAPHandlerIdleBye extends IMAPHandler {
- CountDownLatch latch = new CountDownLatch(1);
+ // must be static because handler is cloned for each connection
+ private static CountDownLatch latch = new CountDownLatch(1);
 
         @Override
         public void idle() throws IOException {

diff -r 84348657bb8a -r d9c2380c7f1b mail/src/test/java/com/sun/mail/imap/IMAPIdleUntaggedResponseTest.java
--- a/mail/src/test/java/com/sun/mail/imap/IMAPIdleUntaggedResponseTest.java Wed Mar 18 15:15:16 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/imap/IMAPIdleUntaggedResponseTest.java Fri Mar 27 15:17:30 2015 -0700
@@ -133,7 +133,8 @@
      * sure the notification of the new message is seen.
      */
     private static final class IMAPHandlerIdleExists extends IMAPHandler {
- CountDownLatch latch = new CountDownLatch(1);
+ // must be static because handler is cloned for each connection
+ private static CountDownLatch latch = new CountDownLatch(1);
 
         @Override
         public void examine() throws IOException {


diff -r d9c2380c7f1b -r 6a2e8d479145 mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java Fri Mar 27 15:17:30 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java Mon Mar 30 15:09:32 2015 -0700
@@ -56,7 +56,12 @@
  * delegated to the wrapped formatter.
  *
  * <p>
- * The LogManager properties are:
+ * By default each <tt>CollectorFormatter</tt> is initialized using the
+ * following LogManager configuration properties where
+ * <tt>&lt;formatter-name&gt;</tt> refers to the fully qualified class name or
+ * the fully qualified derived class name of the formatter. If properties are
+ * not defined, or contain invalid values, then the specified default values are
+ * used.
  * <ul>
  * <li>&lt;formatter-name&gt;.comparator name of a
  * {_at_linkplain java.util.Comparator} class used to choose the collected

diff -r d9c2380c7f1b -r 6a2e8d479145 mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java Fri Mar 27 15:17:30 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java Mon Mar 30 15:09:32 2015 -0700
@@ -49,7 +49,12 @@
  * specified fields support an {_at_link #toAlternate(java.lang.String) alternate}
  * fixed width format.
  * <p>
- * The LogManager properties are:
+ * By default each <tt>CompactFormatter</tt> is initialized using the
+ * following LogManager configuration properties where
+ * <tt>&lt;formatter-name&gt;</tt> refers to the fully qualified class name or
+ * the fully qualified derived class name of the formatter. If properties are
+ * not defined, or contain invalid values, then the specified default values are
+ * used.
  * <ul>
  * <li>&lt;formatter-name&gt;.format - the {_at_link java.util.Formatter
  * format} string used to transform the output. The format string can be used to
@@ -420,9 +425,9 @@
         } catch (Exception ignore) {
         } catch (LinkageError ignore) {
         }
- return (!s.getClassName().endsWith("es")
- && s.getClassName().endsWith("s"))
- || s.getClassName().contains("Util");
+ final String cn = s.getClassName();
+ return (cn.endsWith("s") && !cn.endsWith("es"))
+ || cn.contains("Util") || cn.endsWith("Throwables");
     }
 
     /**

diff -r d9c2380c7f1b -r 6a2e8d479145 mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java
--- a/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java Fri Mar 27 15:17:30 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java Mon Mar 30 15:09:32 2015 -0700
@@ -49,8 +49,6 @@
 import java.util.*;
 import java.util.logging.*;
 import java.util.logging.Formatter;
-import javax.mail.Authenticator;
-import javax.mail.Service;
 
 /**
  * An adapter class to allow the Mail API to access the LogManager properties.
@@ -94,8 +92,9 @@
     private static final Object LOG_MANAGER = loadLogManager();
 
     /**
- * Installs the LogManager or Properties.
+ * Get the LogManager or loads a Properties object to use as the LogManager.
      *
+ * @return the LogManager or a loaded Properties object.
      * @since JavaMail 1.5.3
      */
     private static Object loadLogManager() {
@@ -138,9 +137,9 @@
                     in.close();
                 }
             }
- } catch (RuntimeException permissions) {
- } catch (Exception ioe) {
- } catch (LinkageError unexpected) {
+ } catch (final RuntimeException permissionsOrMalformed) {
+ } catch (final Exception ioe) {
+ } catch (final LinkageError unexpected) {
         }
         return props;
     }
@@ -270,7 +269,7 @@
      * @throws SecurityException if unable to inspect properties of object.
      * @since JavaMail 1.5.3
      */
- static String getLocalHost(final Service s) throws Exception {
+ static String getLocalHost(final Object s) throws Exception {
         try {
             final Method m = s.getClass().getMethod("getLocalHost");
             if (!Modifier.isStatic(m.getModifiers())
@@ -454,27 +453,6 @@
     }
 
     /**
- * Creates a new authenticator from the given class name.
- *
- * @param name the fully qualified class name.
- * @return a new authenticator.
- * @throws ClassCastException if class name does not match the type.
- * @throws ClassNotFoundException if the class name was not found.
- * @throws IllegalAccessException if the constructor is inaccessible.
- * @throws InstantiationException if the given class name is abstract.
- * @throws InvocationTargetException if the constructor throws an exception.
- * @throws LinkageError if the linkage fails.
- * @throws ExceptionInInitializerError if the static initializer fails.
- * @throws Exception to match the error method of the ErrorManager.
- * @throws NoSuchMethodException if the class name does not have a no
- * argument constructor.
- * @since JavaMail 1.4.5
- */
- static Authenticator newAuthenticator(String name) throws Exception {
- return newObjectFrom(name, Authenticator.class);
- }
-
- /**
      * Determines if the given class name identifies a utility class.
      *
      * @param name the fully qualified class name.
@@ -593,7 +571,7 @@
      * argument constructor.
      * @since JavaMail 1.4.5
      */
- private static <T> T newObjectFrom(String name, Class<T> type) throws Exception {
+ static <T> T newObjectFrom(String name, Class<T> type) throws Exception {
         try {
             final Class<?> clazz = LogManagerProperties.findClass(name);
             //This check avoids additional side effects when the name parameter

diff -r d9c2380c7f1b -r 6a2e8d479145 mail/src/main/java/com/sun/mail/util/logging/MailHandler.java
--- a/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Fri Mar 27 15:17:30 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Mon Mar 30 15:09:32 2015 -0700
@@ -109,7 +109,7 @@
  * property. If the prefixed property is not found, then the mail property
  * itself is searched in the LogManager. By default each <tt>MailHandler</tt> is
  * initialized using the following LogManager configuration properties where
- * <tt>&lt;handler-name&gt;</tt> refers to the fully-qualified class name of the
+ * <tt>&lt;handler-name&gt;</tt> refers to the fully qualified class name of the
  * handler. If properties are not defined, or contain invalid values, then the
  * specified default values are used.
  *
@@ -2229,7 +2229,8 @@
         String name = fromLogManager(p.concat(".authenticator"));
         if (hasValue(name)) {
             try {
- this.auth = LogManagerProperties.newAuthenticator(name);
+ this.auth = LogManagerProperties
+ .newObjectFrom(name, Authenticator.class);
             } catch (final SecurityException SE) {
                 throw SE;
             } catch (final ClassNotFoundException literalAuth) {

diff -r d9c2380c7f1b -r 6a2e8d479145 mail/src/test/java/com/sun/mail/util/logging/CompactFormatterTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/CompactFormatterTest.java Fri Mar 27 15:17:30 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/CompactFormatterTest.java Mon Mar 30 15:09:32 2015 -0700
@@ -86,7 +86,7 @@
         assertNull(System.getProperty(LOG_CFG_KEY));
         assertEquals(LogManager.class, LogManager.getLogManager().getClass());
     }
-
+
     private static void fullFence() {
         LogManager.getLogManager().getProperty("");
     }
@@ -677,7 +677,7 @@
     }
 
     @Test
- public void testIgnoreStaticUtilityClassUtilError() {
+ public void testIgnoreStaticUtilityClass_Util() {
         CompactFormatter cf = new CompactFormatter();
         String n = getClass().getName().concat("MimeUtility");
         String f = n.concat(".java");
@@ -686,7 +686,7 @@
     }
 
     @Test
- public void testIgnoreStaticUtilityClassWithSError() {
+ public void testIgnoreStaticUtilityClass_s() {
         CompactFormatter cf = new CompactFormatter();
         String n = getClass().getName().concat("Collections");
         String f = n.concat(".java");
@@ -695,7 +695,7 @@
     }
 
     @Test
- public void testIgnoreStaticUtilityClassWithSeError() {
+ public void testIgnoreStaticUtilityClass_es() {
         CompactFormatter cf = new CompactFormatter();
         String n = getClass().getName().concat("Properties");
         String f = n.concat(".java");
@@ -704,6 +704,15 @@
     }
 
     @Test
+ public void testIgnoreStaticUtilityClass_Throwables() {
+ CompactFormatter cf = new CompactFormatter();
+ String n = getClass().getName().concat("Throwables");
+ String f = n.concat(".java");
+ StackTraceElement s = new StackTraceElement(n, "propagate", f, 400);
+ assertTrue(s.toString(), cf.ignore(s));
+ }
+
+ @Test
     public void testIgnoreSyntheticMethod() {
         CompactFormatter cf = new CompactFormatter();
         String n = UNKNOWN_CLASS_NAME;

diff -r d9c2380c7f1b -r 6a2e8d479145 mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java Fri Mar 27 15:17:30 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java Mon Mar 30 15:09:32 2015 -0700
@@ -41,6 +41,8 @@
 package com.sun.mail.util.logging;
 
 import java.io.*;
+import java.lang.management.CompilationMXBean;
+import java.lang.management.ManagementFactory;
 import java.lang.reflect.*;
 import java.util.Comparator;
 import java.util.Locale;
@@ -75,6 +77,14 @@
         LogManager.getLogManager().getProperty("");
     }
 
+ private static void assumeNoJit() {
+ CompilationMXBean c = ManagementFactory.getCompilationMXBean();
+ if (c != null) { //-Xint
+ Assume.assumeNoException(new IllegalArgumentException(
+ c.getName() + " must be disabled."));
+ }
+ }
+
     @Before
     public void setUp() {
         fullFence();
@@ -113,19 +123,17 @@
         }
     }
 
- @Test
+ @Ignore
     public void testCheckAccessAbsent() throws Exception {
+ assumeNoJit();
         final Class<?> k = LogManagerProperties.class;
         final Field f = k.getDeclaredField("LOG_MANAGER");
- f.setAccessible(true);
- assertTrue(Modifier.isFinal(f.getModifiers()));
- Field mod = Field.class.getDeclaredField("modifiers");
- mod.setAccessible(true);
- mod.setInt(f, f.getModifiers() & ~Modifier.FINAL);
+ Field mod = setAccessible(f);
         try {
             final Object lm = f.get(null);
             f.set(null, null);
             try {
+ fullFence();
                 LogManagerProperties.checkLogManagerAccess();
 
                 LogPermSecurityManager sm = new LogPermSecurityManager();
@@ -144,6 +152,7 @@
                 }
             } finally {
                 f.set(null, lm);
+ fullFence();
             }
         } finally {
             mod.setInt(f, f.getModifiers() | Modifier.FINAL);
@@ -163,6 +172,7 @@
             parent.put("", emptyValue);
 
             read(manager, parent);
+ assertTrue(LogManagerProperties.hasLogManager());
             assertEquals(value, LogManagerProperties.fromLogManager(key));
             assertEquals(emptyValue, LogManagerProperties.fromLogManager(""));
         } finally {
@@ -176,13 +186,15 @@
         }
     }
 
- @Test
+ @Ignore
     public void testFromLogManagerNull() throws Exception {
+ assumeNoJit();
         testFromLogManager((Properties) null);
     }
 
- @Test
+ @Ignore
     public void testFromLogManagerAbsent() throws Exception {
+ assumeNoJit();
         final String cfgKey = "java.util.logging.config.file";
         final Class<?> k = LogManagerProperties.class;
         String old = System.getProperty(cfgKey);
@@ -217,19 +229,17 @@
     }
 
     private void testFromLogManager(Properties parent) throws Exception {
+ assertTrue(LogManagerProperties.hasLogManager());
         final Class<?> k = LogManagerProperties.class;
         final Field f = k.getDeclaredField("LOG_MANAGER");
- f.setAccessible(true);
- assertTrue(Modifier.isFinal(f.getModifiers()));
- Field mod = Field.class.getDeclaredField("modifiers");
- mod.setAccessible(true);
- mod.setInt(f, f.getModifiers() & ~Modifier.FINAL);
+ Field mod = setAccessible(f);
         try {
             fullFence();
             final Object lm = f.get(null);
             f.set(null, parent);
             try {
                 fullFence();
+ assertFalse(LogManagerProperties.hasLogManager());
                 if (parent != null) {
                     assertFalse(parent.isEmpty());
                     for (Map.Entry<Object, Object> e : parent.entrySet()) {
@@ -258,6 +268,51 @@
     }
 
     @Test
+ public void testJavaMailLinkage() throws Exception {
+ for (Method m : LogManagerProperties.class.getDeclaredMethods()) {
+ assertFalse(m.getReturnType().getName(),
+ isFromJavaMail(m.getReturnType()));
+ for (Class<?> p : m.getParameterTypes()) {
+ assertFalse(p.getName(), isFromJavaMail(p));
+ }
+
+ for (Class<?> e : m.getExceptionTypes()) {
+ assertFalse(e.getName(), isFromJavaMail(e));
+ }
+ }
+
+ for (Constructor<?> c : LogManagerProperties.class.getDeclaredConstructors()) {
+ for (Class<?> p : c.getParameterTypes()) {
+ assertFalse(p.getName(), isFromJavaMail(p));
+ }
+
+ for (Class<?> e : c.getExceptionTypes()) {
+ assertFalse(e.getName(), isFromJavaMail(e));
+ }
+ }
+
+ for (Field f : LogManagerProperties.class.getDeclaredFields()) {
+ assertFalse(f.getName(), isFromJavaMail(f.getType()));
+ }
+ }
+
+ private boolean isFromJavaMail(Class<?> k) throws Exception {
+ for (Class<?> t = k; t != null; t = t.getSuperclass()) {
+ final String n = t.getName();
+ if (n.startsWith("javax.mail.")) {
+ return true;
+ }
+
+ //Not included with logging-mailhandler.jar.
+ if (n.startsWith("com.sun.mail.")
+ && !n.startsWith("com.sun.mail.util.logging.")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Test
     public void testClone() throws Exception {
         String prefix = LogManagerPropertiesTest.class.getName();
         Properties parent;
@@ -689,33 +744,65 @@
     }
 
     @Test
- public void testNewAuthenticator() throws Exception {
+ public void testNewObjectFrom() throws Exception {
         try {
- LogManagerProperties.newAuthenticator(null);
+ LogManagerProperties.newObjectFrom((String) null, Object.class);
+ fail("Null name was allowed.");
+ } catch (NullPointerException expect) {
+ }
+
+ try {
+ LogManagerProperties.newObjectFrom(Object.class.getName(),
+ (Class<Object>) null);
+ fail("Null class was allowed.");
+ } catch (NullPointerException expect) {
+ }
+
+ try {
+ LogManagerProperties.newObjectFrom((String) null,
+ (Class<Object>) null);
             fail("Null was allowed.");
         } catch (NullPointerException expect) {
         }
 
         try {
- LogManagerProperties.newAuthenticator("");
+ LogManagerProperties.newObjectFrom("", Object.class);
             fail("Empty class was allowed.");
         } catch (ClassNotFoundException expect) {
         }
 
         try {
- LogManagerProperties.newAuthenticator(Object.class.getName());
+ LogManagerProperties.newObjectFrom(Object.class.getName(),
+ String.class);
             fail("Wrong type was allowed.");
         } catch (ClassCastException expect) {
         }
 
+ Object o = LogManagerProperties.
+ newObjectFrom(String.class.getName(), Object.class);
+ assertEquals(String.class, o.getClass());
+
+ String n = LogManagerProperties.
+ newObjectFrom(String.class.getName(), String.class);
+ assertEquals(String.class, n.getClass());
+ }
+
+ @Test
+ public void testNewAuthenticator() throws Exception {
+ Authenticator a = LogManagerProperties.newObjectFrom(
+ EmptyAuthenticator.class.getName(),
+ Authenticator.class);
+ assertEquals(EmptyAuthenticator.class, a.getClass());
+
         final Class<?> type = ErrorAuthenticator.class;
- final javax.mail.Authenticator a
- = LogManagerProperties.newAuthenticator(type.getName());
+ a = LogManagerProperties.newObjectFrom(
+ type.getName(), Authenticator.class);
         assertEquals(type, a.getClass());
 
         setPending(new RuntimeException());
         try {
- LogManagerProperties.newAuthenticator(type.getName());
+ LogManagerProperties.newObjectFrom(type.getName(),
+ Authenticator.class);
             fail("Exception was not thrown.");
         } catch (InvocationTargetException expect) {
             assertEquals(RuntimeException.class, expect.getCause().getClass());
@@ -897,19 +984,19 @@
             Class<?> k = ErrorAuthenticator.class;
             javax.mail.Authenticator a;
 
- a = LogManagerProperties.newAuthenticator(k.getName());
+ a = LogManagerProperties.newObjectFrom(k.getName(), Authenticator.class);
             assertEquals(k, a.getClass());
 
             setPending(new ThreadDeath());
             try {
- a = LogManagerProperties.newAuthenticator(k.getName());
+ a = LogManagerProperties.newObjectFrom(k.getName(), Authenticator.class);
                 fail(String.valueOf(a));
             } catch (ThreadDeath expect) {
             }
 
             setPending(new OutOfMemoryError());
             try {
- a = LogManagerProperties.newAuthenticator(k.getName());
+ a = LogManagerProperties.newObjectFrom(k.getName(), Authenticator.class);
                 fail(String.valueOf(a));
             } catch (OutOfMemoryError expect) {
             }
@@ -1051,6 +1138,24 @@
         }
     }
 
+ private static Field setAccessible(Field f) {
+ f.setAccessible(true);
+ try {
+ assumeNoJit();
+ if (Modifier.isFinal(f.getModifiers())) {
+ Field mod = Field.class.getDeclaredField("modifiers");
+ mod.setAccessible(true);
+ mod.setInt(f, f.getModifiers() & ~Modifier.FINAL);
+ return mod;
+ }
+ } catch (RuntimeException re) {
+ Assume.assumeNoException(re);
+ } catch (Exception e) {
+ Assume.assumeNoException(e);
+ }
+ throw new AssertionError();
+ }
+
     private void read(LogManager manager, Properties props) throws IOException {
         final ByteArrayOutputStream out = new ByteArrayOutputStream(512);
         props.store(out, "No comment");
@@ -1071,6 +1176,14 @@
         return false;
     }
 
+ public static final class EmptyAuthenticator extends javax.mail.Authenticator {
+
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return null;
+ }
+ }
+
     public static final class ErrorAuthenticator extends javax.mail.Authenticator {
 
         public ErrorAuthenticator() {

diff -r d9c2380c7f1b -r 6a2e8d479145 mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Fri Mar 27 15:17:30 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Mon Mar 30 15:09:32 2015 -0700
@@ -42,6 +42,8 @@
 
 import com.sun.mail.util.SocketConnectException;
 import java.io.*;
+import java.lang.management.CompilationMXBean;
+import java.lang.management.ManagementFactory;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
@@ -111,6 +113,14 @@
         anyClassPathDir = null;
     }
 
+ private static void assumeNoJit() {
+ CompilationMXBean c = ManagementFactory.getCompilationMXBean();
+ if (c != null) { //-Xint
+ Assume.assumeNoException(new IllegalArgumentException(
+ c.getName() + " must be disabled."));
+ }
+ }
+
     private static void fullFence() {
         LogManager.getLogManager().getProperty("");
     }
@@ -170,6 +180,24 @@
         }
     }
 
+ private static Field setAccessible(Field f) {
+ f.setAccessible(true);
+ try {
+ assumeNoJit();
+ if (Modifier.isFinal(f.getModifiers())) {
+ Field mod = Field.class.getDeclaredField("modifiers");
+ mod.setAccessible(true);
+ mod.setInt(f, f.getModifiers() & ~Modifier.FINAL);
+ return mod;
+ }
+ } catch (RuntimeException re) {
+ Assume.assumeNoException(re);
+ } catch (Exception e) {
+ Assume.assumeNoException(e);
+ }
+ throw new AssertionError();
+ }
+
     private static void set(ClassLoader expect) {
         if (expect == null) {
             LOADER.remove();
@@ -4219,6 +4247,127 @@
         }
     }
 
+ @Ignore
+ public void testGaeForbiddenHeaders() throws Exception {
+ assumeNoJit();
+ assertNull(System.getSecurityManager());
+ assertTrue(LogManagerProperties.hasLogManager());
+ final Class<?> k = LogManagerProperties.class;
+ final Field f = k.getDeclaredField("LOG_MANAGER");
+ final Field mod = setAccessible(f);
+ try {
+ final Object lm = f.get(null);
+ f.set(null, new Properties());
+ try {
+ fullFence();
+ assertFalse(LogManagerProperties.hasLogManager());
+ MailHandler instance = createHandlerWithRecords();
+ instance.setErrorManager(new GaeErrorManager(instance));
+ instance.close();
+ } finally {
+ f.set(null, lm);
+ fullFence();
+ }
+ } finally {
+ mod.setInt(f, f.getModifiers() | Modifier.FINAL);
+ }
+ assertTrue(LogManagerProperties.hasLogManager());
+ }
+
+ @Ignore
+ public void testGaeSecurityManager() throws Exception {
+ assumeNoJit();
+ InternalErrorManager em;
+ MailHandler h = null;
+ final GaeSecurityManager manager = new GaeSecurityManager();
+ System.setSecurityManager(manager);
+ try {
+ manager.secure = false;
+ h = new MailHandler(createInitProperties(""));
+ em = new InternalErrorManager();
+ h.setErrorManager(em);
+ manager.secure = true;
+ assertEquals(manager, System.getSecurityManager());
+
+ //GAE allows access to loggers.
+ Logger global = Logger.getLogger("global");
+ hardRef = global;
+ global.addHandler(h);
+ global.removeHandler(h);
+ global.removeHandler((Handler) null);
+ hardRef = null;
+
+ h.postConstruct();
+ h.setAttachmentFormatters(new Formatter[]{new ThrowFormatter()});
+ assertEquals(1, h.getAttachmentFormatters().length);
+
+ h.setAttachmentFilters(new Filter[]{new ThrowFilter()});
+ assertEquals(1, h.getAttachmentFormatters().length);
+
+ assertEquals(1, h.getAttachmentFormatters().length);
+ h.setAttachmentNames(new String[]{"error.txt"});
+
+
+ assertEquals(1, h.getAttachmentFormatters().length);
+ h.setAttachmentNames(new Formatter[]{new ThrowFormatter()});
+
+ h.setAuthenticator((Authenticator) null);
+ h.setComparator((Comparator<? super LogRecord>) null);
+
+ h.setLevel(Level.ALL);
+ h.setFilter(BooleanFilter.FALSE);
+ h.setFilter((Filter) null);
+ h.setFormatter(new EmptyFormatter());
+
+ assertNotNull(h.getErrorManager());
+ h.setErrorManager(new ErrorManager());
+
+ h.setEncoding((String) null);
+
+ h.flush();
+ h.push();
+
+ h.setMailProperties(new Properties());
+
+ h.setPushFilter((Filter) null);
+ h.setPushLevel(Level.OFF);
+
+ h.setSubject(new ThrowFormatter());
+ h.setSubject("test");
+
+ h.getAuthenticator();
+ h.getMailProperties();
+
+ h.preDestroy();
+ h.close();
+
+ //check for internal exceptions caused by security manager.
+ for (Exception e : em.exceptions) {
+ dump(e);
+ }
+ assertTrue(em.exceptions.isEmpty());
+
+ hardRef = h = new MailHandler();
+ h.close();
+
+ hardRef = h = new MailHandler(100);
+ assertEquals(100, h.getCapacity());
+ h.close();
+
+ Properties props = new Properties();
+ props.put("test", "test");
+ hardRef = h = new MailHandler(props);
+ assertEquals(props, h.getMailProperties());
+ } finally {
+ hardRef = null;
+ manager.secure = false;
+ System.setSecurityManager((SecurityManager) null);
+ if (h != null) {
+ h.close();
+ }
+ }
+ }
+
     /**
      * Test logging permissions of the MailHandler. Must run by itself or run in
      * isolated VM. Use system property java.security.debug=all to troubleshoot
@@ -6813,6 +6962,76 @@
         }
     }
 
+ public static final class GaeErrorManager extends MessageErrorManager {
+
+ public GaeErrorManager(MailHandler h) {
+ super(h.getMailProperties());
+ }
+
+ @Override
+ protected void error(MimeMessage message, Throwable t, int code) {
+ try {
+ assertFalse(LogManagerProperties.hasLogManager());
+ String[] a = message.getHeader("auto-submitted");
+ assertTrue(Arrays.toString(a), a == null || a.length == 0);
+ message.saveChanges();
+ } catch (RuntimeException RE) {
+ dump(RE);
+ fail(RE.toString());
+ } catch (Exception ME) {
+ dump(ME);
+ fail(ME.toString());
+ }
+ }
+ }
+
+ public static final class GaeSecurityManager extends SecurityManager {
+
+ boolean secure = false;
+ private final boolean debug;
+
+ public GaeSecurityManager() {
+ debug = isSecurityDebug();
+ }
+
+ @Override
+ public void checkPermission(java.security.Permission perm) {
+ try { //Call super class always for java.security.debug tracing.
+ super.checkPermission(perm);
+ checkPermission(perm, new SecurityException(perm.toString()));
+ } catch (SecurityException se) {
+ checkPermission(perm, se);
+ }
+ }
+
+ @Override
+ public void checkPermission(java.security.Permission perm, Object context) {
+ try { //Call super class always for java.security.debug tracing.
+ super.checkPermission(perm, context);
+ checkPermission(perm, new SecurityException(perm.toString()));
+ } catch (SecurityException se) {
+ checkPermission(perm, se);
+ }
+ }
+
+ private void checkPermission(java.security.Permission perm, SecurityException se) {
+ if (secure && perm instanceof LoggingPermission) {
+ final StackTraceElement[] stack = se.getStackTrace();
+ if (stack.length == 0) {
+ Assume.assumeNoException(se);
+ }
+ for (StackTraceElement e : stack) {
+ if (Handler.class.getName().equals(e.getClassName())) {
+ throw se;
+ }
+ }
+ }
+ if (debug) {
+ securityDebugPrint(se);
+ }
+ }
+ }
+
     public static class ErrorFormatter extends Formatter {
 
         @Override


diff -r 6a2e8d479145 -r 91c54b8c0360 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Mon Mar 30 15:09:32 2015 -0700
+++ b/doc/release/CHANGES.txt Fri Apr 03 12:26:02 2015 -0700
@@ -35,6 +35,7 @@
 K 6687 long parameter values should be split using RFC 2231
 K 6703 javax.mail.Authenticator thread safety
 K 6718 Modify MailHandler to support Google App Engine.
+K 6755 EXPUNGE response during UID FETCH breaks UID->seqnum mapping
 
 
                   CHANGES IN THE 1.5.2 RELEASE

diff -r 6a2e8d479145 -r 91c54b8c0360 mail/src/main/java/com/sun/mail/imap/IMAPFolder.java
--- a/mail/src/main/java/com/sun/mail/imap/IMAPFolder.java Mon Mar 30 15:09:32 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/IMAPFolder.java Fri Apr 03 12:26:02 2015 -0700
@@ -2385,15 +2385,13 @@
 
                 // Check with the server
                 // Issue UID FETCH command
- UID u = getProtocol().fetchSequenceNumber(uid);
-
- if (u != null && u.seqnum <= total) { // Valid UID
- m = getMessageBySeqNumber(u.seqnum);
- if (m != null) {
- m.setUID(u.uid); // set this message's UID ..
- // .. and put this into the hashtable
- uidTable.put(l, m);
- }
+ getProtocol().fetchSequenceNumber(uid);
+
+ if (uidTable != null) {
+ // Check in uidTable
+ m = (IMAPMessage)uidTable.get(l);
+ if (m != null) // found it
+ return m;
                 }
             }
         } catch(ConnectionException cex) {
@@ -2422,19 +2420,16 @@
                     uidTable = new Hashtable();
 
                 // Issue UID FETCH for given range
- UID[] ua = getProtocol().fetchSequenceNumbers(start, end);
-
- msgs = new Message[ua.length];
- IMAPMessage m;
+ long[] ua = getProtocol().fetchSequenceNumbers(start, end);
+
+ List<Message> ma = new ArrayList<Message>();
                 // NOTE: Below must be within messageCacheLock region
                 for (int i = 0; i < ua.length; i++) {
- m = getMessageBySeqNumber(ua[i].seqnum);
- if (m != null) {
- m.setUID(ua[i].uid);
- msgs[i] = m;
- uidTable.put(Long.valueOf(ua[i].uid), m);
- }
+ Message m = (Message)uidTable.get(Long.valueOf(ua[i]));
+ if (m != null) // found it
+ ma.add(m);
                 }
+ msgs = ma.toArray(new Message[ma.size()]);
             }
         } catch(ConnectionException cex) {
             throw new FolderClosedException(this, cex.getMessage());
@@ -2477,15 +2472,7 @@
 
                 if (unavailUids.length > 0) {
                     // Issue UID FETCH request for given uids
- UID[] ua = getProtocol().fetchSequenceNumbers(unavailUids);
- IMAPMessage m;
- for (int i = 0; i < ua.length; i++) {
- m = getMessageBySeqNumber(ua[i].seqnum);
- if (m != null) {
- m.setUID(ua[i].uid);
- uidTable.put(Long.valueOf(ua[i].uid), m);
- }
- }
+ getProtocol().fetchSequenceNumbers(unavailUids);
                 }
 
                 // Return array of size = uids.length

diff -r 6a2e8d479145 -r 91c54b8c0360 mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java Mon Mar 30 15:09:32 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java Fri Apr 03 12:26:02 2015 -0700
@@ -1604,38 +1604,31 @@
     }
                 
     /**
- * Get the sequence number for the given UID. A UID object
- * containing the sequence number is returned. If the given UID
- * is invalid, <code>null</code> is returned.
+ * Get the sequence number for the given UID. Nothing is returned;
+ * the FETCH UID response must be handled by the reponse handler,
+ * along with any possible EXPUNGE responses, to ensure that the
+ * UID is matched with the correct sequence number.
+ *
+ * @since JavaMail 1.5.3
      */
- public UID fetchSequenceNumber(long uid) throws ProtocolException {
- UID u = null;
+ public void fetchSequenceNumber(long uid) throws ProtocolException {
         Response[] r = fetch(String.valueOf(uid), "UID", true);
 
- for (int i = 0, len = r.length; i < len; i++) {
- if (r[i] == null || !(r[i] instanceof FetchResponse))
- continue;
-
- FetchResponse fr = (FetchResponse)r[i];
- if ((u = fr.getItem(UID.class)) != null) {
- if (u.uid == uid) // this is the one we want
- break;
- else
- u = null;
- }
- }
-
         notifyResponseHandlers(r);
         handleResult(r[r.length-1]);
- return u;
     }
 
     /**
      * Get the sequence numbers for UIDs ranging from start till end.
- * UID objects that contain the sequence numbers are returned.
- * If no UIDs in the given range are found, an empty array is returned.
+ * Since the range may be large and sparse, an array of the UIDs actually
+ * found is returned. The caller must map these to messages after
+ * the FETCH UID responses have been handled by the reponse handler,
+ * along with any possible EXPUNGE responses, to ensure that the
+ * UIDs are matched with the correct sequence numbers.
+ *
+ * @since JavaMail 1.5.3
      */
- public UID[] fetchSequenceNumbers(long start, long end)
+ public long[] fetchSequenceNumbers(long start, long end)
                         throws ProtocolException {
         Response[] r = fetch(String.valueOf(start) + ":" +
                                 (end == UIDFolder.LASTUID ? "*" :
@@ -1656,15 +1649,22 @@
         notifyResponseHandlers(r);
         handleResult(r[r.length-1]);
 
- return v.toArray(new UID[v.size()]);
+ long[] lv = new long[v.size()];
+ for (int i = 0; i < v.size(); i++)
+ lv[i] = v.get(i).uid;
+ return lv;
     }
-
+
     /**
      * Get the sequence numbers for UIDs specified in the array.
- * UID objects that contain the sequence numbers are returned.
- * If no UIDs in the given range are found, an empty array is returned.
+ * Nothing is returned. The caller must map the UIDs to messages after
+ * the FETCH UID responses have been handled by the reponse handler,
+ * along with any possible EXPUNGE responses, to ensure that the
+ * UIDs are matched with the correct sequence numbers.
+ *
+ * @since JavaMail 1.5.3
      */
- public UID[] fetchSequenceNumbers(long[] uids) throws ProtocolException {
+ public void fetchSequenceNumbers(long[] uids) throws ProtocolException {
         StringBuffer sb = new StringBuffer();
         for (int i = 0; i < uids.length; i++) {
             if (i > 0)
@@ -1674,21 +1674,8 @@
 
         Response[] r = fetch(sb.toString(), "UID", true);
 
- UID u;
- List<UID> v = new ArrayList<UID>();
- for (int i = 0, len = r.length; i < len; i++) {
- if (r[i] == null || !(r[i] instanceof FetchResponse))
- continue;
-
- FetchResponse fr = (FetchResponse)r[i];
- if ((u = fr.getItem(UID.class)) != null)
- v.add(u);
- }
-
         notifyResponseHandlers(r);
         handleResult(r[r.length-1]);
-
- return v.toArray(new UID[v.size()]);
     }
 
     /**

diff -r 6a2e8d479145 -r 91c54b8c0360 mail/src/test/java/com/sun/mail/imap/IMAPHandler.java
--- a/mail/src/test/java/com/sun/mail/imap/IMAPHandler.java Mon Mar 30 15:09:32 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/imap/IMAPHandler.java Fri Apr 03 12:26:02 2015 -0700
@@ -214,13 +214,26 @@
         } else if (commandName.equals("IDLE")) {
             idle();
         } else if (commandName.equals("FETCH")) {
- fetch();
+ fetch(currentLine);
+ } else if (commandName.equals("STORE")) {
+ store(currentLine);
         } else if (commandName.equals("APPEND")) {
             append(currentLine);
         } else if (commandName.equals("CLOSE")) {
             close();
         } else if (commandName.equals("LOGOUT")) {
             logout();
+ } else if (commandName.equals("UID")) {
+ String subcommandName = ct.nextToken().toUpperCase();
+ if (subcommandName.equals("FETCH")) {
+ uidfetch(currentLine);
+ } else if (subcommandName.equals("STORE")) {
+ uidstore(currentLine);
+ } else {
+ LOGGER.log(Level.SEVERE, "ERROR UID command unknown: {0}",
+ subcommandName);
+ bad("unknown UID command");
+ }
         } else {
             LOGGER.log(Level.SEVERE, "ERROR command unknown: {0}", commandName);
             bad("unknown command");
@@ -302,7 +315,34 @@
      *
      * @throws IOException unable to read/write to socket
      */
- public void fetch() throws IOException {
+ public void fetch(String line) throws IOException {
+ ok(); // XXX
+ }
+
+ /**
+ * STORE command.
+ *
+ * @throws IOException unable to read/write to socket
+ */
+ public void store(String line) throws IOException {
+ ok(); // XXX
+ }
+
+ /**
+ * UID FETCH command.
+ *
+ * @throws IOException unable to read/write to socket
+ */
+ public void uidfetch(String line) throws IOException {
+ ok(); // XXX
+ }
+
+ /**
+ * UID STORE command.
+ *
+ * @throws IOException unable to read/write to socket
+ */
+ public void uidstore(String line) throws IOException {
         ok(); // XXX
     }
 

diff -r 6a2e8d479145 -r 91c54b8c0360 mail/src/test/java/com/sun/mail/imap/IMAPUidExpungeTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mail/src/test/java/com/sun/mail/imap/IMAPUidExpungeTest.java Fri Apr 03 12:26:02 2015 -0700
@@ -0,0 +1,492 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2009-2015 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
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package com.sun.mail.imap;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import javax.mail.Folder;
+import javax.mail.Session;
+import javax.mail.Store;
+import javax.mail.Message;
+import javax.mail.UIDFolder;
+import javax.mail.MessagingException;
+
+import com.sun.mail.test.TestServer;
+
+import org.junit.Test;
+import org.junit.Rule;
+import org.junit.rules.Timeout;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test EXPUNGE responses during UID FETCH.
+ */
+public final class IMAPUidExpungeTest {
+
+ // timeout the test in case of deadlock
+ @Rule
+ public Timeout deadlockTimeout = new Timeout(20000);
+
+ public static interface IMAPTest {
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException;
+ }
+
+ @Test
+ public void testUIDSingle() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message m = ((UIDFolder)folder).getMessageByUID(2);
+ m.getFlags();
+ assertEquals(1, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("1 EXPUNGE");
+ untagged("3 EXISTS");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDSingle2() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message m = ((UIDFolder)folder).getMessageByUID(2);
+ m.getFlags();
+ assertEquals(2, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("3 EXPUNGE");
+ untagged("3 EXISTS");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDRange() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(2, 4);
+ assertTrue(msgs[1] == null || msgs[1].isExpunged());
+ msgs[0].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[2].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("3 FETCH (UID 3)");
+ untagged("4 FETCH (UID 4)");
+ untagged("3 EXPUNGE");
+ untagged("3 EXISTS");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDRange2() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(2, 4);
+ assertTrue(msgs[1] == null || msgs[1].isExpunged());
+ msgs[0].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[2].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("3 FETCH (UID 3)");
+ untagged("3 EXPUNGE");
+ untagged("3 EXISTS");
+ untagged("3 FETCH (UID 4)");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDRange3() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(2, 4);
+ // UID 3 is unknown and not returned
+ assertEquals(2, msgs.length);
+ msgs[0].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[1].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("3 EXPUNGE");
+ untagged("3 EXISTS");
+ untagged("3 FETCH (UID 4)");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDRange4() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(1, 3);
+ assertEquals(3, msgs.length);
+ msgs[0].getFlags();
+ assertEquals(1, handler.getSeqNum());
+ msgs[1].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[2].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("1 FETCH (UID 1)");
+ untagged("2 FETCH (UID 2)");
+ untagged("3 FETCH (UID 3)");
+ untagged("4 EXPUNGE");
+ untagged("3 EXISTS");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDRange5() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(2, 4);
+ assertEquals(3, msgs.length);
+ msgs[0].getFlags();
+ assertEquals(1, handler.getSeqNum());
+ msgs[1].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[2].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("3 FETCH (UID 3)");
+ untagged("4 FETCH (UID 4)");
+ untagged("1 EXPUNGE");
+ untagged("3 EXISTS");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDList() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(
+ new long[] { 2, 3, 4 });
+ assertTrue(msgs[1] == null || msgs[1].isExpunged());
+ msgs[0].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[2].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("3 FETCH (UID 3)");
+ untagged("4 FETCH (UID 4)");
+ untagged("3 EXPUNGE");
+ untagged("3 EXISTS");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDList2() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(
+ new long[] { 2, 3, 4 });
+ assertTrue(msgs[1] == null || msgs[1].isExpunged());
+ msgs[0].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[2].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("3 FETCH (UID 3)");
+ untagged("3 EXPUNGE");
+ untagged("3 EXISTS");
+ untagged("3 FETCH (UID 4)");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDList3() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(
+ new long[] { 2, 3, 4 });
+ assertTrue(msgs[1] == null || msgs[1].isExpunged());
+ msgs[0].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[2].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("2 FETCH (UID 2)");
+ untagged("3 EXPUNGE");
+ untagged("3 EXISTS");
+ untagged("3 FETCH (UID 4)");
+ numberOfMessages--;
+ ok();
+ }
+ });
+ }
+
+ @Test
+ public void testUIDList4() {
+ testWithHandler(
+ new IMAPTest() {
+ @Override
+ public void test(Folder folder, IMAPHandlerExpunge handler)
+ throws MessagingException {
+ Message[] msgs = ((UIDFolder)folder).getMessagesByUID(
+ new long[] { 1, 2, 3 });
+ assertEquals(3, msgs.length);
+ msgs[0].getFlags();
+ assertEquals(1, handler.getSeqNum());
+ msgs[1].getFlags();
+ assertEquals(2, handler.getSeqNum());
+ msgs[2].getFlags();
+ assertEquals(3, handler.getSeqNum());
+ }
+ },
+ new IMAPHandlerExpunge() {
+ @Override
+ public void uidfetch(String line) throws IOException {
+ untagged("1 FETCH (UID 1)");
+ untagged("2 FETCH (UID 2)");
+ untagged("3 FETCH (UID 3)");
+ untagged("4 EXPUNGE");
+ untagged("3 EXISTS");
+ numberOfMessages--;
+ ok()
[truncated due to length]