commits@javamail.java.net

[javamail~mercurial:671] Include logging samples.

From: <shannon_at_java.net>
Date: Mon, 10 Nov 2014 21:28:33 +0000

Project: javamail
Repository: mercurial
Revision: 671
Author: shannon
Date: 2014-11-08 01:09:06 UTC
Link:

Log Message:
------------
Add missing serialVersionUID.
Remove unused Executor field.
Improve debug output.
Allow any non-SSL protocol, not just TLSv1.
Update logging demos to use the new 1.5.2 features - bug 6551

(From Jason)
Include logging samples.


Revisions:
----------
667
668
669
670
671


Modified Paths:
---------------
mail/src/main/java/javax/mail/EventQueue.java
mail/src/main/java/com/sun/mail/imap/IdleManager.java
mail/src/main/java/com/sun/mail/util/SocketFetcher.java
doc/release/CHANGES.txt
logging/src/main/java/FileErrorManager.java
logging/src/main/java/MailHandlerDemo.java
logging/src/main/java/README.txt
logging/src/main/java/SummaryFormatter.java
logging/src/main/java/maildemo.properties
publish/samples.xml


Added Paths:
------------
logging/src/main/java/maildemo.policy


Diffs:
------
diff -r 0953a8f6ec85 -r 2d69d2985dc2 mail/src/main/java/javax/mail/EventQueue.java
--- a/mail/src/main/java/javax/mail/EventQueue.java Wed Oct 01 16:03:05 2014 -0700
+++ b/mail/src/main/java/javax/mail/EventQueue.java Mon Nov 03 12:27:52 2014 -0800
@@ -66,7 +66,7 @@
      * A special event that causes the queue processing task to terminate.
      */
     static class TerminatorEvent extends MailEvent {
- //private static final long serialVersionUID = 5542172141759168416L;
+ private static final long serialVersionUID = -2481895000841664111L;
 
         TerminatorEvent() {
             super(new Object());


diff -r 2d69d2985dc2 -r 9e7eb9ae78b2 mail/src/main/java/com/sun/mail/imap/IdleManager.java
--- a/mail/src/main/java/com/sun/mail/imap/IdleManager.java Mon Nov 03 12:27:52 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/imap/IdleManager.java Mon Nov 03 12:30:54 2014 -0800
@@ -130,7 +130,6 @@
  * @since JavaMail 1.5.2
  */
 public class IdleManager {
- private Executor es;
     private Selector selector;
     private MailLogger logger;
     private volatile boolean die = false;
@@ -148,7 +147,6 @@
      */
     public IdleManager(Session session, Executor es) throws IOException {
         logger = new MailLogger(this.getClass(), "DEBUG IMAP", session);
- this.es = es;
         selector = Selector.open();
         es.execute(new Runnable() {
             public void run() {


diff -r 9e7eb9ae78b2 -r 8fa698ea34b3 mail/src/main/java/com/sun/mail/util/SocketFetcher.java
--- a/mail/src/main/java/com/sun/mail/util/SocketFetcher.java Mon Nov 03 12:30:54 2014 -0800
+++ b/mail/src/main/java/com/sun/mail/util/SocketFetcher.java Fri Nov 07 11:57:46 2014 -0800
@@ -237,8 +237,11 @@
                     host, port, cto, to, props, prefix, null, useSSL);
 
         } else {
- if (to >= 0)
+ if (to >= 0) {
+ if (logger.isLoggable(Level.FINEST))
+ logger.finest("set socket read timeout " + to);
                 socket.setSoTimeout(to);
+ }
         }
 
         return socket;
@@ -263,6 +266,13 @@
                                 throws IOException {
         Socket socket = null;
 
+ if (logger.isLoggable(Level.FINEST))
+ logger.finest("create socket: prefix " + prefix +
+ ", localaddr " + localaddr + ", localport " + localport +
+ ", host " + host + ", port " + port +
+ ", connection timeout " + cto + ", timeout " + to +
+ ", socket factory " + sf + ", useSSL " + useSSL);
+
         String socksHost = props.getProperty(prefix + ".socks.host", null);
         int socksPort = 1080;
         String err = null;
@@ -297,20 +307,29 @@
             } else
                 socket = new Socket();
         }
- if (to >= 0)
+ if (to >= 0) {
+ if (logger.isLoggable(Level.FINEST))
+ logger.finest("set socket read timeout " + to);
             socket.setSoTimeout(to);
+ }
         int writeTimeout = PropUtil.getIntProperty(props,
                                                 prefix + ".writetimeout", -1);
- if (writeTimeout != -1) // wrap original
+ if (writeTimeout != -1) { // wrap original
+ if (logger.isLoggable(Level.FINEST))
+ logger.finest("set socket write timeout " + writeTimeout);
             socket = new WriteTimeoutSocket(socket, writeTimeout);
+ }
         if (localaddr != null)
             socket.bind(new InetSocketAddress(localaddr, localport));
         try {
+ logger.finest("connecting...");
             if (cto >= 0)
                 socket.connect(new InetSocketAddress(host, port), cto);
             else
                 socket.connect(new InetSocketAddress(host, port));
+ logger.finest("success!");
         } catch (IOException ex) {
+ logger.log(Level.FINEST, "connection failed", ex);
             throw new SocketConnectException(err, ex, host, port, cto);
         }
 
@@ -518,20 +537,30 @@
             sslsocket.setEnabledProtocols(stringArray(protocols));
         else {
             /*
- * At least the UW IMAP server insists on only the TLSv1
+ * The UW IMAP server insists on at least the TLSv1
              * protocol for STARTTLS, and won't accept the old SSLv2
- * or SSLv3 protocols. Here we enable only the TLSv1
- * protocol. XXX - this should probably be parameterized.
+ * or SSLv3 protocols. Here we enable only the non-SSL
+ * protocols. XXX - this should probably be parameterized.
              */
- sslsocket.setEnabledProtocols(new String[] {"TLSv1"});
+ String[] prots = sslsocket.getEnabledProtocols();
+ if (logger.isLoggable(Level.FINER))
+ logger.finer("SSL enabled protocols before " +
+ Arrays.asList(prots));
+ List<String> eprots = new ArrayList<String>();
+ for (int i = 0; i < prots.length; i++) {
+ if (prots[i] != null && !prots[i].startsWith("SSL"))
+ eprots.add(prots[i]);
+ }
+ sslsocket.setEnabledProtocols(
+ eprots.toArray(new String[eprots.size()]));
         }
         String ciphers = props.getProperty(prefix + ".ssl.ciphersuites", null);
         if (ciphers != null)
             sslsocket.setEnabledCipherSuites(stringArray(ciphers));
         if (logger.isLoggable(Level.FINER)) {
- logger.finer("SSL protocols after " +
+ logger.finer("SSL enabled protocols after " +
                 Arrays.asList(sslsocket.getEnabledProtocols()));
- logger.finer("SSL ciphers after " +
+ logger.finer("SSL enabled ciphers after " +
                 Arrays.asList(sslsocket.getEnabledCipherSuites()));
         }
 
@@ -630,7 +659,7 @@
                 match.invoke(hostnameChecker, new Object[] { server, cert });
                 return true;
             } catch (InvocationTargetException cex) {
- logger.log(Level.FINER, "FAIL", cex);
+ logger.log(Level.FINER, "HostnameChecker FAIL", cex);
                 return false;
             }
         } catch (Exception ex) {


diff -r 8fa698ea34b3 -r 75b23c207789 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Nov 07 11:57:46 2014 -0800
+++ b/doc/release/CHANGES.txt Fri Nov 07 17:04:46 2014 -0800
@@ -24,6 +24,7 @@
 K 6498 IMAP idle breaks interrupt flag
 K 6526 Date search terms result in wrong greater-than SEARCH commands for IMAP
 K 6535 address similar to (x)<y>(z) will throw StringIndexOutOfBoundsException
+K 6551 Update logging demos to use the new 1.5.2 features
 
 
                   CHANGES IN THE 1.5.2 RELEASE

diff -r 8fa698ea34b3 -r 75b23c207789 logging/src/main/java/FileErrorManager.java
--- a/logging/src/main/java/FileErrorManager.java Fri Nov 07 11:57:46 2014 -0800
+++ b/logging/src/main/java/FileErrorManager.java Fri Nov 07 17:04:46 2014 -0800
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2009-2013 Jason Mehrens. All Rights Reserved.
+ * Copyright (c) 2009-2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009-2014 Jason Mehrens. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,6 +31,7 @@
  */
 
 import java.io.*;
+import java.lang.reflect.Constructor;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.logging.ErrorManager;
@@ -39,9 +40,8 @@
 
 /**
  * An error manager used to store mime messages from the <tt>MailHandler</tt>
- * to the file system when the email server is unavailable or unreachable.
- * The code to manually setup this error manager can be as simple as the
- * following:
+ * to the file system when the email server is unavailable or unreachable. The
+ * code to manually setup this error manager can be as simple as the following:
  * <pre>
  * File dir = new File("path to dir");
  * FileErrorManager em = new FileErrorManager(dir);
@@ -49,8 +49,8 @@
  *
  * <p>
  * <b>Configuration:</b>
- * The code to setup this error manager via the logging properties
- * can be as simple as the following:
+ * The code to setup this error manager via the logging properties can be as
+ * simple as the following:
  * <pre>
  * #Default FileErrorManager settings.
  * FileErrorManager.pattern = path to directory
@@ -82,11 +82,12 @@
     private final File emailStore;
 
     /**
- * Creates a new error manager. Files are stored in the users temp
+ * Creates a new error manager. Files are stored in the users temp
      * directory.
- * @exception SecurityException if unable to access system properties or
- * if a security manager is present and unable to read or write to users
- * temp directory.
+ *
+ * @exception SecurityException if unable to access system properties or if
+ * a security manager is present and unable to read or write to users temp
+ * directory.
      */
     public FileErrorManager() {
         this.emailStore = getEmailStore();
@@ -95,13 +96,14 @@
 
     /**
      * Creates a new error manager.
+ *
      * @param dir a directory to store the email files.
      * @throws NullPointerException if <tt>dir</tt> is <tt>null</tt>
      * @throws IllegalArgumentException if <tt>dir</tt> is a
      * <tt>java.io.File</tt> subclass, not a directory, or is not an absolute
      * path.
- * @throws SecurityException if a security manager is present and unable
- * to read or write to a given directory.
+ * @throws SecurityException if a security manager is present and unable to
+ * read or write to a given directory.
      */
     public FileErrorManager(File dir) {
         this.emailStore = dir;
@@ -110,25 +112,26 @@
 
     /**
      * If the message parameter is a raw email, and passes the store term, then
- * this method will store the email to the file system. If the message
+ * this method will store the email to the file system. If the message
      * parameter is not a raw email then the message is forwarded to the super
      * class. If an email is written to the file system without error, then the
      * original reported error is ignored.
+ *
      * @param msg String raw email or plain error message.
      * @param ex Exception that occurred in the mail handler.
      * @param code int error manager code.
      */
     @Override
     public void error(String msg, Exception ex, int code) {
- if (isRawEmail(msg, ex, code)) {
+ if (isRawEmail(msg)) {
             try {
                 storeEmail(msg);
             } catch (final IOException IOE) {
- super.error(emailStore.toString(), IOE, ErrorManager.WRITE_FAILURE);
                 next.error(msg, ex, code);
+ super.error(emailStore.toString(), IOE, ErrorManager.GENERIC_FAILURE);
             } catch (final RuntimeException RE) {
- super.error(emailStore.toString(), RE, ErrorManager.WRITE_FAILURE);
                 next.error(msg, ex, code);
+ super.error(emailStore.toString(), RE, ErrorManager.GENERIC_FAILURE);
             }
         } else {
             next.error(msg, ex, code);
@@ -144,7 +147,7 @@
         }
 
         File dir = this.emailStore;
- if (dir.getClass() != File.class) { //for security.
+ if (dir.getClass() != File.class) { //For security reasons.
             throw new IllegalArgumentException(dir.getClass().getName());
         }
 
@@ -152,6 +155,11 @@
             throw new IllegalArgumentException("File must be a directory.");
         }
 
+ if (!dir.canWrite()) { //Can throw under a security manager.
+ super.error(dir.getAbsolutePath(),
+ new SecurityException("write"), ErrorManager.OPEN_FAILURE);
+ }
+
         //For now, only absolute paths are allowed.
         if (!dir.isAbsolute()) {
             throw new IllegalArgumentException("Only absolute paths are allowed.");
@@ -161,15 +169,11 @@
             super.error(dir.getAbsolutePath(),
                     new SecurityException("read"), ErrorManager.OPEN_FAILURE);
         }
-
- if (!dir.canWrite()) { //Can throw under a security manager.
- super.error(dir.getAbsolutePath(),
- new SecurityException("write"), ErrorManager.OPEN_FAILURE);
- }
     }
 
     /**
      * Creates a common temp file prefix.
+ *
      * @return the file prefix.
      */
     private String prefixName() {
@@ -178,6 +182,7 @@
 
     /**
      * Creates a common temp file suffix.
+ *
      * @return the file suffix.
      */
     private String suffixName() {
@@ -186,12 +191,11 @@
 
     /**
      * Determines if the given message is a MIME message or just free text.
+ *
      * @param msg the message to examine.
- * @param ex the exception that was reported.
- * @param code the ErrorManager code.
      * @return true if MIME message otherwise false.
      */
- private boolean isRawEmail(String msg, Exception ex, int code) {
+ private boolean isRawEmail(String msg) {
         if (msg != null && msg.length() > 0) {
             return !msg.startsWith(Level.SEVERE.getName());
         }
@@ -200,6 +204,7 @@
 
     /**
      * Stores the given string in a file.
+ *
      * @param email the message to store.
      * @throws IOException if there is a problem.
      */
@@ -219,8 +224,7 @@
         }
 
         try { //Raw email is ASCII.
- PrintStream ps = new PrintStream(
- new NewlineOutputStream(out), false, "US-ASCII");
+ PrintStream ps = new PrintStream(wrap(out), false, "US-ASCII");
             ps.print(email);
             ps.flush();
             tmp = null; //Don't delete 'tmp' if all bytes were written.
@@ -233,6 +237,7 @@
 
     /**
      * Null safe close method.
+ *
      * @param out closes the given stream.
      */
     private void close(OutputStream out) {
@@ -247,6 +252,7 @@
 
     /**
      * Null safe delete method.
+ *
      * @param tmp the file to delete.
      */
     private void delete(File tmp) {
@@ -274,6 +280,7 @@
 
     /**
      * Gets the location of the email store.
+ *
      * @return the File location.
      */
     private File getEmailStore() {
@@ -283,10 +290,43 @@
             dir = AccessController.doPrivileged(new PrivilegedAction<String>() {
 
                 public String run() {
- return System.getProperty("java.io.tmpdir", "");
+ return System.getProperty("java.io.tmpdir", ".");
                 }
             });
         }
         return new File(dir);
     }
+
+ /**
+ * Wraps the given stream as a NewLineOutputStream.
+ *
+ * @param out the stream to wrap.
+ * @return the original or wrapped output stream.
+ */
+ private OutputStream wrap(OutputStream out) {
+ assert out != null;
+ Class<?> k;
+ try {
+ k = Class.forName("NewlineOutputStream");
+ if (OutputStream.class.isAssignableFrom(k)) {
+ Constructor<?> c = k.getConstructor(OutputStream.class);
+ return (OutputStream) c.newInstance(out);
+ } else {
+ super.error("Unable to switch newlines",
+ new ClassNotFoundException(k.getName()),
+ ErrorManager.GENERIC_FAILURE);
+ }
+ } catch (RuntimeException re) {
+ super.error("Unable to switch newlines",
+ re, ErrorManager.GENERIC_FAILURE);
+ } catch (Exception ex) {
+ super.error("Unable to switch newlines",
+ ex, ErrorManager.GENERIC_FAILURE);
+ } catch (LinkageError le) {
+ super.error("Unable to switch newlines",
+ new ClassNotFoundException("", le),
+ ErrorManager.GENERIC_FAILURE);
+ }
+ return out;
+ }
 }

diff -r 8fa698ea34b3 -r 75b23c207789 logging/src/main/java/MailHandlerDemo.java
--- a/logging/src/main/java/MailHandlerDemo.java Fri Nov 07 11:57:46 2014 -0800
+++ b/logging/src/main/java/MailHandlerDemo.java Fri Nov 07 17:04:46 2014 -0800
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2009-2013 Jason Mehrens. All Rights Reserved.
+ * Copyright (c) 2009-2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009-2014 Jason Mehrens. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,23 +30,26 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import com.sun.mail.util.logging.CollectorFormatter;
 import com.sun.mail.util.logging.MailHandler;
+import com.sun.mail.util.logging.SeverityComparator;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintStream;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Properties;
+import java.lang.management.ManagementFactory;
+import java.util.*;
 import java.util.logging.*;
+import javax.activation.DataHandler;
 import javax.mail.MessagingException;
 import javax.mail.Session;
 import javax.mail.internet.InternetAddress;
 
 /**
- * Demo for the different configurations for the MailHandler.
- * If the logging properties file or class is not specified then this
- * demo will apply some default settings to store emails in the users temp dir.
+ * Demo for the different configurations for the MailHandler. If the logging
+ * properties file or class is not specified then this demo will apply some
+ * default settings to store emails in the users temp dir.
+ *
  * @author Jason Mehrens
  */
 public class MailHandlerDemo {
@@ -64,34 +67,72 @@
      * @param args the command line arguments
      */
     public static void main(String[] args) {
- init(); //may create log messages.
- try {
- LOGGER.log(Level.FINEST, "This is the finest part of the demo.",
- new MessagingException("Fake"));
- LOGGER.log(Level.FINER, "This is the finer part of the demo.",
- new NullPointerException("Fake"));
- LOGGER.log(Level.FINE, "This is the fine part of the demo.");
- LOGGER.log(Level.CONFIG, "Logging config file is {0}.", getConfigLocation());
- LOGGER.log(Level.INFO, "Your temp directory is {0}, please wait...", getTempDir());
+ List<String> l = Arrays.asList(args);
+ if (l.contains("/?") || l.contains("-?") || l.contains("-help")) {
+ LOGGER.info("Usage: java MailHandlerDemo "
+ + "[[-all] | [-body] | [-debug] | [-low] | [-simple] "
+ + "| [-pushlevel] | [-pushfilter] | [-pushnormal]"
+ + "| [-pushonly]] "
+ + "\n\n"
+ + "-all\t\t: Execute all demos.\n"
+ + "-body\t\t: An email with all records and only a body.\n"
+ + "-debug\t\t: Output basic debug information about the JVM "
+ + "and log configuration.\n"
+ + "-low\t\t: Generates multiple emails due to low capacity."
+ + "\n"
+ + "-simple\t\t: An email with all records with body and "
+ + "an attachment.\n"
+ + "-pushlevel\t: Generates high priority emails when the"
+ + " push level is triggered and normal priority when "
+ + "flushed.\n"
+ + "-pushFilter\t: Generates high priority emails when the "
+ + "push level and the push filter is triggered and normal "
+ + "priority emails when flushed.\n"
+ + "-pushnormal\t: Generates multiple emails when the "
+ + "MemoryHandler push level is triggered. All generated "
+ + "email are sent as normal priority.\n"
+ + "-pushonly\t: Generates multiple emails when the "
+ + "MemoryHandler push level is triggered. Generates high "
+ + "priority emails when the push level is triggered and "
+ + "normal priority when flushed.\n");
+ } else {
+ init(l); //may create log messages.
+ try {
+ LOGGER.log(Level.FINEST, "This is the finest part of the demo.",
+ new MessagingException("Fake JavaMail issue."));
+ LOGGER.log(Level.FINER, "This is the finer part of the demo.",
+ new NullPointerException("Fake bug."));
+ LOGGER.log(Level.FINE, "This is the fine part of the demo.");
+ LOGGER.log(Level.CONFIG, "Logging config file is {0}.",
+ getConfigLocation());
+ LOGGER.log(Level.INFO, "Your temp directory is {0}, "
+ + "please wait...", getTempDir());
 
- try { //waste some time for the custom formatter.
- Thread.sleep(3L * 1000L);
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
+ try { //Waste some time for the custom formatter.
+ Thread.sleep(3L * 1000L);
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+
+ LOGGER.log(Level.WARNING, "This is a warning.",
+ new FileNotFoundException("Fake file chooser issue."));
+ LOGGER.log(Level.SEVERE, "The end of the demo.",
+ new IOException("Fake access denied issue."));
+ } finally {
+ closeHandlers();
             }
-
- LOGGER.log(Level.WARNING, "This is a warning.", new FileNotFoundException("Fake"));
- LOGGER.log(Level.SEVERE, "The end of the demo.", new IOException("Fake"));
- } finally {
- closeHandlers();
         }
     }
 
     /**
- * Used debug problems with the logging.properties.
+ * Used debug problems with the logging.properties. The system property
+ * java.security.debug=access,stack can be used to trace access to the
+ * LogManager reset.
+ *
      * @param prefix a string to prefix the output.
      * @param err any PrintStream or null for System.out.
      */
+ @SuppressWarnings("UseOfSystemOutOrSystemErr")
     private static void checkConfig(String prefix, PrintStream err) {
         if (prefix == null || prefix.trim().length() == 0) {
             prefix = "DEBUG";
@@ -102,6 +143,9 @@
         }
 
         try {
+ err.println(prefix + ": LOGGER=" + LOGGER.getLevel());
+ err.println(prefix + ": JVM id " + ManagementFactory.getRuntimeMXBean().getName());
+ err.println(prefix + ": java.security.debug=" + System.getProperty("java.security.debug"));
             SecurityManager sm = System.getSecurityManager();
             if (sm != null) {
                 err.println(prefix + ": SecurityManager.class=" + sm.getClass().getName());
@@ -110,6 +154,17 @@
                 err.println(prefix + ": SecurityManager.class=" + null);
                 err.println(prefix + ": SecurityManager.toString=" + null);
             }
+
+ String policy = System.getProperty("java.security.policy");
+ if (policy != null) {
+ File f = new File(policy);
+ err.println(prefix + ": AbsolutePath=" + f.getAbsolutePath());
+ err.println(prefix + ": CanonicalPath=" + f.getCanonicalPath());
+ err.println(prefix + ": length=" + f.length());
+ err.println(prefix + ": canRead=" + f.canRead());
+ err.println(prefix + ": lastModified="
+ + new java.util.Date(f.lastModified()));
+ }
 
             LogManager manager = LogManager.getLogManager();
             String key = "java.util.logging.config.file";
@@ -123,22 +178,26 @@
                 err.println(prefix + ": canRead=" + f.canRead());
                 err.println(prefix + ": lastModified="
                         + new java.util.Date(f.lastModified()));
- //force any errors, only safe is key is present.
+ //Force any errors. This is only safe if key is present.
                 manager.readConfiguration();
             } else {
                 err.println(prefix + ": " + key + " is not set as a system property.");
             }
             err.println(prefix + ": LogManager.class=" + manager.getClass().getName());
             err.println(prefix + ": LogManager.toString=" + manager);
+ err.println(prefix + ": MailHandler classLoader=" + MailHandler.class.getClassLoader());
+ err.println(prefix + ": Context ClassLoader=" + Thread.currentThread().getContextClassLoader());
+ err.println(prefix + ": Session ClassLoader=" + Session.class.getClassLoader());
+ err.println(prefix + ": DataHandler ClassLoader=" + DataHandler.class.getClassLoader());
 
             final String p = MailHandler.class.getName();
             key = p.concat(".mail.to");
             String to = manager.getProperty(key);
             err.println(prefix + ": TO=" + to);
- err.println(prefix + ": TO="
- + Arrays.toString(InternetAddress.parse(to, false)));
- err.println(prefix + ": TO="
- + Arrays.toString(InternetAddress.parse(to, true)));
+ if (to != null) {
+ err.println(prefix + ": TO="
+ + Arrays.toString(InternetAddress.parse(to, true)));
+ }
 
             key = p.concat(".mail.from");
             String from = manager.getProperty(key);
@@ -152,15 +211,99 @@
                 err.println(prefix + ": FROM="
                         + Arrays.asList(InternetAddress.parse(from, true)));
             }
+
+ synchronized (manager) {
+ final Enumeration<String> e = manager.getLoggerNames();
+ while (e.hasMoreElements()) {
+ final Logger l = manager.getLogger(e.nextElement());
+ if (l != null) {
+ final Handler[] handlers = l.getHandlers();
+ if (handlers.length > 0) {
+ err.println(prefix + ": " + l.getClass().getName()
+ + ", " + l.getName());
+ for (Handler h : handlers) {
+ err.println(prefix + ":\t" + toString(prefix, err, h));
+ }
+ }
+ }
+ }
+ }
         } catch (Throwable error) {
             err.print(prefix + ": ");
             error.printStackTrace(err);
         }
+ err.flush();
     }
 
     /**
- * Example for body only messages.
- * On close the remaining messages are sent.
+ * Gets a formatting string describing the given handler.
+ *
+ * @param prefix the output prefix.
+ * @param err the error stream.
+ * @param h the handler.
+ * @return the formatted string.
+ */
+ private static String toString(String prefix, PrintStream err, Handler h) {
+ StringBuilder buf = new StringBuilder();
+ buf.append(h.getClass().getName());
+ try {
+ if (h instanceof MailHandler) {
+ MailHandler mh = (MailHandler) h;
+ buf.append(", ").append(mh.getSubject());
+ }
+ } catch (SecurityException error) {
+ err.print(prefix + ": ");
+ error.printStackTrace(err);
+ }
+
+ try {
+ buf.append(", ").append(h.getFormatter());
+ } catch (SecurityException error) {
+ err.print(prefix + ": ");
+ error.printStackTrace(err);
+ }
+
+ try {
+ if (h instanceof MailHandler) {
+ MailHandler mh = (MailHandler) h;
+ buf.append(", ").append(Arrays.toString(
+ mh.getAttachmentFormatters()));
+ }
+ } catch (SecurityException error) {
+ err.print(prefix + ": ");
+ error.printStackTrace(err);
+ }
+
+ try {
+ buf.append(", ").append(h.getLevel());
+ } catch (SecurityException error) {
+ err.print(prefix + ": ");
+ error.printStackTrace(err);
+ }
+
+ try {
+ buf.append(", ").append(h.getFilter());
+ } catch (SecurityException error) {
+ err.print(prefix + ": ");
+ error.printStackTrace(err);
+ }
+
+ try {
+ buf.append(", ").append(h.getErrorManager());
+ } catch (SecurityException error) {
+ err.print(prefix + ": ");
+ error.printStackTrace(err);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Example for body only messages. On close the remaining messages are sent. <code>
+ * ##logging.properties
+ * MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
+ * com.sun.mail.util.logging.MailHandler.subject=Body only demo
+ * ##
+ * </code>
      */
     private static void initBodyOnly() {
         MailHandler h = new MailHandler();
@@ -169,10 +312,16 @@
     }
 
     /**
- * Example showing that when the mail handler reaches capacity it
- * will format and send the current records. Capacity is used to roughly
- * limit the size of an outgoing message.
- * On close any remaining messages are sent.
+ * Example showing that when the mail handler reaches capacity it will
+ * format and send the current records. Capacity is used to roughly limit
+ * the size of an outgoing message. On close any remaining messages are
+ * sent. <code>
+ * ##logging.properties
+ * MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
+ * com.sun.mail.util.logging.MailHandler.subject=Low capacity demo
+ * com.sun.mail.util.logging.MailHandler.capacity=5
+ * ##
+ * </code>
      */
     private static void initLowCapacity() {
         MailHandler h = new MailHandler(5);
@@ -181,21 +330,33 @@
     }
 
     /**
- * Example for body only messages.
- * On close any remaining messages are sent.
+ * Example for body only messages. On close any remaining messages are sent. <code>
+ * ##logging.properties
+ * MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
+ * com.sun.mail.util.logging.MailHandler.subject=Body and attachment demo
+ * com.sun.mail.util.logging.MailHandler.attachment.formatters=java.util.logging.XMLFormatter
+ * com.sun.mail.util.logging.MailHandler.attachment.names=data.xml
+ * ##
+ * </code>
      */
     private static void initSimpleAttachment() {
         MailHandler h = new MailHandler();
         h.setSubject("Body and attachment demo");
- h.setAttachmentFormatters(new Formatter[]{new XMLFormatter()});
- h.setAttachmentNames(new String[]{"data.xml"});
+ h.setAttachmentFormatters(new XMLFormatter());
+ h.setAttachmentNames("data.xml");
         LOGGER.addHandler(h);
     }
 
     /**
- * Example setup for priority messages by level.
- * If the push level is triggered the message is high priority.
- * Otherwise, on close any remaining messages are sent.
+ * Example setup for priority messages by level. If the push level is
+ * triggered the message is high priority. Otherwise, on close any remaining
+ * messages are sent. <code>
+ * ##logging.properties
+ * MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
+ * com.sun.mail.util.logging.MailHandler.subject=Push level demo
+ * com.sun.mail.util.logging.MailHandler.pushLevel=WARNING
+ * ##
+ * </code>
      */
     private static void initWithPushLevel() {
         MailHandler h = new MailHandler();
@@ -205,9 +366,16 @@
     }
 
     /**
- * Example for priority messages by custom trigger.
- * If the push filter is triggered the message is high priority.
- * Otherwise, on close any remaining messages are sent.
+ * Example for priority messages by custom trigger. If the push filter is
+ * triggered the message is high priority. Otherwise, on close any remaining
+ * messages are sent. <code>
+ * ##logging.properties
+ * MailHandlerDemo.handlers=com.sun.mail.util.logging.MailHandler
+ * com.sun.mail.util.logging.MailHandler.subject=Push on MessagingException demo
+ * com.sun.mail.util.logging.MailHandler.pushLevel=ALL
+ * com.sun.mail.util.logging.MailHandler.pushFilter=MailHandlerDemo$MessageErrorsFilter
+ * ##
+ * </code>
      */
     private static void initWithPushFilter() {
         MailHandler h = new MailHandler();
@@ -218,11 +386,21 @@
     }
 
     /**
- * Example for circular buffer behavior. The level, push level, and
- * capacity are set the same so that the memory handler push results
- * in a mail handler push. All messages are high priority.
- * On close any remaining records are discarded because they never reach
- * the mail handler.
+ * Example for circular buffer behavior. The level, push level, and capacity
+ * are set the same so that the memory handler push results in a mail
+ * handler push. All messages are high priority. On close any remaining
+ * records are discarded because they never reach the mail handler. <code>
+ * ##logging.properties
+ * MailHandlerDemo.handlers=java.util.logging.MemoryHandler
+ * java.util.logging.MemoryHandler.target=com.sun.mail.util.logging.MailHandler
+ * com.sun.mail.util.logging.MailHandler.level=ALL
+ * java.util.logging.MemoryHandler.level=ALL
+ * java.util.logging.MemoryHandler.push=WARNING
+ * com.sun.mail.util.logging.MailHandler.subject=Push on MessagingException demo
+ * com.sun.mail.util.logging.MailHandler.pushLevel=ALL
+ * com.sun.mail.util.logging.MailHandler.pushFilter=MailHandlerDemo$MessageErrorsFilter
+ * ##
+ * </code>
      */
     private static void initPushOnly() {
         final int capacity = 3;
@@ -237,17 +415,18 @@
     }
 
     /**
- * Holds on to the push only handler.
- * Only declared here to apply fallback settings.
+ * Holds on to the push only handler. Only declared here to apply fallback
+ * settings.
      */
     private static Handler pushOnlyHandler;
 
     /**
- * Example for circular buffer behavior as normal priority. The push level,
- * and capacity are set the same so that the memory handler push results
- * in a mail handler push. All messages are normal priority.
- * On close any remaining records are discarded because they never reach
- * the mail handler.
+ * Example for circular buffer behavior as normal priority. The push level,
+ * and capacity are set the same so that the memory handler push results in
+ * a mail handler push. All messages are normal priority. On close any
+ * remaining records are discarded because they never reach the mail
+ * handler. Use the LogManager config option or extend the MemoryHandler to
+ * emulate this behavior via the logging.properties.
      */
     private static void initPushNormal() {
         final int capacity = 3;
@@ -266,68 +445,106 @@
     }
 
     /**
- * Holds on to the push normal handler.
- * Only declared here to apply fallback settings.
+ * Holds on to the push normal handler. Only declared here to apply fallback
+ * settings.
      */
     private static Handler pushNormalHandler;
 
     /**
- * Example for various kinds of custom sorting, formatting, and filtering
- * for multiple attachment messages.
- * On close any remaining messages are sent.
+ * Example for various kinds of custom sorting, formatting, and filtering
+ * for multiple attachment messages. The subject will contain the most
+ * severe record and a count of remaining records. The log records are
+ * ordered from most severe to least severe. The body uses a custom
+ * formatter that includes a summary by date and time. The attachment use
+ * XML and plain text formats. Each attachment has a different set of
+ * filtering. The attachment names are generated from either a fixed name or
+ * are built using the number and type of the records formatted. On close
+ * any remaining messages are sent. Use the LogManager config option or
+ * extend the MemoryHandler to emulate this behavior via the
+ * logging.properties.
      */
     private static void initCustomAttachments() {
         MailHandler h = new MailHandler();
 
         //Sort records by level keeping the severe messages at the top.
- h.setComparator(new LevelAndSeqComparator(true));
+ h.setComparator(Collections.reverseOrder(new SeverityComparator()));
 
         //Use subject to provide a hint as to what is in the email.
- h.setSubject(new SummaryNameFormatter("Log containing {0} records with {1} errors"));
+ h.setSubject(new CollectorFormatter());
 
         //Make the body give a simple summary of what happened.
         h.setFormatter(new SummaryFormatter());
 
         //Create 3 attachments.
- h.setAttachmentFormatters(new Formatter[]{new XMLFormatter(), new XMLFormatter(), new SimpleFormatter()});
+ h.setAttachmentFormatters(new XMLFormatter(),
+ new XMLFormatter(), new SimpleFormatter());
 
- //filter each attachment differently.
- h.setAttachmentFilters(new Filter[]{null, new MessageErrorsFilter(false),
- new MessageErrorsFilter(true)});
+ //Filter each attachment differently.
+ h.setAttachmentFilters(null, new MessageErrorsFilter(false),
+ new MessageErrorsFilter(true));
 
-
- //create simple names.
- h.setAttachmentNames(new String[]{"all.xml", "errors.xml", "errors.txt"});
-
- //extract simple name, replace the rest with formatters.
- h.setAttachmentNames(new Formatter[]{h.getAttachmentNames()[0],
- new SummaryNameFormatter("{0} records and {1} errors"),
- new SummaryNameFormatter("{0,choice,0#no records|1#1 record|"
- + "1<{0,number,integer} records} and "
- + "{1,choice,0#no errors|1#1 error|1<"
- + "{1,number,integer} errors}")});
+ //Creating the attachment name formatters.
+ h.setAttachmentNames(new CollectorFormatter("all.xml"),
+ new CollectorFormatter("{3} records and {5} errors.xml"),
+ new CollectorFormatter("{5,choice,0#no errors|1#1 error|1<"
+ + "{5,number,integer} errors}.txt"));
 
         LOGGER.addHandler(h);
     }
 
     /**
      * Sets up the demos that will run.
+ *
+ * @param l the list of arguments.
      */
- private static void init() {
+ private static void init(List<String> l) {
+ l = new ArrayList<String>(l);
         Session session = Session.getInstance(System.getProperties());
- if (session.getDebug()) {
+ boolean all = l.remove("-all") || l.isEmpty();
+ if (l.remove("-body") || all) {
+ initBodyOnly();
+ }
+
+ if (l.remove("-custom") || all) {
+ initCustomAttachments();
+ }
+
+ if (l.remove("-low") || all) {
+ initLowCapacity();
+ }
+
+ if (l.remove("-pushfilter") || all) {
+ initWithPushFilter();
+ }
+
+ if (l.remove("-pushlevel") || all) {
+ initWithPushLevel();
+ }
+
+ if (l.remove("-pushnormal") || all) {
+ initPushNormal();
+ }
+
+ if (l.remove("-pushonly") || all) {
+ initPushOnly();
+ }
+
+ if (l.remove("-simple") || all) {
+ initSimpleAttachment();
+ }
+
+ boolean fallback = applyFallbackSettings();
+ if (l.remove("-debug") || session.getDebug()) {
             checkConfig(CLASS_NAME, session.getDebugOut());
         }
 
- initBodyOnly();
- initLowCapacity();
- initSimpleAttachment();
- initWithPushLevel();
- initWithPushFilter();
- initCustomAttachments();
- initPushOnly();
- initPushNormal();
- applyFallbackSettings();
+ if (!l.isEmpty()) {
+ LOGGER.log(Level.SEVERE, "Unknown commands: {0}", l);
+ }
+
+ if (fallback) {
+ LOGGER.info("Check your user temp dir for output.");
+ }
     }
 
     /**
@@ -335,8 +552,7 @@
      */
     private static void closeHandlers() {
         Handler[] handlers = LOGGER.getHandlers();
- for (int i = 0; i < handlers.length; i++) {
- Handler h = handlers[i];
+ for (Handler h : handlers) {
             h.close();
             LOGGER.removeHandler(h);
         }
@@ -344,23 +560,26 @@
 
     /**
      * Apply some fallback settings if no configuration file was specified.
+ *
+ * @return true if fallback settings were applied.
      */
- private static void applyFallbackSettings() {
+ private static boolean applyFallbackSettings() {
         if (getConfigLocation() == null) {
             LOGGER.setLevel(Level.ALL);
- LOGGER.info("Check your user temp dir for output.");
             Handler[] handlers = LOGGER.getHandlers();
- for (int i = 0; i < handlers.length; i++) {
- Handler h = handlers[i];
+ for (Handler h : handlers) {
                 fallbackSettings(h);
             }
             fallbackSettings(pushOnlyHandler);
             fallbackSettings(pushNormalHandler);
+ return true;
         }
+ return false;
     }
 
     /**
      * Common fallback settings for a single handler.
+ *
      * @param h the handler.
      */
     private static void fallbackSettings(Handler h) {
@@ -372,6 +591,7 @@
 
     /**
      * Gets the system temp directory.
+ *
      * @return the system temp directory.
      */
     private static String getTempDir() {
@@ -380,6 +600,7 @@
 
     /**
      * Gets the configuration file or class name.
+ *
      * @return the file name or class name.
      */
     private static String getConfigLocation() {
@@ -393,7 +614,7 @@
     /**
      * A simple message filter example.
      */
- private static final class MessageErrorsFilter implements Filter {
+ public static final class MessageErrorsFilter implements Filter {
 
         /**
          * Used to negate this filter.
@@ -403,15 +624,16 @@
         /**
          * Default constructor.
          */
- MessageErrorsFilter() {
+ public MessageErrorsFilter() {
             this(true);
         }
 
         /**
          * Creates the message filter.
+ *
          * @param complement used to allow or deny message exceptions.
          */
- MessageErrorsFilter(final boolean complement) {
+ public MessageErrorsFilter(final boolean complement) {
             this.complement = complement;
         }
 
@@ -419,72 +641,4 @@
             return r.getThrown() instanceof MessagingException == complement;
         }
     }
-
- /**
- * Orders log records by level then sequence number.
- */
- private static final class LevelAndSeqComparator
- implements Comparator<LogRecord>, java.io.Serializable {
-
- /**
- * Generated serial id.
- */
- private static final long serialVersionUID = 6269562326337300267L;
-
- /**
- * Used to reverse the ordering.
- */
- private final boolean reverse;
-
- /**
- * The default constructor.
- */
- LevelAndSeqComparator() {
- this(false);
- }
-
- /**
- * Creates the comparator.
- * @param reverse to specify reverse ordering.
- */
- LevelAndSeqComparator(final boolean reverse) {
- this.reverse = reverse;
- }
-
- /**
- * Compare by level.
- * @param r1 the first log record.
- * @param r2 the second log record.
- * @return -1 for less than, 0 for equal, or 1 for greater than.
- */
- public int compare(LogRecord r1, LogRecord r2) {
- final int first = r1.getLevel().intValue();
- final int second = r2.getLevel().intValue();
- if (first < second) {
- return reverse ? 1 : -1;
- } else if (first > second) {
- return reverse ? -1 : 1;
- } else {
- return compareSeq(r1, r2);
- }
- }
-
- /**
- * Compare by sequence.
- * @param r1 the first log record.
- * @param r2 the second log record.
- * @return -1 for less than, 0 for equal, or 1 for greater than.
- */
- private int compareSeq(LogRecord r1, LogRecord r2) {
- final long first = r1.getSequenceNumber();
- final long second = r2.getSequenceNumber();
- if (first < second) {
- return reverse ? 1 : -1;
- } else if (first > second) {
- return reverse ? -1 : 1;
- } else {
- return 0;
- }
- }
- }
 }

diff -r 8fa698ea34b3 -r 75b23c207789 logging/src/main/java/README.txt
--- a/logging/src/main/java/README.txt Fri Nov 07 11:57:46 2014 -0800
+++ b/logging/src/main/java/README.txt Fri Nov 07 17:04:46 2014 -0800
@@ -12,7 +12,7 @@
 
     1. The demo requires Java version 1.5 or newer.
         We *strongly* encourage you to use the latest version of J2SE,
- which you can download from
+ which you can download from
         http://www.oracle.com/technetwork/java/javase/downloads.
 
     2. Set your CLASSPATH to include the "mail.jar" and "activation.jar".
@@ -30,8 +30,8 @@
     4. Compile all the files using your Java compiler. For example:
 
           javac *.java
-
- 5. Not required but, you should edit the maildemo.properties and change the
+
+ 5. Not required but, you should edit the maildemo.properties and change the
         mail.to address and mail.host to your mail server ip or host name.
 
     6. Run the demo. For example:
@@ -50,22 +50,53 @@
                                 initXXX methods describe some of the
                                 common setup code for different types
                                 of email messages.
-
+
+ Usage: java MailHandlerDemo [[-all] | [-body] | [-debug]
+ | [-low] | [-simple] | [-pushlevel]
+ | [-pushfilter] | [-pushnormal]| [-pushonly]]
+
+ Options:
+ -all : Execute all demos.
+ -body : An email with all records and only a body.
+ -debug : Output basic debug information about the
+ JVM and log configuration.
+ -low : Generates multiple emails due to low
+ capacity.
+ -simple : An email with all records with body and an
+ attachment.
+ -pushlevel : Generates high priority emails when
+ the push level is triggered and
+ normal priority when flushed.
+ -pushFilter : Generates high priority emails when
+ the push level and the push filter
+ is triggered and normal priority
+ emails when flushed.
+ -pushnormal : Generates multiple emails when the
+ MemoryHandler push level is
+ triggered. All generated email are
+ sent as normal priority.
+ -pushonly : Generates multiple emails when the
+ MemoryHandler push level is
+ triggered. Generates high priority
+ emails when the push level is
+ triggered and normal priority when
+ flushed.
+
+
         FileErrorManager = Used to store email messages to the
                                 local file system when mail transport
                                 fails. This is installed as a
                                 fallback in case the logging config is
                                 not specified.
 
- SummaryFormatter = An example compact formatter for the
- body of an email message.
-
- SummaryNameFormatter = An example formatter used to generate
- subject lines and attachment file
- names based on the contents of the
- email message.
+ SummaryFormatter = An example compact formatter with summary
+ for use with the body of an email message.
 
 Support files:
 
         maildemo.properties = A sample LogManager properties file for
                                 the MailHandlerDemo.
+
+ maildemo.policy = A sample security policy file to use with
+ the MailHandlerDemo. This can be used to
+ enable security tracing.

diff -r 8fa698ea34b3 -r 75b23c207789 logging/src/main/java/SummaryFormatter.java
--- a/logging/src/main/java/SummaryFormatter.java Fri Nov 07 11:57:46 2014 -0800
+++ b/logging/src/main/java/SummaryFormatter.java Fri Nov 07 17:04:46 2014 -0800
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2009-2013 Jason Mehrens. All Rights Reserved.
+ * Copyright (c) 2009-2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009-2014 Jason Mehrens. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,8 +30,8 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-import java.text.DateFormat;
-import java.util.Date;
+import com.sun.mail.util.logging.CollectorFormatter;
+import com.sun.mail.util.logging.CompactFormatter;
 import java.util.logging.Formatter;
 import java.util.logging.Handler;
 import java.util.logging.LogRecord;
@@ -41,93 +41,61 @@
  *
  * @author Jason Mehrens
  */
-public class SummaryFormatter extends Formatter {
+public final class SummaryFormatter extends Formatter {
 
     /**
- * The oldest record.getMillis() recorded.
+ * The line formatter.
      */
- private long oldest;
+ private final CompactFormatter format;
     /**
- * The newest record.getMillis() recorded.
+ * The footer formatter.
      */
- private long newest;
- /**
- * The number of log records formatted before the tail is written.
- */
- private long count;
+ private final CollectorFormatter footer;
 
     /**
      * Creates the formatter.
      */
     public SummaryFormatter() {
- reset();
+ format = new CompactFormatter("[%4$s]\t%5$s %6$s%n");
+ footer = new CollectorFormatter("\nThese {3} messages occurred between "
+ + "{7,time,EEE, MMM dd HH:mm:ss:S ZZZ yyyy} and "
+ + "{8,time,EEE, MMM dd HH:mm:ss:S ZZZ yyyy}\n", format, null);
     }
 
+ /**
+ * Gets the header information.
+ *
[truncated due to length]