commits@javamail.java.net

[javamail~mercurial:776] DurationFilter support for ISO-8601 duration format.

From: <shannon_at_java.net>
Date: Mon, 12 Oct 2015 18:49:14 +0000

Project: javamail
Repository: mercurial
Revision: 776
Author: shannon
Date: 2015-10-10 00:01:38 UTC
Link:

Log Message:
------------
Simplify DurationFilter regex.
Fix MailHandlerDemo push filter demo config.

(From Jason)
make url field volatile instead of synchronizing access to allow toString
to be used in log messages without violating the locking hierarchy and
causing deadlocks
fix logging levels - fine for lifecycle, finest for others
handle race condition that caused CancelledKeyException - bug 7019
IndexOutOfBoundsException reading IMAP literal when connection fails - bug 7014
increase test timeout to avoid spurious failures
DurationFilter support for ISO-8601 duration format.
DurationFilterTest FindBug fixes.

(From Jason)


Revisions:
----------
770
771
772
773
774
775
776


Modified Paths:
---------------
logging/src/main/java/MailHandlerDemo.java
mail/src/main/java/com/sun/mail/util/logging/DurationFilter.java
mail/src/main/java/javax/mail/Service.java
mail/src/main/java/com/sun/mail/imap/IdleManager.java
doc/release/CHANGES.txt
mail/src/main/java/com/sun/mail/iap/ResponseInputStream.java
mail/src/test/java/com/sun/mail/imap/IMAPIdleManagerTest.java
mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java
mail/src/test/java/com/sun/mail/util/logging/DurationFilterTest.java
mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java


Added Paths:
------------
mail/src/test/java/com/sun/mail/iap/ResponseInputStreamTest.java


Diffs:
------
diff -r d22ab7996acc -r ae8e5f406e7f logging/src/main/java/MailHandlerDemo.java
--- a/logging/src/main/java/MailHandlerDemo.java Fri Oct 02 15:57:37 2015 -0700
+++ b/logging/src/main/java/MailHandlerDemo.java Mon Oct 05 16:02:06 2015 -0700
@@ -372,7 +372,7 @@
      * messages are sent. If the capacity is set to the <code>
      * ##logging.properties
      * MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
- * com.sun.mail.util.logging.MailHandler.subject=Push if under two records per minute.
+ * com.sun.mail.util.logging.MailHandler.subject=Push filter demo
      * com.sun.mail.util.logging.MailHandler.pushLevel=ALL
      * com.sun.mail.util.logging.MailHandler.pushFilter=com.sun.mail.util.logging.DurationFilter
      * com.sun.mail.util.logging.DurationFilter.records=2

diff -r d22ab7996acc -r ae8e5f406e7f mail/src/main/java/com/sun/mail/util/logging/DurationFilter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/DurationFilter.java Fri Oct 02 15:57:37 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/DurationFilter.java Mon Oct 05 16:02:06 2015 -0700
@@ -344,8 +344,7 @@
         value = value.trim();
         String[] e;
         final int i = value.indexOf('*');
