changeset 6560:37a4b4892e8e

7159567: inconsistent configuration of MemoryHandler Reviewed-by: mchung, alanb
author jgish
date Thu, 25 Oct 2012 15:04:09 -0700
parents 909676adaefd
children 27677928a937
files src/share/classes/java/util/logging/ConsoleHandler.java src/share/classes/java/util/logging/FileHandler.java src/share/classes/java/util/logging/MemoryHandler.java src/share/classes/java/util/logging/SocketHandler.java src/share/classes/java/util/logging/StreamHandler.java test/java/util/logging/MemoryHandlerTest.java test/java/util/logging/MemoryHandlerTest.props
diffstat 7 files changed, 321 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/logging/ConsoleHandler.java	Wed Oct 24 21:20:40 2012 +0100
+++ b/src/share/classes/java/util/logging/ConsoleHandler.java	Thu Oct 25 15:04:09 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -35,26 +35,39 @@
  * <p>
  * <b>Configuration:</b>
  * By default each <tt>ConsoleHandler</tt> is initialized using the following
- * <tt>LogManager</tt> configuration properties.  If properties are not defined
+ * <tt>LogManager</tt> configuration properties where <tt><handler-name></tt>
+ * refers to the fully-qualified class name of the handler.
+ * If properties are not defined
  * (or have invalid values) then the specified default values are used.
  * <ul>
- * <li>   java.util.logging.ConsoleHandler.level
+ * <li>   &lt;handler-name&gt;.level
  *        specifies the default level for the <tt>Handler</tt>
- *        (defaults to <tt>Level.INFO</tt>).
- * <li>   java.util.logging.ConsoleHandler.filter
+ *        (defaults to <tt>Level.INFO</tt>). </li>
+ * <li>   &lt;handler-name&gt;.filter
  *        specifies the name of a <tt>Filter</tt> class to use
- *        (defaults to no <tt>Filter</tt>).
- * <li>   java.util.logging.ConsoleHandler.formatter
+ *        (defaults to no <tt>Filter</tt>). </li>
+ * <li>   &lt;handler-name&gt;.formatter
  *        specifies the name of a <tt>Formatter</tt> class to use
- *        (defaults to <tt>java.util.logging.SimpleFormatter</tt>).
- * <li>   java.util.logging.ConsoleHandler.encoding
+ *        (defaults to <tt>java.util.logging.SimpleFormatter</tt>). </li>
+ * <li>   &lt;handler-name&gt;.encoding
  *        the name of the character set encoding to use (defaults to
- *        the default platform encoding).
+ *        the default platform encoding). </li>
+ * </ul>
+ * <p>
+ * For example, the properties for {@code ConsoleHandler} would be:
+ * <ul>
+ * <li>   java.util.logging.ConsoleHandler.level=INFO </li>
+ * <li>   java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter </li>
+ * </ul>
+ * <p>
+ * For a custom handler, e.g. com.foo.MyHandler, the properties would be:
+ * <ul>
+ * <li>   com.foo.MyHandler.level=INFO </li>
+ * <li>   com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
  * </ul>
  * <p>
  * @since 1.4
  */
-
 public class ConsoleHandler extends StreamHandler {
     // Private method to configure a ConsoleHandler from LogManager
     // properties and/or default values as specified in the class
--- a/src/share/classes/java/util/logging/FileHandler.java	Wed Oct 24 21:20:40 2012 +0100
+++ b/src/share/classes/java/util/logging/FileHandler.java	Thu Oct 25 15:04:09 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -48,45 +48,58 @@
  * <p>
  * <b>Configuration:</b>
  * By default each <tt>FileHandler</tt> is initialized using the following
- * <tt>LogManager</tt> configuration properties.  If properties are not defined
+ * <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
+ * refers to the fully-qualified class name of the handler.
+ * If properties are not defined
  * (or have invalid values) then the specified default values are used.
  * <ul>
- * <li>   java.util.logging.FileHandler.level
+ * <li>   &lt;handler-name&gt;.level
  *        specifies the default level for the <tt>Handler</tt>
- *        (defaults to <tt>Level.ALL</tt>).
- * <li>   java.util.logging.FileHandler.filter
+ *        (defaults to <tt>Level.ALL</tt>). </li>
+ * <li>   &lt;handler-name&gt;.filter
  *        specifies the name of a <tt>Filter</tt> class to use
- *        (defaults to no <tt>Filter</tt>).
- * <li>   java.util.logging.FileHandler.formatter
+ *        (defaults to no <tt>Filter</tt>). </li>
+ * <li>   &lt;handler-name&gt;.formatter
  *        specifies the name of a <tt>Formatter</tt> class to use
- *        (defaults to <tt>java.util.logging.XMLFormatter</tt>)
- * <li>   java.util.logging.FileHandler.encoding
+ *        (defaults to <tt>java.util.logging.XMLFormatter</tt>) </li>
+ * <li>   &lt;handler-name&gt;.encoding
  *        the name of the character set encoding to use (defaults to
- *        the default platform encoding).
- * <li>   java.util.logging.FileHandler.limit
+ *        the default platform encoding). </li>
+ * <li>   &lt;handler-name&gt;.limit
  *        specifies an approximate maximum amount to write (in bytes)
  *        to any one file.  If this is zero, then there is no limit.
