commits@javamail.java.net

[javamail~mercurial:731] Expose getCopyUID and createFlagList methods for subclasses - bug 6863

From: <shannon_at_java.net>
Date: Fri, 19 Jun 2015 20:13:26 +0000

Project: javamail
Repository: mercurial
Revision: 731
Author: shannon
Date: 2015-06-18 22:33:41 UTC
Link:

Log Message:
------------
fix example for IdleManager.watch and remove throw of IOException
ID command shouldn't escape NIL value - bug 6860
Include elapsed time, thread id, and sequence for logging formatters. - bug 6719
Fix JavaDoc warnings.
Fix FindBugs 3.0.1 warnings.

(From Jason)
Explain why the test is more strict than the WebappClassLoader - bug 6767
Upgrade to FindBugs 3.0.1 and fix or exclude some of the new errors.
Expose getCopyUID and createFlagList methods for subclasses - bug 6863


Revisions:
----------
726
727
728
729
730
731


Modified Paths:
---------------
doc/release/COMPAT.txt
mail/src/main/java/com/sun/mail/imap/IdleManager.java
doc/release/CHANGES.txt
mail/src/main/java/com/sun/mail/imap/protocol/ID.java
mail/src/test/java/com/sun/mail/imap/IMAPHandler.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/CollectorFormatterTest.java
mail/src/test/java/com/sun/mail/util/logging/CompactFormatterTest.java
mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
mail/exclude.xml
mail/src/main/java/com/sun/mail/imap/IMAPFolder.java
mail/src/main/java/com/sun/mail/util/MailSSLSocketFactory.java
mail/src/main/java/javax/mail/Authenticator.java
mail/src/main/java/javax/mail/internet/ParameterList.java
pom.xml
mail/src/main/java/com/sun/mail/imap/protocol/IMAPProtocol.java


Added Paths:
------------
mail/src/test/java/com/sun/mail/imap/IMAPIDTest.java


Diffs:
------
diff -r d0160695c4f4 -r 055b7845aba1 doc/release/COMPAT.txt
--- a/doc/release/COMPAT.txt Fri Jun 05 14:03:07 2015 -0700
+++ b/doc/release/COMPAT.txt Thu Jun 11 15:49:30 2015 -0700
@@ -14,6 +14,17 @@
 with this release of the JavaMail API.
 
 
+-- JavaMail 1.5.4 --
+
+- Idlemanager.watch no longer throws IOException
+
+ The IdleManager.watch method was declared to throw IOException,
+ but never actually threw it. The declaraction has been changed,
+ which will cause a source incompatibility for code expecting to
+ catch IOException when calling the watch method.
+
+
+
 -- JavaMail 1.5.3 --
 
 - Date search terms result in wrong greater-than SEARCH commands for IMAP

diff -r d0160695c4f4 -r 055b7845aba1 mail/src/main/java/com/sun/mail/imap/IdleManager.java
--- a/mail/src/main/java/com/sun/mail/imap/IdleManager.java Fri Jun 05 14:03:07 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/IdleManager.java Thu Jun 11 15:49:30 2015 -0700
@@ -83,8 +83,12 @@
  * Message[] msgs = ev.getMessages();
  * System.out.println("Folder: " + folder +
  * " got " + msgs.length + " new messages");
- * // process new messages
- * idleManager.watch(folder); // keep watching for new messages
+ * try {
+ * // process new messages
+ * idleManager.watch(folder); // keep watching for new messages
+ * } catch (MessagingException mex) {
+ * // handle exception related to the Folder
+ * }
  * }
  * });
  * idleManager.watch(folder);
@@ -169,10 +173,9 @@
      *
      * @param folder the folder to watch
      * @exception MessagingException for errors related to the folder
- * @exception IOException for SocketChannel errors
      */
     public synchronized void watch(Folder folder)