- if (i > -1 && (e = value.split(
- "[\\s]*[\\x2A]{1}[\\s]*")).length != 0) {
+ if (i > -1 && (e = value.split("\\s*\\*\\s*")).length != 0) {
             if (i == 0 || value.charAt(value.length() - 1) == '*') {
                 throw new NumberFormatException(value);
             }


diff -r ae8e5f406e7f -r 9e43e2b002e6 mail/src/main/java/javax/mail/Service.java
--- a/mail/src/main/java/javax/mail/Service.java Mon Oct 05 16:02:06 2015 -0700
+++ b/mail/src/main/java/javax/mail/Service.java Fri Oct 09 12:45:06 2015 -0700
@@ -69,7 +69,7 @@
     /**
      * The <code>URLName</code> of this service.
      */
- protected URLName url = null;
+ protected volatile URLName url = null;
 
     /**
      * Debug flag for this service. Set from the session's debug
@@ -522,7 +522,8 @@
      * @return the URLName representing this service
      * @see URLName
      */
- public synchronized URLName getURLName() {
+ public URLName getURLName() {
+ URLName url = this.url; // snapshot
         if (url != null && (url.getPassword() != null || url.getFile() != null))
             return new URLName(url.getProtocol(), url.getHost(),
                         url.getPort(), null /* no file */,
@@ -549,7 +550,7 @@
      * @param url the URLName
      * @see URLName
      */
- protected synchronized void setURLName(URLName url) {
+ protected void setURLName(URLName url) {
         this.url = url;
     }
 


diff -r 9e43e2b002e6 -r f5f051a7ecdd mail/src/main/java/com/sun/mail/imap/IdleManager.java
--- a/mail/src/main/java/com/sun/mail/imap/IdleManager.java Fri Oct 09 12:45:06 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/IdleManager.java Fri Oct 09 13:25:53 2015 -0700
@@ -279,17 +279,17 @@
                     ;
             }
         } catch (InterruptedIOException ex) {
- logger.log(Level.FINE, "IdleManager interrupted", ex);
+ logger.log(Level.FINEST, "IdleManager interrupted", ex);
         } catch (IOException ex) {
- logger.log(Level.FINE, "IdleManager got exception", ex);
+ logger.log(Level.FINEST, "IdleManager got exception", ex);
         } finally {
- logger.fine("IdleManager unwatchAll");
+ logger.finest("IdleManager unwatchAll");
             try {
                 unwatchAll();
                 selector.close();
             } catch (IOException ex2) {
                 // nothing to do...
- logger.log(Level.FINE, "IdleManager unwatch exception", ex2);
+ logger.log(Level.FINEST, "IdleManager unwatch exception", ex2);
             }
             logger.fine("IdleManager exiting");
         }
@@ -353,7 +353,7 @@
             sk.cancel();
             folder = (IMAPFolder)sk.attachment();
             if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                     "IdleManager selected folder: {0}", folderName(folder));
             SelectableChannel sc = sk.channel();
             // switch back to blocking to allow normal I/O
@@ -361,7 +361,7 @@
             try {
                 if (folder.handleIdle(false)) {
                     if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                             "IdleManager continue watching folder {0}",
                                                         folderName(folder));
                     // more to do with this folder, select on it again
@@ -371,13 +371,13 @@
                 } else {
                     // done watching this folder,
                     if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                             "IdleManager done watching folder {0}",
                                                         folderName(folder));
                 }
             } catch (MessagingException ex) {
                 // something went wrong, stop watching this folder
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                     "IdleManager got exception for folder: " +
                                                     folderName(folder), ex);
             }
@@ -388,7 +388,7 @@
          */
         while ((folder = toAbort.poll()) != null) {
             if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                     "IdleManager aborting IDLE for folder: {0}",
                                                         folderName(folder));
             SocketChannel sc = folder.getChannel();
@@ -404,7 +404,7 @@
             // if there's a read timeout, have to do the abort in a new thread
             Socket sock = sc.socket();
             if (sock != null && sock.getSoTimeout() > 0) {
- logger.fine("IdleManager requesting DONE with timeout");
+ logger.finest("IdleManager requesting DONE with timeout");
                 toWatch.remove(folder);
                 final IMAPFolder folder0 = folder;
                 es.execute(new Runnable() {
@@ -439,7 +439,7 @@
             sk.cancel();
             folder = (IMAPFolder)sk.attachment();
             if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                     "IdleManager no longer watching folder: {0}",
                                                         folderName(folder));
             SelectableChannel sc = sk.channel();
@@ -449,7 +449,7 @@
                 folder.idleAbortWait(); // send the DONE message and wait
             } catch (IOException ex) {
                 // ignore it, channel might be closed
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                     "IdleManager exception while aborting idle for folder: " +
                                                     folderName(folder), ex);
             }
@@ -460,7 +460,7 @@
          */
         while ((folder = toWatch.poll()) != null) {
             if (logger.isLoggable(Level.FINEST))
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                     "IdleManager aborting IDLE for unwatched folder: {0}",
                                                         folderName(folder));
             SocketChannel sc = folder.getChannel();