- *        (Defaults to no limit).
- * <li>   java.util.logging.FileHandler.count
- *        specifies how many output files to cycle through (defaults to 1).
- * <li>   java.util.logging.FileHandler.pattern
+ *        (Defaults to no limit). </li>
+ * <li>   &lt;handler-name&gt;.count
+ *        specifies how many output files to cycle through (defaults to 1). </li>
+ * <li>   &lt;handler-name&gt;.pattern
  *        specifies a pattern for generating the output file name.  See
- *        below for details. (Defaults to "%h/java%u.log").
- * <li>   java.util.logging.FileHandler.append
+ *        below for details. (Defaults to "%h/java%u.log"). </li>
+ * <li>   &lt;handler-name&gt;.append
  *        specifies whether the FileHandler should append onto
- *        any existing files (defaults to false).
+ *        any existing files (defaults to false). </li>
  * </ul>
  * <p>
+ * For example, the properties for {@code FileHandler} would be:
+ * <ul>
+ * <li>   java.util.logging.FileHandler.level=INFO </li>
+ * <li>   java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter </li>
+ * </ul>
+ * <p>
+ * For a custom handler, e.g. com.foo.MyHandler, the properties would be:
+ * <ul>
+ * <li>   com.foo.MyHandler.level=INFO </li>
+ * <li>   com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
+ * </ul>
  * <p>
  * A pattern consists of a string that includes the following special
  * components that will be replaced at runtime:
  * <ul>
- * <li>    "/"    the local pathname separator
- * <li>     "%t"   the system temporary directory
- * <li>     "%h"   the value of the "user.home" system property
- * <li>     "%g"   the generation number to distinguish rotated logs
- * <li>     "%u"   a unique number to resolve conflicts
- * <li>     "%%"   translates to a single percent sign "%"
+ * <li>    "/"    the local pathname separator </li>
+ * <li>     "%t"   the system temporary directory </li>
+ * <li>     "%h"   the value of the "user.home" system property </li>
+ * <li>     "%g"   the generation number to distinguish rotated logs </li>
+ * <li>     "%u"   a unique number to resolve conflicts </li>
+ * <li>     "%%"   translates to a single percent sign "%" </li>
  * </ul>
  * If no "%g" field has been specified and the file count is greater
  * than one, then the generation number will be added to the end of
--- a/src/share/classes/java/util/logging/MemoryHandler.java	Wed Oct 24 21:20:40 2012 +0100
+++ b/src/share/classes/java/util/logging/MemoryHandler.java	Thu Oct 25 15:04:09 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -39,36 +39,50 @@
  * <ul>
  * <li>
  * An incoming <tt>LogRecord</tt> has a type that is greater than
- * a pre-defined level, the <tt>pushLevel</tt>.
+ * a pre-defined level, the <tt>pushLevel</tt>. </li>
  * <li>
- * An external class calls the <tt>push</tt> method explicitly.
+ * An external class calls the <tt>push</tt> method explicitly. </li>
  * <li>
  * A subclass overrides the <tt>log</tt> method and scans each incoming
  * <tt>LogRecord</tt> and calls <tt>push</tt> if a record matches some
- * desired criteria.
+ * desired criteria. </li>
  * </ul>
  * <p>
  * <b>Configuration:</b>
  * By default each <tt>MemoryHandler</tt> is initialized using the following
- * LogManager configuration properties.  If properties are not defined
+ * <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
+ * refers to the fully-qualified class name of the handler.
+ * If properties are not defined
  * (or have invalid values) then the specified default values are used.
  * If no default value is defined then a RuntimeException is thrown.
  * <ul>