- throws IOException, MessagingException {
+ throws MessagingException {
         if (!(folder instanceof IMAPFolder))
             throw new MessagingException("Can only watch IMAP folders");
         IMAPFolder ifolder = (IMAPFolder)folder;


diff -r 055b7845aba1 -r 6926f3556fa8 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Thu Jun 11 15:49:30 2015 -0700
+++ b/doc/release/CHANGES.txt Thu Jun 11 15:50:07 2015 -0700
@@ -25,6 +25,7 @@
 K 6844 Space character lost from end of quoted-printable body parts
 K 6850 GmailMessage extensions are not cached after implicit FETCH
 K 6852 IMAP message sets should be sorted in cases where order doesn't matter
+K 6860 ID command shouldn't escape NIL value
 
 
                   CHANGES IN THE 1.5.3 RELEASE

diff -r 055b7845aba1 -r 6926f3556fa8 mail/src/main/java/com/sun/mail/imap/protocol/ID.java
--- a/mail/src/main/java/com/sun/mail/imap/protocol/ID.java Thu Jun 11 15:49:30 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/protocol/ID.java Thu Jun 11 15:50:07 2015 -0700
@@ -106,7 +106,7 @@
     static Argument getArgumentList(Map<String,String> clientParams) {
         Argument arg = new Argument();
         if (clientParams == null) {
- arg.writeString("NIL");
+ arg.writeAtom("NIL");
             return arg;
         }
         Argument list = new Argument();

diff -r 055b7845aba1 -r 6926f3556fa8 mail/src/test/java/com/sun/mail/imap/IMAPHandler.java
--- a/mail/src/test/java/com/sun/mail/imap/IMAPHandler.java Thu Jun 11 15:49:30 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/imap/IMAPHandler.java Thu Jun 11 15:50:07 2015 -0700
@@ -61,7 +61,7 @@
     protected String tag;
 
     /** IMAP capabilities supported */
- protected String capabilities = "IMAP4REV1 IDLE";
+ protected String capabilities = "IMAP4REV1 IDLE ID";
 
     /** Number of messages */
     protected int numberOfMessages = 0;
@@ -251,6 +251,8 @@
                                                                 subcommandName);
                 bad("unknown UID command");
             }
+ } else if (commandName.equals("ID")) {
+ id(currentLine);
         } else {
             LOGGER.log(Level.SEVERE, "ERROR command unknown: {0}",
                                                         escape(currentLine));
@@ -398,6 +400,16 @@
     }
 
     /**
+ * ID command.
+ *
+ * @throws IOException unable to read/write to socket
+ */
+ public void id(String line) throws IOException {
+ untagged("ID NIL");
+ ok();
+ }
+
+ /**
      * Collect "bytes" worth of data for the message being appended.
      */
     protected void collectMessage(int bytes) throws IOException {

diff -r 055b7845aba1 -r 6926f3556fa8 mail/src/test/java/com/sun/mail/imap/IMAPIDTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mail/src/test/java/com/sun/mail/imap/IMAPIDTest.java Thu Jun 11 15:50:07 2015 -0700
@@ -0,0 +1,120 @@
+/*
+ * 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 java.util.Map;
+
+import javax.mail.Session;
+import javax.mail.Store;
+
+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.fail;
+
+/**
+ * Test the IMAP ID command.
+ */
+public final class IMAPIDTest {
+
+ // timeout the test in case of deadlock
+ @Rule
+ public Timeout deadlockTimeout = new Timeout(20000);
+
+ @Test
+ public void testIDNIL() {
+ TestServer server = null;
+ try {
+ final IMAPHandler handler = new IMAPHandlerID();
+ server = new TestServer(handler);
+ server.start();
+
+ final Properties properties = new Properties();
+ properties.setProperty("mail.imap.host", "localhost");
+ properties.setProperty("mail.imap.port", "" + server.getPort());
+ final Session session = Session.getInstance(properties);
+ //session.setDebug(true);
+
+ final IMAPStore store = (IMAPStore)session.getStore("imap");
+ try {
+ store.connect("test", "test");
+ Map<String,String> id = store.id(null);
+ assertEquals("true", id.get("test"));
+
+ } catch (Exception ex) {
+ System.out.println(ex);
+ ex.printStackTrace();
+ fail(ex.toString());
+ } finally {
+ store.close();
+ }
+ } catch (final Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ if (server != null) {
+ server.quit();
+ }
+ }
+ }
+
+ /**
+ * Custom handler.
+ */
+ private static final class IMAPHandlerID extends IMAPHandler {
+
+ @Override
+ public void id(String line) throws IOException {
+ StringTokenizer st = new StringTokenizer(line);
+ String tag = st.nextToken();
+ String cmd = st.nextToken();
+ String arg = st.nextToken();
+ untagged("ID (\"test\" \"" + arg.equals("NIL") + "\")");
+ ok();
+ }
+ }
+}


diff -r 6926f3556fa8 -r f6c7aaf24e0b doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Thu Jun 11 15:50:07 2015 -0700
+++ b/doc/release/CHANGES.txt Tue Jun 16 23:43:07 2015 -0700
@@ -18,6 +18,7 @@
                   ----------------------------
 The following bugs have been fixed in the 1.5.4 release.
 
+K 6719 Include elapsed time, thread id, and sequence for logging formatters.
 K 6804 IdleManager can deadlock with frequent notifications
 K 6817 IdleManager can deadlock when connection fails
 K 6824 IMAP provider should support the MOVE extension (RFC 6851)

diff -r 6926f3556fa8 -r f6c7aaf24e0b mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java Thu Jun 11 15:50:07 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java Tue Jun 16 23:43:07 2015 -0700
@@ -68,7 +68,7 @@
  * <tt>LogRecord</tt>. If a comparator is specified then the max
  * <tt>LogRecord</tt> is chosen. If comparator is set to the string literal
  * null, then the last record is chosen. (defaults to
- * {_at_link SeverityComparator})
+ * {_at_linkplain SeverityComparator})
  *
  * <li>&lt;formatter-name&gt;.comparator.reverse a boolean
  * <tt>true</tt> to collect the min <tt>LogRecord</tt> or <tt>false</tt> to
@@ -82,7 +82,7 @@
  * more}\n</tt>)
  *
  * <li>&lt;formatter-name&gt;.formatter name of a <tt>Formatter</tt> class used
- * to format the collected LogRecord. (defaults to {_at_link CompactFormatter})
+ * to format the collected LogRecord. (defaults to {_at_linkplain CompactFormatter})
  *
  * </ul>
  *
