commits@javamail.java.net

[javamail~mercurial:694] Create a separate maven artifact for the mail handler to make it easier t

From: <shannon_at_java.net>
Date: Mon, 16 Mar 2015 20:45:25 +0000

Project: javamail
Repository: mercurial
Revision: 694
Author: shannon
Date: 2015-03-13 23:25:37 UTC
Link:

Log Message:
------------
Add support for testing APPEND.
Modify MailHandler to support Google App Engine - bug 6718
LogManagerProperties non-string key and non-string value support.
Fix more Netbeans warnings.

(From Jason)
Create a separate maven artifact for the mail handler to make it easier to use
on Google App Engine - part of bug 6718


Revisions:
----------
692
693
694


Modified Paths:
---------------
mail/src/test/java/com/sun/mail/imap/IMAPHandler.java
doc/release/CHANGES.txt
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/LogManagerPropertiesTest.java
mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java
parent-distrib/pom.xml


Added Paths:
------------
mailhandler/pom.xml


Diffs:
------
diff -r 4a32d8901f79 -r 8020318c5561 mail/src/test/java/com/sun/mail/imap/IMAPHandler.java
--- a/mail/src/test/java/com/sun/mail/imap/IMAPHandler.java Wed Feb 18 10:58:43 2015 -0800
+++ b/mail/src/test/java/com/sun/mail/imap/IMAPHandler.java Fri Mar 06 16:15:06 2015 -0800
@@ -215,6 +215,8 @@
             idle();
         } else if (commandName.equals("FETCH")) {
             fetch();
+ } else if (commandName.equals("APPEND")) {
+ append(currentLine);
         } else if (commandName.equals("CLOSE")) {
             close();
         } else if (commandName.equals("LOGOUT")) {
@@ -305,6 +307,27 @@
     }
 
     /**
+ * APPEND command.
+ *
+ * @throws IOException unable to read/write to socket
+ */
+ public void append(String line) throws IOException {
+ int left = line.lastIndexOf('{');
+ int right = line.indexOf('}', left);
+ int bytes = Integer.parseInt(line.substring(left + 1, right));
+ cont("waiting for message");
+ collectMessage(bytes);
+ ok(); // XXX
+ }
+
+ protected void collectMessage(int bytes) throws IOException {
+ // should be bytes, but simpler to assume chars == bytes with ASCII
+ char[] data = new char[bytes];
+ reader.read(data); // read the data and throw it away
+ reader.readLine(); // data followed by a newline
+ }
+
+ /**
      * CLOSE command.
      *
      * @throws IOException unable to read/write to socket


diff -r 8020318c5561 -r 7db87dbedebf doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Mar 06 16:15:06 2015 -0800
+++ b/doc/release/CHANGES.txt Fri Mar 06 16:53:50 2015 -0800
@@ -34,6 +34,7 @@
 K 6668 skip unusable Store and Transport classes
 K 6687 long parameter values should be split using RFC 2231
 K 6703 javax.mail.Authenticator thread safety
+K 6718 Modify MailHandler to support Google App Engine.
 
 
                   CHANGES IN THE 1.5.2 RELEASE

diff -r 8020318c5561 -r 7db87dbedebf mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java Fri Mar 06 16:15:06 2015 -0800
+++ b/mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java Fri Mar 06 16:53:50 2015 -0800
@@ -40,6 +40,7 @@
  */
 package com.sun.mail.util.logging;
 
+import static com.sun.mail.util.logging.LogManagerProperties.fromLogManager;
 import java.lang.reflect.UndeclaredThrowableException;
 import java.text.MessageFormat;
 import java.util.Comparator;
@@ -47,7 +48,6 @@
 import java.util.ResourceBundle;
 import java.util.logging.Formatter;
 import java.util.logging.Handler;