- * <li>   java.util.logging.MemoryHandler.level
+ * <li>   &lt;handler-name&gt;.level
  *        specifies the level for the <tt>Handler</tt>
- *        (defaults to <tt>Level.ALL</tt>).
- * <li>   java.util.logging.MemoryHandler.filter
+ *        (defaults to <tt>Level.ALL</tt>). </li>
+ * <li>   &lt;handler-name&gt;.filter
  *        specifies the name of a <tt>Filter</tt> class to use
- *        (defaults to no <tt>Filter</tt>).
- * <li>   java.util.logging.MemoryHandler.size
- *        defines the buffer size (defaults to 1000).
- * <li>   java.util.logging.MemoryHandler.push
- *        defines the <tt>pushLevel</tt> (defaults to <tt>level.SEVERE</tt>).
- * <li>   java.util.logging.MemoryHandler.target
+ *        (defaults to no <tt>Filter</tt>). </li>
+ * <li>   &lt;handler-name&gt;.size
+ *        defines the buffer size (defaults to 1000). </li>
+ * <li>   &lt;handler-name>.push
+ *        defines the <tt>pushLevel</tt> (defaults to <tt>level.SEVERE</tt>). </li>
+ * <li>   &lt;handler-name&gt;.target
  *        specifies the name of the target <tt>Handler </tt> class.
- *        (no default).
+ *        (no default). </li>
  * </ul>
- *
+ * <p>
+ * For example, the properties for {@code MemoryHandler} would be:
+ * <ul>
+ * <li>   java.util.logging.MemoryHandler.level=INFO </li>
+ * <li>   java.util.logging.MemoryHandler.formatter=java.util.logging.SimpleFormatter </li>
+ * </ul>
+ * <p>
+ * For a custom handler, e.g. com.foo.MyHandler, the properties would be:
+ * <ul>
+ * <li>   com.foo.MyHandler.level=INFO </li>
+ * <li>   com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
+ * </ul>
+ * <p>
  * @since 1.4
  */
 
@@ -80,7 +94,7 @@
     private LogRecord buffer[];
     int start, count;
 
-    // Private method to configure a ConsoleHandler from LogManager
+    // Private method to configure a MemoryHandler from LogManager
     // properties and/or default values as specified in the class
     // javadoc.
     private void configure() {
@@ -106,14 +120,19 @@
         configure();
         sealed = true;
 
-        String name = "???";
+        LogManager manager = LogManager.getLogManager();
+        String handlerName = getClass().getName();
+        String targetName = manager.getProperty(handlerName+".target");
+        if (targetName == null) {
+            throw new RuntimeException("The handler " + handlerName
+                    + " does not specify a target");
+        }
+        Class<?> clz;
         try {
-            LogManager manager = LogManager.getLogManager();
-            name = manager.getProperty("java.util.logging.MemoryHandler.target");
-            Class clz = ClassLoader.getSystemClassLoader().loadClass(name);
+            clz = ClassLoader.getSystemClassLoader().loadClass(targetName);
             target = (Handler) clz.newInstance();
-        } catch (Exception ex) {
-            throw new RuntimeException("MemoryHandler can't load handler \"" + name + "\"" , ex);
+        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+            throw new RuntimeException("MemoryHandler can't load handler target \"" + targetName + "\"" , e);
         }
         init();
     }
--- a/src/share/classes/java/util/logging/SocketHandler.java	Wed Oct 24 21:20:40 2012 +0100
+++ b/src/share/classes/java/util/logging/SocketHandler.java	Thu Oct 25 15:04:09 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -37,25 +37,39 @@
  * <p>
  * <b>Configuration:</b>
  * By default each <tt>SocketHandler</tt> is initialized using the following
- * <tt>LogManager</tt> configuration properties.  If properties are not defined
+ * <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
+ * refers to the fully-qualified class name of the handler.
+ * If properties are not defined
  * (or have invalid values) then the specified default values are used.
  * <ul>
- * <li>   java.util.logging.SocketHandler.level
+ * <li>   &lt;handler-name&gt;.level
  *        specifies the default level for the <tt>Handler</tt>
- *        (defaults to <tt>Level.ALL</tt>).
- * <li>   java.util.logging.SocketHandler.filter
+ *        (defaults to <tt>Level.ALL</tt>). </li>
+ * <li>   &lt;handler-name&gt;.filter
  *        specifies the name of a <tt>Filter</tt> class to use