@@ -90,7 +90,6 @@
  * @since JavaMail 1.5.2
  */
 public class CollectorFormatter extends Formatter {
-
     /**
      * Avoid depending on JMX runtime bean to get the start time.
      */
@@ -117,17 +116,22 @@
      */
     private long count;
     /**
+ * The number of log produced containing at least one log record.
+ * Only incremented when this formatter is reset.
+ */
+ private long generation = 1L;
+ /**
      * The number of log records that have been formatted with a thrown object.
      */
     private long thrown;
     /**
- * The eldest log record time.
+ * The eldest log record time or eldest time possible for this instance.
      */
- private long minMillis;
+ private long minMillis = INIT_TIME;
     /**
      * The newest log record time.
      */
- private long maxMillis;
+ private long maxMillis = Long.MIN_VALUE;
 
     /**
      * Creates the formatter using the LogManager defaults.
@@ -142,7 +146,6 @@
         this.fmt = initFormat(p);
         this.formatter = initFormatter(p);
         this.comparator = initComparator(p);
- reset();
     }
 
     /**
@@ -159,7 +162,6 @@
         this.fmt = format == null ? initFormat(p) : format;
         this.formatter = initFormatter(p);
         this.comparator = initComparator(p);
- reset();
     }
 
     /**
@@ -181,7 +183,6 @@
         this.fmt = format == null ? initFormat(p) : format;
         this.formatter = f;
         this.comparator = c;
- reset();
     }
 
     /**
@@ -205,7 +206,7 @@
             //The self compare of the first record acts like a type check.
             LogRecord update = apply(peek != null ? peek : record, record);
             if (peek != update) { //Not identical.
- update.getSourceMethodName(); //Infer caller.
+ update.getSourceMethodName(); //Infer caller, null check.
                 accepted = acceptAndUpdate(peek, update);
             } else {
                 accepted = true;
@@ -218,7 +219,7 @@
     /**
      * Formats the collected LogRecord and summary statistics. The collected
      * results are reset after calling this method. The
- * {_at_link java.text.MessageFormat java.text} argument indexes are assigned
+ * {_at_linkplain java.text.MessageFormat java.text} argument indexes are assigned
      * to the following properties:
      *
      * <ol start='0'>
@@ -229,7 +230,8 @@
      * <li>{_at_code formatted} the current log record
      * {_at_linkplain Formatter#format(java.util.logging.LogRecord) formatted} by
      * the target formatter and {_at_linkplain #finish(java.lang.String) finished}
- * by this formatter.
+ * by this formatter. If the formatter is null then record is formatted by
+ * this {_at_linkplain #formatMessage(java.util.logging.LogRecord) formatter}.
      * <li>{_at_code tail} the
      * {_at_linkplain Formatter#getTail(java.util.logging.Handler) tail} string
      * returned from the target formatter and
@@ -244,17 +246,34 @@
      * <li>{_at_code minMillis} the eldest log record
      * {_at_linkplain java.util.logging.LogRecord#getMillis event time}
      * {_at_linkplain #format consumed} by this formatter. If the count is zero
- * then this is set to the approximate start time of the JVM. By
- * default this parameter is defined as a number. The format type and format
- * style rules from the {_at_link java.text.MessageFormat} should be used to
- * convert this from milliseconds to a date or time.
+ * then this is set to the previous max or approximate start time if there
+ * was no previous max. By default this parameter is defined as a number.
+ * The format type and format style rules from the
+ * {_at_linkplain java.text.MessageFormat} should be used to convert this from
+ * milliseconds to a date or time.
      * <li>{_at_code maxMillis} the most recent log record
      * {_at_linkplain java.util.logging.LogRecord#getMillis event time}
      * {_at_linkplain #format consumed} by this formatter. If the count is zero
- * then this is set to the {_at_link System#currentTimeMillis() current time}.
+ * then this is set to the {_at_linkplain System#currentTimeMillis() current time}.
      * By default this parameter is defined as a number. The format type and
- * format style rules from the {_at_link java.text.MessageFormat} should be
+ * format style rules from the {_at_linkplain java.text.MessageFormat} should be
      * used to convert this from milliseconds to a date or time.
+ * <li>{_at_code elapsed} the elapsed time in milliseconds between the
+ * {_at_code maxMillis} and {_at_code minMillis}.
+ * <li>{_at_code startTime} the approximate start time in milliseconds. By
+ * default this parameter is defined as a number. The format type and format
+ * style rules from the {_at_linkplain java.text.MessageFormat} should be used to
+ * convert this from milliseconds to a date or time.
+ * <li>{_at_code currentTime} the
+ * {_at_linkplain System#currentTimeMillis() current time} in milliseconds. By
+ * default this parameter is defined as a number. The format type and format
+ * style rules from the {_at_linkplain java.text.MessageFormat} should be used to
+ * convert this from milliseconds to a date or time.
+ * <li>{_at_code uptime} the elapsed time in milliseconds between the
+ * {_at_code currentTime} and {_at_code startTime}.
+ * <li>{_at_code generation} the number times this method produced output with
+ * at least one {_at_linkplain #format consumed} log record. This can be used
+ * to track the number of complete reports this formatter has produced.
      * </ol>
      *
      * <p>
@@ -278,6 +297,26 @@
      * These 292 messages occurred between
      * Tue, Jul 21 14:11:42:449 -0500 2009 and Fri, Nov 20 07:29:24:0 -0600 2009
      * </pre>
+ * <li>{_at_code com.sun.mail.util.logging.CollectorFormatter.format=These {3} messages occurred between {9,choice,86400000#{7,date} {7,time} and {8,time}|86400000<{7,date} and {8,date}}\n}
+ * <p>
+ * This prints the count ({_at_code {3}}) and then chooses the format based on
+ * the elapsed time ({_at_code {9}}). If the elapsed time is less than one day
+ * then the eldest log record ({_at_code {7}}) date and time is formatted
+ * followed by just the time of the most recent log record ({_at_code {8}}.
+ * Otherwise, the just the date of the eldest log record ({_at_code {7}}) and
+ * just the date of most recent log record ({_at_code {8}} is formatted.
+ * <pre>
+ * These 73 messages occurred between Jul 21, 2009 2:11:42 PM and 2:13:32 PM
+ *
+ * These 116 messages occurred between Jul 21, 2009 and Aug 20, 2009
+ * </pre>
+ * <li>{_at_code com.sun.mail.util.logging.CollectorFormatter.format={13} alert reports since {10,date}.\n}
+ * <p>
+ * This prints the generation ({_at_code {13}}) followed by the start time
+ * ({_at_code {10}}) formatted as a date.
+ * <pre>
+ * 4,320 alert reports since Jul 21, 2012.
+ * </pre>
      * </ul>
      *
      * @param h the handler or null.
@@ -334,9 +373,13 @@
      */
     private synchronized void accept(final LogRecord record) {
         final long millis = record.getMillis();
- minMillis = Math.min(minMillis, millis);
+ if (++count != 1L) {
+ minMillis = Math.min(minMillis, millis);
+ } else { //Show single records as instant and not a time period.
+ minMillis = millis;
+ }
         maxMillis = Math.max(maxMillis, millis);
- ++count;
+
         if (record.getThrown() != null) {
             ++thrown;
         }
@@ -344,12 +387,17 @@
 
     /**
      * Resets all of the collected summary statistics including the LogRecord.
+ * @param min the current min milliseconds.
      */
- private synchronized void reset() {
- last = null;
+ private synchronized void reset(final long min) {
+ if (last != null) {
+ last = null;
+ ++generation;
+ }
+
         count = 0L;
         thrown = 0L;
- minMillis = Long.MAX_VALUE;
+ minMillis = min;
         maxMillis = Long.MIN_VALUE;
     }
 
@@ -366,25 +414,27 @@
         final LogRecord record;
         final long c;
         final long t;
+ final long g;
         long msl;
         long msh;
+ long now;
         synchronized (this) {
             record = last;
             c = count;
+ g = generation;
             t = thrown;
             msl = minMillis;
             msh = maxMillis;
+ now = System.currentTimeMillis();
+ if (c == 0L) {
+ msh = now;
+ }
 
             if (reset) { //BUG ID 6351685
- reset();
+ reset(msh);
             }
         }
 
- if (c == 0L) { //Use the estimated lifespan of this class.
- msl = INIT_TIME;
- msh = System.currentTimeMillis();
- }
-
         final String head;
         final String msg;
         final String tail;
@@ -396,7 +446,9 @@
                 tail = f.getTail(h);
             }
         } else {
- head = msg = tail = "";
+ head = "";
+ msg = record != null ? formatMessage(record) : "";
+ tail = "";
         }
 
         Locale l = null;
@@ -416,7 +468,8 @@
          * These arguments are described in the getTail documentation.
          */
         return mf.format(new Object[]{finish(head), finish(msg), finish(tail),
- c, (c - 1), t, (c - t), msl, msh});
+ c, (c - 1), t, (c - t), msl, msh, (msh - msl), INIT_TIME, now,
+ (now - INIT_TIME), g});
     }
 
     /**

diff -r 6926f3556fa8 -r f6c7aaf24e0b mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java Thu Jun 11 15:50:07 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java Tue Jun 16 23:43:07 2015 -0700
@@ -45,9 +45,9 @@
 
 /**
  * A plain text formatter that can produce fixed width output. By default this
- * formatter will produce output no greater than 160 characters wide. Only
- * specified fields support an {_at_link #toAlternate(java.lang.String) alternate}
- * fixed width format.
+ * formatter will produce output no greater than 160 characters wide plus the
+ * separator and newline characters. Only specified fields support an
+ * {_at_linkplain #toAlternate(java.lang.String) alternate} fixed width format.
  * <p>
  * By default each <tt>CompactFormatter</tt> is initialized using the
  * following LogManager configuration properties where
@@ -56,9 +56,9 @@
  * 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
- * fix the output size. (defaults to <tt>%7$#.160s%n</tt>)</li>
+ * <li>&lt;formatter-name&gt;.format - the {_at_linkplain java.util.Formatter
+ * format} string used to transform the output. The format string can be
+ * used to fix the output size. (defaults to <tt>%7$#.160s%n</tt>)</li>
  * </ul>
  *
  * @author Jason Mehrens
@@ -82,9 +82,9 @@
     /**
      * Creates an instance with the given format pattern.
      *
- * @param format the {_at_link java.util.Formatter pattern} or null to use the
- * LogManager default. The arguments are described in the
- * {_at_link #format(java.util.logging.LogRecord) format} method.
+ * @param format the {_at_linkplain java.util.Formatter pattern} or null to use
+ * the LogManager default. The arguments are described in the
+ * {_at_linkplain #format(java.util.logging.LogRecord) format} method.
      */
     public CompactFormatter(final String format) {
         String p = getClass().getName();
@@ -93,33 +93,57 @@
 
     /**
      * Format the given log record and returns the formatted string. The
- * {_at_link java.util.Formatter#format(java.lang.String, java.lang.Object...)
+ * {_at_linkplain java.util.Formatter#format(java.lang.String, java.lang.Object...)
      * java.util} argument indexes are assigned to the following properties:
      *
      * <ol start='0'>
- * <li>{_at_code format} - the {_at_link java.util.Formatter
+ * <li>{_at_code format} - the {_at_linkplain java.util.Formatter
      * java.util.Formatter} format string specified in the
      * &lt;formatter-name&gt;.format property or the format that was given when
      * this formatter was created.</li>
- * <li>{_at_code date} - a {_at_link Date} object representing
+ * <li>{_at_code date} - a {_at_linkplain Date} object representing
      * {_at_linkplain LogRecord#getMillis event time} of the log record.</li>
      * <li>{_at_code source} - a string representing the caller, if available;
      * otherwise, the logger's name.</li>
- * <li>{_at_code logger} - the logger's name.</li>
+ * <li>{_at_code logger} - the logger's
+ * {_at_linkplain LogRecord#getLoggerName() name}.</li>
      * <li>{_at_code level} - the
      * {_at_linkplain java.util.logging.Level#getLocalizedName log level}.</li>
      * <li>{_at_code message} - the formatted log message returned from the
- * {_at_link #formatMessage(LogRecord)} method.</li>
+ * {_at_linkplain #formatMessage(LogRecord)} method.</li>
      * <li>{_at_code thrown} - a string representing the
      * {_at_linkplain LogRecord#getThrown throwable} associated with the log record
- * and a relevant stack trace element if available. Otherwise, an empty
+ * and a relevant stack trace element if available; otherwise, an empty
      * string is used.</li>
      * <li>{_at_code message|thrown} The message and the thrown properties joined
      * as one parameter. This parameter supports
- * {_at_link #toAlternate(java.lang.String) alternate} form.</li>
+ * {_at_linkplain #toAlternate(java.lang.String) alternate} form.</li>
      * <li>{_at_code thrown|message} The thrown and message properties joined as
      * one parameter. This parameter supports
- * {_at_link #toAlternate(java.lang.String) alternate} form.</li>
+ * {_at_linkplain #toAlternate(java.lang.String) alternate} form.</li>
+ * <li>{_at_code sequence} the
+ * {_at_linkplain LogRecord#getSequenceNumber() sequence number} if the given
+ * log record.</li>
+ * <li>{_at_code thread id} the {_at_linkplain LogRecord#getThreadID() thread id}
+ * of the given log record. By default this is formatted as a {_at_code long}
+ * by an unsigned conversion.</li>
+ * <li>{_at_code error} the throwable simple class name and
+ * {_at_linkplain #formatError(LogRecord) error message} without any
+ * stack trace.</li>
+ * <li>{_at_code message|error} The message and error properties joined as
+ * one parameter. This parameter supports
+ * {_at_linkplain #toAlternate(java.lang.String) alternate} form.</li>
+ * <li>{_at_code error|message} The error and message properties joined as
+ * one parameter. This parameter supports
+ * {_at_linkplain #toAlternate(java.lang.String) alternate} form.</li>
+ * <li>{_at_code backtrace} only the
+ * {_at_linkplain #formatBackTrace(LogRecord) stack trace} of the given
+ * throwable.</li>
+ * <li>{_at_code bundlename} the resource bundle
+ * {_at_linkplain LogRecord#getResourceBundleName() name} of the given log
+ * record.</li>
+ * <li>{_at_code key} the {_at_linkplain LogRecord#getMessage() raw message}
+ * before localization or formatting.</li>
      * </ol>
      *
      * <p>
@@ -128,11 +152,23 @@
      * <li>{_at_code com.sun.mail.util.logging.CompactFormatter.format=%7$#.160s%n}
      * <p>
      * This prints only 160 characters of the message|thrown ({_at_code 7$}) using
- * the alternate form.
+ * the {_at_linkplain #toAlternate(java.lang.String) alternate} form. The
+ * separator is not included as part of the total width.
      * <pre>
      * Encoding failed.|NullPointerException: null String.getBytes(:913)
      * </pre>
      *
+ * <li>{_at_code com.sun.mail.util.logging.CompactFormatter.format=%7$#.20s%n}
+ * <p>
+ * This prints only 20 characters of the message|thrown ({_at_code 7$}) using
+ * the {_at_linkplain #toAlternate(java.lang.String) alternate} form. This
+ * will perform a weighted truncation of both the message and thrown
+ * properties of the log record. The separator is not included as part of
+ * the total width.
+ * <pre>
+ * Encoding|NullPointerE
+ * </pre>
+ *
      * <li>{_at_code com.sun.mail.util.logging.CompactFormatter.format=%1$tc %2$s%n%4$s: %5$s%6$s%n}
      * <p>
      * This prints the timestamp ({_at_code 1$}) and the source ({_at_code 2$}) on the
@@ -143,6 +179,25 @@
      * Fri Nov 20 07:29:24 CST 2009 MyClass fatal
      * SEVERE: Encoding failed.NullPointerException: null String.getBytes(:913)
      * </pre>
+ *
+ * <li>{_at_code com.sun.mail.util.logging.CompactFormatter.format=%4$s: %12$#.160s%n}
+ * <p>
+ * This prints the log level ({_at_code 4$}) and only 160 characters of the
+ * message|error ({_at_code 12$}) using the alternate form.
+ * <pre>
+ * SEVERE: Unable to send notification.|SocketException: Permission denied: connect
+ * </pre>
+ *
+ * <li>{_at_code com.sun.mail.util.logging.CompactFormatter.format=[%9$d][%1$tT][%10$d][%2$s] %5$s%n%6$s%n}
+ * <p>
+ * This prints the sequence ({_at_code 9$}), event time ({_at_code 1$}) as
+ * 24 hour time, thread id ({_at_code 10$}), source ({_at_code 2$}), log message
+ * ({_at_code 5$}), and the throwable with back trace ({_at_code 6$}).
+ * <pre>
+ * [125][14:11:42][38][MyClass fatal] Unable to send notification.
+ * SocketException: Permission denied: connect SMTPTransport.openServer(:1949)
+ * </pre>
+ *
      * </ul>
      *
      * @param record the log record to format.
@@ -157,6 +212,7 @@
 
         String msg = formatMessage(record);
         String thrown = formatThrown(record);
+ String err = formatError(record);
         Object[] params = {
             new Date(record.getMillis()),
             formatSource(record),
@@ -165,7 +221,15 @@
             msg,
             thrown,
             new Alternate(msg, thrown),
- new Alternate(thrown, msg)};
+ new Alternate(thrown, msg),
+ record.getSequenceNumber(),
+ formatThreadID(record),
+ err,
+ new Alternate(msg, err),
+ new Alternate(err, msg),
+ formatBackTrace(record),
+ record.getResourceBundleName(),
+ record.getMessage()};
 
         if (l == null) { //BUG ID 6282094
             return String.format(fmt, params);
@@ -194,8 +258,9 @@
      * method removes any fully qualified throwable class names from the message
      * cause chain.
      *
- * @param t the throwable to format.
- * @return the formatted message string from the throwable.
+ * @param t the throwable to format or null.
+ * @return the empty string if null was given or the formatted message
+ * string from the throwable which may be null.
      */
     public String formatMessage(final Throwable t) {
         return t != null ? replaceClassName(apply(t).getMessage(), t) : "";
@@ -246,6 +311,24 @@
     }
 
     /**
+ * Formats the thread id property of the given log record. By default this
+ * is formatted as a {_at_code long} by an unsigned conversion.
+ *
+ * @param record the record.
+ * @return the formatted thread id as a number.
+ * @throws NullPointerException if the given record is null.
+ * @since JavaMail 1.5.4
+ */
+ public Number formatThreadID(final LogRecord record) {
+ /**
+ * Thread.getID is defined as long and LogRecord.getThreadID is defined
+ * as int. Convert to unsigned as a means to better map the two types
+ * of thread identifiers.
+ */
+ return (((long) record.getThreadID()) & 0xffffffffL);
+ }
+
+ /**
      * Formats the thrown property of a LogRecord. The returned string will
      * contain a throwable message with a back trace.
      *
@@ -259,15 +342,8 @@
         String msg;
         final Throwable t = record.getThrown();
         if (t != null) {
- final Throwable root = apply(t);
- if (root != null) {
- msg = formatMessage(t);
- String site = formatBackTrace(record);
- msg = simpleClassName(root.getClass()) + ": " + msg
- + (isNullOrSpaces(site) ? "" : ' ' + site);
- } else {
- msg = "";
- }
+ String site = formatBackTrace(record);
+ msg = formatToString(t) + (isNullOrSpaces(site) ? "" : ' ' + site);
         } else {
             msg = "";
         }
@@ -275,6 +351,40 @@
     }
 
     /**
+ * Formats the thrown property of a LogRecord as an error message. The
+ * returned string will not contain a back trace.
+ *
+ * @param record the record.
+ * @return empty string if nothing was thrown or formatted string.
+ * @throws NullPointerException if the given record is null.
+ * @see Throwable#toString()
+ * @see #apply(java.lang.Throwable)
+ * @see #formatMessage(java.lang.Throwable)
+ * @since JavaMail 1.5.4
+ */
+ public String formatError(final LogRecord record) {
+ Throwable t = record.getThrown();
+ if (t != null) {
+ return formatToString(t);
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * Gets the simple class name of the reduced throwable and message of
+ * the reduced throwable.
+ *
+ * @param t the given throwable.
+ * @return the formatted throwable.
+ * @since JavaMail 1.5.4
+ * @throws NullPointerException if given throwable is null.
+ */
+ private String formatToString(Throwable t) {
+ return simpleClassName(apply(t).getClass()) + ": " + formatMessage(t);
+ }
+
+ /**
      * Formats the back trace for the given log record.
      *
      * @param record the log record to format.
@@ -289,21 +399,19 @@
         final Throwable t = record.getThrown();
         if (t != null) {
             final Throwable root = apply(t);
- if (root != null) {
- site = findAndFormat(root.getStackTrace());
- if (isNullOrSpaces(site)) {
- int limit = 0;
- for (Throwable c = t; c != null; c = c.getCause()) {
- site = findAndFormat(c.getStackTrace());
- if (!isNullOrSpaces(site)) {
- break;
- }
+ site = findAndFormat(root.getStackTrace());
+ if (isNullOrSpaces(site)) {
+ int limit = 0;
+ for (Throwable c = t; c != null; c = c.getCause()) {
+ site = findAndFormat(c.getStackTrace());
+ if (!isNullOrSpaces(site)) {
+ break;
+ }
 
- //Deal with excessive cause chains
- //and cyclic throwables.
- if (++limit == (1 << 16)) {
- break; //Give up.
- }
+ //Deal with excessive cause chains
+ //and cyclic throwables.
+ if (++limit == (1 << 16)) {
+ break; //Give up.
                     }
                 }
             }
@@ -371,7 +479,7 @@
      * formatting.
      *
      * @param t the throwable from the log record.
- * @return the throwable or null.
+ * @return the chosen throwable or null only if the given argument is null.
      * @see #formatThrown(java.util.logging.LogRecord)
      */
     protected Throwable apply(final Throwable t) {
@@ -390,7 +498,7 @@
     }
 
     /**
- * Defines the alternate format. This implementation removes all control
+ * Defines the alternate format. This implementation removes all control
      * characters from the given string.
      *
      * @param s any string or null.
@@ -536,7 +644,7 @@
      * Gets the simple class name from the given class. This is a workaround for
      * BUG ID JDK-8057919.
      *
- * @param name the fully qualified class name or null.
+ * @param k the class object.
      * @return the simple class name or null.
      * @since JavaMail 1.5.3
      */

diff -r 6926f3556fa8 -r f6c7aaf24e0b mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java
--- a/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java Thu Jun 11 15:50:07 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java Tue Jun 16 23:43:07 2015 -0700
@@ -148,6 +148,7 @@
      * Gets LogManger property for the running JVM. If the LogManager doesn't
      * exist then the default LogManger properties are used.
      *
+ * @param name the property name.
      * @return the LogManager.
      * @throws NullPointerException if the given name is null.
      * @since JavaMail 1.5.3
@@ -723,14 +724,10 @@
      */
     LogManagerProperties(final Properties parent, final String prefix) {
         super(parent);
- parent.isEmpty(); //null check, happens-before
- if (prefix == null) {
+ if (parent == null || prefix == null) {
             throw new NullPointerException();
         }
         this.prefix = prefix;
-
- //'defaults' is not decalared final.
- super.isEmpty(); //happens-before.
     }
 
     /**

diff -r 6926f3556fa8 -r f6c7aaf24e0b mail/src/main/java/com/sun/mail/util/logging/MailHandler.java
--- a/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Thu Jun 11 15:50:07 2015 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Tue Jun 16 23:43:07 2015 -0700
@@ -2085,7 +2085,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2123,7 +2122,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2171,7 +2169,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2219,7 +2216,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2245,7 +2241,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2269,7 +2264,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2290,7 +2284,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if argument is null.
      * @throws SecurityException if not allowed.
@@ -2320,7 +2313,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2363,7 +2355,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2384,7 +2375,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2415,7 +2405,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2446,7 +2435,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2469,7 +2457,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2490,7 +2477,6 @@
 
     /**
      * Parses LogManager string values into objects used by this handler.
- * @param manager the manager.
      * @param p the handler class name used as the prefix.
      * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
@@ -2614,7 +2600,10 @@
                 if (size != 1) {
                     Arrays.sort(data, 0, size, comparator);
                 } else {
- comparator.compare(data[0], data[0]);
+ if (comparator.compare(data[0], data[0]) != 0) {
+ throw new IllegalArgumentException(
+ comparator.getClass().getName());
+ }
                 }
             } catch (final RuntimeException RE) {
                 reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE);
@@ -3964,7 +3953,7 @@
      */
     private boolean allowRestrictedHeaders() {
         //GAE will prevent delivery of email with forbidden headers.
- //Assume the envrionment is GAE if access to the LogManager is
+ //Assume the environment is GAE if access to the LogManager is
         //forbidden.
         return LogManagerProperties.hasLogManager();
     }

diff -r 6926f3556fa8 -r f6c7aaf24e0b mail/src/test/java/com/sun/mail/util/logging/CollectorFormatterTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/CollectorFormatterTest.java Thu Jun 11 15:50:07 2015 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/CollectorFormatterTest.java Tue Jun 16 23:43:07 2015 -0700
@@ -44,7 +44,10 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.DateFormat;
 import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.logging.*;
 import java.util.logging.Formatter;
@@ -63,6 +66,10 @@
      * See LogManager.
      */
     private static final String LOG_CFG_KEY = "java.util.logging.config.file";
+ /**
+ * Date and time simple format pattern.
+ */
+ private static final String DATE_TIME_FMT = "EEE, MMM dd HH:mm:ss:S ZZZ yyyy";
 
     /**
      * The line separator.
@@ -76,11 +83,26 @@
         assertNull(System.getProperty(LOG_CFG_KEY));
         assertEquals(LogManager.class, LogManager.getLogManager().getClass());
     }
-
+
     private static void fullFence() {
         LogManager.getLogManager().getProperty("");
     }
 
+ @SuppressWarnings("SleepWhileInLoop")
+ private static void tick() throws InterruptedException {
+ final long delay = 1L;
+ long then = System.currentTimeMillis();
+ for (int i = 0; i < Short.MAX_VALUE; i++) {
+ long now = System.currentTimeMillis();
+ long delta = (now - then);
+ if (delta >= delay) {
+ return;
+ }
+ Thread.sleep(delay - delta);
+ }
+ throw new AssertionError(then + " " + System.currentTimeMillis());
+ }
+
     @BeforeClass
     public static void setUpClass() throws Exception {
         checkJVMOptions();
@@ -90,7 +112,7 @@
     public static void tearDownClass() throws Exception {
         checkJVMOptions();
     }
-
+
     @Before
     public void setUp() {
         fullFence();
@@ -117,6 +139,15 @@
     }
 
     @Test(expected = NullPointerException.class)
+ public void testFormatApplyReturnsNull() {
+ CollectorFormatter f = new ApplyReturnsNull();
+ for (int i = 0; i < 10; i++) {
+ String o = f.format(new LogRecord(Level.INFO, ""));
+ assertNotNull(o);
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
     public void testFormatNull() {
         CollectorFormatter f = new CollectorFormatter("{1}", (Formatter) null,
                 (Comparator<LogRecord>) null);
@@ -323,7 +354,32 @@
     }
 
     @Test
- public void testFormatMinMillis() {
+ public void testFormatNextMin() throws Exception {
+ CollectorFormatter minF = new CollectorFormatter("{7}",
+ (Formatter) null,
+ (Comparator<LogRecord>) null);
+
+ tick(); //Make sure the max not equal to the start time.
+
+ final String min = minF.getTail((Handler) null);
+ NumberFormat.getIntegerInstance().parse(min);
+ tick();
+
+ //Next min is not old min.
+ String next = minF.getTail((Handler) null);
+ assertFalse(min + ' ' + next, min.equals(next));
+
+ //All mins start at the init time.
+ CollectorFormatter initF = new CollectorFormatter("{10}",
+ (Formatter) null,
+ (Comparator<LogRecord>) null);
+
+ next = initF.getTail((Handler) null);
+ assertEquals(min, next);
+ }
+
+ @Test
+ public void testFormatMinDateTime() {
         String msg = "message";
         CollectorFormatter f = new CollectorFormatter("{7,date,short} {7,time}",
                 (Formatter) null,
@@ -356,7 +412,22 @@
     }
 
     @Test
- public void testFormatMaxMillis() {
+ public void testFormatNextMax() throws Exception {
+ CollectorFormatter f = new CollectorFormatter("{8}", (Formatter) null,
+ (Comparator<LogRecord>) null);
+
+ String now = f.getTail((Handler) null);
+ Number num = NumberFormat.getIntegerInstance().parse(now);
+ assertFalse(Long.MIN_VALUE == num.longValue());
+ tick();
+ String next = f.getTail((Handler) null);
+ assertFalse(NumberFormat.getIntegerInstance().parse(now).longValue()
+ == Long.MIN_VALUE);
+ assertFalse(now.equals(next));
+ }
+
+ @Test
+ public void testFormatMaxDateTime() {
         String msg = "message";
         CollectorFormatter f = new CollectorFormatter("{8,date,short} {8,time}",
                 (Formatter) null,
@@ -390,9 +461,41 @@
     }
 
     @Test
+ public void testFormatWindowToInstant() throws Exception {
+ CollectorFormatter f = new CollectorFormatter("{7}_{8}",
+ (Formatter) null,
+ (Comparator<LogRecord>) null);
+
+ LogRecord r = new LogRecord(Level.SEVERE, "");
+ r.setMillis(100);
+ f.format(r);
+
+ r = new LogRecord(Level.SEVERE, "");
+ r.setMillis(200);
+ f.format(r);
+
+ //Check that the min and max are different.
+ String output = f.getTail((Handler) null);
+ int fence = output.indexOf('_');
+ assertFalse(output.regionMatches(0, output, fence + 1,
+ (output.length() - fence) - 1));
+
+ r = new LogRecord(Level.SEVERE, "");
+ r.setMillis(400);
+ f.format(r);
+
+ //Previous max is 200 so at this point the min and max better be 400.
+ output = f.getTail((Handler) null);
+ fence = output.indexOf('_');
+ assertTrue(output.regionMatches(0, output, fence + 1,
+ (output.length() - fence) - 1));
+ assertEquals("400_400", output);
+ }
+
+ @Test
     public void testGetTail() {
         CollectorFormatter f = new CollectorFormatter(
- "{0}{1}{2}{3}{4}{5}{6}{7}{8}",
+ "{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}",
                 (Formatter) null,
                 (Comparator<LogRecord>) null);
         assertTrue(f.getTail((Handler) null).length() != 0);
@@ -416,12 +519,15 @@
 
         cf.format(new LogRecord(Level.INFO, "info"));
         cf.format(new LogRecord(Level.INFO, "info"));
- cf.getTail((Handler) null);
+ String output = cf.getTail((Handler) null);
+ assertNotNull(output);
     }
 
     @Test
     public void testGetTailExample2() {
- String p = "These {3} messages occurred between\n{7,date,EEE, MMM dd HH:mm:ss:S ZZZ yyyy} and {8,time,EEE, MMM dd HH:mm:ss:S ZZZ yyyy}\n";
+ String p = "These {3} messages occurred between\n"
+ + "{7,date,EEE, MMM dd HH:mm:ss:S ZZZ yyyy} and "
+ + "{8,time,EEE, MMM dd HH:mm:ss:S ZZZ yyyy}\n";
         CollectorFormatter cf = new CollectorFormatter(p);
         LogRecord min = new LogRecord(Level.SEVERE, "");
         min.setMillis(1248203502449L);
@@ -442,7 +548,88 @@
         args[7] = min.getMillis();
         args[8] = max.getMillis();
         assertEquals(MessageFormat.format(p, args), cf.toString());
- cf.getTail((Handler) null);
+ String output = cf.getTail((Handler) null);
+ assertNotNull(output);
+ }
+
+ @Test
+ public void testGetTailExample3a() {
+ String p = "These {3} messages occurred between "
+ + "{9,choice,86400000#{7,date} {7,time} and {8,time}"
+ + "|86400000<{7,date} and {8,date}}\n";
+ CollectorFormatter cf = new CollectorFormatter(p);
+ LogRecord min = new LogRecord(Level.SEVERE, "");
+ min.setMillis(1248203502449L);
+ cf.format(min);
+
+ for (int i = 0; i < 71; ++i) {
+ LogRecord mid = new LogRecord(Level.SEVERE, "");
+ mid.setMillis(min.getMillis());
+ cf.format(mid);
+ }
+
+ LogRecord max = new LogRecord(Level.SEVERE, "");
+ max.setMillis(min.getMillis() + 110500);
+ cf.format(max);
+
+ String output = cf.getTail((Handler) null);
+ assertNotNull(output);
+
+
+ cf.format(min);
+ for (int i = 0; i < 114; ++i) {
+ LogRecord mid = new LogRecord(Level.SEVERE, "");
+ mid.setMillis(min.getMillis());
+ cf.format(mid);
+ }
+
+
+ max.setMillis(min.getMillis() + 2591000000L);
+ cf.format(max);
+
+ output = cf.getTail((Handler) null);
+ assertNotNull(output);
+ }
+
+ public void testGetTailExample3b() {
+ String p = "These {3} messages occurred between "
+ + "{9,choice,86400000#{7,date} {7,time} and {8,time}"
+ + "|86400000<{7,date} and {8,date}}\n";
+ CollectorFormatter cf = new CollectorFor
[truncated due to length]