commits@javamail.java.net

[javamail~mercurial:833] MailHandler verify should load additional content handlers - Bug K7506

From: <shannon_at_java.net>
Date: Sat, 25 Jun 2016 06:30:23 +0000

Project: javamail
Repository: mercurial
Revision: 833
Author: shannon
Date: 2016-06-25 00:15:25 UTC
Link:

Log Message:
------------
Store finalizers should not talk to server - bug 7472
fix possible NPE if SASL is enabled on Android - bug 7512
kludge WriteTimeoutSocket to work properly on Android - bug 7513
fix javadoc error
MailHandler verify should load additional content handlers - Bug K7506
MailHandlerTest add content type of formatter test.
MailHandlerDemo correct javadoc example.

(From Jason)


Revisions:
----------
829
830
831
832
833


Modified Paths:
---------------
doc/release/CHANGES.txt
doc/release/COMPAT.txt
mail/src/main/java/com/sun/mail/imap/IMAPStore.java
mail/src/main/java/com/sun/mail/imap/package.html
mail/src/main/java/com/sun/mail/pop3/POP3Folder.java
mail/src/main/java/com/sun/mail/pop3/POP3Store.java
mail/src/main/java/com/sun/mail/pop3/Protocol.java
mail/src/main/java/com/sun/mail/pop3/package.html
mail/src/main/java/com/sun/mail/util/WriteTimeoutSocket.java
mail/src/test/java/com/sun/mail/test/ProtocolHandler.java
mail/src/test/java/com/sun/mail/util/WriteTimeoutSocketTest.java
mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java
logging/src/main/java/MailHandlerDemo.java
mail/src/main/java/com/sun/mail/util/logging/MailHandler.java
mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java


Diffs:
------
diff -r b141c179a836 -r 4d54159387fe doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Tue Jun 14 14:18:45 2016 -0700
+++ b/doc/release/CHANGES.txt Fri Jun 24 16:42:22 2016 -0700
@@ -31,6 +31,7 @@
 K 7378 Deadlock in IMAPFolder.doProtocolCommand()
 K 7471 InternetAddress.getLocalAddress should use
         InetAddress.getCanonicalHostName
+K 7472 Store finalizers should not talk to server
 
 
                   CHANGES IN THE 1.5.5 RELEASE

diff -r b141c179a836 -r 4d54159387fe doc/release/COMPAT.txt
--- a/doc/release/COMPAT.txt Tue Jun 14 14:18:45 2016 -0700
+++ b/doc/release/COMPAT.txt Fri Jun 24 16:42:22 2016 -0700
@@ -16,6 +16,20 @@
 
 -- JavaMail 1.5.6 --
 
+- finalizers close sockets abruptly
+
+ It's important for finalizers to close an open socket
+ connection to prevent file descriptor leaks. Previously the
+ finalizers for the IMAP and POP3 providers would try to close
+ the connection cleanly, which could result in a timeout waiting
+ for the server. They now close the connection without
+ performing any socket I/O, which may result in an unclean
+ shutdown when seen from the server. Applications should always
+ close Stores and Folders when done with them to avoid the need
+ for the finalizer to do this cleanup. The Session property
+ "mail.<protocol>.finalizecleanclose" can be set to "true" to
+ force the connection to be closed cleanly in the finalizer.
+
 - InternetAddress.getLocalAddress uses canonical host name
 
         The InternetAddress.getLocalAddress method now uses the

diff -r b141c179a836 -r 4d54159387fe mail/src/main/java/com/sun/mail/imap/IMAPStore.java
--- a/mail/src/main/java/com/sun/mail/imap/IMAPStore.java Tue Jun 14 14:18:45 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/IMAPStore.java Fri Jun 24 16:42:22 2016 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997-2016 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
@@ -225,6 +225,7 @@
     private boolean peek = false;
     private boolean closeFoldersOnStoreFailure = true;
     private boolean enableCompress = false; // enable COMPRESS=DEFLATE