- *        (defaults to no <tt>Filter</tt>).
- * <li>   java.util.logging.SocketHandler.formatter
+ *        (defaults to no <tt>Filter</tt>). </li>
+ * <li>   &lt;handler-name&gt;.formatter
  *        specifies the name of a <tt>Formatter</tt> class to use
- *        (defaults to <tt>java.util.logging.XMLFormatter</tt>).
- * <li>   java.util.logging.SocketHandler.encoding
+ *        (defaults to <tt>java.util.logging.XMLFormatter</tt>). </li>
+ * <li>   &lt;handler-name&gt;.encoding
  *        the name of the character set encoding to use (defaults to
- *        the default platform encoding).
- * <li>   java.util.logging.SocketHandler.host
- *        specifies the target host name to connect to (no default).
- * <li>   java.util.logging.SocketHandler.port
- *        specifies the target TCP port to use (no default).
+ *        the default platform encoding). </li>
+ * <li>   &lt;handler-name&gt;.host
+ *        specifies the target host name to connect to (no default). </li>
+ * <li>   &lt;handler-name&gt;.port
+ *        specifies the target TCP port to use (no default). </li>
+ * </ul>
+ * <p>
+ * For example, the properties for {@code SocketHandler} would be:
+ * <ul>
+ * <li>   java.util.logging.SocketHandler.level=INFO </li>
+ * <li>   java.util.logging.SocketHandler.formatter=java.util.logging.SimpleFormatter </li>
+ * </ul>
+ * <p>
+ * For a custom handler, e.g. com.foo.MyHandler, the properties would be:
+ * <ul>
+ * <li>   com.foo.MyHandler.level=INFO </li>
+ * <li>   com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
  * </ul>
  * <p>
  * The output IO stream is buffered, but is flushed after each
--- a/src/share/classes/java/util/logging/StreamHandler.java	Wed Oct 24 21:20:40 2012 +0100
+++ b/src/share/classes/java/util/logging/StreamHandler.java	Thu Oct 25 15:04:09 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,23 +38,37 @@
  * <p>
  * <b>Configuration:</b>
  * By default each <tt>StreamHandler</tt> is initialized using the following
- * <tt>LogManager</tt> configuration properties.  If properties are not defined
+ * <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
+ * refers to the fully-qualified class name of the handler.
+ * If properties are not defined
  * (or have invalid values) then the specified default values are used.
  * <ul>
- * <li>   java.util.logging.StreamHandler.level
+ * <li>   &lt;handler-name&gt;.level
  *        specifies the default level for the <tt>Handler</tt>
- *        (defaults to <tt>Level.INFO</tt>).
- * <li>   java.util.logging.StreamHandler.filter
+ *        (defaults to <tt>Level.INFO</tt>). </li>
+ * <li>   &lt;handler-name&gt;.filter
  *        specifies the name of a <tt>Filter</tt> class to use
- *         (defaults to no <tt>Filter</tt>).
- * <li>   java.util.logging.StreamHandler.formatter
+ *         (defaults to no <tt>Filter</tt>). </li>
+ * <li>   &lt;handler-name&gt;.formatter
  *        specifies the name of a <tt>Formatter</tt> class to use
- *        (defaults to <tt>java.util.logging.SimpleFormatter</tt>).
- * <li>   java.util.logging.StreamHandler.encoding
+ *        (defaults to <tt>java.util.logging.SimpleFormatter</tt>). </li>
+ * <li>   &lt;handler-name&gt;.encoding
  *        the name of the character set encoding to use (defaults to
- *        the default platform encoding).
+ *        the default platform encoding). </li>
  * </ul>