-import java.util.logging.LogManager;
 import java.util.logging.LogRecord;
 
 /**
@@ -459,10 +459,10 @@
      *
      * @param p the class name prefix.
      * @return the format string.
+ * @throws NullPointerException if the given argument is null.
      */
     private String initFormat(final String p) {
- final LogManager m = LogManagerProperties.getLogManager();
- String v = m.getProperty(p.concat(".format"));
+ String v = fromLogManager(p.concat(".format"));
         if (v == null || v.length() == 0) {
             v = "{0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer} more}\n";
         }
@@ -475,12 +475,12 @@
      *
      * @param p the class name prefix.
      * @return the formatter.
+ * @throws NullPointerException if the given argument is null.
      * @throws UndeclaredThrowableException if the formatter can not be created.
      */
     private Formatter initFormatter(final String p) {
- final LogManager m = LogManagerProperties.getLogManager();
         Formatter f;
- String v = m.getProperty(p.concat(".formatter"));
+ String v = fromLogManager(p.concat(".formatter"));
         if (v != null && v.length() != 0) {
             if (!"null".equalsIgnoreCase(v)) {
                 try {
@@ -508,15 +508,15 @@
      * @return the comparator or null.
      * @throws IllegalArgumentException if it was specified that the comparator
      * should be reversed but no initial comparator was specified.
+ * @throws NullPointerException if the given argument is null.
      * @throws UndeclaredThrowableException if the comparator can not be
      * created.
      */
     @SuppressWarnings("unchecked")
     private Comparator<? super LogRecord> initComparator(final String p) {
- final LogManager m = LogManagerProperties.getLogManager();
         Comparator<? super LogRecord> c;
- final String name = m.getProperty(p.concat(".comparator"));
- final String reverse = m.getProperty(p.concat(".comparator.reverse"));
+ final String name = fromLogManager(p.concat(".comparator"));
+ final String reverse = fromLogManager(p.concat(".comparator.reverse"));
         try {
             if (name != null && name.length() != 0) {
                 if (!"null".equalsIgnoreCase(name)) {

diff -r 8020318c5561 -r 7db87dbedebf mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java Fri Mar 06 16:15:06 2015 -0800
+++ b/mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java Fri Mar 06 16:53:50 2015 -0800
@@ -40,11 +40,7 @@
  */
 package com.sun.mail.util.logging;
 
-import java.util.Collections;
-import java.util.Date;
-import java.util.Locale;
-import java.util.ResourceBundle;
-import java.util.logging.LogManager;
+import java.util.*;
 import java.util.logging.LogRecord;
 
 /**
@@ -478,8 +474,7 @@
      * @throws NullPointerException if the given class name is null.
      */
     private String initFormat(final String p) {
- LogManager m = LogManagerProperties.getLogManager();
- String v = m.getProperty(p.concat(".format"));
+ String v = LogManagerProperties.fromLogManager(p.concat(".format"));
         if (isNullOrSpaces(v)) {
             v = "%7$#.160s%n"; //160 chars split between message and thrown.
         }

diff -r 8020318c5561 -r 7db87dbedebf mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java
--- a/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java Fri Mar 06 16:15:06 2015 -0800
+++ b/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java Fri Mar 06 16:53:50 2015 -0800
@@ -1,8 +1,8 @@
 /*
  * 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-2014 Jason Mehrens. All rights reserved.
+ * Copyright (c) 2009-2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009-2015 Jason Mehrens. 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,7 +40,7 @@
  */
 package com.sun.mail.util.logging;
 
-import java.io.ObjectStreamException;
+import java.io.*;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -50,6 +50,7 @@
 import java.util.logging.*;
 import java.util.logging.Formatter;
 import javax.mail.Authenticator;
+import javax.mail.Service;
 
 /**
  * An adapter class to allow the Mail API to access the LogManager properties.
@@ -80,25 +81,209 @@
      */
     private static final long serialVersionUID = -2239983349056806252L;
     /**
- * Caches the LogManager so we only read the config once.
- */
- private static final LogManager LOG_MANAGER = LogManager.getLogManager();
- /**
      * Caches the read only reflection class names string array. Declared
      * volatile for safe publishing only. The VO_VOLATILE_REFERENCE_TO_ARRAY
      * warning is a false positive.
      */
     @SuppressWarnings("VolatileArrayField")
     private static volatile String[] REFLECT_NAMES;
+ /**
+ * Caches the LogManager or Properties so we only read the configuration
+ * once.
+ */
+ private static final Object LOG_MANAGER = loadLogManager();
 
     /**
- * Gets the LogManger for the running JVM.
+ * Installs the LogManager or Properties.
+ *
+ * @since JavaMail 1.5.3
+ */
+ private static Object loadLogManager() {
+ Object m;
+ try {
+ m = LogManager.getLogManager();
+ } catch (final LinkageError restricted) {
+ m = readConfiguration();
+ } catch (final RuntimeException unexpected) {
+ m = readConfiguration();
+ }
+ return m;
+ }
+
+ /**
+ * Create a properties object from the default logging configuration file.
+ * Since the LogManager is not available in restricted environments, only
+ * the default configuration is applicable.
+ *
+ * @return a properties object loaded with the default configuration.
+ * @since JavaMail 1.5.3
+ */
+ private static Properties readConfiguration() {
+ /**
+ * Load the properties file so the default settings are available when
+ * user code creates a logging object. The class loader for the
+ * restricted LogManager can't access these classes to attach them to a
+ * logger or handler on startup. Creating logging objects at this point
+ * is both useless and risky.
+ */
+ final Properties props = new Properties();
+ try {
+ String n = System.getProperty("java.util.logging.config.file");
+ if (n != null) {
+ final File f = new File(n).getCanonicalFile();
+ final InputStream in = new FileInputStream(f);
+ try {
+ props.load(in);
+ } finally {
+ in.close();
+ }
+ }
+ } catch (RuntimeException permissions) {
+ } catch (Exception ioe) {
+ } catch (LinkageError unexpected) {
+ }
+ return props;
+ }
+
+ /**
+ * Gets LogManger property for the running JVM. If the LogManager doesn't
+ * exist then the default LogManger properties are used.
      *
      * @return the LogManager.
- * @since JavaMail 1.4.5
+ * @throws NullPointerException if the given name is null.
+ * @since JavaMail 1.5.3
      */
- static LogManager getLogManager() {
- return LOG_MANAGER;
+ static String fromLogManager(final String name) {
+ if (name == null) {
+ throw new NullPointerException();
+ }
+
+ final Object m = LOG_MANAGER;
+ try {
+ if (m instanceof Properties) {
+ return ((Properties) m).getProperty(name);
+ }
+ } catch (final RuntimeException unexpected) {
+ }
+
+ if (m != null) {
+ try {
+ if (m instanceof LogManager) {
+ return ((LogManager) m).getProperty(name);
+ }
+ } catch (final LinkageError restricted) {
+ } catch (final RuntimeException unexpected) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check that the current context is trusted to modify the logging
+ * configuration. This requires LoggingPermission("control").
+ *
+ * @since JavaMail 1.5.3
+ */
+ static void checkLogManagerAccess() {
+ boolean checked = false;
+ final Object m = LOG_MANAGER;
+ if (m != null) {
+ try {
+ if (m instanceof LogManager) {
+ checked = true;
+ ((LogManager) m).checkAccess();
+ }
+ } catch (final SecurityException notAllowed) {
+ if (checked) {
+ throw notAllowed;
+ }
+ } catch (final LinkageError restricted) {
+ } catch (final RuntimeException unexpected) {
+ }
+ }
+
+ if (!checked) {
+ checkLoggingAccess();
+ }
+ }
+
+ /**
+ * Check that the current context is trusted to modify the logging
+ * configuration when the LogManager is not present. This requires
+ * LoggingPermission("control").
+ *
+ * @since JavaMail 1.5.3
+ */
+ private static void checkLoggingAccess() {
+ /**
+ * Some environments selectively enforce logging permissions by allowing
+ * access to loggers but not allowing access to handlers. This is an
+ * indirect way of checking for LoggingPermission when the LogManager is
+ * not present. The root logger will lazy create handlers so the global
+ * logger is used instead as it is a known named logger with well
+ * defined behavior. If the global logger is a subclass then fallback to
+ * using the SecurityManager.
+ */
+ boolean checked = false;
+ final Logger global = Logger.getLogger("global");
+ try {
+ if (Logger.class == global.getClass()) {
+ global.removeHandler((Handler) null);
+ checked = true;
+ }
+ } catch (final NullPointerException unexpected) {
+ }
+
+ if (!checked) {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new LoggingPermission("control", null));
+ }
+ }
+ }
+
+ /**
+ * Determines if access to the {_at_code java.util.logging.LogManager} class is
+ * restricted by the class loader.
+ *
+ * @return true if a LogManager is present.
+ * @since JavaMail 1.5.3
+ */
+ static boolean hasLogManager() {
+ final Object m = LOG_MANAGER;
+ return m != null && !(m instanceof Properties);
+ }
+
+ /**
+ * Gets the local host name from the given service.
+ *
+ * @param s the service to examine.
+ * @return the local host name or null.
+ * @throws IllegalAccessException if the method is inaccessible.
+ * @throws InvocationTargetException if the method throws an exception.
+ * @throws LinkageError if the linkage fails.
+ * @throws NullPointerException if the given service is null.
+ * @throws ExceptionInInitializerError if the static initializer fails.
+ * @throws Exception if there is a problem.
+ * @throws NoSuchMethodException if the given service does not have a method
+ * to get the local host name as a string.
+ * @throws SecurityException if unable to inspect properties of object.
+ * @since JavaMail 1.5.3
+ */
+ static String getLocalHost(final Service s) throws Exception {
+ try {
+ final Method m = s.getClass().getMethod("getLocalHost");
+ if (!Modifier.isStatic(m.getModifiers())
+ && m.getReturnType() == String.class) {
+ return (String) m.invoke(s);
+ } else {
+ throw new NoSuchMethodException(m.toString());
+ }
+ } catch (final ExceptionInInitializerError EIIE) {
+ throw wrapOrThrow(EIIE);
+ } catch (final InvocationTargetException ite) {
+ throw paramOrError(ite);
+ }
     }
 
     /**
@@ -212,7 +397,7 @@
      * @throws NullPointerException if the given comparator is null.
      * @since JavaMail 1.5.0
      */
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "ThrowableResultIgnored"})
     static <T> Comparator<T> reverseOrder(final Comparator<T> c) {
         if (c == null) {
             throw new NullPointerException();
@@ -238,7 +423,7 @@
         } catch (final IllegalAccessException ignore) {
         } catch (final RuntimeException ignore) {
         } catch (final InvocationTargetException ite) {
- paramOrError(ite); //Ignore invocation bugs.
+ paramOrError(ite); //Ignore invocation bugs (returned values).
         }
 
         if (reverse == null) {
@@ -305,9 +490,9 @@
     static boolean isStaticUtilityClass(String name) throws Exception {
         final Class<?> c = findClass(name);
         final Class<?> obj = Object.class;
- Method[] methods = c.getMethods();
+ Method[] methods;
         boolean util;
- if (c != obj && methods.length != 0) {
+ if (c != obj && (methods = c.getMethods()).length != 0) {
             util = true;
             for (Method m : methods) {
                 if (m.getDeclaringClass() != obj
@@ -337,8 +522,8 @@
      * @since JavaMail 1.5.2
      */
     static boolean isReflectionClass(String name) throws Exception {
- String[] names;
- if ((names = REFLECT_NAMES) == null) { //Benign data race.
+ String[] names = REFLECT_NAMES;
+ if (names == null) { //Benign data race.
             REFLECT_NAMES = names = reflectionClassNames();
         }
 
@@ -585,8 +770,8 @@
     }
 
     /**
- * Searches defaults, then searches the log manager by the prefix property,
- * and then by the key itself.
+ * Searches defaults, then searches the log manager if available or the
+ * system properties by the prefix property, and then by the key itself.
      *
      * @param key a non null key.
      * @return the value for that key.
@@ -595,13 +780,12 @@
     public synchronized String getProperty(final String key) {
         String value = defaults.getProperty(key);
         if (value == null) {
- final LogManager manager = getLogManager();
             if (key.length() > 0) {
- value = manager.getProperty(prefix + '.' + key);
+ value = fromLogManager(prefix + '.' + key);
             }
 
             if (value == null) {
- value = manager.getProperty(key);
+ value = fromLogManager(key);
             }
 
             /**
@@ -637,19 +821,30 @@
 
     /**
      * Required to work with PropUtil. Calls getProperty directly if the given
- * key is a string. Otherwise, performs a normal get operation.
+ * key is a string. Otherwise, performs a get operation on the defaults
+ * followed by the normal hash table get.
      *
      * @param key any key.
      * @return the value for the key or null.
      * @since JavaMail 1.4.5
      */
     @Override
- public Object get(final Object key) {
+ public synchronized Object get(final Object key) {
+ Object value;
         if (key instanceof String) {
- return this.getProperty((String) key);
+ value = getProperty((String) key);
         } else {
- return super.get(key);
+ value = null;
         }
+
+ //Search for non-string value.
+ if (value == null) {
+ value = defaults.get(key);
+ if (value == null && !defaults.containsKey(key)) {
+ value = super.get(key);
+ }
+ }
+ return value;
     }
 
     /**
@@ -662,9 +857,13 @@
      */
     @Override
     public synchronized Object put(final Object key, final Object value) {
- final Object def = preWrite(key);
- final Object man = super.put(key, value);
- return man == null ? def : man;
+ if (key instanceof String && value instanceof String) {
+ final Object def = preWrite(key);
+ final Object man = super.put(key, value);
+ return man == null ? def : man;
+ } else {
+ return super.put(key, value);
+ }
     }
 
     /**
@@ -688,12 +887,13 @@
      * @since JavaMail 1.4.5
      */
     @Override
- public boolean containsKey(final Object key) {
- if (key instanceof String) {
- return this.getProperty((String) key) != null;
- } else {
- return super.containsKey(key);
+ public synchronized boolean containsKey(final Object key) {
+ boolean found = key instanceof String
+ && getProperty((String) key) != null;
+ if (!found) {
+ found = defaults.containsKey(key) || super.containsKey(key);
         }
+ return found;
     }
 
     /**
@@ -767,13 +967,7 @@
      */
     private Object preWrite(final Object key) {
         assert Thread.holdsLock(this);
- Object value;
- if (key instanceof String && !super.containsKey(key)) {
- value = this.getProperty((String) key); //fetch and cache.
- } else {
- value = null;
- }
- return value;
+ return get(key);
     }
 
     /**

diff -r 8020318c5561 -r 7db87dbedebf mail/src/main/java/com/sun/mail/util/logging/MailHandler.java
--- a/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Fri Mar 06 16:15:06 2015 -0800
+++ b/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java Fri Mar 06 16:53:50 2015 -0800
@@ -41,9 +41,10 @@
 
 package com.sun.mail.util.logging;
 
-import com.sun.mail.smtp.SMTPTransport;
+import static com.sun.mail.util.logging.LogManagerProperties.fromLogManager;
 import java.io.*;
 import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
 import java.net.InetAddress;
 import java.net.URLConnection;
 import java.net.UnknownHostException;
@@ -458,6 +459,16 @@
      */
     private Filter pushFilter;
     /**
+ * Holds the entry and body filter for this handler.
+ * There is no way to un-seal the super handler.
+ */
+ private volatile Filter filter;
+ /**
+ * Holds the level for this handler.
+ * There is no way to un-seal the super handler.
+ */
+ private volatile Level logLevel = Level.ALL;
+ /**
      * Holds the filters for each attachment. Filters are optional for
      * each attachment. This is declared volatile because this is treated as
      * copy-on-write. The VO_VOLATILE_REFERENCE_TO_ARRAY warning is a false
@@ -466,6 +477,16 @@
     @SuppressWarnings("VolatileArrayField")
     private volatile Filter[] attachmentFilters;
     /**
+ * Holds the encoding name for this handler.
+ * There is no way to un-seal the super handler.
+ */
+ private String encoding;
+ /**
+ * Holds the entry and body filter for this handler.
+ * There is no way to un-seal the super handler.
+ */
+ private Formatter formatter;
+ /**
      * Holds the formatters that create the content for each attachment.
      * Each formatter maps directly to an attachment. The formatters
      * getHead, format, and getTail methods are only called if one or more
@@ -484,6 +505,11 @@
      * for each attachment.
      */
     private FileTypeMap contentTypes;
+ /**
+ * Holds the error manager for this handler.
+ * There is no way to un-seal the super handler.
+ */
+ private volatile ErrorManager errorManager = defaultErrorManager();
 
     /**
      * Creates a <tt>MailHandler</tt> that is configured by the
@@ -494,6 +520,7 @@
     public MailHandler() {
         init((Properties) null);
         sealed = true;
+ checkAccess();
     }
 
     /**
@@ -752,7 +779,7 @@
             try {
                 msg = writeLogRecords(ErrorManager.CLOSE_FAILURE);
             } finally { //Change level after formatting.
- super.setLevel(Level.OFF);
+ this.logLevel = Level.OFF;
                 /**
                  * The sign bit of the capacity is set to ensure that
                  * records that have passed isLoggable, but have yet to be
@@ -785,18 +812,181 @@
      * the caller does not have <tt>LoggingPermission("control")</tt>.
      */
     @Override
- public synchronized void setLevel(final Level newLevel) {
- if (this.capacity > 0) {
- super.setLevel(newLevel);
- } else { //Don't allow a closed handler to be opened (half way).
- if (newLevel == null) {
- throw new NullPointerException();
+ public void setLevel(final Level newLevel) {
+ if (newLevel == null) {
+ throw new NullPointerException();
+ }
+ checkAccess();
+
+ //Don't allow a closed handler to be opened (half way).
+ synchronized (this) { //Wait for writeLogRecords.
+ if (this.capacity > 0) {
+ this.logLevel = newLevel;
             }
- checkAccess();
         }
     }
 
     /**
+ * Get the log level specifying which messages will be logged by this
+ * <tt>Handler</tt>. Message levels lower than this level will be
+ * discarded.
+ *
+ * @return the level of messages being logged.
+ */
+ @Override
+ public Level getLevel() {
+ return logLevel; //Volatile access.
+ }
+
+ /**
+ * Retrieves the ErrorManager for this Handler.
+ *
+ * @return the ErrorManager for this Handler
+ * @throws SecurityException if a security manager exists and if the caller
+ * does not have <tt>LoggingPermission("control")</tt>.
+ */
+ @Override
+ public ErrorManager getErrorManager() {
+ checkAccess();
+ return this.errorManager; //Volatile access.
+ }
+
+ /**
+ * Define an ErrorManager for this Handler.
+ * <p>
+ * The ErrorManager's "error" method will be invoked if any errors occur
+ * while using this Handler.
+ *
+ * @param em the new ErrorManager
+ * @throws SecurityException if a security manager exists and if the
+ * caller does not have <tt>LoggingPermission("control")</tt>.
+ * @throws NullPointerException if the given error manager is null.
+ */
+ @Override
+ public void setErrorManager(final ErrorManager em) {
+ checkAccess();
+ if (em == null) {
+ throw new NullPointerException();
+ }
+ synchronized (this) { //Wait for writeLogRecords.
+ this.errorManager = em;
+ }
+ }
+
+ /**
+ * Get the current <tt>Filter</tt> for this <tt>Handler</tt>.
+ *
+ * @return a <tt>Filter</tt> object (may be null)
+ */
+ @Override
+ public Filter getFilter() {
+ return this.filter; //Volatile access.
+ }
+
+ /**
+ * Set a <tt>Filter</tt> to control output on this <tt>Handler</tt>.
+ * <P>
+ * For each call of <tt>publish</tt> the <tt>Handler</tt> will call this
+ * <tt>Filter</tt> (if it is non-null) to check if the <tt>LogRecord</tt>
+ * should be published or discarded.
+ *
+ * @param newFilter a <tt>Filter</tt> object (may be null)
+ * @throws SecurityException if a security manager exists and if the caller
+ * does not have <tt>LoggingPermission("control")</tt>.
+ */
+ @Override
+ public void setFilter(final Filter newFilter) {
+ checkAccess();
+ synchronized (this) { //Wait for writeLogRecords.
+ this.filter = newFilter;
+ }
+ }
+
+ /**
+ * Return the character encoding for this <tt>Handler</tt>.
+ *
+ * @return The encoding name. May be null, which indicates the default
+ * encoding should be used.
+ */
+ @Override
+ public synchronized String getEncoding() {
+ return this.encoding;
+ }
+
+ /**
+ * Set the character encoding used by this <tt>Handler</tt>.
+ * <p>
+ * The encoding should be set before any <tt>LogRecords</tt> are written
+ * to the <tt>Handler</tt>.
+ *
+ * @param encoding The name of a supported character encoding. May be
+ * null, to indicate the default platform encoding.
+ * @throws SecurityException if a security manager exists and if the caller
+ * does not have <tt>LoggingPermission("control")</tt>.
+ * @throws UnsupportedEncodingException if the named encoding is not
+ * supported.
+ */
+ @Override
+ public void setEncoding(String encoding) throws UnsupportedEncodingException {
+ checkAccess();
+ setEncoding0(encoding);
+ }
+
+ /**
+ * Set the character encoding used by this handler. This method does not
+ * check permissions of the caller.
+ *
+ * @param e any encoding name or null for the default.
+ * @throws UnsupportedEncodingException if the given encoding is not supported.
+ */
+ private void setEncoding0(String e) throws UnsupportedEncodingException {
+ if (e != null) {
+ try {
+ if (!java.nio.charset.Charset.isSupported(e)) {
+ throw new UnsupportedEncodingException(e);
+ }
+ } catch (java.nio.charset.IllegalCharsetNameException icne) {
+ throw new UnsupportedEncodingException(e);
+ }
+ }
+
+ synchronized (this) { //Wait for writeLogRecords.
+ this.encoding = e;
+ }
+ }
+
+ /**
+ * Return the <tt>Formatter</tt> for this <tt>Handler</tt>.
+ *
+ * @return the <tt>Formatter</tt> (may be null).
+ */
+ @Override
+ public synchronized Formatter getFormatter() {
+ return this.formatter;
+ }
+
+ /**
+ * Set a <tt>Formatter</tt>. This <tt>Formatter</tt> will be used to format
+ * <tt>LogRecords</tt> for this <tt>Handler</tt>.
+ * <p>
+ * Some <tt>Handlers</tt> may not use <tt>Formatters</tt>, in which case the
+ * <tt>Formatter</tt> will be remembered, but not used.
+ * <p>
+ * @param newFormatter the <tt>Formatter</tt> to use (may not be null)
+ * @throws SecurityException if a security manager exists and if the caller
+ * does not have <tt>LoggingPermission("control")</tt>.
+ * @throws NullPointerException if the given formatter is null.
+ */
+ @Override
+ public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
+ checkAccess();
+ if (newFormatter == null) {
+ throw new NullPointerException();
+ }
+ this.formatter = newFormatter;
+ }
+
+ /**
      * Gets the push level. The default is <tt>Level.OFF</tt> meaning that
      * this <tt>Handler</tt> will only push when the internal buffer is full.
      * @return the push level.
@@ -1270,10 +1460,10 @@
     protected void reportError(String msg, Exception ex, int code) {
         try {
             if (msg != null) {
- super.reportError(Level.SEVERE.getName()
+ errorManager.error(Level.SEVERE.getName()
                         .concat(": ").concat(msg), ex, code);
             } else {
- super.reportError(null, ex, code);
+ errorManager.error(null, ex, code);
             }
         } catch (final RuntimeException GLASSFISH_21258) {
             reportLinkageError(GLASSFISH_21258, code);
@@ -1285,9 +1475,9 @@
     /**
      * Calls log manager checkAccess if this is sealed.
      */
- final void checkAccess() {
+ private void checkAccess() {
         if (sealed) {
- LogManagerProperties.getLogManager().checkAccess();
+ LogManagerProperties.checkLogManagerAccess();
         }
     }
 
@@ -1307,9 +1497,9 @@
                 head = head.substring(0, MAX_CHARS);
             }
             try {
- final String encoding = getEncodingName();
+ final String charset = getEncodingName();
                 final ByteArrayInputStream in
- = new ByteArrayInputStream(head.getBytes(encoding));
+ = new ByteArrayInputStream(head.getBytes(charset));
 
                 assert in.markSupported() : in.getClass().getName();
                 return URLConnection.guessContentTypeFromStream(in);
@@ -1360,8 +1550,8 @@
      * @since JavaMail 1.4.5
      */
     private void reportError(Message msg, Exception ex, int code) {
- try { //Use super call so we do not prefix raw email.
- super.reportError(toRawString(msg), ex, 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) {
@@ -1455,11 +1645,11 @@
      * @since JavaMail 1.4.5
      */
     private String getEncodingName() {
- String encoding = getEncoding();
- if (encoding == null) {
- encoding = MimeUtility.getDefaultJavaCharset();
+ String charset = getEncoding();
+ if (charset == null) {
+ charset = MimeUtility.getDefaultJavaCharset();
         }
- return encoding;
+ return charset;
     }
 
     /**
@@ -1470,18 +1660,18 @@
      * @throws MessagingException if there is a problem.
      */
     private void setContent(MimeBodyPart part, CharSequence buf, String type) throws MessagingException {
- final String encoding = getEncodingName();
+ final String charset = getEncodingName();
         if (type != null && !"text/plain".equalsIgnoreCase(type)) {
- type = contentWithEncoding(type, encoding);
+ type = contentWithEncoding(type, charset);
             try {
                 DataSource source = new ByteArrayDataSource(buf.toString(), type);
                 part.setDataHandler(new DataHandler(source));
             } catch (final IOException IOE) {
                 reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE);
- part.setText(buf.toString(), encoding);
+ part.setText(buf.toString(), charset);
             }
         } else {
- part.setText(buf.toString(), MimeUtility.mimeCharset(encoding));
+ part.setText(buf.toString(), MimeUtility.mimeCharset(charset));
         }
     }
 
@@ -1512,9 +1702,12 @@
      * greater than the capacity.
      * I.E. 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>.
      * @throws IllegalStateException if called from inside a push.
      */
     private synchronized void setCapacity0(final int newCapacity) {
+ checkAccess();
         if (newCapacity <= 0) {
             throw new IllegalArgumentException("Capacity must be greater than zero.");
         }
@@ -1602,7 +1795,7 @@
 
             //Array elements default to null so skip filling if body filter
             //is null. If not null then only assign to expanded elements.
- final Filter body = super.getFilter();
+ final Filter body = this.filter;
             if (body != null) {
                 for (int i = current; i < expect; ++i) {
                     this.attachmentFilters[i] = body;
@@ -1682,8 +1875,8 @@
      * caller does not have <tt>LoggingPermission("control")</tt>.
      */
     private synchronized void init(final Properties props) {
+ assert this.errorManager != null;
         final String p = getClass().getName();
- final LogManager manager = LogManagerProperties.getLogManager();
         this.mailProps = new Properties(); //See method param comments.
         final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER);
         try {
@@ -1693,26 +1886,26 @@
         }
 
         //Assign any custom error manager first so it can detect all failures.
- initErrorManager(manager, p);
-
- initLevel(manager, p);
- initFilter(manager, p);
- initCapacity(manager, p);
- initAuthenticator(manager, p);
-
- initEncoding(manager, p);
- initFormatter(manager, p);
- initComparator(manager, p);
- initPushLevel(manager, p);
- initPushFilter(manager, p);
-
- initSubject(manager, p);
-
- initAttachmentFormaters(manager, p);
- initAttachmentFilters(manager, p);
- initAttachmentNames(manager, p);
-
- if (props == null && manager.getProperty(p.concat(".verify")) != null) {
+ initErrorManager(p);
+
+ initLevel(p);
+ initFilter(p);
+ initCapacity(p);
+ initAuthenticator(p);
+
+ initEncoding(p);
+ initFormatter(p);
+ initComparator(p);
+ initPushLevel(p);
+ initPushFilter(p);
+
+ initSubject(p);
+
+ initAttachmentFormaters(p);
+ initAttachmentFilters(p);
+ initAttachmentNames(p);
+
+ if (props == null && fromLogManager(p.concat(".verify")) != null) {
             verifySettings(initSession());
         }
         intern(); //Show verify warnings first.
@@ -1732,22 +1925,22 @@
             Object result;
             final Map<Object, Object> seen = new HashMap<Object, Object>();
             try {
- intern(seen, super.getErrorManager());
+ intern(seen, this.errorManager);
             } catch (final SecurityException se) {
                 reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE);
             }
 
             try {
- canidate = super.getFilter();
+ canidate = this.filter;
                 result = intern(seen, canidate);
                 if (result != canidate && result instanceof Filter) {
- super.setFilter((Filter) result);
+ this.filter = (Filter) result;
                 }
 
- canidate = super.getFormatter();
+ canidate = this.formatter;
                 result = intern(seen, canidate);
                 if (result != canidate && result instanceof Formatter) {
- super.setFormatter((Formatter) result);
+ this.formatter = (Formatter) result;
                 }
             } catch (final SecurityException se) {
                 reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE);
@@ -1786,6 +1979,9 @@
             }
         } catch (final Exception skip) {
             reportError(skip.getMessage(), skip, ErrorManager.OPEN_FAILURE);
+ } catch (final LinkageError skip) {
+ reportError(skip.getMessage(), new InvocationTargetException(skip),
+ ErrorManager.OPEN_FAILURE);
         }
     }
 
@@ -1891,13 +2087,13 @@
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initAttachmentFilters(LogManager manager, String p) {
+ private void initAttachmentFilters(final String p) {
         assert Thread.holdsLock(this);
         assert this.attachmentFormatters != null;
- final String list = manager.getProperty(p.concat(".attachment.filters"));
+ final String list = fromLogManager(p.concat(".attachment.filters"));
         if (!isEmpty(list)) {
             final String[] names = list.split(",");
             Filter[] a = new Filter[names.length];
@@ -1929,12 +2125,12 @@
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initAttachmentFormaters(LogManager manager, String p) {
+ private void initAttachmentFormaters(final String p) {
         assert Thread.holdsLock(this);
- final String list = manager.getProperty(p.concat(".attachment.formatters"));
+ final String list = fromLogManager(p.concat(".attachment.formatters"));
         if (!isEmpty(list)) {
             final Formatter[] a;
             final String[] names = list.split(",");
@@ -1977,14 +2173,14 @@
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initAttachmentNames(LogManager manager, String p) {
+ private void initAttachmentNames(final String p) {
         assert Thread.holdsLock(this);
         assert this.attachmentFormatters != null;
 
- final String list = manager.getProperty(p.concat(".attachment.names"));
+ final String list = fromLogManager(p.concat(".attachment.names"));
         if (!isEmpty(list)) {
             final String[] names = list.split(",");
             final Formatter[] a = new Formatter[names.length];
@@ -2025,12 +2221,12 @@
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initAuthenticator(LogManager manager, String p) {
+ private void initAuthenticator(final String p) {
         assert Thread.holdsLock(this);
- String name = manager.getProperty(p.concat(".authenticator"));
+ String name = fromLogManager(p.concat(".authenticator"));
         if (hasValue(name)) {
             try {
                 this.auth = LogManagerProperties.newAuthenticator(name);
@@ -2050,27 +2246,23 @@
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initLevel(LogManager manager, String p) {
+ private void initLevel(final String p) {
         assert Thread.holdsLock(this);
         try {
- final String val = manager.getProperty(p.concat(".level"));
+ final String val = fromLogManager(p.concat(".level"));
             if (val != null) {
- super.setLevel(Level.parse(val));
+ logLevel = Level.parse(val);
             } else {
- super.setLevel(Level.WARNING);
+ logLevel = Level.WARNING;
             }
         } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
+ throw SE; //Avoid catch all.
         } catch (final RuntimeException RE) {
             reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE);
- try {
- super.setLevel(Level.WARNING);
- } catch (final RuntimeException fail) {
- reportError(fail.getMessage(), fail, ErrorManager.OPEN_FAILURE);
- }
+ logLevel = Level.WARNING;
         }
     }
 
@@ -2078,15 +2270,15 @@
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initFilter(LogManager manager, String p) {
+ private void initFilter(final String p) {
         assert Thread.holdsLock(this);
         try {
- String name = manager.getProperty(p.concat(".filter"));
+ String name = fromLogManager(p.concat(".filter"));
             if (hasValue(name)) {
- super.setFilter(LogManagerProperties.newFilter(name));
+ filter = LogManagerProperties.newFilter(name);
             }
         } catch (final SecurityException SE) {
             throw SE; //Avoid catch all.
@@ -2099,19 +2291,21 @@
      * 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 either argument is null.
+ * @throws NullPointerException if argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initCapacity(LogManager manager, String p) {
+ private void initCapacity(final String p) {
         assert Thread.holdsLock(this);
         final int DEFAULT_CAPACITY = 1000;
         try {
- final String value = manager.getProperty(p.concat(".capacity"));
+ final String value = fromLogManager(p.concat(".capacity"));
             if (value != null) {
                 this.setCapacity0(Integer.parseInt(value));
             } else {
                 this.setCapacity0(DEFAULT_CAPACITY);
             }
+ } catch (final SecurityException SE) {
+ throw SE; //Avoid catch all.
         } catch (final RuntimeException RE) {
             reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE);
         }
@@ -2127,13 +2321,16 @@
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initEncoding(LogManager manager, String p) {
+ private void initEncoding(final String p) {
         assert Thread.holdsLock(this);
         try {
- super.setEncoding(manager.getProperty(p.concat(".encoding")));
+ String e = fromLogManager(p.concat(".encoding"));
+ if (e != null) {
+ setEncoding0(e);
+ }
         } catch (final SecurityException SE) {
             throw SE; //Avoid catch all.
         } catch (final UnsupportedEncodingException UEE) {
@@ -2144,58 +2341,43 @@
     }
 
     /**
- * 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 either argument is null.
- * @throws SecurityException if not allowed.
+ * Used to get or create the default ErrorManager used before init.
+ * @return the super error manager or a new ErrorManager.
+ * @since JavaMail 1.5.3
      */
- private void initErrorManager(LogManager manager, String p) {
- assert Thread.holdsLock(this);
- String name = manager.getProperty(p.concat(".errorManager"));
- if (name != null) {
- try {
- ErrorManager em = LogManagerProperties.newErrorManager(name);
- super.setErrorManager(em);
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (final Exception E) {
- reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
- }
+ private ErrorManager defaultErrorManager() {
+ ErrorManager em;
+ try { //Try to share the super error manager.
+ em = super.getErrorManager();
+ } catch (final RuntimeException ignore) {
+ em = null;
         }
+
+ //Don't assume that the super call is not null.
+ if (em == null) {
+ em = new ErrorManager();
+ }
+ return em;
     }
 
     /**
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initFormatter(LogManager manager, String p) {
+ private void initErrorManager(final String p) {
         assert Thread.holdsLock(this);
- String name = manager.getProperty(p.concat(".formatter"));
- if (hasValue(name)) {
- try {
- final Formatter formatter = LogManagerProperties.newFormatter(name);
- assert formatter != null;
- if (formatter instanceof TailNameFormatter == false) {
- super.setFormatter(formatter);
- } else {
- super.setFormatter(new SimpleFormatter());
- }
- } catch (final SecurityException SE) {
- throw SE; //Avoid catch all.
- } catch (final Exception E) {
- reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
- try {
- super.setFormatter(new SimpleFormatter());
- } catch (final RuntimeException fail) {
- reportError(fail.getMessage(), fail, ErrorManager.OPEN_FAILURE);
- }
+ try {
+ String name = fromLogManager(p.concat(".errorManager"));
+ if (name != null) {
+ this.errorManager = LogManagerProperties.newErrorManager(name);
             }
- } else {
- super.setFormatter(new SimpleFormatter());
+ } catch (final SecurityException SE) {
+ throw SE; //Avoid catch all.
+ } catch (final Exception E) {
+ reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
         }
     }
 
@@ -2203,14 +2385,45 @@
      * 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 either argument is null.
+ * @throws NullPointerException if the given argument is null.
      * @throws SecurityException if not allowed.
      */
- private void initComparator(LogManager manager, String p) {
+ private void initFormatter(final String p) {
         assert Thread.holdsLock(this);
- String name = manager.getProperty(p.concat(".comparator"));
- String reverse = manager.getProperty(p.concat(".comparator.reverse"));
         try {
+ String name = fromLogManager(p.concat(".formatter"));
+ if (hasValue(name)) {
+ final Formatter f
+ = LogManagerProperties.newFormatter(name);
+ assert f != null;
+ if (f instanceof TailNameFormatter == false) {
+ formatter = f;
+ } else {
+ formatter = new SimpleFormatter();
+ }
+ } else {
+ formatter = new SimpleFormatter();
+ }
+ } catch (final SecurityException SE) {
+ throw SE; //Avoid catch all.
+ } catch (final Exception E) {
+ reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE);
+ formatter = new SimpleFormatter();
+ }
+ }
+
+ /**
+ * 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 a
[truncated due to length]