+ private boolean finalizeCleanClose = false;
 
     /*
      * This field is set in the Store's response handler if we see
@@ -598,6 +599,12 @@
         if (enableCompress)
             logger.config("enable COMPRESS");
 
+ // check if finalizeCleanClose is enabled
+ finalizeCleanClose = PropUtil.getBooleanSessionProperty(session,
+ "mail." + name + ".finalizecleanclose", false);
+ if (finalizeCleanClose)
+ logger.config("close connection cleanly in finalize");
+
         s = session.getProperty("mail." + name + ".folder.class");
         if (s != null) {
             logger.log(Level.CONFIG, "IMAP: folder class: {0}", s);
@@ -1662,6 +1669,14 @@
     }
 
     protected void finalize() throws Throwable {
+ if (!finalizeCleanClose) {
+ // when finalizing, close connections abruptly
+ synchronized (connectionFailedLock) {
+ connectionFailed = true;
+ forceClose = true;
+ }
+ closeFoldersOnStoreFailure = true; // make sure folders get closed
+ }
         try {
             close();
         } finally {

diff -r b141c179a836 -r 4d54159387fe mail/src/main/java/com/sun/mail/imap/package.html
--- a/mail/src/main/java/com/sun/mail/imap/package.html Tue Jun 14 14:18:45 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/package.html Fri Jun 24 16:42:22 2016 -0700
@@ -5,7 +5,7 @@
 
     DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
- Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 1997-2016 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
@@ -867,6 +867,20 @@
 </TD>
 </TR>
 
+<A NAME="mail.imap.finalizecleanclose"></A>
+<TR id="mail.imap.finalizecleanclose">
+<TD>mail.imap.finalizecleanclose</TD>
+<TD>boolean</TD>
+<TD>
+When the finalizer for IMAPStore is called,
+should the connection to the server be closed cleanly, as if the
+application called the close method?
+Or should the connection to the server be closed without sending
+any commands to the server?
+Defaults to false, the connection is not closed cleanly.
+</TD>
+</TR>
+
 <A NAME="mail.imap.referralexception"></A>
 <TR id="mail.imap.referralexception">
 <TD>mail.imap.referralexception</TD>

diff -r b141c179a836 -r 4d54159387fe mail/src/main/java/com/sun/mail/pop3/POP3Folder.java
--- a/mail/src/main/java/com/sun/mail/pop3/POP3Folder.java Tue Jun 14 14:18:45 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/pop3/POP3Folder.java Fri Jun 24 16:42:22 2016 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997-2016 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
@@ -75,6 +75,7 @@
     private POP3Message[] message_cache;
     private boolean doneUidl = false;
     private volatile TempFile fileCache = null;
+ private boolean forceClose;
 
     MailLogger logger; // package private, for POP3Message
 
@@ -250,10 +251,10 @@
              * the "marked for deletion" flags. We can then explicitly
              * delete messages as desired.
              */
- if (store.rsetBeforeQuit)
+ if (store.rsetBeforeQuit && !forceClose)
                 port.rset();
             POP3Message m;