- *
+ * <p>
+ * For example, the properties for {@code StreamHandler} would be:
+ * <ul>
+ * <li>   java.util.logging.StreamHandler.level=INFO </li>
+ * <li>   java.util.logging.StreamHandler.formatter=java.util.logging.SimpleFormatter </li>
+ * </ul>
+ * <p>
+ * For a custom handler, e.g. com.foo.MyHandler, the properties would be:
+ * <ul>
+ * <li>   com.foo.MyHandler.level=INFO </li>
+ * <li>   com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
+ * </ul>
+ * <p>
  * @since 1.4
  */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/MemoryHandlerTest.java	Thu Oct 25 15:04:09 2012 -0700
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7159567
+ * @summary Test of configuring a MemoryHandler sub-class handler target via logging.properties
+ * @run main/othervm MemoryHandlerTest
+ */
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.logging.MemoryHandler;
+
+public class MemoryHandlerTest {
+
+    static final String CFG_FILE_PROP = "java.util.logging.config.file";
+    static final String LM_PROP_FNAME = "MemoryHandlerTest.props";
+    static Logger logger;
+
+    public static void main(String... args) throws IOException {
+        // load logging.propertes for the test
+        String tstSrc = System.getProperty("test.src", ".");
+        File fname = new File(tstSrc, LM_PROP_FNAME);
+        String prop = fname.getCanonicalPath();
+        System.setProperty(CFG_FILE_PROP, prop);
+        LogManager logMgr = LogManager.getLogManager();
+        // create a logger
+        logger = Logger.getLogger(MemoryHandlerTest.class.getName());
+        // don't have parent handlers get log messages
+        logger.setUseParentHandlers(false);
+        //
+        // Test 1,2: create a CustomMemoryHandler which in the config has
+        // specified a target of CustomTargetHandler.  (1) Make sure that it
+        // is created and (2) that the target handler is loaded.
+        //
+        CustomMemoryHandler cmh = new CustomMemoryHandler();
+        try {
+            logger.addHandler(cmh);
+        } catch (RuntimeException rte) {
+            throw new RuntimeException(
+                "Test Failed: did not load java.util.logging.ConsoleHandler as expected",
+                rte);
+        }
+        // if we get here and our config has been processed properly, then we
+        // should have loaded our target handler
+        if (CustomTargetHandler.numLoaded !=1) {
+            throw new RuntimeException(
+                "Test failed: did not load CustomTargetHandler as expected");
+        }
+        //
+        // Test 3: try to add a handler with no target.  This should fail with
+        // an exception
+        CustomMemoryHandlerNoTarget cmhnt = null;
+        try {
+            cmhnt = new CustomMemoryHandlerNoTarget();
+        } catch (RuntimeException re) {
+            // expected -- no target specified
+            System.out.println("Info: " + re.getMessage() + " as expected.");
+        }
+        if (cmhnt != null) {
+            throw new RuntimeException(
+                "Test Failed: erroneously loaded CustomMemoryHandlerNoTarget");
+        }
+
+        // Test 4: log a message and check that the target handler is actually used
+        logger.log(Level.WARNING, "Unused");
+        if (CustomTargetHandler.numPublished != 1) {
+            throw new RuntimeException("Test failed: CustomTargetHandler was not used");
+        }
+
+        // Test 5: make sure that SimpleTargetHandler hasn't been erroneously called
+        if (SimpleTargetHandler.numPublished != 0) {
+            throw new RuntimeException("Test failed: SimpleTargetHandler has been used");
+        }
+
+        // Test 6: try using SimpleTargetHanlder via standard MemoryHandler
+        // (which has target set to SimpleTargetHandler)
+        MemoryHandler mh = new MemoryHandler();
+        mh.publish(new LogRecord(Level.INFO, "Unused msg to MemoryHandler"));
+        // see if it made it to the SimpleTargetHandler
+        if (SimpleTargetHandler.numPublished != 1) {
+            throw new RuntimeException("Test failed: SimpleTargetHandler was not used");
+        }
+    }
+
+    public static class CustomMemoryHandler extends MemoryHandler {
+    }
+
+    public static class CustomMemoryHandlerNoTarget extends MemoryHandler {
+    }
+
+    public static class CustomTargetHandler extends Handler {
+
+        public static int numPublished;
+        public static int numLoaded;
+
+        public CustomTargetHandler() {
+            numLoaded++;
+        }
+
+        @Override
+        public void publish(LogRecord unused) {
+            numPublished++;
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+        }
+    }
+
+    public static class SimpleTargetHandler extends Handler {
+        public static int numPublished;
+
+        @Override
+        public void publish(LogRecord unused) {
+            numPublished++;
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/MemoryHandlerTest.props	Thu Oct 25 15:04:09 2012 -0700
@@ -0,0 +1,9 @@
+.level= INFO
+MemoryHandlerTest$CustomMemoryHandler.push=WARNING
+MemoryHandlerTest$CustomMemoryHandlerNoTarget.push=WARNING
+MemoryHandlerTest$CustomMemoryHandler.target=MemoryHandlerTest$CustomTargetHandler
+handlers=java.util.logging.ConsoleHandler,MemoryHandlerTest$CustomMemoryHandler,MemoryHandlerTest$CustomMemoryHandlerNoTarget
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+java.util.logging.ConsoleHandler.level = INFO
+java.util.logging.MemoryHandler.target=MemoryHandlerTest$SimpleTargetHandler
+java.util.logging.MemoryHandler.push = INFO