# HG changeset patch # User dfuchs # Date 1378301579 -7200 # Node ID 39cb79123ab2aef9f5020b83079ab68355df9ba8 # Parent 9f7860fad128b01a7f3d05cc37b3e8218f0337b3 6823527: java.util.logging.Handler has thread safety issues Reviewed-by: dholmes, mchung diff -r 9f7860fad128 -r 39cb79123ab2 jdk/src/share/classes/java/util/logging/ConsoleHandler.java --- a/jdk/src/share/classes/java/util/logging/ConsoleHandler.java Wed Sep 04 11:40:23 2013 +0100 +++ b/jdk/src/share/classes/java/util/logging/ConsoleHandler.java Wed Sep 04 15:32:59 2013 +0200 @@ -26,9 +26,6 @@ package java.util.logging; -import java.io.*; -import java.net.*; - /** * This Handler publishes log records to System.err. * By default the SimpleFormatter is used to generate brief summaries. @@ -114,6 +111,7 @@ * @param record description of the log event. A null record is * silently ignored and is not published */ + @Override public void publish(LogRecord record) { super.publish(record); flush(); @@ -124,6 +122,7 @@ * to close the output stream. That is, we do not * close System.err. */ + @Override public void close() { flush(); } diff -r 9f7860fad128 -r 39cb79123ab2 jdk/src/share/classes/java/util/logging/FileHandler.java --- a/jdk/src/share/classes/java/util/logging/FileHandler.java Wed Sep 04 11:40:23 2013 +0100 +++ b/jdk/src/share/classes/java/util/logging/FileHandler.java Wed Sep 04 15:32:59 2013 +0200 @@ -149,7 +149,7 @@ private FileChannel lockFileChannel; private File files[]; private static final int MAX_LOCKS = 100; - private static java.util.HashMap locks = new java.util.HashMap<>(); + private static final java.util.HashMap locks = new java.util.HashMap<>(); /** * A metered stream is a subclass of OutputStream that @@ -157,7 +157,7 @@ * (b) keeps track of how many bytes have been written */ private class MeteredStream extends OutputStream { - OutputStream out; + final OutputStream out; int written; MeteredStream(OutputStream out, int written) { @@ -165,25 +165,30 @@ this.written = written; } + @Override public void write(int b) throws IOException { out.write(b); written++; } + @Override public void write(byte buff[]) throws IOException { out.write(buff); written += buff.length; } + @Override public void write(byte buff[], int off, int len) throws IOException { out.write(buff,off,len); written += len; } + @Override public void flush() throws IOException { out.flush(); } + @Override public void close() throws IOException { out.close(); } @@ -607,6 +612,7 @@ * @param record description of the log event. A null record is * silently ignored and is not published */ + @Override public synchronized void publish(LogRecord record) { if (!isLoggable(record)) { return; @@ -620,6 +626,7 @@ // currently being called from untrusted code. // So it is safe to raise privilege here. AccessController.doPrivileged(new PrivilegedAction() { + @Override public Object run() { rotate(); return null; @@ -634,6 +641,7 @@ * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ + @Override public synchronized void close() throws SecurityException { super.close(); // Unlock any lock file. @@ -656,6 +664,7 @@ private static class InitializationErrorManager extends ErrorManager { Exception lastException; + @Override public void error(String msg, Exception ex, int code) { lastException = ex; } diff -r 9f7860fad128 -r 39cb79123ab2 jdk/src/share/classes/java/util/logging/Handler.java --- a/jdk/src/share/classes/java/util/logging/Handler.java Wed Sep 04 11:40:23 2013 +0100 +++ b/jdk/src/share/classes/java/util/logging/Handler.java Wed Sep 04 15:32:59 2013 +0200 @@ -47,12 +47,20 @@ public abstract class Handler { private static final int offValue = Level.OFF.intValue(); - private LogManager manager = LogManager.getLogManager(); - private Filter filter; - private Formatter formatter; - private Level logLevel = Level.ALL; - private ErrorManager errorManager = new ErrorManager(); - private String encoding; + private final LogManager manager = LogManager.getLogManager(); + + // We're using volatile here to avoid synchronizing getters, which + // would prevent other threads from calling isLoggable() + // while publish() is executing. + // On the other hand, setters will be synchronized to exclude concurrent + // execution with more complex methods, such as StreamHandler.publish(). + // We wouldn't want 'level' to be changed by another thread in the middle + // of the execution of a 'publish' call. + private volatile Filter filter; + private volatile Formatter formatter; + private volatile Level logLevel = Level.ALL; + private volatile ErrorManager errorManager = new ErrorManager(); + private volatile String encoding; // Package private support for security checking. When sealed // is true, we access check updates to the class. @@ -110,7 +118,7 @@ * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ - public void setFormatter(Formatter newFormatter) throws SecurityException { + public synchronized void setFormatter(Formatter newFormatter) throws SecurityException { checkPermission(); // Check for a null pointer: newFormatter.getClass(); @@ -138,7 +146,7 @@ * @exception UnsupportedEncodingException if the named encoding is * not supported. */ - public void setEncoding(String encoding) + public synchronized void setEncoding(String encoding) throws SecurityException, java.io.UnsupportedEncodingException { checkPermission(); if (encoding != null) { @@ -174,7 +182,7 @@ * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ - public void setFilter(Filter newFilter) throws SecurityException { + public synchronized void setFilter(Filter newFilter) throws SecurityException { checkPermission(); filter = newFilter; } @@ -198,7 +206,7 @@ * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ - public void setErrorManager(ErrorManager em) { + public synchronized void setErrorManager(ErrorManager em) { checkPermission(); if (em == null) { throw new NullPointerException(); @@ -264,7 +272,7 @@ * than this level will be discarded. * @return the level of messages being logged. */ - public synchronized Level getLevel() { + public Level getLevel() { return logLevel; } @@ -282,11 +290,11 @@ * */ public boolean isLoggable(LogRecord record) { - int levelValue = getLevel().intValue(); + final int levelValue = getLevel().intValue(); if (record.getLevel().intValue() < levelValue || levelValue == offValue) { return false; } - Filter filter = getFilter(); + final Filter filter = getFilter(); if (filter == null) { return true; } diff -r 9f7860fad128 -r 39cb79123ab2 jdk/src/share/classes/java/util/logging/MemoryHandler.java --- a/jdk/src/share/classes/java/util/logging/MemoryHandler.java Wed Sep 04 11:40:23 2013 +0100 +++ b/jdk/src/share/classes/java/util/logging/MemoryHandler.java Wed Sep 04 15:32:59 2013 +0200 @@ -88,7 +88,7 @@ public class MemoryHandler extends Handler { private final static int DEFAULT_SIZE = 1000; - private Level pushLevel; + private volatile Level pushLevel; private int size; private Handler target; private LogRecord buffer[]; @@ -188,6 +188,7 @@ * @param record description of the log event. A null record is * silently ignored and is not published */ + @Override public synchronized void publish(LogRecord record) { if (!isLoggable(record)) { return; @@ -227,6 +228,7 @@ * Note that the current contents of the MemoryHandler * buffer are not written out. That requires a "push". */ + @Override public void flush() { target.flush(); } @@ -238,6 +240,7 @@ * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ + @Override public void close() throws SecurityException { target.close(); setLevel(Level.OFF); @@ -252,11 +255,10 @@ * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ - public void setPushLevel(Level newLevel) throws SecurityException { + public synchronized void setPushLevel(Level newLevel) throws SecurityException { if (newLevel == null) { throw new NullPointerException(); } - LogManager manager = LogManager.getLogManager(); checkPermission(); pushLevel = newLevel; } @@ -266,7 +268,7 @@ * * @return the value of the pushLevel */ - public synchronized Level getPushLevel() { + public Level getPushLevel() { return pushLevel; } @@ -283,6 +285,7 @@ * @return true if the LogRecord would be logged. * */ + @Override public boolean isLoggable(LogRecord record) { return super.isLoggable(record); } diff -r 9f7860fad128 -r 39cb79123ab2 jdk/src/share/classes/java/util/logging/SocketHandler.java --- a/jdk/src/share/classes/java/util/logging/SocketHandler.java Wed Sep 04 11:40:23 2013 +0100 +++ b/jdk/src/share/classes/java/util/logging/SocketHandler.java Wed Sep 04 15:32:59 2013 +0200 @@ -82,7 +82,6 @@ private Socket sock; private String host; private int port; - private String portProperty; // Private method to configure a SocketHandler from LogManager // properties and/or default values as specified in the class @@ -177,6 +176,7 @@ * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ + @Override public synchronized void close() throws SecurityException { super.close(); if (sock != null) { @@ -195,6 +195,7 @@ * @param record description of the log event. A null record is * silently ignored and is not published */ + @Override public synchronized void publish(LogRecord record) { if (!isLoggable(record)) { return; diff -r 9f7860fad128 -r 39cb79123ab2 jdk/src/share/classes/java/util/logging/StreamHandler.java --- a/jdk/src/share/classes/java/util/logging/StreamHandler.java Wed Sep 04 11:40:23 2013 +0100 +++ b/jdk/src/share/classes/java/util/logging/StreamHandler.java Wed Sep 04 15:32:59 2013 +0200 @@ -73,10 +73,9 @@ */ public class StreamHandler extends Handler { - private LogManager manager = LogManager.getLogManager(); private OutputStream output; private boolean doneHeader; - private Writer writer; + private volatile Writer writer; // Private method to configure a StreamHandler from LogManager // properties and/or default values as specified in the class @@ -169,7 +168,8 @@ * @exception UnsupportedEncodingException if the named encoding is * not supported. */ - public void setEncoding(String encoding) + @Override + public synchronized void setEncoding(String encoding) throws SecurityException, java.io.UnsupportedEncodingException { super.setEncoding(encoding); if (output == null) { @@ -201,6 +201,7 @@ * @param record description of the log event. A null record is * silently ignored and is not published */ + @Override public synchronized void publish(LogRecord record) { if (!isLoggable(record)) { return; @@ -240,6 +241,7 @@ * @return true if the LogRecord would be logged. * */ + @Override public boolean isLoggable(LogRecord record) { if (writer == null || record == null) { return false; @@ -250,6 +252,7 @@ /** * Flush any buffered messages. */ + @Override public synchronized void flush() { if (writer != null) { try { @@ -294,6 +297,7 @@ * @exception SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ + @Override public synchronized void close() throws SecurityException { flushAndClose(); }