- if (expunge && mode == READ_WRITE) {
+ if (expunge && mode == READ_WRITE && !forceClose) {
                 // find all messages marked deleted and issue DELE commands
                 for (int i = 0; i < message_cache.length; i++) {
                     if ((m = message_cache[i]) != null) {
@@ -277,7 +278,10 @@
                     m.invalidate(true);
             }
 
- port.quit();
+ if (forceClose)
+ port.close();
+ else
+ port.quit();
         } catch (IOException ex) {
             // do nothing
         } finally {
@@ -545,11 +549,13 @@
      * Close the folder when we're finalized.
      */
     protected void finalize() throws Throwable {
+ forceClose = !store.finalizeCleanClose;
         try {
             if (opened)
                 close(false);
         } finally {
             super.finalize();
+ forceClose = false;
         }
     }
 

diff -r b141c179a836 -r 4d54159387fe mail/src/main/java/com/sun/mail/pop3/POP3Store.java
--- a/mail/src/main/java/com/sun/mail/pop3/POP3Store.java Tue Jun 14 14:18:45 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/pop3/POP3Store.java Fri Jun 24 16:42:22 2016 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997-2016 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
@@ -95,6 +95,7 @@
     volatile boolean useFileCache = false;
     volatile File fileCacheDir = null;
     volatile boolean keepMessageContent = false;
+ volatile boolean finalizeCleanClose = false;
 
     public POP3Store(Session session, URLName url) {
         this(session, url, "pop3", false);
@@ -136,6 +137,9 @@
         // mail.pop3.starttls.required requires use of STLS command
         requireStartTLS = getBoolProp("starttls.required");
 
+ // mail.pop3.finalizecleanclose requires clean close when finalizing
+ finalizeCleanClose = getBoolProp("finalizecleanclose");
+
         String s = session.getProperty("mail." + name + ".message.class");
         if (s != null) {
             logger.log(Level.CONFIG, "message class: {0}", s);
@@ -341,9 +345,17 @@
     }
 
     public synchronized void close() throws MessagingException {
+ close(false);
+ }
+
+ synchronized void close(boolean force) throws MessagingException {
         try {
- if (port != null)
- port.quit();
+ if (port != null) {
+ if (force)
+ port.close();
+ else
+ port.quit();
+ }
         } catch (IOException ioex) {
         } finally {
             port = null;
@@ -410,7 +422,7 @@
     protected void finalize() throws Throwable {
         try {
             if (port != null) // don't force a connection attempt
- close();
+ close(!finalizeCleanClose);
         } finally {
             super.finalize();
         }

diff -r b141c179a836 -r 4d54159387fe mail/src/main/java/com/sun/mail/pop3/Protocol.java
--- a/mail/src/main/java/com/sun/mail/pop3/Protocol.java Tue Jun 14 14:18:45 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/pop3/Protocol.java Fri Jun 24 16:42:22 2016 -0700
@@ -346,18 +346,27 @@
             Response r = simpleCommand("QUIT");
             ok = r.ok;
         } finally {
- try {
- socket.close();
- } finally {
- socket = null;
- input = null;
- output = null;
- }
+ close();
         }
         return ok;
     }
 
     /**
+ * Close the connection without sending any commands.
+ */
+ void close() {
+ try {
+ socket.close();
+ } catch (IOException ex) {
+ // ignore it
+ } finally {
+ socket = null;
+ input = null;
+ output = null;
+ }
+ }
+
+ /**
      * Return the total number of messages and mailbox size,
      * using the STAT command.
      */

diff -r b141c179a836 -r 4d54159387fe mail/src/main/java/com/sun/mail/pop3/package.html
--- a/mail/src/main/java/com/sun/mail/pop3/package.html Tue Jun 14 14:18:45 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/pop3/package.html Fri Jun 24 16:42:22 2016 -0700
@@ -5,7 +5,7 @@
 
     DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
- Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 1997-2016 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
@@ -606,6 +606,20 @@
 </TD>
 </TR>
 
+<A NAME="mail.pop3.finalizecleanclose"></A>
+<TR id="mail.pop3.finalizecleanclose">
+<TD>mail.pop3.finalizecleanclose</TD>
+<TD>boolean</TD>
+<TD>
+When the finalizer for POP3Store or POP3Folder is called,
+should the connection to the server be closed cleanly, as if the
+application called the close method?
+Or should the connection to the server be closed without sending
+any commands to the server?
+Defaults to false, the connection is not closed cleanly.
+</TD>
+</TR>
+
 </TABLE>
 <P>
 In general, applications should not need to use the classes in this


diff -r 4d54159387fe -r ee2c77a43ffc doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Jun 24 16:42:22 2016 -0700
+++ b/doc/release/CHANGES.txt Fri Jun 24 16:43:55 2016 -0700
@@ -32,6 +32,7 @@
 K 7471 InternetAddress.getLocalAddress should use
         InetAddress.getCanonicalHostName
 K 7472 Store finalizers should not talk to server
+K 7512 NullPointerException if SASL is enabled on Android
 
 
                   CHANGES IN THE 1.5.5 RELEASE

diff -r 4d54159387fe -r ee2c77a43ffc mail/src/main/java/com/sun/mail/imap/IMAPStore.java
--- a/mail/src/main/java/com/sun/mail/imap/IMAPStore.java Fri Jun 24 16:42:22 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/imap/IMAPStore.java Fri Jun 24 16:43:55 2016 -0700
@@ -720,8 +720,9 @@
             if (protocol != null)
                 protocol.disconnect();
             protocol = null;
+ Response r = cex.getResponse();
             throw new AuthenticationFailedException(
- cex.getResponse().getRest());
+ r != null ? r.getRest() : cex.getMessage());
         } catch (ProtocolException pex) { // any other exception
             // failure in login command, close connection to server
             if (protocol != null)


diff -r ee2c77a43ffc -r ca92e4135bef doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Jun 24 16:43:55 2016 -0700
+++ b/doc/release/CHANGES.txt Fri Jun 24 16:54:11 2016 -0700
@@ -33,6 +33,7 @@
         InetAddress.getCanonicalHostName
 K 7472 Store finalizers should not talk to server
 K 7512 NullPointerException if SASL is enabled on Android
+K 7513 write timeouts don't work with SSL on Android
 
 
                   CHANGES IN THE 1.5.5 RELEASE

diff -r ee2c77a43ffc -r ca92e4135bef mail/src/main/java/com/sun/mail/util/WriteTimeoutSocket.java
--- a/mail/src/main/java/com/sun/mail/util/WriteTimeoutSocket.java Fri Jun 24 16:43:55 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/util/WriteTimeoutSocket.java Fri Jun 24 16:54:11 2016 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013-2016 Oracle and/or its affiliates. All rights reserved.
  *
  * The contents of this file are subject to the terms of either the GNU
  * General Public License Version 2 only ("GPL") or the Common Development
@@ -44,6 +44,7 @@
 import java.net.*;
 import java.util.concurrent.*;
 import java.nio.channels.SocketChannel;
+import java.lang.reflect.*;
 
 /**
  * A special Socket that uses a ScheduledExecutorService to
@@ -118,6 +119,22 @@
     }
 
     @Override
+ public SocketAddress getRemoteSocketAddress() {
+ return socket.getRemoteSocketAddress();
+ }
+
+ @Override
+ public SocketAddress getLocalSocketAddress() {
+ return socket.getLocalSocketAddress();
+ }
+
+ @Override
+ public void setPerformancePreferences(int connectionTime, int latency,
+ int bandwidth) {
+ socket.setPerformancePreferences(connectionTime, latency, bandwidth);
+ }
+
+ @Override
     public SocketChannel getChannel() {
         return socket.getChannel();
     }
@@ -296,6 +313,18 @@
     public boolean isOutputShutdown() {
         return socket.isOutputShutdown();
     }
+
+ /**
+ * KLUDGE for Android, which has this illegal non-Java Compatible method.
+ */
+ public FileDescriptor getFileDescriptor$() {
+ try {
+ Method m = Socket.class.getDeclaredMethod("getFileDescriptor$");
+ return (FileDescriptor)m.invoke(socket);
+ } catch (Exception ex) {
+ return null;
+ }
+ }
 }
 
 

diff -r ee2c77a43ffc -r ca92e4135bef mail/src/test/java/com/sun/mail/test/ProtocolHandler.java
--- a/mail/src/test/java/com/sun/mail/test/ProtocolHandler.java Fri Jun 24 16:43:55 2016 -0700
+++ b/mail/src/test/java/com/sun/mail/test/ProtocolHandler.java Fri Jun 24 16:54:11 2016 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 2009-2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009-2016 Oracle and/or its affiliates. All rights reserved.
  *
  * The contents of this file are subject to the terms of either the GNU
  * General Public License Version 2 only ("GPL") or the Common Development
@@ -48,6 +48,7 @@
 import java.net.SocketException;
 import java.util.logging.Logger;
 import java.util.logging.Level;
+import javax.net.ssl.SSLException;
 
 /**
  * Handle protocol connection.
@@ -113,6 +114,8 @@
             //clientSocket.close();
         } catch (SocketException sex) {
             // ignore it, often get "connection reset" when client closes
+ } catch (SSLException sex) {
+ // ignore it, often occurs when testing SSL
         } catch (Exception e) {
             LOGGER.log(Level.SEVERE, "Error", e);
         } finally {

diff -r ee2c77a43ffc -r ca92e4135bef mail/src/test/java/com/sun/mail/util/WriteTimeoutSocketTest.java
--- a/mail/src/test/java/com/sun/mail/util/WriteTimeoutSocketTest.java Fri Jun 24 16:43:55 2016 -0700
+++ b/mail/src/test/java/com/sun/mail/util/WriteTimeoutSocketTest.java Fri Jun 24 16:54:11 2016 -0700
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 2009-2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009-2016 Oracle and/or its affiliates. All rights reserved.
  *
  * The contents of this file are subject to the terms of either the GNU
  * General Public License Version 2 only ("GPL") or the Common Development
@@ -40,11 +40,15 @@
 
 package com.sun.mail.util;
 
+import java.lang.reflect.*;
+
 import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.util.Properties;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
 
 import javax.mail.*;
 import javax.mail.internet.MimeMessage;
@@ -142,6 +146,40 @@
         assertTrue(sf.getSocketWrapped() || sf.getSocketCreated());
     }
 
+ /**
+ * Test that WriteTimeoutSocket overrides all methods from Socket.
+ * XXX - this is kind of hacky since it depends on Method.toString
+ */
+ @Test
+ public void testOverrides() throws Exception {
+ Set<String> socketMethods = new HashSet<String>();
+ Method[] m = java.net.Socket.class.getDeclaredMethods();
+ String className = java.net.Socket.class.getName() + ".";
+ for (int i = 0; i < m.length; i++) {
+ if (Modifier.isPublic(m[i].getModifiers()) &&
+ !Modifier.isStatic(m[i].getModifiers())) {
+ String name = m[i].toString().
+ replace("synchronized ", "").
+ replace(className, "");
+ socketMethods.add(name);
+ }
+ }
+ Set<String> wtsocketMethods = new HashSet<String>();
+ m = WriteTimeoutSocket.class.getDeclaredMethods();
+ className = WriteTimeoutSocket.class.getName() + ".";
+ for (int i = 0; i < m.length; i++) {
+ if (Modifier.isPublic(m[i].getModifiers())) {
+ String name = m[i].toString().
+ replace("synchronized ", "").
+ replace(className, "");
+ socketMethods.remove(name);
+ }
+ }
+ for (String s : socketMethods)
+ System.out.println("WriteTimeoutSocket did not override: " + s);
+ assertTrue(socketMethods.isEmpty());
+ }
+
     private static String[] getAnonCipherSuitesArray() {
         SSLSocketFactory sf = (SSLSocketFactory)SSLSocketFactory.getDefault();
         List<String> anon = new ArrayList<String>();


diff -r ca92e4135bef -r 9031f1e5787a mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java
--- a/mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java Fri Jun 24 16:54:11 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/smtp/SMTPTransport.java Fri Jun 24 17:13:19 2016 -0700
@@ -652,7 +652,7 @@
      * @param host the name of the host to connect to
      * @param port the port to use (-1 means use default port)
      * @param user the name of the user to login as
- * @param passwd the user's password
+ * @param password the user's password
      * @return true if connection successful, false if authentication failed
      * @exception MessagingException for non-authentication failures
      */


diff -r 9031f1e5787a -r 9b7804254f97 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Jun 24 17:13:19 2016 -0700
+++ b/doc/release/CHANGES.txt Fri Jun 24 17:15:25 2016 -0700
@@ -32,6 +32,7 @@
 K 7471 InternetAddress.getLocalAddress should use
         InetAddress.getCanonicalHostName
 K 7472 Store finalizers should not talk to server
+K 7506 MailHandler verify should load additional content handlers
 K 7512 NullPointerException if SASL is enabled on Android
 K 7513 write timeouts don't work with SSL on Android
 

diff -r 9031f1e5787a -r 9b7804254f97 logging/src/main/java/MailHandlerDemo.java
--- a/logging/src/main/java/MailHandlerDemo.java Fri Jun 24 17:13:19 2016 -0700
+++ b/logging/src/main/java/MailHandlerDemo.java Fri Jun 24 17:15:25 2016 -0700
@@ -49,7 +49,7 @@
 /**
  * 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.
+ * default settings to store emails in the user's temp directory.
  *
  * @author Jason Mehrens
  */
@@ -440,9 +440,8 @@
      * 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
+ * com.sun.mail.util.logging.MailHandler.subject=Push only demo
+ * com.sun.mail.util.logging.MailHandler.pushLevel=WARNING
      * ##
      * </code>
      */
@@ -658,4 +657,12 @@
         }
         return file;
     }
+
+ /**
+ * No objects are allowed.
+ * @throws IllegalAccessException always.
+ */
+ private MailHandlerDemo() throws IllegalAccessException {
+ throw new IllegalAccessException();
+ }
 }

diff -r 9031f1e5787a -r 9b7804254f97 mail/src/main/java/com/sun/mail/util/logging/MailHandler.java
--- a/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Fri Jun 24 17:13:19 2016 -0700
+++ b/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Fri Jun 24 17:15:25 2016 -0700
@@ -384,16 +384,17 @@
      * prepared to deal with unexpected null values since the
      * WebappClassLoader.clearReferencesThreadLocals() and
      * InnocuousThread.eraseThreadLocals() can remove thread local values.
- * The MUTEX has 4 states:
+ * The MUTEX has 5 states:
      * 1. A null value meaning default state of not publishing.
      * 2. MUTEX_PUBLISH on first entry of a push or publish.
      * 3. The index of the first filter to accept a log record.
      * 4. MUTEX_REPORT when cycle of records is detected.
+ * 5. MUTEXT_LINKAGE when a linkage error is reported.
      */
     private static final ThreadLocal<Integer> MUTEX = new ThreadLocal<Integer>();
     /**
      * The marker object used to report a publishing state.
- * This must be less than the body filter index.
+ * This must be less than the body filter index (-1).
      */
     private static final Integer MUTEX_PUBLISH = -2;
     /**
@@ -1632,6 +1633,39 @@
         return null; //text/plain
     }
 
+ /**
+ * Determines the mimeType of a formatter by the class name. This method
+ * avoids calling getHead and getTail of content formatters during verify
+ * because they might trigger side effects or excessive work. The name
+ * formatters and subject are usually safe to call.
+ * Package-private for unit testing.
+ *
+ * @param f the formatter or null.
+ * @return return the mime type or text/plain.
+ * @since JavaMail 1.5.6
+ */
+ final String contentTypeOf(final Formatter f) {
+ if (f != null) {
+ for (Class<?> k = f.getClass(); k != Formatter.class;
+ k = k.getSuperclass()) {
+ String name = k.getName().toLowerCase(Locale.ENGLISH);
+ for (int idx = name.indexOf('$') + 1;
+ (idx = name.indexOf("ml", idx)) > -1; idx += 2) {
+ if (idx > 0) {
+ if (name.charAt(idx - 1) == 'x') {
+ return "application/xml";
+ }
+ if (idx > 1 && name.charAt(idx - 2) == 'h'
+ && name.charAt(idx - 1) == 't') {
+ return "text/html";
+ }
+ }
+ }
+ }
+ }
+ return "text/plain";
+ }
+
    /**
      * Determines if the given throwable is a no content exception. It is
      * assumed Transport.sendMessage will call Message.writeTo so we need to
@@ -1677,12 +1711,14 @@
      */
     @SuppressWarnings("UseSpecificCatch")
     private void reportError(Message msg, Exception ex, int code) {
- try { //Use direct call so we do not prefix raw email.
- errorManager.error(toRawString(msg), ex, code);
- } catch (final RuntimeException re) {
- reportError(toMsgString(re), ex, code);
- } catch (final Exception e) {
- reportError(toMsgString(e), ex, code);
+ try {
+ try { //Use direct call so we do not prefix raw email.
+ errorManager.error(toRawString(msg), ex, code);
+ } catch (final RuntimeException re) {
+ reportError(toMsgString(re), ex, code);
+ } catch (final Exception e) {
+ reportError(toMsgString(e), ex, code);
+ }
         } catch (final LinkageError GLASSFISH_21258) {
             reportLinkageError(GLASSFISH_21258, code);
         }
@@ -1798,7 +1834,7 @@
      * Sets the capacity for this handler. This method is kept private
      * because we would have to define a public policy for when the size is
      * greater than the capacity.
- * I.E. do nothing, flush now, truncate now, push now and resize.
+ * E.G. do nothing, flush now, truncate now, push now and resize.
      * @param newCapacity the max number of records.
      * @throws SecurityException if a security manager exists and the
      * caller does not have <tt>LoggingPermission("control")</tt>.
@@ -2998,9 +3034,19 @@
         }
 
         //Perform all of the copy actions first.
+ String[] atn;
         synchronized (this) { //Create the subject.
             appendSubject(abort, head(subjectFormatter));
             appendSubject(abort, tail(subjectFormatter, ""));
+ atn = new String[attachmentNames.length];
+ for (int i = 0; i < atn.length; ++i) {
+ atn[i] = head(attachmentNames[i]);
+ if (atn[i].length() == 0) {
+ atn[i] = tail(attachmentNames[i], "");
+ } else {
+ atn[i] = atn[i].concat(tail(attachmentNames[i], ""));
+ }
+ }
         }
 
         setIncompleteCopy(abort); //Original body part is never added.
@@ -3151,13 +3197,29 @@
                 try { //Verify that the DataHandler can be loaded.
                     Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
                     try {
- final MimeMultipart multipart = new MimeMultipart();
- final MimeBodyPart body = new MimeBodyPart();
- body.setDisposition(Part.INLINE);
+ MimeMultipart multipart = new MimeMultipart();
+ MimeBodyPart[] ambp = new MimeBodyPart[atn.length];
+ final MimeBodyPart body;
+ final String bodyContentType;
+ synchronized (this) {
+ bodyContentType = contentTypeOf(getFormatter());
+ body = createBodyPart();
+ for (int i = 0; i < atn.length; ++i) {
+ ambp[i] = createBodyPart(i);
+ ambp[i].setFileName(atn[i]);
+ //Convert names to mime type.
+ atn[i] = getContentType(atn[i]);
+ }
+ }
+
                         body.setDescription(verify);
- setAcceptLang(body);
- setContent(body, "", "text/plain");
+ setContent(body, "", bodyContentType);
                         multipart.addBodyPart(body);
+ for (int i = 0; i < ambp.length; ++i) {
+ ambp[i].setDescription(verify);
+ setContent(ambp[i], "", atn[i]);
+ }
+
                         abort.setContent(multipart);
                         abort.saveChanges();
                         abort.writeTo(new ByteArrayOutputStream(MIN_HEADER_SIZE));
@@ -3220,6 +3282,8 @@
      * @param host the host or null.
      * @return the address.
      * @throws IOException if the host name is not valid.
+ * @throws SecurityException if security manager is present and doesn't
+ * allow access to check connect permission.
      * @since JavaMail 1.5.0
      */
     private static InetAddress verifyHost(String host) throws IOException {

diff -r 9031f1e5787a -r 9b7804254f97 mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Fri Jun 24 17:13:19 2016 -0700
+++ b/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java Fri Jun 24 17:15:25 2016 -0700
@@ -2482,6 +2482,53 @@
     }
 
     @Test
+ public void testContentTypeOfFormatter() {
+ MailHandler instance = new MailHandler(createInitProperties(""));
+ InternalErrorManager em = new InternalErrorManager();
+ instance.setErrorManager(em);
+
+ assertEquals("text/plain", instance.contentTypeOf(new SimpleFormatter()));
+ assertEquals("text/plain", instance.contentTypeOf(new SimpleFormatter(){}));
+
+ assertEquals("application/xml", instance.contentTypeOf(new XMLFormatter()));
+ assertEquals("application/xml", instance.contentTypeOf(new XMLFormatter(){}));
+
+ /**
+ * None of the Formatter methods that can generate content should be
+ * invoked during a verify as that could lead to poor startup times.
+ */
+ class UnsupportedHTML extends Formatter {
+
+ @Override
+ public String getHead(Handler h) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String format(LogRecord record) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getTail(Handler h) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ throw new UnsupportedOperationException();
+ }
+ }
+ assertEquals("text/html", instance.contentTypeOf(new UnsupportedHTML()));
+ assertEquals("text/html", instance.contentTypeOf(new UnsupportedHTML(){}));
+
+ instance.close();
+ for (Exception exception : em.exceptions) {
+ fail(exception.toString());
+ }
+ }
+
+ @Test
     public void testGuessContentTypeReadlimit() throws Exception {
         class LastMarkInputStream extends ByteArrayInputStream {
 
@@ -5627,7 +5674,7 @@
             Properties props = createInitProperties(p);
             props.put(p.concat(".subject"), p.concat(" test"));
             props.put(p.concat(".errorManager"), InternalErrorManager.class.getName());
-
+ props.put(p.concat(".formatter"), XMLFormatter.class.getName());
             read(manager, props);
 
             props = createInitProperties("");
@@ -5656,7 +5703,7 @@
             props.put("subject", "test");
             props.put("mail.from", "badAddress");
             props.put("verify", "local");
-
+
             instance = new MailHandler(props);
             try {
                 InternalErrorManager em = internalErrorManagerFrom(instance);