@@ -472,7 +472,7 @@
                 folder.idleAbortWait(); // send the DONE message and wait
             } catch (IOException ex) {
                 // ignore it, channel might be closed
- logger.log(Level.FINE,
+ logger.log(Level.FINEST,
                     "IdleManager exception while aborting idle for folder: " +
                                                     folderName(folder), ex);
             }
@@ -484,7 +484,7 @@
      */
     public synchronized void stop() {
         die = true;
- logger.finest("IdleManager stopping");
+ logger.fine("IdleManager stopping");
         selector.wakeup();
     }
 


diff -r f5f051a7ecdd -r db991b9ecd0b doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Oct 09 13:25:53 2015 -0700
+++ b/doc/release/CHANGES.txt Fri Oct 09 16:23:33 2015 -0700
@@ -32,6 +32,7 @@
 K 6989 MailHandler needs better support for stateful filters.
 K 6997 add support for IMAP login referrals (RFC 2221)
 K 7009 whitespace line at beginning confuses InternetHeaders
+K 7019 IdleManager dies with CancelledKeyException
 
 
                   CHANGES IN THE 1.5.4 RELEASE

diff -r f5f051a7ecdd -r db991b9ecd0b mail/src/main/java/com/sun/mail/imap/IdleManager.java
--- a/mail/src/main/java/com/sun/mail/imap/IdleManager.java Fri Oct 09 13:25:53 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/IdleManager.java Fri Oct 09 16:23:33 2015 -0700
@@ -267,22 +267,28 @@
                  * need to continue watching that folder it's added
                  * to the toWatch list again. We can't actually
                  * register that folder again until the previous
- * selectionkey is cancelled, so we call selectNow()
+ * selection key is cancelled, so we call selectNow()
                  * just for the side effect of cancelling the selection
                  * keys. But if selectNow() selects something, we
                  * process it before adding folders from the toWatch
                  * queue. And so on until there is nothing to do, at
                  * which point it's safe to register folders from the
- * toWatch queue.
+ * toWatch queue. This should be "fair" since each
+ * selection key is used only once before being added
+ * to the toWatch list.
                  */
- while (processKeys() && selector.selectNow() > 0)
- ;
+ do {
+ processKeys();
+ } while (selector.selectNow() > 0);
             }
         } catch (InterruptedIOException ex) {
             logger.log(Level.FINEST, "IdleManager interrupted", ex);
         } catch (IOException ex) {
+ logger.log(Level.FINEST, "IdleManager got I/O exception", ex);
+ } catch (Exception ex) {
             logger.log(Level.FINEST, "IdleManager got exception", ex);
         } finally {
+ die = true; // prevent new watches in case of exception
             logger.finest("IdleManager unwatchAll");
             try {
                 unwatchAll();
@@ -320,16 +326,18 @@
                 // oh well, nothing to do
                 logger.log(Level.FINEST,
                     "IdleManager can't register folder", ex);
+ } catch (CancelledKeyException ex) {
+ // this should never happen
+ logger.log(Level.FINEST,
+ "IdleManager can't register folder", ex);
             }
         }
     }
 
     /**
- * Process the selected keys, returning true if any folders have
- * been added to the watch list.
+ * Process the selected keys.
      */
- private boolean processKeys() throws IOException {
- boolean more = false;
+ private void processKeys() throws IOException {
         IMAPFolder folder;
 
         /*
@@ -365,9 +373,7 @@
                             "IdleManager continue watching folder {0}",
                                                         folderName(folder));
                     // more to do with this folder, select on it again
- // XXX - what if we also added it above?
                     toWatch.add(folder);
- more = true;
                 } else {
                     // done watching this folder,
                     if (logger.isLoggable(Level.FINEST))
@@ -417,12 +423,10 @@
             } else {
                 folder.idleAbort(); // send the DONE message
                 // watch for OK response to DONE
+ // XXX - what if we also added it above? should be a nop
                 toWatch.add(folder);
- more = true;
             }
         }
-
- return more;
     }
 
     /**


diff -r db991b9ecd0b -r 81dcf9021fba doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Oct 09 16:23:33 2015 -0700
+++ b/doc/release/CHANGES.txt Fri Oct 09 16:47:09 2015 -0700
@@ -32,6 +32,7 @@
 K 6989 MailHandler needs better support for stateful filters.
 K 6997 add support for IMAP login referrals (RFC 2221)
 K 7009 whitespace line at beginning confuses InternetHeaders
+K 7014 IndexOutOfBoundsException reading IMAP literal when connection fails
 K 7019 IdleManager dies with CancelledKeyException
 
 

diff -r db991b9ecd0b -r 81dcf9021fba mail/src/main/java/com/sun/mail/iap/ResponseInputStream.java
--- a/mail/src/main/java/com/sun/mail/iap/ResponseInputStream.java Fri Oct 09 16:23:33 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/iap/ResponseInputStream.java Fri Oct 09 16:47:09 2015 -0700
@@ -157,6 +157,8 @@
                 int actual;
                 while (count > 0) {
                     actual = bin.read(buffer, idx, count);
+ if (actual == -1)
+ throw new IOException("Connection dropped by server?");
                     count -= actual;
                     idx += actual;
                 }

diff -r db991b9ecd0b -r 81dcf9021fba mail/src/test/java/com/sun/mail/iap/ResponseInputStreamTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mail/src/test/java/com/sun/mail/iap/ResponseInputStreamTest.java Fri Oct 09 16:47:09 2015 -0700
@@ -0,0 +1,70 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013-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.iap;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+/**
+ * Test ResponseInputStream.
+ */
+public class ResponseInputStreamTest {
+
+ /**
+ * Test that an EOF while reading a literal throws an IOException.
+ */
+ @Test
+ public void testEofWhileReadingLiteral() throws Exception {
+ ByteArrayInputStream bis = new ByteArrayInputStream(
+ "test{1}\r\n".getBytes("ISO-8859-1"));
+ ResponseInputStream ris = new ResponseInputStream(bis);
+ try {
+ ris.readResponse();
+ } catch (IOException ex) {
+ // success!
+ return;
+ }
+ fail("no exception");
+ }
+}


diff -r 81dcf9021fba -r 78d938036450 mail/src/test/java/com/sun/mail/imap/IMAPIdleManagerTest.java
--- a/mail/src/test/java/com/sun/mail/imap/IMAPIdleManagerTest.java Fri Oct 09 16:47:09 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/imap/IMAPIdleManagerTest.java Fri Oct 09 17:01:00 2015 -0700
@@ -73,7 +73,7 @@
 
     // timeout the test in case of deadlock
     @Rule
- public Timeout deadlockTimeout = new Timeout(5 * TIMEOUT);
+ public Timeout deadlockTimeout = new Timeout(10 * TIMEOUT);
 
     /**
      * Test that IdleManager handles multiple responses in a single packet.


diff -r 78d938036450 -r 996e4c69f94d mail/src/main/java/com/sun/mail/util/logging/DurationFilter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/DurationFilter.java Fri Oct 09 17:01:00 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/DurationFilter.java Fri Oct 09 17:01:38 2015 -0700
@@ -64,7 +64,11 @@
  * <li>{_at_literal <filter-name>}.duration the number of milliseconds to suppress
  * log records from being published. This is also used as duration to determine
  * the log record rate. A numeric long integer or a multiplication expression
- * can be used as the value. (defaults to {_at_code 15L * 60L * 1000L})
+ * can be used as the value. If the {_at_code java.time} package is avaliable then
+ * an ISO-8601 duration format of {_at_code PnDTnHnMn.nS} can be used as the value.
+ * The suffixes of "D", "H", "M" and "S" are for days, hours, minutes and
+ * seconds. The suffixes must occur in order. The seconds can be specified with
+ * a fractional component to declare milliseconds. (defaults to {_at_code PT15M})
  * </ul>
  *
  * <p>
@@ -76,7 +80,7 @@
  * com.sun.mail.util.logging.MailHandler.filter = com.sun.mail.util.logging.DurationFilter
  * com.sun.mail.util.logging.MailHandler.capacity = 1000
  * com.sun.mail.util.logging.DurationFilter.records = 2L * 1000L
- * com.sun.mail.util.logging.DurationFilter.duration = 6L * 60L * 1000L
+ * com.sun.mail.util.logging.DurationFilter.duration = PT6M
  * }
  * </pre>
  *
@@ -311,20 +315,31 @@
      * @throws NullPointerException if suffix is null.
      */
     private long initLong(final String suffix) {
- long result;
+ long result = 0L;
         final String p = getClass().getName();
         String value = fromLogManager(p.concat(suffix));
         if (value != null && value.length() != 0) {
- try {
- result = 1L;
- for (String s : tokenizeLongs(value)) {
- if (s.endsWith("L") || s.endsWith("l")) {
- s = s.substring(0, s.length() - 1);
+ if (isTimeEntry(suffix, value)) {
+ try {
+ result = LogManagerProperties.parseDurationToMillis(value);
+ } catch (final RuntimeException ignore) {
+ } catch (final Exception ignore) {
+ } catch (final LinkageError ignore) {
+ }
+ }
+
+ if (result == 0L) { //Zero is invalid.
+ try {
+ result = 1L;
+ for (String s : tokenizeLongs(value)) {
+ if (s.endsWith("L") || s.endsWith("l")) {
+ s = s.substring(0, s.length() - 1);
+ }
+ result = multiplyExact(result, Long.parseLong(s));
                     }
- result = multiplyExact(result, Long.parseLong(s));
+ } catch (final RuntimeException ignore) {
+ result = Long.MIN_VALUE;
                 }
- } catch (final RuntimeException ignore) {
- result = Long.MIN_VALUE;
             }
         } else {
             result = Long.MIN_VALUE;
@@ -333,6 +348,21 @@
     }
 
     /**
+ * Determines if the given suffix can be a time unit and the value is
+ * encoded as an ISO ISO-8601 duration format.
+ *
+ * @param suffix the suffix property.
+ * @param value the value of the property.
+ * @return true if the entry is a time entry.
+ * @throws IndexOutOfBoundsException if value is empty.
+ * @throws NullPointerException if either argument is null.
+ */
+ private boolean isTimeEntry(final String suffix, final String value) {
+ return (value.charAt(0) == 'P' || value.charAt(0) == 'p')
+ && suffix.equals(".duration");
+ }
+
+ /**
      * Parse any long value or multiplication expressions into tokens.
      *
      * @param value the expression or value.

diff -r 78d938036450 -r 996e4c69f94d mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java
--- a/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java Fri Oct 09 17:01:00 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java Fri Oct 09 17:01:38 2015 -0700
@@ -287,6 +287,45 @@
     }
 
     /**
+ * Used to parse an ISO-8601 duration format of {_at_code PnDTnHnMn.nS}.
+ *
+ * @param value ISO-8601 duration string.
+ * @return the number of milliseconds parsed from the duration.
+ * @throws ClassNotFoundException if the java.time classes are not present.
+ * @throws IllegalAccessException if the method is inaccessible.
+ * @throws InvocationTargetException if the method throws an exception.
+ * @throws LinkageError if the linkage fails.
+ * @throws NullPointerException if the given duration is null.
+ * @throws ExceptionInInitializerError if the static initializer fails.
+ * @throws Exception if there is a problem.
+ * @throws NoSuchMethodException if the correct time methods are missing.
+ * @throws SecurityException if reflective access to the java.time classes
+ * are not allowed.
+ * @since JavaMail 1.5.5
+ */
+ static long parseDurationToMillis(final String value) throws Exception {
+ try {
+ final Class<?> k = findClass("java.time.Duration");
+ final Method parse = k.getMethod("parse", CharSequence.class);
+ if (!k.isAssignableFrom(parse.getReturnType())
+ || !Modifier.isStatic(parse.getModifiers())) {
+ throw new NoSuchMethodException(parse.toString());
+ }
+
+ final Method toMillis = k.getMethod("toMillis");
+ if (!Long.TYPE.isAssignableFrom(toMillis.getReturnType())
+ || Modifier.isStatic(toMillis.getModifiers())) {
+ throw new NoSuchMethodException(toMillis.toString());
+ }
+ return (Long) toMillis.invoke(parse.invoke(null, value));
+ } catch (final ExceptionInInitializerError EIIE) {
+ throw wrapOrThrow(EIIE);
+ } catch (final InvocationTargetException ite) {
+ throw paramOrError(ite);
+ }
+ }
+
+ /**
      * Converts a locale to a language tag.
      *
      * @param locale the locale to convert.

diff -r 78d938036450 -r 996e4c69f94d mail/src/test/java/com/sun/mail/util/logging/DurationFilterTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/DurationFilterTest.java Fri Oct 09 17:01:00 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/DurationFilterTest.java Fri Oct 09 17:01:38 2015 -0700
@@ -78,7 +78,6 @@
     @Test
     public void testClone() throws Exception {
         DurationFilterExt source = new DurationFilterExt();
- assertTrue(source instanceof Cloneable);
         final Filter clone = source.clone();
         assertNotNull(clone);
         assertFalse(source == clone);
@@ -544,6 +543,13 @@
     }
 
     @Test
+ public void testInitRecordIso8601() throws Exception {
+ if (isoDurationAllowed()) {
+ testInitRecords("PT30M", 1000);
+ }
+ }
+
+ @Test
     public void testInitDuration() throws Exception {
         testInitDuration("1024", 1024);
     }
@@ -623,6 +629,50 @@
         testInitDuration("-1024", 15L * 60L * 1000L);
     }
 
+ @Test
+ public void testInitDurationIso8601Ms() throws Exception {
+ if (isoDurationAllowed()) {
+ testInitDuration("PT0.345S", 345);
+ }
+ }
+
+ @Test
+ public void testInitDurationIso8601Sec() throws Exception {
+ if (isoDurationAllowed()) {
+ testInitDuration("PT20.345S", (20L * 1000L) + 345);
+ }
+ }
+
+ @Test
+ public void testInitDurationIso8601Min() throws Exception {
+ if (isoDurationAllowed()) {
+ testInitDuration("PT30M", 30L * 60L * 1000L);
+ }
+ }
+
+ @Test
+ public void testInitDurationIso8601Hour() throws Exception {
+ if (isoDurationAllowed()) {
+ testInitDuration("PT10H", 10L * 60L * 60L * 1000L);
+ }
+ }
+
+ @Test
+ public void testInitDurationIso8601Day() throws Exception {
+ if (isoDurationAllowed()) {
+ testInitDuration("P2D", 2L * 24L * 60L * 60L * 1000L);
+ }
+ }
+
+ @Test
+ public void testInitDurationIso8601All() throws Exception {
+ if (isoDurationAllowed()) {
+ testInitDuration("P2DT3H4M20.345S", (2L * 24L * 60L * 60L * 1000L)
+ + (3L * 60L * 60L * 1000L) + (4L * 60L * 1000L)
+ + ((20L * 1000L) + 345));
+ }
+ }
+
     private void testInitDuration(String d, long expect) throws Exception {
         testInit("duration", d, expect);
     }
@@ -647,13 +697,23 @@
         }
     }
 
+ private boolean isoDurationAllowed() {
+ try {
+ Class.forName("java.time.Duration");
+ return true;
+ } catch (final ClassNotFoundException notSupported) {
+ } catch (final NoClassDefFoundError notSupported) {
+ }
+ return false;
+ }
+
     private void read(LogManager manager, Properties props) throws IOException {
         final ByteArrayOutputStream out = new ByteArrayOutputStream(512);
         props.store(out, "No comment");
         manager.readConfiguration(new ByteArrayInputStream(out.toByteArray()));
     }
 
- public final class DurationFilterExt extends DurationFilter
+ public static final class DurationFilterExt extends DurationFilter
             implements Cloneable {
 
         public DurationFilterExt() {

diff -r 78d938036450 -r 996e4c69f94d mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java Fri Oct 09 17:01:00 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java Fri Oct 09 17:01:38 2015 -0700
@@ -463,6 +463,69 @@
         }
         Assert.assertFalse(fail);
     }
+
+ @Test
+ public void testParseDurationMs() throws Exception {
+ try {
+ long ms = LogManagerProperties.parseDurationToMillis("PT0.345S");
+ assertEquals(345L, ms);
+ } catch (ClassNotFoundException ignore) {
+ } catch (NoClassDefFoundError ignore) {
+ }
+ }
+
+ @Test
+ public void testParseDurationSec() throws Exception {
+ try {
+ long ms = LogManagerProperties.parseDurationToMillis("PT20.345S");
+ assertEquals((20L * 1000L) + 345L, ms);
+ } catch (ClassNotFoundException ignore) {
+ } catch (NoClassDefFoundError ignore) {
+ }
+ }
+
+ @Test
+ public void testParseDurationMin() throws Exception {
+ try {
+ long ms = LogManagerProperties.parseDurationToMillis("PT15M");
+ assertEquals(15L * 60L * 1000L, ms);
+ } catch (ClassNotFoundException ignore) {
+ } catch (NoClassDefFoundError ignore) {
+ }
+ }
+
+ @Test
+ public void testParseDurationHour() throws Exception {
+ try {
+ long ms = LogManagerProperties.parseDurationToMillis("PT10H");
+ assertEquals(10L * 60L * 60L * 1000L, ms);
+ } catch (ClassNotFoundException ignore) {
+ } catch (NoClassDefFoundError ignore) {
+ }
+ }
+
+ @Test
+ public void testParseDurationDay() throws Exception {
+ try {
+ long ms = LogManagerProperties.parseDurationToMillis("P2D");
+ assertEquals(2L * 24L * 60L * 60L * 1000L, ms);
+ } catch (ClassNotFoundException ignore) {
+ } catch (NoClassDefFoundError ignore) {
+ }
+ }
+
+ @Test
+ public void testParseDurationAll() throws Exception {
+ try {
+ long ms = LogManagerProperties
+ .parseDurationToMillis("P2DT3H4M20.345S");
+ assertEquals((2L * 24L * 60L * 60L * 1000L)
+ + (3L * 60L * 60L * 1000L) + (4L * 60L * 1000L)
+ + ((20L * 1000L) + 345), ms);
+ } catch (ClassNotFoundException ignore) {
+ } catch (NoClassDefFoundError ignore) {
+ }
+ }
 
     @Test
     public void testGetProperty_String() throws Exception {