summaryrefslogtreecommitdiff
path: root/libjava/classpath/java/util/logging
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/util/logging')
-rw-r--r--libjava/classpath/java/util/logging/ConsoleHandler.java125
-rw-r--r--libjava/classpath/java/util/logging/ErrorManager.java193
-rw-r--r--libjava/classpath/java/util/logging/FileHandler.java665
-rw-r--r--libjava/classpath/java/util/logging/Filter.java64
-rw-r--r--libjava/classpath/java/util/logging/Formatter.java171
-rw-r--r--libjava/classpath/java/util/logging/Handler.java386
-rw-r--r--libjava/classpath/java/util/logging/Level.java416
-rw-r--r--libjava/classpath/java/util/logging/LogManager.java986
-rw-r--r--libjava/classpath/java/util/logging/LogRecord.java672
-rw-r--r--libjava/classpath/java/util/logging/Logger.java1193
-rw-r--r--libjava/classpath/java/util/logging/LoggingMXBean.java85
-rw-r--r--libjava/classpath/java/util/logging/LoggingPermission.java75
-rw-r--r--libjava/classpath/java/util/logging/MemoryHandler.java345
-rw-r--r--libjava/classpath/java/util/logging/SimpleFormatter.java131
-rw-r--r--libjava/classpath/java/util/logging/SocketHandler.java220
-rw-r--r--libjava/classpath/java/util/logging/StreamHandler.java521
-rw-r--r--libjava/classpath/java/util/logging/XMLFormatter.java389
-rw-r--r--libjava/classpath/java/util/logging/package.html46
18 files changed, 6683 insertions, 0 deletions
diff --git a/libjava/classpath/java/util/logging/ConsoleHandler.java b/libjava/classpath/java/util/logging/ConsoleHandler.java
new file mode 100644
index 000000000..9f6bb7240
--- /dev/null
+++ b/libjava/classpath/java/util/logging/ConsoleHandler.java
@@ -0,0 +1,125 @@
+/* ConsoleHandler.java -- a class for publishing log messages to System.err
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+/**
+ * A <code>ConsoleHandler</code> publishes log records to
+ * <code>System.err</code>.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>ConsoleHandler</code> is initialized.
+ * If a property is not defined, or if it has an invalid
+ * value, a default is taken without an exception being thrown.
+ *
+ * <ul>
+ *
+ * <li><code>java.util.logging.ConsoleHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.INFO</code>.</li>
+ *
+ * <li><code>java.util.logging.ConsoleHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ *
+ * <li><code>java.util.logging.ConsoleHandler.formatter</code> - specifies
+ * the name of a Formatter class. Default value:
+ * <code>java.util.logging.SimpleFormatter</code>.</li>
+ *
+ * <li><code>java.util.logging.ConsoleHandler.encoding</code> - specifies
+ * the name of the character encoding. Default value:
+ * the default platform encoding.</li>
+ *
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class ConsoleHandler
+ extends StreamHandler
+{
+ /**
+ * Constructs a <code>StreamHandler</code> that publishes
+ * log records to <code>System.err</code>. The initial
+ * configuration is determined by the <code>LogManager</code>
+ * properties described above.
+ */
+ public ConsoleHandler()
+ {
+ super(System.err, "java.util.logging.ConsoleHandler", Level.INFO,
+ /* formatter */ null, SimpleFormatter.class);
+ }
+
+
+ /**
+ * Forces any data that may have been buffered to the underlying
+ * output device, but does <i>not</i> close <code>System.err</code>.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>ConsoleHandler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ */
+ public void close()
+ {
+ flush();
+ }
+
+
+ /**
+ * Publishes a <code>LogRecord</code> to the console, provided the
+ * record passes all tests for being loggable.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use use a <code>Logger</code>, which will
+ * create LogRecords and distribute them to registered handlers.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>SocketHandler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * <p>The GNU implementation of <code>ConsoleHandler.publish</code>
+ * calls flush() for every request to publish a record, so
+ * they appear immediately on the console.
+ *
+ * @param record the log event to be published.
+ */
+ public void publish(LogRecord record)
+ {
+ super.publish(record);
+ flush();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/ErrorManager.java b/libjava/classpath/java/util/logging/ErrorManager.java
new file mode 100644
index 000000000..291efb2f1
--- /dev/null
+++ b/libjava/classpath/java/util/logging/ErrorManager.java
@@ -0,0 +1,193 @@
+/* ErrorManager.java --
+ A class for dealing with errors that a Handler encounters
+ during logging
+ Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+/**
+ * An <code>ErrorManager</code> deals with errors that a <code>Handler</code>
+ * encounters while logging.
+ *
+ * @see Handler#setErrorManager(ErrorManager)
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class ErrorManager
+{
+ /* The values have been taken from Sun's public J2SE 1.4 API
+ * documentation.
+ * See http://java.sun.com/j2se/1.4/docs/api/constant-values.html
+ */
+
+ /**
+ * Indicates that there was a failure that does not readily
+ * fall into any of the other categories.
+ */
+ public static final int GENERIC_FAILURE = 0;
+
+
+ /**
+ * Indicates that there was a problem upon writing to
+ * an output stream.
+ */
+ public static final int WRITE_FAILURE = 1;
+
+
+ /**
+ * Indicates that there was a problem upon flushing
+ * an output stream.
+ */
+ public static final int FLUSH_FAILURE = 2;
+
+
+ /**
+ * Indicates that there was a problem upon closing
+ * an output stream.
+ */
+ public static final int CLOSE_FAILURE = 3;
+
+
+ /**
+ * Indicates that there was a problem upon opening
+ * an output stream.
+ */
+ public static final int OPEN_FAILURE = 4;
+
+
+ /**
+ * Indicates that there was a problem upon formatting
+ * the message of a log record.
+ */
+ public static final int FORMAT_FAILURE = 5;
+
+
+ /**
+ * Indicates whether the {@link #error} method of this ErrorManager
+ * has ever been used.
+ *
+ * Declared volatile in order to correctly support the
+ * double-checked locking idiom (once the revised Java Memory Model
+ * gets adopted); see Classpath bug #2944.
+ */
+ private volatile boolean everUsed = false;
+
+
+ public ErrorManager()
+ {
+ }
+
+
+ /**
+ * Reports an error that occured upon logging. The default implementation
+ * emits the very first error to System.err, ignoring subsequent errors.
+ *
+ * @param message a message describing the error, or <code>null</code> if
+ * there is no suitable description.
+ *
+ * @param ex an exception, or <code>null</code> if the error is not
+ * related to an exception.
+ *
+ * @param errorCode one of the defined error codes, for example
+ * <code>ErrorManager.CLOSE_FAILURE</code>.
+ */
+ public void error(String message, Exception ex, int errorCode)
+ {
+ if (everUsed)
+ return;
+
+ synchronized (this)
+ {
+ /* The double check is intentional. If the first check was
+ * omitted, the monitor would have to be entered every time
+ * error() method was called. If the second check was
+ * omitted, the code below could be executed by multiple
+ * threads simultaneously.
+ *
+ * This is the 'double-checked locking' idiom, which is broken
+ * with the current version of the Java memory model. However,
+ * we assume that JVMs will have adopted a revised version of
+ * the Java Memory Model by the time GNU Classpath gains
+ * widespread acceptance. See Classpath bug #2944.
+ */
+ if (everUsed)
+ return;
+
+ everUsed = true;
+ }
+
+ String codeMsg;
+ switch (errorCode)
+ {
+ case GENERIC_FAILURE:
+ codeMsg = "GENERIC_FAILURE";
+ break;
+
+ case WRITE_FAILURE:
+ codeMsg = "WRITE_FAILURE";
+ break;
+
+ case FLUSH_FAILURE:
+ codeMsg = "FLUSH_FAILURE";
+ break;
+
+ case CLOSE_FAILURE:
+ codeMsg = "CLOSE_FAILURE";
+ break;
+
+ case OPEN_FAILURE:
+ codeMsg = "OPEN_FAILURE";
+ break;
+
+ case FORMAT_FAILURE:
+ codeMsg = "FORMAT_FAILURE";
+ break;
+
+ default:
+ codeMsg = String.valueOf(errorCode);
+ break;
+ }
+
+ System.err.println("Error upon logging: " + codeMsg);
+ if ((message != null) && (message.length() > 0))
+ System.err.println(message);
+
+ if (ex != null)
+ ex.printStackTrace();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/FileHandler.java b/libjava/classpath/java/util/logging/FileHandler.java
new file mode 100644
index 000000000..00d967f57
--- /dev/null
+++ b/libjava/classpath/java/util/logging/FileHandler.java
@@ -0,0 +1,665 @@
+/* FileHandler.java -- a class for publishing log messages to log files
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+/**
+ * A <code>FileHandler</code> publishes log records to a set of log
+ * files. A maximum file size can be specified; as soon as a log file
+ * reaches the size limit, it is closed and the next file in the set
+ * is taken.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>FileHandler</code> is initialized. If a property is
+ * not defined, or if it has an invalid value, a default is taken
+ * without an exception being thrown.
+ *
+ * <ul>
+ *
+ * <li><code>java.util.FileHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.ALL</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ *
+ * <li><code>java.util.FileHandler.formatter</code> - specifies
+ * the name of a Formatter class. Default value:
+ * <code>java.util.logging.XMLFormatter</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.encoding</code> - specifies
+ * the name of the character encoding. Default value:
+ * the default platform encoding.</li>
+ *
+ * <li><code>java.util.FileHandler.limit</code> - specifies the number
+ * of bytes a log file is approximately allowed to reach before it
+ * is closed and the handler switches to the next file in the
+ * rotating set. A value of zero means that files can grow
+ * without limit. Default value: 0 (unlimited growth).</li>
+ *
+ * <li><code>java.util.FileHandler.count</code> - specifies the number
+ * of log files through which this handler cycles. Default value:
+ * 1.</li>
+ *
+ * <li><code>java.util.FileHandler.pattern</code> - specifies a
+ * pattern for the location and name of the produced log files.
+ * See the section on <a href="#filePatterns">file name
+ * patterns</a> for details. Default value:
+ * <code>"%h/java%u.log"</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.append</code> - specifies
+ * whether the handler will append log records to existing
+ * files, or whether the handler will clear log files
+ * upon switching to them. Default value: <code>false</code>,
+ * indicating that files will be cleared.</li>
+ *
+ * </ul>
+ *
+ * <p><a name="filePatterns"><strong>File Name Patterns:</strong></a>
+ * The name and location and log files are specified with pattern
+ * strings. The handler will replace the following character sequences
+ * when opening log files:
+ *
+ * <p><ul>
+ * <li><code>/</code> - replaced by the platform-specific path name
+ * separator. This value is taken from the system property
+ * <code>file.separator</code>.</li>
+ *
+ * <li><code>%t</code> - replaced by the platform-specific location of
+ * the directory intended for temporary files. This value is
+ * taken from the system property <code>java.io.tmpdir</code>.</li>
+ *
+ * <li><code>%h</code> - replaced by the location of the home
+ * directory of the current user. This value is taken from the
+ * system property <code>user.home</code>.</li>
+ *
+ * <li><code>%g</code> - replaced by a generation number for
+ * distinguisthing the individual items in the rotating set
+ * of log files. The generation number cycles through the
+ * sequence 0, 1, ..., <code>count</code> - 1.</li>
+ *
+ * <li><code>%u</code> - replaced by a unique number for
+ * distinguisthing the output files of several concurrently
+ * running processes. The <code>FileHandler</code> starts
+ * with 0 when it tries to open a log file. If the file
+ * cannot be opened because it is currently in use,
+ * the unique number is incremented by one and opening
+ * is tried again. These steps are repeated until the
+ * opening operation succeeds.
+ *
+ * <p>FIXME: Is the following correct? Please review. The unique
+ * number is determined for each log file individually when it is
+ * opened upon switching to the next file. Therefore, it is not
+ * correct to assume that all log files in a rotating set bear the
+ * same unique number.
+ *
+ * <p>FIXME: The Javadoc for the Sun reference implementation
+ * says: "Note that the use of unique ids to avoid conflicts is
+ * only guaranteed to work reliably when using a local disk file
+ * system." Why? This needs to be mentioned as well, in case
+ * the reviewers decide the statement is true. Otherwise,
+ * file a bug report with Sun.</li>
+ *
+ * <li><code>%%</code> - replaced by a single percent sign.</li>
+ * </ul>
+ *
+ * <p>If the pattern string does not contain <code>%g</code> and
+ * <code>count</code> is greater than one, the handler will append
+ * the string <code>.%g</code> to the specified pattern.
+ *
+ * <p>If the handler attempts to open a log file, this log file
+ * is being used at the time of the attempt, and the pattern string
+ * does not contain <code>%u</code>, the handler will append
+ * the string <code>.%u</code> to the specified pattern. This
+ * step is performed after any generation number has been
+ * appended.
+ *
+ * <p><em>Examples for the GNU platform:</em>
+ *
+ * <p><ul>
+ *
+ * <li><code>%h/java%u.log</code> will lead to a single log file
+ * <code>/home/janet/java0.log</code>, assuming <code>count</code>
+ * equals 1, the user's home directory is
+ * <code>/home/janet</code>, and the attempt to open the file
+ * succeeds.</li>
+ *
+ * <li><code>%h/java%u.log</code> will lead to three log files
+ * <code>/home/janet/java0.log.0</code>,
+ * <code>/home/janet/java0.log.1</code>, and
+ * <code>/home/janet/java0.log.2</code>,
+ * assuming <code>count</code> equals 3, the user's home
+ * directory is <code>/home/janet</code>, and all attempts
+ * to open files succeed.</li>
+ *
+ * <li><code>%h/java%u.log</code> will lead to three log files
+ * <code>/home/janet/java0.log.0</code>,
+ * <code>/home/janet/java1.log.1</code>, and
+ * <code>/home/janet/java0.log.2</code>,
+ * assuming <code>count</code> equals 3, the user's home
+ * directory is <code>/home/janet</code>, and the attempt
+ * to open <code>/home/janet/java0.log.1</code> fails.</li>
+ *
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class FileHandler
+ extends StreamHandler
+{
+ /**
+ * A literal that prefixes all file-handler related properties in the
+ * logging.properties file.
+ */
+ private static final String PROPERTY_PREFIX = "java.util.logging.FileHandler";
+ /**
+ * The name of the property to set for specifying a file naming (incl. path)
+ * pattern to use with rotating log files.
+ */
+ private static final String PATTERN_KEY = PROPERTY_PREFIX + ".pattern";
+ /**
+ * The default pattern to use when the <code>PATTERN_KEY</code> property was
+ * not specified in the logging.properties file.
+ */
+ private static final String DEFAULT_PATTERN = "%h/java%u.log";
+ /**
+ * The name of the property to set for specifying an approximate maximum
+ * amount, in bytes, to write to any one log output file. A value of zero
+ * (which is the default) implies a no limit.
+ */
+ private static final String LIMIT_KEY = PROPERTY_PREFIX + ".limit";
+ private static final int DEFAULT_LIMIT = 0;
+ /**
+ * The name of the property to set for specifying how many output files to
+ * cycle through. The default value is 1.
+ */
+ private static final String COUNT_KEY = PROPERTY_PREFIX + ".count";
+ private static final int DEFAULT_COUNT = 1;
+ /**
+ * The name of the property to set for specifying whether this handler should
+ * append, or not, its output to existing files. The default value is
+ * <code>false</code> meaning NOT to append.
+ */
+ private static final String APPEND_KEY = PROPERTY_PREFIX + ".append";
+ private static final boolean DEFAULT_APPEND = false;
+
+ /**
+ * The number of bytes a log file is approximately allowed to reach
+ * before it is closed and the handler switches to the next file in
+ * the rotating set. A value of zero means that files can grow
+ * without limit.
+ */
+ private final int limit;
+
+
+ /**
+ * The number of log files through which this handler cycles.
+ */
+ private final int count;
+
+
+ /**
+ * The pattern for the location and name of the produced log files.
+ * See the section on <a href="#filePatterns">file name patterns</a>
+ * for details.
+ */
+ private final String pattern;
+
+
+ /**
+ * Indicates whether the handler will append log records to existing
+ * files (<code>true</code>), or whether the handler will clear log files
+ * upon switching to them (<code>false</code>).
+ */
+ private final boolean append;
+
+
+ /**
+ * The number of bytes that have currently been written to the stream.
+ * Package private for use in inner classes.
+ */
+ long written;
+
+
+ /**
+ * A linked list of files we are, or have written to. The entries
+ * are file path strings, kept in the order
+ */
+ private LinkedList logFiles;
+
+
+ /**
+ * Constructs a <code>FileHandler</code>, taking all property values
+ * from the current {@link LogManager LogManager} configuration.
+ *
+ * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
+ * there are IO problems opening the files." This conflicts
+ * with the general principle that configuration errors do
+ * not prohibit construction. Needs review.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public FileHandler()
+ throws IOException, SecurityException
+ {
+ this(LogManager.getLogManager().getProperty(PATTERN_KEY),
+ LogManager.getIntProperty(LIMIT_KEY, DEFAULT_LIMIT),
+ LogManager.getIntProperty(COUNT_KEY, DEFAULT_COUNT),
+ LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND));
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern)
+ throws IOException, SecurityException
+ {
+ this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, DEFAULT_APPEND);
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern, boolean append)
+ throws IOException, SecurityException
+ {
+ this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, append);
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern, int limit, int count)
+ throws IOException, SecurityException
+ {
+ this(pattern, limit, count,
+ LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND));
+ }
+
+
+ /**
+ * Constructs a <code>FileHandler</code> given the pattern for the
+ * location and name of the produced log files, the size limit, the
+ * number of log files thorough which the handler will rotate, and
+ * the <code>append</code> property. All other property values are
+ * taken from the current {@link LogManager LogManager}
+ * configuration.
+ *
+ * @param pattern The pattern for the location and name of the
+ * produced log files. See the section on <a
+ * href="#filePatterns">file name patterns</a> for details.
+ * If <code>pattern</code> is <code>null</code>, the value is
+ * taken from the {@link LogManager LogManager} configuration
+ * property
+ * <code>java.util.logging.FileHandler.pattern</code>.
+ * However, this is a pecularity of the GNU implementation,
+ * and Sun's API specification does not mention what behavior
+ * is to be expected for <code>null</code>. Therefore,
+ * applications should not rely on this feature.
+ *
+ * @param limit specifies the number of bytes a log file is
+ * approximately allowed to reach before it is closed and the
+ * handler switches to the next file in the rotating set. A
+ * value of zero means that files can grow without limit.
+ *
+ * @param count specifies the number of log files through which this
+ * handler cycles.
+ *
+ * @param append specifies whether the handler will append log
+ * records to existing files (<code>true</code>), or whether the
+ * handler will clear log files upon switching to them
+ * (<code>false</code>).
+ *
+ * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
+ * there are IO problems opening the files." This conflicts
+ * with the general principle that configuration errors do
+ * not prohibit construction. Needs review.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ * <p>FIXME: This seems in contrast to all other handler
+ * constructors -- verify this by running tests against
+ * the Sun reference implementation.
+ */
+ public FileHandler(String pattern,
+ int limit,
+ int count,
+ boolean append)
+ throws IOException, SecurityException
+ {
+ super(/* output stream, created below */ null,
+ PROPERTY_PREFIX,
+ /* default level */ Level.ALL,
+ /* formatter */ null,
+ /* default formatter */ XMLFormatter.class);
+
+ if ((limit <0) || (count < 1))
+ throw new IllegalArgumentException();
+
+ this.pattern = pattern != null ? pattern : DEFAULT_PATTERN;
+ this.limit = limit;
+ this.count = count;
+ this.append = append;
+ this.written = 0;
+ this.logFiles = new LinkedList ();
+
+ setOutputStream (createFileStream (this.pattern, limit, count, append,
+ /* generation */ 0));
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ private OutputStream createFileStream(String pattern,
+ int limit,
+ int count,
+ boolean append,
+ int generation)
+ {
+ String path;
+ int unique = 0;
+
+ /* Throws a SecurityException if the caller does not have
+ * LoggingPermission("control").
+ */
+ LogManager.getLogManager().checkAccess();
+
+ /* Default value from the java.util.logging.FileHandler.pattern
+ * LogManager configuration property.
+ */
+ if (pattern == null)
+ pattern = LogManager.getLogManager().getProperty(PATTERN_KEY);
+ if (pattern == null)
+ pattern = DEFAULT_PATTERN;
+
+ if (count > 1 && !has (pattern, 'g'))
+ pattern = pattern + ".%g";
+
+ do
+ {
+ path = replaceFileNameEscapes(pattern, generation, unique, count);
+
+ try
+ {
+ File file = new File(path);
+ if (!file.exists () || append)
+ {
+ FileOutputStream fout = new FileOutputStream (file, append);
+ // FIXME we need file locks for this to work properly, but they
+ // are not implemented yet in Classpath! Madness!
+// FileChannel channel = fout.getChannel ();
+// FileLock lock = channel.tryLock ();
+// if (lock != null) // We've locked the file.
+// {
+ if (logFiles.isEmpty ())
+ logFiles.addFirst (path);
+ return new ostr (fout);
+// }
+ }
+ }
+ catch (Exception ex)
+ {
+ reportError (null, ex, ErrorManager.OPEN_FAILURE);
+ }
+
+ unique = unique + 1;
+ if (!has (pattern, 'u'))
+ pattern = pattern + ".%u";
+ }
+ while (true);
+ }
+
+
+ /**
+ * Replaces the substrings <code>"/"</code> by the value of the
+ * system property <code>"file.separator"</code>, <code>"%t"</code>
+ * by the value of the system property
+ * <code>"java.io.tmpdir"</code>, <code>"%h"</code> by the value of
+ * the system property <code>"user.home"</code>, <code>"%g"</code>
+ * by the value of <code>generation</code>, <code>"%u"</code> by the
+ * value of <code>uniqueNumber</code>, and <code>"%%"</code> by a
+ * single percent character. If <code>pattern</code> does
+ * <em>not</em> contain the sequence <code>"%g"</code>,
+ * the value of <code>generation</code> will be appended to
+ * the result.
+ *
+ * @throws NullPointerException if one of the system properties
+ * <code>"file.separator"</code>,
+ * <code>"java.io.tmpdir"</code>, or
+ * <code>"user.home"</code> has no value and the
+ * corresponding escape sequence appears in
+ * <code>pattern</code>.
+ */
+ private static String replaceFileNameEscapes(String pattern,
+ int generation,
+ int uniqueNumber,
+ int count)
+ {
+ CPStringBuilder buf = new CPStringBuilder(pattern);
+ String replaceWith;
+ boolean foundGeneration = false;
+
+ int pos = 0;
+ do
+ {
+ // Uncomment the next line for finding bugs.
+ // System.out.println(buf.substring(0,pos) + '|' + buf.substring(pos));
+
+ if (buf.charAt(pos) == '/')
+ {
+ /* The same value is also provided by java.io.File.separator. */
+ replaceWith = System.getProperty("file.separator");
+ buf.replace(pos, pos + 1, replaceWith);
+ pos = pos + replaceWith.length() - 1;
+ continue;
+ }
+
+ if (buf.charAt(pos) == '%')
+ {
+ switch (buf.charAt(pos + 1))
+ {
+ case 't':
+ replaceWith = System.getProperty("java.io.tmpdir");
+ break;
+
+ case 'h':
+ replaceWith = System.getProperty("user.home");
+ break;
+
+ case 'g':
+ replaceWith = Integer.toString(generation);
+ foundGeneration = true;
+ break;
+
+ case 'u':
+ replaceWith = Integer.toString(uniqueNumber);
+ break;
+
+ case '%':
+ replaceWith = "%";
+ break;
+
+ default:
+ replaceWith = "??";
+ break; // FIXME: Throw exception?
+ }
+
+ buf.replace(pos, pos + 2, replaceWith);
+ pos = pos + replaceWith.length() - 1;
+ continue;
+ }
+ }
+ while (++pos < buf.length() - 1);
+
+ if (!foundGeneration && (count > 1))
+ {
+ buf.append('.');
+ buf.append(generation);
+ }
+
+ return buf.toString();
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public void publish(LogRecord record)
+ {
+ if (limit > 0 && written >= limit)
+ rotate ();
+ super.publish(record);
+ flush ();
+ }
+
+ /**
+ * Rotates the current log files, possibly removing one if we
+ * exceed the file count.
+ */
+ private synchronized void rotate ()
+ {
+ if (logFiles.size () > 0)
+ {
+ File f1 = null;
+ ListIterator lit = null;
+
+ // If we reach the file count, ditch the oldest file.
+ if (logFiles.size () == count)
+ {
+ f1 = new File ((String) logFiles.getLast ());
+ f1.delete ();
+ lit = logFiles.listIterator (logFiles.size () - 1);
+ }
+ // Otherwise, move the oldest to a new location.
+ else
+ {
+ String path = replaceFileNameEscapes (pattern, logFiles.size (),
+ /* unique */ 0, count);
+ f1 = new File (path);
+ logFiles.addLast (path);
+ lit = logFiles.listIterator (logFiles.size () - 1);
+ }
+
+ // Now rotate the files.
+ while (lit.hasPrevious ())
+ {
+ String s = (String) lit.previous ();
+ File f2 = new File (s);
+ f2.renameTo (f1);
+ f1 = f2;
+ }
+ }
+
+ setOutputStream (createFileStream (pattern, limit, count, append,
+ /* generation */ 0));
+
+ // Reset written count.
+ written = 0;
+ }
+
+ /**
+ * Tell if <code>pattern</code> contains the pattern sequence
+ * with character <code>escape</code>. That is, if <code>escape</code>
+ * is 'g', this method returns true if the given pattern contains
+ * "%g", and not just the substring "%g" (for example, in the case of
+ * "%%g").
+ *
+ * @param pattern The pattern to test.
+ * @param escape The escape character to search for.
+ * @return True iff the pattern contains the escape sequence with the
+ * given character.
+ */
+ private static boolean has (final String pattern, final char escape)
+ {
+ final int len = pattern.length ();
+ boolean sawPercent = false;
+ for (int i = 0; i < len; i++)
+ {
+ char c = pattern.charAt (i);
+ if (sawPercent)
+ {
+ if (c == escape)
+ return true;
+ if (c == '%') // Double percent
+ {
+ sawPercent = false;
+ continue;
+ }
+ }
+ sawPercent = (c == '%');
+ }
+ return false;
+ }
+
+ /**
+ * An output stream that tracks the number of bytes written to it.
+ */
+ private final class ostr extends FilterOutputStream
+ {
+ private ostr (OutputStream out)
+ {
+ super (out);
+ }
+
+ public void write (final int b) throws IOException
+ {
+ out.write (b);
+ FileHandler.this.written++; // FIXME: synchronize?
+ }
+
+ public void write (final byte[] b) throws IOException
+ {
+ write (b, 0, b.length);
+ }
+
+ public void write (final byte[] b, final int offset, final int length)
+ throws IOException
+ {
+ out.write (b, offset, length);
+ FileHandler.this.written += length; // FIXME: synchronize?
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/logging/Filter.java b/libjava/classpath/java/util/logging/Filter.java
new file mode 100644
index 000000000..ec4597670
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Filter.java
@@ -0,0 +1,64 @@
+/* Filter.java -- an interface for filters that decide whether a
+ LogRecord should be published or discarded
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+/**
+ * By implementing the <code>Filter</code> interface, applications
+ * can control what is being logged based on arbitrary properties,
+ * not just the severity level. Both <code>Handler</code> and
+ * <code>Logger</code> allow to register Filters whose
+ * <code>isLoggable</code> method will be called when a
+ * <code>LogRecord</code> has passed the test based on the
+ * severity level.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public interface Filter
+{
+ /**
+ * Determines whether a LogRecord should be published or discarded.
+ *
+ * @param record the <code>LogRecord</code> to be inspected.
+ *
+ * @return <code>true</code> if the record should be published,
+ * <code>false</code> if it should be discarded.
+ */
+ boolean isLoggable(LogRecord record);
+}
diff --git a/libjava/classpath/java/util/logging/Formatter.java b/libjava/classpath/java/util/logging/Formatter.java
new file mode 100644
index 000000000..feaf55315
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Formatter.java
@@ -0,0 +1,171 @@
+/* Formatter.java --
+ A class for formatting log messages by localizing message texts
+ and performing substitution of parameters
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+/**
+ * A <code>Formatter</code> supports handlers by localizing
+ * message texts and by subsituting parameter values for their
+ * placeholders.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public abstract class Formatter
+{
+ /**
+ * Constructs a new Formatter.
+ */
+ protected Formatter()
+ {
+ }
+
+
+ /**
+ * Formats a LogRecord into a string. Usually called by handlers
+ * which need a string for a log record, for example to append
+ * a record to a log file or to transmit a record over the network.
+ *
+ * @param record the log record for which a string form is requested.
+ */
+ public abstract String format(LogRecord record);
+
+
+ /**
+ * Returns a string that handlers are supposed to emit before
+ * the first log record. The base implementation returns an
+ * empty string, but subclasses such as {@link XMLFormatter}
+ * override this method in order to provide a suitable header.
+ *
+ * @return a string for the header.
+ *
+ * @param handler the handler which will prepend the returned
+ * string in front of the first log record. This method
+ * may inspect certain properties of the handler, for
+ * example its encoding, in order to construct the header.
+ */
+ public String getHead(Handler handler)
+ {
+ return "";
+ }
+
+
+ /**
+ * Returns a string that handlers are supposed to emit after
+ * the last log record. The base implementation returns an
+ * empty string, but subclasses such as {@link XMLFormatter}
+ * override this method in order to provide a suitable tail.
+ *
+ * @return a string for the header.
+ *
+ * @param handler the handler which will append the returned
+ * string after the last log record. This method
+ * may inspect certain properties of the handler
+ * in order to construct the tail.
+ */
+ public String getTail(Handler handler)
+ {
+ return "";
+ }
+
+
+ /**
+ * Formats the message part of a log record.
+ *
+ * <p>First, the Formatter localizes the record message to the
+ * default locale by looking up the message in the record's
+ * localization resource bundle. If this step fails because there
+ * is no resource bundle associated with the record, or because the
+ * record message is not a key in the bundle, the raw message is
+ * used instead.
+ *
+ * <p>Second, the Formatter substitutes appropriate strings for
+ * the message parameters. If the record returns a non-empty
+ * array for <code>getParameters()</code> and the localized
+ * message string contains the character sequence "{0", the
+ * formatter uses <code>java.text.MessageFormat</code> to format
+ * the message. Otherwise, no parameter substitution is performed.
+ *
+ * @param record the log record to be localized and formatted.
+ *
+ * @return the localized message text where parameters have been
+ * substituted by suitable strings.
+ *
+ * @throws NullPointerException if <code>record</code>
+ * is <code>null</code>.
+ */
+ public String formatMessage(LogRecord record)
+ {
+ String msg;
+ ResourceBundle bundle;
+ Object[] params;
+
+ /* This will throw a NullPointerExceptionif record is null. */
+ msg = record.getMessage();
+ if (msg == null)
+ msg = "";
+
+ /* Try to localize the message. */
+ bundle = record.getResourceBundle();
+ if (bundle != null)
+ {
+ try
+ {
+ msg = bundle.getString(msg);
+ }
+ catch (java.util.MissingResourceException _)
+ {
+ }
+ }
+
+ /* Format the message if there are parameters. */
+ params = record.getParameters();
+ if ((params != null)
+ && (params.length > 0)
+ && (msg.indexOf("{0") >= 0))
+ {
+ msg = MessageFormat.format(msg, params);
+ }
+
+ return msg;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/Handler.java b/libjava/classpath/java/util/logging/Handler.java
new file mode 100644
index 000000000..e254b4d35
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Handler.java
@@ -0,0 +1,386 @@
+/* Handler.java -- a class for publishing log messages
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * A <code>Handler</code> publishes <code>LogRecords</code> to
+ * a sink, for example a file, the console or a network socket.
+ * There are different subclasses of <code>Handler</code>
+ * to deal with different kinds of sinks.
+ *
+ * <p>FIXME: Are handlers thread-safe, or is the assumption that only
+ * loggers are, and a handler can belong only to one single logger? If
+ * the latter, should we enforce it? (Spec not clear). In any
+ * case, it needs documentation.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public abstract class Handler
+{
+ Formatter formatter;
+ Filter filter;
+ Level level;
+ ErrorManager errorManager;
+ String encoding;
+
+ /**
+ * Constructs a Handler with a logging severity level of
+ * <code>Level.ALL</code>, no formatter, no filter, and
+ * an instance of <code>ErrorManager</code> managing errors.
+ *
+ * <p><strong>Specification Note:</strong> The specification of the
+ * Java<sup>TM</sup> Logging API does not mention which character
+ * encoding is to be used by freshly constructed Handlers. The GNU
+ * implementation uses the default platform encoding, but other
+ * Java implementations might behave differently.
+ *
+ * <p><strong>Specification Note:</strong> While a freshly constructed
+ * Handler is required to have <em>no filter</em> according to the
+ * specification, <code>null</code> is not a valid parameter for
+ * <code>Handler.setFormatter</code>. Therefore, the following
+ * code will throw a <code>java.lang.NullPointerException</code>:
+ *
+ * <p><pre>Handler h = new MyConcreteSubclassOfHandler();
+h.setFormatter(h.getFormatter());</pre>
+ *
+ * It seems strange that a freshly constructed Handler is not
+ * supposed to provide a Formatter, but this is what the specification
+ * says.
+ */
+ protected Handler()
+ {
+ level = Level.ALL;
+ }
+
+
+ /**
+ * Publishes a <code>LogRecord</code> to an appropriate sink,
+ * provided the record passes all tests for being loggable. The
+ * <code>Handler</code> will localize the message of the log
+ * record and substitute any message parameters.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use use a {@link Logger}, which will
+ * create LogRecords and distribute them to registered handlers.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * @param record the log event to be published.
+ */
+ public abstract void publish(LogRecord record);
+
+
+ /**
+ * Forces any data that may have been buffered to the underlying
+ * output device.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ */
+ public abstract void flush();
+
+
+ /**
+ * Closes this <code>Handler</code> after having flushed
+ * the buffers. As soon as <code>close</code> has been called,
+ * a <code>Handler</code> should not be used anymore. Attempts
+ * to publish log records, to flush buffers, or to modify the
+ * <code>Handler</code> in any other way may throw runtime
+ * exceptions after calling <code>close</code>.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public abstract void close()
+ throws SecurityException;
+
+
+ /**
+ * Returns the <code>Formatter</code> which will be used to
+ * localize the text of log messages and to substitute
+ * message parameters. A <code>Handler</code> is encouraged,
+ * but not required to actually use an assigned
+ * <code>Formatter</code>.
+ *
+ * @return the <code>Formatter</code> being used, or
+ * <code>null</code> if this <code>Handler</code>
+ * does not use formatters and no formatter has
+ * ever been set by calling <code>setFormatter</code>.
+ */
+ public Formatter getFormatter()
+ {
+ return formatter;
+ }
+
+
+ /**
+ * Sets the <code>Formatter</code> which will be used to
+ * localize the text of log messages and to substitute
+ * message parameters. A <code>Handler</code> is encouraged,
+ * but not required to actually use an assigned
+ * <code>Formatter</code>.
+ *
+ * @param formatter the new <code>Formatter</code> to use.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @throws NullPointerException if <code>formatter</code> is
+ * <code>null</code>.
+ */
+ public void setFormatter(Formatter formatter)
+ throws SecurityException
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Throws a NullPointerException if formatter is null. */
+ formatter.getClass();
+
+ this.formatter = formatter;
+ }
+
+
+ /**
+ * Returns the character encoding which this handler uses for publishing
+ * log records.
+ *
+ * @return the name of a character encoding, or <code>null</code>
+ * for the default platform encoding.
+ */
+ public String getEncoding()
+ {
+ return encoding;
+ }
+
+
+ /**
+ * Sets the character encoding which this handler uses for publishing
+ * log records. The encoding of a <code>Handler</code> must be
+ * set before any log records have been published.
+ *
+ * @param encoding the name of a character encoding, or <code>null</code>
+ * for the default encoding.
+ *
+ * @exception SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ */
+ public void setEncoding(String encoding)
+ throws SecurityException, UnsupportedEncodingException
+ {
+ /* Should any developer ever change this implementation, they are
+ * advised to have a look at StreamHandler.setEncoding(String),
+ * which overrides this method without calling super.setEncoding.
+ */
+ LogManager.getLogManager().checkAccess();
+
+ /* Simple check for supported encodings. This is more expensive
+ * than it could be, but this method is overwritten by StreamHandler
+ * anyway.
+ */
+ if (encoding != null)
+ new String(new byte[0], encoding);
+
+ this.encoding = encoding;
+ }
+
+
+ /**
+ * Returns the <code>Filter</code> that currently controls which
+ * log records are being published by this <code>Handler</code>.
+ *
+ * @return the currently active <code>Filter</code>, or
+ * <code>null</code> if no filter has been associated.
+ * In the latter case, log records are filtered purely
+ * based on their severity level.
+ */
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+
+ /**
+ * Sets the <code>Filter</code> for controlling which
+ * log records will be published by this <code>Handler</code>.
+ *
+ * @param filter the <code>Filter</code> to use, or
+ * <code>null</code> to filter log records purely based
+ * on their severity level.
+ */
+ public void setFilter(Filter filter)
+ throws SecurityException
+ {
+ LogManager.getLogManager().checkAccess();
+ this.filter = filter;
+ }
+
+
+ /**
+ * Returns the <code>ErrorManager</code> that currently deals
+ * with errors originating from this Handler.
+ *
+ * @exception SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public ErrorManager getErrorManager()
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Developers wanting to change the subsequent code should
+ * have a look at Handler.reportError -- it also can create
+ * an ErrorManager, but does so without checking permissions
+ * to control the logging infrastructure.
+ */
+ if (errorManager == null)
+ errorManager = new ErrorManager();
+
+ return errorManager;
+ }
+
+
+ public void setErrorManager(ErrorManager manager)
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Make sure manager is not null. */
+ manager.getClass();
+
+ this.errorManager = manager;
+ }
+
+
+ protected void reportError(String message, Exception ex, int code)
+ {
+ if (errorManager == null)
+ errorManager = new ErrorManager();
+
+ errorManager.error(message, ex, code);
+ }
+
+
+ /**
+ * Returns the severity level threshold for this <code>Handler</code>
+ * All log records with a lower severity level will be discarded;
+ * a log record of the same or a higher level will be published
+ * unless an installed <code>Filter</code> decides to discard it.
+ *
+ * @return the severity level below which all log messages
+ * will be discarded.
+ */
+ public Level getLevel()
+ {
+ return level;
+ }
+
+
+ /**
+ * Sets the severity level threshold for this <code>Handler</code>.
+ * All log records with a lower severity level will be discarded;
+ * a log record of the same or a higher level will be published
+ * unless an installed <code>Filter</code> decides to discard it.
+ *
+ * @param level the severity level below which all log messages
+ * will be discarded.
+ *
+ * @exception SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @exception NullPointerException if <code>level</code> is
+ * <code>null</code>.
+ */
+ public void setLevel(Level level)
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Throw NullPointerException if level is null. */
+ level.getClass();
+ this.level = level;
+ }
+
+
+ /**
+ * Checks whether a <code>LogRecord</code> would be logged
+ * if it was passed to this <code>Handler</code> for publication.
+ *
+ * <p>The <code>Handler</code> implementation considers a record as
+ * loggable if its level is greater than or equal to the severity
+ * level threshold. In a second step, if a {@link Filter} has
+ * been installed, its {@link Filter#isLoggable(LogRecord) isLoggable}
+ * method is invoked. Subclasses of <code>Handler</code> can override
+ * this method to impose their own constraints.
+ *
+ * @param record the <code>LogRecord</code> to be checked.
+ *
+ * @return <code>true</code> if <code>record</code> would
+ * be published by {@link #publish(LogRecord) publish},
+ * <code>false</code> if it would be discarded.
+ *
+ * @see #setLevel(Level)
+ * @see #setFilter(Filter)
+ * @see Filter#isLoggable(LogRecord)
+ *
+ * @throws NullPointerException if <code>record</code>
+ * is <code>null</code>.
+ */
+ public boolean isLoggable(LogRecord record)
+ {
+ if (record.getLevel().intValue() < level.intValue())
+ return false;
+
+ if (filter != null)
+ return filter.isLoggable(record);
+ else
+ return true;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/Level.java b/libjava/classpath/java/util/logging/Level.java
new file mode 100644
index 000000000..ea2c83fb2
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Level.java
@@ -0,0 +1,416 @@
+/* Level.java -- a class for indicating logging levels
+ Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import java.io.Serializable;
+import java.util.ResourceBundle;
+
+/**
+ * A class for indicating logging levels. A number of commonly used
+ * levels is pre-defined (such as <code>java.util.logging.Level.INFO</code>),
+ * and applications should utilize those whenever possible. For specialized
+ * purposes, however, applications can sub-class Level in order to define
+ * custom logging levels.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class Level implements Serializable
+{
+ /* The integer values are the same as in the Sun J2SE 1.4.
+ * They have been obtained with a test program. In J2SE 1.4.1,
+ * Sun has amended the API documentation; these values are now
+ * publicly documented.
+ */
+
+ /**
+ * The <code>OFF</code> level is used as a threshold for filtering
+ * log records, meaning that no message should be logged.
+ *
+ * @see Logger#setLevel(java.util.logging.Level)
+ */
+ public static final Level OFF = new Level ("OFF", Integer.MAX_VALUE);
+
+ /**
+ * Log records whose level is <code>SEVERE</code> indicate a serious
+ * failure that prevents normal program execution. Messages at this
+ * level should be understandable to an inexperienced, non-technical
+ * end user. Ideally, they explain in simple words what actions the
+ * user can take in order to resolve the problem.
+ */
+ public static final Level SEVERE = new Level ("SEVERE", 1000);
+
+
+ /**
+ * Log records whose level is <code>WARNING</code> indicate a
+ * potential problem that does not prevent normal program execution.
+ * Messages at this level should be understandable to an
+ * inexperienced, non-technical end user. Ideally, they explain in
+ * simple words what actions the user can take in order to resolve
+ * the problem.
+ */
+ public static final Level WARNING = new Level ("WARNING", 900);
+
+
+ /**
+ * Log records whose level is <code>INFO</code> are used in purely
+ * informational situations that do not constitute serious errors or
+ * potential problems. In the default logging configuration, INFO
+ * messages will be written to the system console. For this reason,
+ * the INFO level should be used only for messages that are
+ * important to end users and system administrators. Messages at
+ * this level should be understandable to an inexperienced,
+ * non-technical user.
+ */
+ public static final Level INFO = new Level ("INFO", 800);
+
+
+ /**
+ * Log records whose level is <code>CONFIG</code> are used for
+ * describing the static configuration, for example the windowing
+ * environment, the operating system version, etc.
+ */
+ public static final Level CONFIG = new Level ("CONFIG", 700);
+
+
+ /**
+ * Log records whose level is <code>FINE</code> are typically used
+ * for messages that are relevant for developers using
+ * the component generating log messages. Examples include minor,
+ * recoverable failures, or possible inefficiencies.
+ */
+ public static final Level FINE = new Level ("FINE", 500);
+
+
+ /**
+ * Log records whose level is <code>FINER</code> are intended for
+ * rather detailed tracing, for example entering a method, returning
+ * from a method, or throwing an exception.
+ */
+ public static final Level FINER = new Level ("FINER", 400);
+
+
+ /**
+ * Log records whose level is <code>FINEST</code> are used for
+ * highly detailed tracing, for example to indicate that a certain
+ * point inside the body of a method has been reached.
+ */
+ public static final Level FINEST = new Level ("FINEST", 300);
+
+
+ /**
+ * The <code>ALL</code> level is used as a threshold for filtering
+ * log records, meaning that every message should be logged.
+ *
+ * @see Logger#setLevel(java.util.logging.Level)
+ */
+ public static final Level ALL = new Level ("ALL", Integer.MIN_VALUE);
+
+
+ private static final Level[] knownLevels = {
+ ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, OFF
+ };
+
+
+ /**
+ * The name of the Level without localizing it, for example
+ * "WARNING".
+ */
+ private String name;
+
+
+ /**
+ * The integer value of this <code>Level</code>.
+ */
+ private int value;
+
+
+ /**
+ * The name of the resource bundle used for localizing the level
+ * name, or <code>null</code> if the name does not undergo
+ * localization.
+ */
+ private String resourceBundleName;
+
+
+ /**
+ * Creates a logging level given a name and an integer value.
+ * It rarely is necessary to create custom levels,
+ * as most applications should be well served with one of the
+ * standard levels such as <code>Level.CONFIG</code>,
+ * <code>Level.INFO</code>, or <code>Level.FINE</code>.
+ *
+ * @param name the name of the level.
+ *
+ * @param value the integer value of the level. Please note
+ * that the Java<small><sup>TM</sup></small>
+ * Logging API does not specify integer
+ * values for standard levels (such as
+ * Level.FINE). Therefore, a custom
+ * level should pass an integer value that
+ * is calculated at run-time, e.g.
+ * <code>(Level.FINE.intValue() + Level.CONFIG.intValue())
+ * / 2</code> for a level between FINE and CONFIG.
+ */
+ protected Level(String name, int value)
+ {
+ this(name, value, null);
+ }
+
+
+ /**
+ * Create a logging level given a name, an integer value and a name
+ * of a resource bundle for localizing the level name. It rarely
+ * is necessary to create custom levels, as most applications
+ * should be well served with one of the standard levels such as
+ * <code>Level.CONFIG</code>, <code>Level.INFO</code>, or
+ * <code>Level.FINE</code>.
+ *
+ * @param name the name of the level.
+ *
+ * @param value the integer value of the level. Please note
+ * that the Java<small><sup>TM</sup></small>
+ * Logging API does not specify integer
+ * values for standard levels (such as
+ * Level.FINE). Therefore, a custom
+ * level should pass an integer value that
+ * is calculated at run-time, e.g.
+ * <code>(Level.FINE.intValue() + Level.CONFIG.intValue())
+ * / 2</code> for a level between FINE and CONFIG.
+ *
+ * @param resourceBundleName the name of a resource bundle
+ * for localizing the level name, or <code>null</code>
+ * if the name does not need to be localized.
+ */
+ protected Level(String name, int value, String resourceBundleName)
+ {
+ this.name = name;
+ this.value = value;
+ this.resourceBundleName = resourceBundleName;
+ }
+
+
+ static final long serialVersionUID = -8176160795706313070L;
+
+
+ /**
+ * Checks whether the Level has the same intValue as one of the
+ * pre-defined levels. If so, the pre-defined level object is
+ * returned.
+ *
+ * <br/>Since the resource bundle name is not taken into
+ * consideration, it is possible to resolve Level objects that have
+ * been de-serialized by another implementation, even if the other
+ * implementation uses a different resource bundle for localizing
+ * the names of pre-defined levels.
+ */
+ private Object readResolve()
+ {
+ for (int i = 0; i < knownLevels.length; i++)
+ if (value == knownLevels[i].intValue())
+ return knownLevels[i];
+
+ return this;
+ }
+
+
+ /**
+ * Returns the name of the resource bundle used for localizing the
+ * level name.
+ *
+ * @return the name of the resource bundle used for localizing the
+ * level name, or <code>null</code> if the name does not undergo
+ * localization.
+ */
+ public String getResourceBundleName()
+ {
+ return resourceBundleName;
+ }
+
+
+ /**
+ * Returns the name of the Level without localizing it, for example
+ * "WARNING".
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+
+ /**
+ * Returns the name of the Level after localizing it, for example
+ * "WARNUNG".
+ */
+ public String getLocalizedName()
+ {
+ String localizedName = null;
+
+ if (resourceBundleName != null)
+ {
+ try
+ {
+ ResourceBundle b = ResourceBundle.getBundle(resourceBundleName);
+ localizedName = b.getString(name);
+ }
+ catch (Exception _)
+ {
+ }
+ }
+
+ if (localizedName != null)
+ return localizedName;
+ else
+ return name;
+ }
+
+
+ /**
+ * Returns the name of the Level without localizing it, for example
+ * "WARNING".
+ */
+ public final String toString()
+ {
+ return getName();
+ }
+
+
+ /**
+ * Returns the integer value of the Level.
+ */
+ public final int intValue()
+ {
+ return value;
+ }
+
+
+ /**
+ * Returns one of the standard Levels given either its name or its
+ * integer value. Custom subclasses of Level will not be returned
+ * by this method.
+ *
+ * @throws IllegalArgumentException if <code>name</code> is neither
+ * the name nor the integer value of one of the pre-defined standard
+ * logging levels.
+ *
+ * @throws NullPointerException if <code>name</code> is null.
+ *
+ */
+ public static Level parse(String name)
+ throws IllegalArgumentException
+ {
+ /* This will throw a NullPointerException if name is null,
+ * as required by the API specification.
+ */
+ name = name.intern();
+
+ for (int i = 0; i < knownLevels.length; i++)
+ {
+ // It's safe to use == instead of .equals here because only the
+ // standard logging levels will be returned by this method, and
+ // they are all created using string literals.
+ if (name == knownLevels[i].name)
+ return knownLevels[i];
+ }
+
+ try
+ {
+ int num = Integer.parseInt(name);
+ for (int i = 0; i < knownLevels.length; i++)
+ if (num == knownLevels[i].value)
+ return knownLevels[i];
+ }
+ catch (NumberFormatException _)
+ {
+ }
+
+ String msg = "Not the name of a standard logging level: \"" + name + "\"";
+ throw new IllegalArgumentException(msg);
+ }
+
+
+ /**
+ * Checks whether this Level's integer value is equal to that of
+ * another object.
+ *
+ * @return <code>true</code> if <code>other</code> is an instance of
+ * <code>java.util.logging.Level</code> and has the same integer
+ * value, <code>false</code> otherwise.
+ */
+ public boolean equals(Object other)
+ {
+ if (!(other instanceof Level))
+ return false;
+
+ return value == ((Level) other).value;
+ }
+
+
+ /**
+ * Returns a hash code for this Level which is based on its numeric
+ * value.
+ */
+ public int hashCode()
+ {
+ return value;
+ }
+
+
+ /**
+ * Determines whether or not this Level is one of the standard
+ * levels specified in the Logging API.
+ *
+ * <p>This method is package-private because it is not part
+ * of the logging API specification. However, an XMLFormatter
+ * is supposed to emit the numeric value for a custom log
+ * level, but the name for a pre-defined level. It seems
+ * cleaner to put this method to Level than to write some
+ * procedural code for XMLFormatter.
+ *
+ * @return <code>true</code> if this Level is a standard level,
+ * <code>false</code> otherwise.
+ */
+ final boolean isStandardLevel()
+ {
+ for (int i = 0; i < knownLevels.length; i++)
+ if (knownLevels[i] == this)
+ return true;
+
+ return false;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/LogManager.java b/libjava/classpath/java/util/logging/LogManager.java
new file mode 100644
index 000000000..dffa44d9c
--- /dev/null
+++ b/libjava/classpath/java/util/logging/LogManager.java
@@ -0,0 +1,986 @@
+/* LogManager.java -- a class for maintaining Loggers and managing
+ configuration properties
+ Copyright (C) 2002, 2005, 2006, 2007 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import gnu.classpath.SystemProperties;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * The <code>LogManager</code> maintains a hierarchical namespace
+ * of Logger objects and manages properties for configuring the logging
+ * framework. There exists only one single <code>LogManager</code>
+ * per virtual machine. This instance can be retrieved using the
+ * static method {@link #getLogManager()}.
+ *
+ * <p><strong>Configuration Process:</strong> The global LogManager
+ * object is created and configured when the class
+ * <code>java.util.logging.LogManager</code> is initialized.
+ * The configuration process includes the subsequent steps:
+ *
+ * <ul>
+ * <li>If the system property <code>java.util.logging.manager</code>
+ * is set to the name of a subclass of
+ * <code>java.util.logging.LogManager</code>, an instance of
+ * that subclass is created and becomes the global LogManager.
+ * Otherwise, a new instance of LogManager is created.</li>
+ * <li>The <code>LogManager</code> constructor tries to create
+ * a new instance of the class specified by the system
+ * property <code>java.util.logging.config.class</code>.
+ * Typically, the constructor of this class will call
+ * <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code>
+ * for configuring the logging framework.
+ * The configuration process stops at this point if
+ * the system property <code>java.util.logging.config.class</code>
+ * is set (irrespective of whether the class constructor
+ * could be called or an exception was thrown).</li>
+ *
+ * <li>If the system property <code>java.util.logging.config.class</code>
+ * is <em>not</em> set, the configuration parameters are read in from
+ * a file and passed to
+ * {@link #readConfiguration(java.io.InputStream)}.
+ * The name and location of this file are specified by the system
+ * property <code>java.util.logging.config.file</code>.</li>
+ * <li>If the system property <code>java.util.logging.config.file</code>
+ * is not set, however, the contents of the URL
+ * "{gnu.classpath.home.url}/logging.properties" are passed to
+ * {@link #readConfiguration(java.io.InputStream)}.
+ * Here, "{gnu.classpath.home.url}" stands for the value of
+ * the system property <code>gnu.classpath.home.url</code>.</li>
+ * </ul>
+ *
+ * <p>The <code>LogManager</code> has a level of <code>INFO</code> by
+ * default, and this will be inherited by <code>Logger</code>s unless they
+ * override it either by properties or programmatically.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class LogManager
+{
+ /**
+ * The object name for the logging management bean.
+ * @since 1.5
+ */
+ public static final String LOGGING_MXBEAN_NAME
+ = "java.util.logging:type=Logging";
+
+ /**
+ * The singleton LogManager instance.
+ */
+ private static LogManager logManager;
+
+ /**
+ * The singleton logging bean.
+ */
+ private static LoggingMXBean loggingBean;
+
+ /**
+ * The registered named loggers; maps the name of a Logger to
+ * a WeakReference to it.
+ */
+ private Map<String, WeakReference<Logger>> loggers;
+
+ /**
+ * The properties for the logging framework which have been
+ * read in last.
+ */
+ private Properties properties;
+
+ /**
+ * A delegate object that provides support for handling
+ * PropertyChangeEvents. The API specification does not
+ * mention which bean should be the source in the distributed
+ * PropertyChangeEvents, but Mauve test code has determined that
+ * the Sun J2SE 1.4 reference implementation uses the LogManager
+ * class object. This is somewhat strange, as the class object
+ * is not the bean with which listeners have to register, but
+ * there is no reason for the GNU Classpath implementation to
+ * behave differently from the reference implementation in
+ * this case.
+ */
+ private final PropertyChangeSupport pcs = new PropertyChangeSupport( /* source bean */
+ LogManager.class);
+
+ protected LogManager()
+ {
+ loggers = new HashMap();
+ }
+
+ /**
+ * Returns the globally shared LogManager instance.
+ */
+ public static synchronized LogManager getLogManager()
+ {
+ if (logManager == null)
+ {
+ logManager = makeLogManager();
+ initLogManager();
+ }
+ return logManager;
+ }
+
+ private static final String MANAGER_PROPERTY = "java.util.logging.manager";
+
+ private static LogManager makeLogManager()
+ {
+ String managerClassName = SystemProperties.getProperty(MANAGER_PROPERTY);
+ LogManager manager = (LogManager) createInstance
+ (managerClassName, LogManager.class, MANAGER_PROPERTY);
+ if (manager == null)
+ manager = new LogManager();
+ return manager;
+ }
+
+ private static final String CONFIG_PROPERTY = "java.util.logging.config.class";
+
+ private static void initLogManager()
+ {
+ LogManager manager = getLogManager();
+ Logger.root.setLevel(Level.INFO);
+ manager.addLogger(Logger.root);
+
+ /* The Javadoc description of the class explains
+ * what is going on here.
+ */
+ Object configurator = createInstance(System.getProperty(CONFIG_PROPERTY),
+ /* must be instance of */ Object.class,
+ CONFIG_PROPERTY);
+
+ try
+ {
+ if (configurator == null)
+ manager.readConfiguration();
+ }
+ catch (IOException ex)
+ {
+ /* FIXME: Is it ok to ignore exceptions here? */
+ }
+ }
+
+ /**
+ * Registers a listener which will be notified when the
+ * logging properties are re-read.
+ */
+ public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ /* do not register null. */
+ listener.getClass();
+
+ pcs.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Unregisters a listener.
+ *
+ * If <code>listener</code> has not been registered previously,
+ * nothing happens. Also, no exception is thrown if
+ * <code>listener</code> is <code>null</code>.
+ */
+ public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ if (listener != null)
+ pcs.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Adds a named logger. If a logger with the same name has
+ * already been registered, the method returns <code>false</code>
+ * without adding the logger.
+ *
+ * <p>The <code>LogManager</code> only keeps weak references
+ * to registered loggers. Therefore, names can become available
+ * after automatic garbage collection.
+ *
+ * @param logger the logger to be added.
+ *
+ * @return <code>true</code>if <code>logger</code> was added,
+ * <code>false</code> otherwise.
+ *
+ * @throws NullPointerException if <code>name</code> is
+ * <code>null</code>.
+ */
+ public synchronized boolean addLogger(Logger logger)
+ {
+ /* To developers thinking about to remove the 'synchronized'
+ * declaration from this method: Please read the comment
+ * in java.util.logging.Logger.getLogger(String, String)
+ * and make sure that whatever you change wrt. synchronization
+ * does not endanger thread-safety of Logger.getLogger.
+ * The current implementation of Logger.getLogger assumes
+ * that LogManager does its synchronization on the globally
+ * shared instance of LogManager.
+ */
+ String name;
+ WeakReference ref;
+
+ /* This will throw a NullPointerException if logger is null,
+ * as required by the API specification.
+ */
+ name = logger.getName();
+
+ ref = loggers.get(name);
+ if (ref != null)
+ {
+ if (ref.get() != null)
+ return false;
+
+ /* There has been a logger under this name in the past,
+ * but it has been garbage collected.
+ */
+ loggers.remove(ref);
+ }
+
+ /* Adding a named logger requires a security permission. */
+ if ((name != null) && ! name.equals(""))
+ checkAccess();
+
+ Logger parent = findAncestor(logger);
+ loggers.put(name, new WeakReference<Logger>(logger));
+ if (parent != logger.getParent())
+ logger.setParent(parent);
+
+ // The level of the newly added logger must be specified.
+ // The easiest case is if there is a level for exactly this logger
+ // in the properties. If no such level exists the level needs to be
+ // searched along the hirachy. So if there is a new logger 'foo.blah.blub'
+ // and an existing parent logger 'foo' the properties 'foo.blah.blub.level'
+ // and 'foo.blah.level' need to be checked. If both do not exist in the
+ // properties the level of the new logger is set to 'null' (i.e. it uses the
+ // level of its parent 'foo').
+ Level logLevel = logger.getLevel();
+ String searchName = name;
+ String parentName = parent != null ? parent.getName() : "";
+ while (logLevel == null && ! searchName.equals(parentName))
+ {
+ logLevel = getLevelProperty(searchName + ".level", logLevel);
+ int index = searchName.lastIndexOf('.');
+ if(index > -1)
+ searchName = searchName.substring(0,index);
+ else
+ searchName = "";
+ }
+ logger.setLevel(logLevel);
+
+ /* It can happen that existing loggers should be children of
+ * the newly added logger. For example, assume that there
+ * already exist loggers under the names "", "foo", and "foo.bar.baz".
+ * When adding "foo.bar", the logger "foo.bar.baz" should change
+ * its parent to "foo.bar".
+ */
+ for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
+ {
+ Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next()))
+ .get();
+ if ((possChild == null) || (possChild == logger)
+ || (possChild.getParent() != parent))
+ continue;
+
+ if (! possChild.getName().startsWith(name))
+ continue;
+
+ if (possChild.getName().charAt(name.length()) != '.')
+ continue;
+
+ possChild.setParent(logger);
+ }
+
+ return true;
+ }
+
+ /**
+ * Finds the closest ancestor for a logger among the currently
+ * registered ones. For example, if the currently registered
+ * loggers have the names "", "foo", and "foo.bar", the result for
+ * "foo.bar.baz" will be the logger whose name is "foo.bar".
+ *
+ * @param child a logger for whose name no logger has been
+ * registered.
+ *
+ * @return the closest ancestor for <code>child</code>,
+ * or <code>null</code> if <code>child</code>
+ * is the root logger.
+ *
+ * @throws NullPointerException if <code>child</code>
+ * is <code>null</code>.
+ */
+ private synchronized Logger findAncestor(Logger child)
+ {
+ String childName = child.getName();
+ int childNameLength = childName.length();
+ Logger best = Logger.root;
+ int bestNameLength = 0;
+
+ Logger cand;
+ int candNameLength;
+
+ if (child == Logger.root)
+ return null;
+
+ for (String candName : loggers.keySet())
+ {
+ candNameLength = candName.length();
+
+ if (candNameLength > bestNameLength
+ && childNameLength > candNameLength
+ && childName.startsWith(candName)
+ && childName.charAt(candNameLength) == '.')
+ {
+ cand = loggers.get(candName).get();
+ if ((cand == null) || (cand == child))
+ continue;
+
+ bestNameLength = candName.length();
+ best = cand;
+ }
+ }
+
+ return best;
+ }
+
+ /**
+ * Returns a Logger given its name.
+ *
+ * @param name the name of the logger.
+ *
+ * @return a named Logger, or <code>null</code> if there is no
+ * logger with that name.
+ *
+ * @throw java.lang.NullPointerException if <code>name</code>
+ * is <code>null</code>.
+ */
+ public synchronized Logger getLogger(String name)
+ {
+ WeakReference<Logger> ref;
+
+ /* Throw a NullPointerException if name is null. */
+ name.getClass();
+
+ ref = loggers.get(name);
+ if (ref != null)
+ return ref.get();
+ else
+ return null;
+ }
+
+ /**
+ * Returns an Enumeration of currently registered Logger names.
+ * Since other threads can register loggers at any time, the
+ * result could be different any time this method is called.
+ *
+ * @return an Enumeration with the names of the currently
+ * registered Loggers.
+ */
+ public synchronized Enumeration<String> getLoggerNames()
+ {
+ return Collections.enumeration(loggers.keySet());
+ }
+
+ /**
+ * Resets the logging configuration by removing all handlers for
+ * registered named loggers and setting their level to <code>null</code>.
+ * The level of the root logger will be set to <code>Level.INFO</code>.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public synchronized void reset() throws SecurityException
+ {
+ /* Throw a SecurityException if the caller does not have the
+ * permission to control the logging infrastructure.
+ */
+ checkAccess();
+
+ properties = new Properties();
+
+ Iterator<WeakReference<Logger>> iter = loggers.values().iterator();
+ while (iter.hasNext())
+ {
+ WeakReference<Logger> ref;
+ Logger logger;
+
+ ref = iter.next();
+ if (ref != null)
+ {
+ logger = ref.get();
+
+ if (logger == null)
+ iter.remove();
+ else if (logger != Logger.root)
+ {
+ logger.resetLogger();
+ logger.setLevel(null);
+ }
+ }
+ }
+
+ Logger.root.setLevel(Level.INFO);
+ Logger.root.resetLogger();
+ }
+
+ /**
+ * Configures the logging framework by reading a configuration file.
+ * The name and location of this file are specified by the system
+ * property <code>java.util.logging.config.file</code>. If this
+ * property is not set, the URL
+ * "{gnu.classpath.home.url}/logging.properties" is taken, where
+ * "{gnu.classpath.home.url}" stands for the value of the system
+ * property <code>gnu.classpath.home.url</code>.
+ *
+ * <p>The task of configuring the framework is then delegated to
+ * {@link #readConfiguration(java.io.InputStream)}, which will
+ * notify registered listeners after having read the properties.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure, or if the caller is
+ * not granted the permission to read the configuration
+ * file.
+ *
+ * @throws IOException if there is a problem reading in the
+ * configuration file.
+ */
+ public synchronized void readConfiguration()
+ throws IOException, SecurityException
+ {
+ String path;
+ InputStream inputStream;
+
+ path = System.getProperty("java.util.logging.config.file");
+ if ((path == null) || (path.length() == 0))
+ {
+ String url = (System.getProperty("gnu.classpath.home.url")
+ + "/logging.properties");
+ try
+ {
+ inputStream = new URL(url).openStream();
+ }
+ catch (Exception e)
+ {
+ inputStream=null;
+ }
+
+ // If no config file could be found use a default configuration.
+ if(inputStream == null)
+ {
+ String defaultConfig = "handlers = java.util.logging.ConsoleHandler \n"
+ + ".level=INFO \n";
+ inputStream = new ByteArrayInputStream(defaultConfig.getBytes());
+ }
+ }
+ else
+ inputStream = new java.io.FileInputStream(path);
+
+ try
+ {
+ readConfiguration(inputStream);
+ }
+ finally
+ {
+ // Close the stream in order to save
+ // resources such as file descriptors.
+ inputStream.close();
+ }
+ }
+
+ public synchronized void readConfiguration(InputStream inputStream)
+ throws IOException, SecurityException
+ {
+ Properties newProperties;
+ Enumeration keys;
+
+ checkAccess();
+ newProperties = new Properties();
+ newProperties.load(inputStream);
+ reset();
+ this.properties = newProperties;
+ keys = newProperties.propertyNames();
+
+ while (keys.hasMoreElements())
+ {
+ String key = ((String) keys.nextElement()).trim();
+ String value = newProperties.getProperty(key);
+
+ if (value == null)
+ continue;
+
+ value = value.trim();
+
+ if ("handlers".equals(key))
+ {
+ // In Java 5 and earlier this was specified to be
+ // whitespace-separated, but in reality it also accepted
+ // commas (tomcat relied on this), and in Java 6 the
+ // documentation was updated to fit the implementation.
+ StringTokenizer tokenizer = new StringTokenizer(value,
+ " \t\n\r\f,");
+ while (tokenizer.hasMoreTokens())
+ {
+ String handlerName = tokenizer.nextToken();
+ Handler handler = (Handler)
+ createInstance(handlerName, Handler.class, key);
+ // Tomcat also relies on the implementation ignoring
+ // items in 'handlers' which are not class names.
+ if (handler != null)
+ Logger.root.addHandler(handler);
+ }
+ }
+
+ if (key.endsWith(".level"))
+ {
+ String loggerName = key.substring(0, key.length() - 6);
+ Logger logger = getLogger(loggerName);
+
+ if (logger == null)
+ {
+ logger = Logger.getLogger(loggerName);
+ addLogger(logger);
+ }
+ Level level = null;
+ try
+ {
+ level = Level.parse(value);
+ }
+ catch (IllegalArgumentException e)
+ {
+ warn("bad level \'" + value + "\'", e);
+ }
+ if (level != null)
+ {
+ logger.setLevel(level);
+ }
+ continue;
+ }
+ }
+
+ /* The API specification does not talk about the
+ * property name that is distributed with the
+ * PropertyChangeEvent. With test code, it could
+ * be determined that the Sun J2SE 1.4 reference
+ * implementation uses null for the property name.
+ */
+ pcs.firePropertyChange(null, null, null);
+ }
+
+ /**
+ * Returns the value of a configuration property as a String.
+ */
+ public synchronized String getProperty(String name)
+ {
+ if (properties != null)
+ return properties.getProperty(name);
+ else
+ return null;
+ }
+
+ /**
+ * Returns the value of a configuration property as an integer.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is not an integer
+ * number.
+ */
+ static int getIntProperty(String name, int defaultValue)
+ {
+ try
+ {
+ return Integer.parseInt(getLogManager().getProperty(name));
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as an integer,
+ * provided it is inside the acceptable range.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param minValue the lowest acceptable value.
+ *
+ * @param maxValue the highest acceptable value.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is not an integer
+ * number, or if it is less than the minimum value,
+ * or if it is greater than the maximum value.
+ */
+ static int getIntPropertyClamped(String name, int defaultValue,
+ int minValue, int maxValue)
+ {
+ int val = getIntProperty(name, defaultValue);
+ if ((val < minValue) || (val > maxValue))
+ val = defaultValue;
+ return val;
+ }
+
+ /**
+ * Returns the value of a configuration property as a boolean.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is neither
+ * <code>"true"</code> nor <code>"false"</code>.
+ */
+ static boolean getBooleanProperty(String name, boolean defaultValue)
+ {
+ try
+ {
+ return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue();
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as a Level.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param propertyName the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if
+ * {@link Level#parse(java.lang.String)} does not like
+ * the property value.
+ */
+ static Level getLevelProperty(String propertyName, Level defaultValue)
+ {
+ try
+ {
+ String value = getLogManager().getProperty(propertyName);
+ if (value != null)
+ return Level.parse(getLogManager().getProperty(propertyName));
+ else
+ return defaultValue;
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as a Class.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param propertyName the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if it does not specify
+ * the name of a loadable class.
+ */
+ static final Class getClassProperty(String propertyName, Class defaultValue)
+ {
+ String propertyValue = logManager.getProperty(propertyName);
+
+ if (propertyValue != null)
+ try
+ {
+ return locateClass(propertyValue);
+ }
+ catch (ClassNotFoundException e)
+ {
+ warn(propertyName + " = " + propertyValue, e);
+ }
+
+ return defaultValue;
+ }
+
+ static final Object getInstanceProperty(String propertyName, Class ofClass,
+ Class defaultClass)
+ {
+ Class klass = getClassProperty(propertyName, defaultClass);
+ if (klass == null)
+ return null;
+
+ try
+ {
+ Object obj = klass.newInstance();
+ if (ofClass.isInstance(obj))
+ return obj;
+ }
+ catch (InstantiationException e)
+ {
+ warn(propertyName + " = " + klass.getName(), e);
+ }
+ catch (IllegalAccessException e)
+ {
+ warn(propertyName + " = " + klass.getName(), e);
+ }
+
+ if (defaultClass == null)
+ return null;
+
+ try
+ {
+ return defaultClass.newInstance();
+ }
+ catch (java.lang.InstantiationException ex)
+ {
+ throw new RuntimeException(ex.getMessage());
+ }
+ catch (java.lang.IllegalAccessException ex)
+ {
+ throw new RuntimeException(ex.getMessage());
+ }
+ }
+
+ /**
+ * An instance of <code>LoggingPermission("control")</code>
+ * that is shared between calls to <code>checkAccess()</code>.
+ */
+ private static final LoggingPermission controlPermission = new LoggingPermission("control",
+ null);
+
+ /**
+ * Checks whether the current security context allows changing
+ * the configuration of the logging framework. For the security
+ * context to be trusted, it has to be granted
+ * a LoggingPermission("control").
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public void checkAccess() throws SecurityException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(controlPermission);
+ }
+
+ /**
+ * Creates a new instance of a class specified by name and verifies
+ * that it is an instance (or subclass of) a given type.
+ *
+ * @param className the name of the class of which a new instance
+ * should be created.
+ *
+ * @param type the object created must be an instance of
+ * <code>type</code> or any subclass of <code>type</code>
+ *
+ * @param property the system property to reference in error
+ * messages
+ *
+ * @return the new instance, or <code>null</code> if
+ * <code>className</code> is <code>null</code>, if no class
+ * with that name could be found, if there was an error
+ * loading that class, or if the constructor of the class
+ * has thrown an exception.
+ */
+ private static final Object createInstance(String className, Class type,
+ String property)
+ {
+ Class klass = null;
+
+ if ((className == null) || (className.length() == 0))
+ return null;
+
+ try
+ {
+ klass = locateClass(className);
+ if (type.isAssignableFrom(klass))
+ return klass.newInstance();
+ warn(property, className, "not an instance of " + type.getName());
+ }
+ catch (ClassNotFoundException e)
+ {
+ warn(property, className, "class not found", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ warn(property, className, "illegal access", e);
+ }
+ catch (InstantiationException e)
+ {
+ warn(property, className, e);
+ }
+ catch (java.lang.LinkageError e)
+ {
+ warn(property, className, "linkage error", e);
+ }
+
+ return null;
+ }
+
+ private static final void warn(String property, String klass, Throwable t)
+ {
+ warn(property, klass, null, t);
+ }
+
+ private static final void warn(String property, String klass, String msg)
+ {
+ warn(property, klass, msg, null);
+ }
+
+ private static final void warn(String property, String klass, String msg,
+ Throwable t)
+ {
+ warn("error instantiating '" + klass + "' referenced by " + property +
+ (msg == null ? "" : ", " + msg), t);
+ }
+
+ /**
+ * All debug warnings go through this method.
+ */
+
+ private static final void warn(String msg, Throwable t)
+ {
+ System.err.println("WARNING: " + msg);
+ if (t != null)
+ t.printStackTrace(System.err);
+ }
+
+ /**
+ * Locates a class by first checking the system class loader and
+ * then checking the context class loader.
+ *
+ * @param name the fully qualified name of the Class to locate
+ * @return Class the located Class
+ */
+
+ private static Class locateClass(String name) throws ClassNotFoundException
+ {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ return Class.forName(name, true, loader);
+ }
+ catch (ClassNotFoundException e)
+ {
+ loader = ClassLoader.getSystemClassLoader();
+ return Class.forName(name, true, loader);
+ }
+ }
+
+ /**
+ * Return the logging bean. There is a single logging bean per
+ * VM instance.
+ * @since 1.5
+ */
+ public static synchronized LoggingMXBean getLoggingMXBean()
+ {
+ if (loggingBean == null)
+ {
+ loggingBean = new LoggingMXBean()
+ {
+ public String getLoggerLevel(String logger)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ return null;
+ Level lev = l.getLevel();
+ if (lev == null)
+ return "";
+ return lev.getName();
+ }
+
+ public List getLoggerNames()
+ {
+ LogManager mgr = getLogManager();
+ // This is inefficient, but perhaps better for maintenance.
+ return Collections.list(mgr.getLoggerNames());
+ }
+
+ public String getParentLoggerName(String logger)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ return null;
+ l = l.getParent();
+ if (l == null)
+ return "";
+ return l.getName();
+ }
+
+ public void setLoggerLevel(String logger, String level)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ throw new IllegalArgumentException("no logger named " + logger);
+ Level newLevel;
+ if (level == null)
+ newLevel = null;
+ else
+ newLevel = Level.parse(level);
+ l.setLevel(newLevel);
+ }
+ };
+ }
+ return loggingBean;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/LogRecord.java b/libjava/classpath/java/util/logging/LogRecord.java
new file mode 100644
index 000000000..ee99ee69a
--- /dev/null
+++ b/libjava/classpath/java/util/logging/LogRecord.java
@@ -0,0 +1,672 @@
+/* LogRecord.java --
+ A class for the state associated with individual logging events
+ Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import java.util.ResourceBundle;
+
+
+/**
+ * A <code>LogRecord</code> contains the state for an individual
+ * event to be logged.
+ *
+ * <p>As soon as a LogRecord instance has been handed over to the
+ * logging framework, applications should not manipulate it anymore.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class LogRecord
+ implements java.io.Serializable
+{
+ /**
+ * The severity level of this <code>LogRecord</code>.
+ */
+ private Level level;
+
+
+ /**
+ * The sequence number of this <code>LogRecord</code>.
+ */
+ private long sequenceNumber;
+
+
+ /**
+ * The name of the class that issued the logging request, or
+ * <code>null</code> if this information could not be obtained.
+ */
+ private String sourceClassName;
+
+
+ /**
+ * The name of the method that issued the logging request, or
+ * <code>null</code> if this information could not be obtained.
+ */
+ private String sourceMethodName;
+
+
+ /**
+ * The message for this <code>LogRecord</code> before
+ * any localization or formatting.
+ */
+ private String message;
+
+
+ /**
+ * An identifier for the thread in which this <code>LogRecord</code>
+ * was created. The identifier is not necessarily related to any
+ * thread identifiers used by the operating system.
+ */
+ private int threadID;
+
+
+ /**
+ * The time when this <code>LogRecord</code> was created,
+ * in milliseconds since the beginning of January 1, 1970.
+ */
+ private long millis;
+
+
+ /**
+ * The Throwable associated with this <code>LogRecord</code>, or
+ * <code>null</code> if the logged event is not related to an
+ * exception or error.
+ */
+ private Throwable thrown;
+
+
+ /**
+ * The name of the logger where this <code>LogRecord</code> has
+ * originated, or <code>null</code> if this <code>LogRecord</code>
+ * does not originate from a <code>Logger</code>.
+ */
+ private String loggerName;
+
+
+ /**
+ * The name of the resource bundle used for localizing log messages,
+ * or <code>null</code> if no bundle has been specified.
+ */
+ private String resourceBundleName;
+
+ private transient Object[] parameters;
+
+ private transient ResourceBundle bundle;
+
+
+ /**
+ * Constructs a <code>LogRecord</code> given a severity level and
+ * an unlocalized message text. In addition, the sequence number,
+ * creation time (as returned by <code>getMillis()</code>) and
+ * thread ID are assigned. All other properties are set to
+ * <code>null</code>.
+ *
+ * @param level the severity level, for example <code>Level.WARNING</code>.
+ *
+ * @param message the message text (which will be used as key
+ * for looking up the localized message text
+ * if a resource bundle has been associated).
+ */
+ public LogRecord(Level level, String message)
+ {
+ this.level = level;
+ this.message = message;
+ this.millis = System.currentTimeMillis();
+
+ /* A subclass of java.lang.Thread could override hashCode(),
+ * in which case the result would not be guaranteed anymore
+ * to be unique among all threads. While System.identityHashCode
+ * is not necessarily unique either, it at least cannot be
+ * overridden by user code. However, is might be a good idea
+ * to use something better for generating thread IDs.
+ */
+ this.threadID = System.identityHashCode(Thread.currentThread());
+
+ sequenceNumber = allocateSeqNum();
+ }
+
+
+ /**
+ * Determined with the serialver tool of the Sun J2SE 1.4.
+ */
+ static final long serialVersionUID = 5372048053134512534L;
+
+ private void readObject(java.io.ObjectInputStream in)
+ throws java.io.IOException, java.lang.ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ /* We assume that future versions will be downwards compatible,
+ * so we can ignore the versions.
+ */
+ byte majorVersion = in.readByte();
+ byte minorVersion = in.readByte();
+
+ int numParams = in.readInt();
+ if (numParams >= 0)
+ {
+ parameters = new Object[numParams];
+ for (int i = 0; i < numParams; i++)
+ parameters[i] = in.readObject();
+ }
+ }
+
+
+ /**
+ * @serialData The default fields, followed by a major byte version
+ * number, followed by a minor byte version number, followed by
+ * information about the log record parameters. If
+ * <code>parameters</code> is <code>null</code>, the integer -1 is
+ * written, otherwise the length of the <code>parameters</code>
+ * array (which can be zero), followed by the result of calling
+ * {@link Object#toString() toString()} on the parameter (or
+ * <code>null</code> if the parameter is <code>null</code>).
+ *
+ * <p><strong>Specification Note:</strong> The Javadoc for the
+ * Sun reference implementation does not specify the version
+ * number. FIXME: Reverse-engineer the JDK and file a bug
+ * report with Sun, asking for amendment of the specification.
+ */
+ private void writeObject(java.io.ObjectOutputStream out)
+ throws java.io.IOException
+ {
+ out.defaultWriteObject();
+
+ /* Major, minor version number: The Javadoc for J2SE1.4 does not
+ * specify the values.
+ */
+ out.writeByte(0);
+ out.writeByte(0);
+
+ if (parameters == null)
+ out.writeInt(-1);
+ else
+ {
+ out.writeInt(parameters.length);
+ for (int i = 0; i < parameters.length; i++)
+ {
+ if (parameters[i] == null)
+ out.writeObject(null);
+ else
+ out.writeObject(parameters[i].toString());
+ }
+ }
+ }
+
+
+ /**
+ * Returns the name of the logger where this <code>LogRecord</code>
+ * has originated.
+ *
+ * @return the name of the source {@link Logger}, or
+ * <code>null</code> if this <code>LogRecord</code>
+ * does not originate from a <code>Logger</code>.
+ */
+ public String getLoggerName()
+ {
+ return loggerName;
+ }
+
+
+ /**
+ * Sets the name of the logger where this <code>LogRecord</code>
+ * has originated.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param name the name of the source logger, or <code>null</code> to
+ * indicate that this <code>LogRecord</code> does not
+ * originate from a <code>Logger</code>.
+ */
+ public void setLoggerName(String name)
+ {
+ loggerName = name;
+ }
+
+
+ /**
+ * Returns the resource bundle that is used when the message
+ * of this <code>LogRecord</code> needs to be localized.
+ *
+ * @return the resource bundle used for localization,
+ * or <code>null</code> if this message does not need
+ * to be localized.
+ */
+ public ResourceBundle getResourceBundle()
+ {
+ return bundle;
+ }
+
+
+ /**
+ * Sets the resource bundle that is used when the message
+ * of this <code>LogRecord</code> needs to be localized.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param bundle the resource bundle to be used, or
+ * <code>null</code> to indicate that this
+ * message does not need to be localized.
+ */
+ public void setResourceBundle(ResourceBundle bundle)
+ {
+ this.bundle = bundle;
+
+ /* FIXME: Is there a way to infer the name
+ * of a resource bundle from a ResourceBundle object?
+ */
+ this.resourceBundleName = null;
+ }
+
+
+ /**
+ * Returns the name of the resource bundle that is used when the
+ * message of this <code>LogRecord</code> needs to be localized.
+ *
+ * @return the name of the resource bundle used for localization,
+ * or <code>null</code> if this message does not need
+ * to be localized.
+ */
+ public String getResourceBundleName()
+ {
+ return resourceBundleName;
+ }
+
+
+ /**
+ * Sets the name of the resource bundle that is used when the
+ * message of this <code>LogRecord</code> needs to be localized.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param name the name of the resource bundle to be used, or
+ * <code>null</code> to indicate that this message
+ * does not need to be localized.
+ */
+ public void setResourceBundleName(String name)
+ {
+ resourceBundleName = name;
+ bundle = null;
+
+ try
+ {
+ if (resourceBundleName != null)
+ bundle = ResourceBundle.getBundle(resourceBundleName);
+ }
+ catch (java.util.MissingResourceException _)
+ {
+ }
+ }
+
+
+ /**
+ * Returns the level of the LogRecord.
+ *
+ * <p>Applications should be aware of the possibility that the
+ * result is not necessarily one of the standard logging levels,
+ * since the logging framework allows to create custom subclasses
+ * of <code>java.util.logging.Level</code>. Therefore, filters
+ * should perform checks like <code>theRecord.getLevel().intValue()
+ * == Level.INFO.intValue()</code> instead of <code>theRecord.getLevel()
+ * == Level.INFO</code>.
+ */
+ public Level getLevel()
+ {
+ return level;
+ }
+
+
+ /**
+ * Sets the severity level of this <code>LogRecord</code> to a new
+ * value.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param level the new severity level, for example
+ * <code>Level.WARNING</code>.
+ */
+ public void setLevel(Level level)
+ {
+ this.level = level;
+ }
+
+
+ /**
+ * The last used sequence number for any LogRecord.
+ */
+ private static long lastSeqNum;
+
+
+ /**
+ * Allocates a sequence number for a new LogRecord. This class
+ * method is only called by the LogRecord constructor.
+ */
+ private static synchronized long allocateSeqNum()
+ {
+ lastSeqNum += 1;
+ return lastSeqNum;
+ }
+
+
+ /**
+ * Returns the sequence number of this <code>LogRecord</code>.
+ */
+ public long getSequenceNumber()
+ {
+ return sequenceNumber;
+ }
+
+
+ /**
+ * Sets the sequence number of this <code>LogRecord</code> to a new
+ * value.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param seqNum the new sequence number.
+ */
+ public void setSequenceNumber(long seqNum)
+ {
+ this.sequenceNumber = seqNum;
+ }
+
+
+ /**
+ * Returns the name of the class where the event being logged
+ * has had its origin. This information can be passed as
+ * parameter to some logging calls, and in certain cases, the
+ * logging framework tries to determine an approximation
+ * (which may or may not be accurate).
+ *
+ * @return the name of the class that issued the logging request,
+ * or <code>null</code> if this information could not
+ * be obtained.
+ */
+ public String getSourceClassName()
+ {
+ if (sourceClassName != null)
+ return sourceClassName;
+
+ /* FIXME: Should infer this information from the call stack. */
+ return null;
+ }
+
+
+ /**
+ * Sets the name of the class where the event being logged
+ * has had its origin.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param sourceClassName the name of the class that issued the
+ * logging request, or <code>null</code> to indicate that
+ * this information could not be obtained.
+ */
+ public void setSourceClassName(String sourceClassName)
+ {
+ this.sourceClassName = sourceClassName;
+ }
+
+
+ /**
+ * Returns the name of the method where the event being logged
+ * has had its origin. This information can be passed as
+ * parameter to some logging calls, and in certain cases, the
+ * logging framework tries to determine an approximation
+ * (which may or may not be accurate).
+ *
+ * @return the name of the method that issued the logging request,
+ * or <code>null</code> if this information could not
+ * be obtained.
+ */
+ public String getSourceMethodName()
+ {
+ if (sourceMethodName != null)
+ return sourceMethodName;
+
+ /* FIXME: Should infer this information from the call stack. */
+ return null;
+ }
+
+
+ /**
+ * Sets the name of the method where the event being logged
+ * has had its origin.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param sourceMethodName the name of the method that issued the
+ * logging request, or <code>null</code> to indicate that
+ * this information could not be obtained.
+ */
+ public void setSourceMethodName(String sourceMethodName)
+ {
+ this.sourceMethodName = sourceMethodName;
+ }
+
+
+ /**
+ * Returns the message for this <code>LogRecord</code> before
+ * any localization or parameter substitution.
+ *
+ * <p>A {@link Logger} will try to localize the message
+ * if a resource bundle has been associated with this
+ * <code>LogRecord</code>. In this case, the logger will call
+ * <code>getMessage()</code> and use the result as the key
+ * for looking up the localized message in the bundle.
+ * If no bundle has been associated, or if the result of
+ * <code>getMessage()</code> is not a valid key in the
+ * bundle, the logger will use the raw message text as
+ * returned by this method.
+ *
+ * @return the message text, or <code>null</code> if there
+ * is no message text.
+ */
+ public String getMessage()
+ {
+ return message;
+ }
+
+
+ /**
+ * Sets the message for this <code>LogRecord</code>.
+ *
+ * <p>A <code>Logger</code> will try to localize the message
+ * if a resource bundle has been associated with this
+ * <code>LogRecord</code>. In this case, the logger will call
+ * <code>getMessage()</code> and use the result as the key
+ * for looking up the localized message in the bundle.
+ * If no bundle has been associated, or if the result of
+ * <code>getMessage()</code> is not a valid key in the
+ * bundle, the logger will use the raw message text as
+ * returned by this method.
+ *
+ * <p>It is possible to set the message to either an empty String or
+ * <code>null</code>, although this does not make the the message
+ * very helpful to human users.
+ *
+ * @param message the message text (which will be used as key
+ * for looking up the localized message text
+ * if a resource bundle has been associated).
+ */
+ public void setMessage(String message)
+ {
+ this.message = message;
+ }
+
+
+ /**
+ * Returns the parameters to the log message.
+ *
+ * @return the parameters to the message, or <code>null</code> if
+ * the message has no parameters.
+ */
+ public Object[] getParameters()
+ {
+ return parameters;
+ }
+
+
+ /**
+ * Sets the parameters to the log message.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param parameters the parameters to the message, or <code>null</code>
+ * to indicate that the message has no parameters.
+ */
+ public void setParameters(Object[] parameters)
+ {
+ this.parameters = parameters;
+ }
+
+
+ /**
+ * Returns an identifier for the thread in which this
+ * <code>LogRecord</code> was created. The identifier is not
+ * necessarily related to any thread identifiers used by the
+ * operating system.
+ *
+ * @return an identifier for the source thread.
+ */
+ public int getThreadID()
+ {
+ return threadID;
+ }
+
+
+ /**
+ * Sets the identifier indicating in which thread this
+ * <code>LogRecord</code> was created. The identifier is not
+ * necessarily related to any thread identifiers used by the
+ * operating system.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param threadID the identifier for the source thread.
+ */
+ public void setThreadID(int threadID)
+ {
+ this.threadID = threadID;
+ }
+
+
+ /**
+ * Returns the time when this <code>LogRecord</code> was created.
+ *
+ * @return the time of creation in milliseconds since the beginning
+ * of January 1, 1970.
+ */
+ public long getMillis()
+ {
+ return millis;
+ }
+
+
+ /**
+ * Sets the time when this <code>LogRecord</code> was created.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param millis the time of creation in milliseconds since the
+ * beginning of January 1, 1970.
+ */
+ public void setMillis(long millis)
+ {
+ this.millis = millis;
+ }
+
+
+ /**
+ * Returns the Throwable associated with this <code>LogRecord</code>,
+ * or <code>null</code> if the logged event is not related to an exception
+ * or error.
+ */
+ public Throwable getThrown()
+ {
+ return thrown;
+ }
+
+
+ /**
+ * Associates this <code>LogRecord</code> with an exception or error.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param thrown the exception or error to associate with, or
+ * <code>null</code> if this <code>LogRecord</code>
+ * should be made unrelated to an exception or error.
+ */
+ public void setThrown(Throwable thrown)
+ {
+ this.thrown = thrown;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/Logger.java b/libjava/classpath/java/util/logging/Logger.java
new file mode 100644
index 000000000..c55e133e5
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Logger.java
@@ -0,0 +1,1193 @@
+/* Logger.java -- a class for logging messages
+ Copyright (C) 2002, 2004, 2006, 2007 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * A Logger is used for logging information about events. Usually, there is a
+ * seprate logger for each subsystem or component, although there is a shared
+ * instance for components that make only occasional use of the logging
+ * framework.
+ * <p>
+ * It is common to name a logger after the name of a corresponding Java package.
+ * Loggers are organized into a hierarchical namespace; for example, the logger
+ * <code>"org.gnu.foo"</code> is the <em>parent</em> of logger
+ * <code>"org.gnu.foo.bar"</code>.
+ * <p>
+ * A logger for a named subsystem can be obtained through {@link
+ * java.util.logging.Logger#getLogger(java.lang.String)}. However, only code
+ * which has been granted the permission to control the logging infrastructure
+ * will be allowed to customize that logger. Untrusted code can obtain a
+ * private, anonymous logger through {@link #getAnonymousLogger()} if it wants
+ * to perform any modifications to the logger.
+ * <p>
+ * FIXME: Write more documentation.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class Logger
+{
+ static final Logger root = new Logger("", null);
+
+ /**
+ * A logger provided to applications that make only occasional use of the
+ * logging framework, typically early prototypes. Serious products are
+ * supposed to create and use their own Loggers, so they can be controlled
+ * individually.
+ */
+ public static final Logger global;
+
+ /**
+ * Use to lock methods on this class instead of calling synchronize on methods
+ * to avoid deadlocks. Yeah, no kidding, we got them :)
+ */
+ private static final Object[] lock = new Object[0];
+
+ static
+ {
+ // Our class might be initialized from an unprivileged context
+ global = (Logger) AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return getLogger("global");
+ }
+ });
+ }
+
+ /**
+ * The name of the Logger, or <code>null</code> if the logger is anonymous.
+ * <p>
+ * A previous version of the GNU Classpath implementation granted untrusted
+ * code the permission to control any logger whose name was null. However,
+ * test code revealed that the Sun J2SE 1.4 reference implementation enforces
+ * the security control for any logger that was not created through
+ * getAnonymousLogger, even if it has a null name. Therefore, a separate flag
+ * {@link Logger#anonymous} was introduced.
+ */
+ private final String name;
+
+ /**
+ * The name of the resource bundle used for localization.
+ * <p>
+ * This variable cannot be declared as <code>final</code> because its value
+ * can change as a result of calling getLogger(String,String).
+ */
+ private String resourceBundleName;
+
+ /**
+ * The resource bundle used for localization.
+ * <p>
+ * This variable cannot be declared as <code>final</code> because its value
+ * can change as a result of calling getLogger(String,String).
+ */
+ private ResourceBundle resourceBundle;
+
+ private Filter filter;
+
+ private final List handlerList = new java.util.ArrayList(4);
+
+ private Handler[] handlers = new Handler[0];
+
+ /**
+ * Indicates whether or not this logger is anonymous. While a
+ * LoggingPermission is required for any modifications to a normal logger,
+ * untrusted code can obtain an anonymous logger and modify it according to
+ * its needs.
+ * <p>
+ * A previous version of the GNU Classpath implementation granted access to
+ * every logger whose name was null. However, test code revealed that the Sun
+ * J2SE 1.4 reference implementation enforces the security control for any
+ * logger that was not created through getAnonymousLogger, even if it has a
+ * null name.
+ */
+ private boolean anonymous;
+
+ private boolean useParentHandlers;
+
+ private Level level;
+
+ private Logger parent;
+
+ /**
+ * Constructs a Logger for a subsystem. Most applications do not need to
+ * create new Loggers explicitly; instead, they should call the static factory
+ * methods {@link #getLogger(java.lang.String,java.lang.String) getLogger}
+ * (with ResourceBundle for localization) or
+ * {@link #getLogger(java.lang.String) getLogger} (without ResourceBundle),
+ * respectively.
+ *
+ * @param name the name for the logger, for example "java.awt" or
+ * "com.foo.bar". The name should be based on the name of the
+ * package issuing log records and consist of dot-separated Java
+ * identifiers.
+ * @param resourceBundleName the name of a resource bundle for localizing
+ * messages, or <code>null</code> to indicate that messages do
+ * not need to be localized.
+ * @throws java.util.MissingResourceException if
+ * <code>resourceBundleName</code> is not <code>null</code>
+ * and no such bundle could be located.
+ */
+ protected Logger(String name, String resourceBundleName)
+ throws MissingResourceException
+ {
+ this.name = name;
+ this.resourceBundleName = resourceBundleName;
+
+ if (resourceBundleName == null)
+ resourceBundle = null;
+ else
+ resourceBundle = ResourceBundle.getBundle(resourceBundleName);
+
+ level = null;
+
+ /*
+ * This is null when the root logger is being constructed, and the root
+ * logger afterwards.
+ */
+ parent = root;
+
+ useParentHandlers = (parent != null);
+ }
+
+ /**
+ * Finds a registered logger for a subsystem, or creates one in case no logger
+ * has been registered yet.
+ *
+ * @param name the name for the logger, for example "java.awt" or
+ * "com.foo.bar". The name should be based on the name of the
+ * package issuing log records and consist of dot-separated Java
+ * identifiers.
+ * @throws IllegalArgumentException if a logger for the subsystem identified
+ * by <code>name</code> has already been created, but uses a a
+ * resource bundle for localizing messages.
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ * @return a logger for the subsystem specified by <code>name</code> that
+ * does not localize messages.
+ */
+ public static Logger getLogger(String name)
+ {
+ return getLogger(name, null);
+ }
+
+ /**
+ * Finds a registered logger for a subsystem, or creates one in case no logger
+ * has been registered yet.
+ * <p>
+ * If a logger with the specified name has already been registered, the
+ * behavior depends on the resource bundle that is currently associated with
+ * the existing logger.
+ * <ul>
+ * <li>If the existing logger uses the same resource bundle as specified by
+ * <code>resourceBundleName</code>, the existing logger is returned.</li>
+ * <li>If the existing logger currently does not localize messages, the
+ * existing logger is modified to use the bundle specified by
+ * <code>resourceBundleName</code>. The existing logger is then returned.
+ * Therefore, all subsystems currently using this logger will produce
+ * localized messages from now on.</li>
+ * <li>If the existing logger already has an associated resource bundle, but
+ * a different one than specified by <code>resourceBundleName</code>, an
+ * <code>IllegalArgumentException</code> is thrown.</li>
+ * </ul>
+ *
+ * @param name the name for the logger, for example "java.awt" or
+ * "org.gnu.foo". The name should be based on the name of the
+ * package issuing log records and consist of dot-separated Java
+ * identifiers.
+ * @param resourceBundleName the name of a resource bundle for localizing
+ * messages, or <code>null</code> to indicate that messages do
+ * not need to be localized.
+ * @return a logger for the subsystem specified by <code>name</code>.
+ * @throws java.util.MissingResourceException if
+ * <code>resourceBundleName</code> is not <code>null</code>
+ * and no such bundle could be located.
+ * @throws IllegalArgumentException if a logger for the subsystem identified
+ * by <code>name</code> has already been created, but uses a
+ * different resource bundle for localizing messages.
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ public static Logger getLogger(String name, String resourceBundleName)
+ {
+ LogManager lm = LogManager.getLogManager();
+ Logger result;
+
+ if (name == null)
+ throw new NullPointerException();
+
+ /*
+ * Without synchronized(lm), it could happen that another thread would
+ * create a logger between our calls to getLogger and addLogger. While
+ * addLogger would indicate this by returning false, we could not be sure
+ * that this other logger was still existing when we called getLogger a
+ * second time in order to retrieve it -- note that LogManager is only
+ * allowed to keep weak references to registered loggers, so Loggers can be
+ * garbage collected at any time in general, and between our call to
+ * addLogger and our second call go getLogger in particular. Of course, we
+ * assume here that LogManager.addLogger etc. are synchronizing on the
+ * global LogManager object. There is a comment in the implementation of
+ * LogManager.addLogger referring to this comment here, so that any change
+ * in the synchronization of LogManager will be reflected here.
+ */
+ synchronized (lock)
+ {
+ synchronized (lm)
+ {
+ result = lm.getLogger(name);
+ if (result == null)
+ {
+ boolean couldBeAdded;
+
+ result = new Logger(name, resourceBundleName);
+ couldBeAdded = lm.addLogger(result);
+ if (! couldBeAdded)
+ throw new IllegalStateException("cannot register new logger");
+ }
+ else
+ {
+ /*
+ * The logger already exists. Make sure it uses the same
+ * resource bundle for localizing messages.
+ */
+ String existingBundleName = result.getResourceBundleName();
+
+ /*
+ * The Sun J2SE 1.4 reference implementation will return the
+ * registered logger object, even if it does not have a resource
+ * bundle associated with it. However, it seems to change the
+ * resourceBundle of the registered logger to the bundle whose
+ * name was passed to getLogger.
+ */
+ if ((existingBundleName == null) &&
+ (resourceBundleName != null))
+ {
+ /*
+ * If ResourceBundle.getBundle throws an exception, the
+ * existing logger will be unchanged. This would be
+ * different if the assignment to resourceBundleName came
+ * first.
+ */
+ result.resourceBundle =
+ ResourceBundle.getBundle(resourceBundleName);
+
+ result.resourceBundleName = resourceBundleName;
+ return result;
+ }
+
+ if ((existingBundleName != resourceBundleName)
+ && ((existingBundleName == null)
+ || !existingBundleName.equals(resourceBundleName)))
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a new, unnamed logger. Unnamed loggers are not registered in the
+ * namespace of the LogManager, and no special security permission is required
+ * for changing their state. Therefore, untrusted applets are able to modify
+ * their private logger instance obtained through this method.
+ * <p>
+ * The parent of the newly created logger will the the root logger, from which
+ * the level threshold and the handlers are inherited.
+ */
+ public static Logger getAnonymousLogger()
+ {
+ return getAnonymousLogger(null);
+ }
+
+ /**
+ * Creates a new, unnamed logger. Unnamed loggers are not registered in the
+ * namespace of the LogManager, and no special security permission is required
+ * for changing their state. Therefore, untrusted applets are able to modify
+ * their private logger instance obtained through this method.
+ * <p>
+ * The parent of the newly created logger will the the root logger, from which
+ * the level threshold and the handlers are inherited.
+ *
+ * @param resourceBundleName the name of a resource bundle for localizing
+ * messages, or <code>null</code> to indicate that messages do
+ * not need to be localized.
+ * @throws java.util.MissingResourceException if
+ * <code>resourceBundleName</code> is not <code>null</code>
+ * and no such bundle could be located.
+ */
+ public static Logger getAnonymousLogger(String resourceBundleName)
+ throws MissingResourceException
+ {
+ Logger result;
+
+ result = new Logger(null, resourceBundleName);
+ result.anonymous = true;
+ return result;
+ }
+
+ /**
+ * Returns the name of the resource bundle that is being used for localizing
+ * messages.
+ *
+ * @return the name of the resource bundle used for localizing messages, or
+ * <code>null</code> if the parent's resource bundle is used for
+ * this purpose.
+ */
+ public String getResourceBundleName()
+ {
+ synchronized (lock)
+ {
+ return resourceBundleName;
+ }
+ }
+
+ /**
+ * Returns the resource bundle that is being used for localizing messages.
+ *
+ * @return the resource bundle used for localizing messages, or
+ * <code>null</code> if the parent's resource bundle is used for
+ * this purpose.
+ */
+ public ResourceBundle getResourceBundle()
+ {
+ synchronized (lock)
+ {
+ return resourceBundle;
+ }
+ }
+
+ /**
+ * Returns the severity level threshold for this <code>Handler</code>. All
+ * log records with a lower severity level will be discarded; a log record of
+ * the same or a higher level will be published unless an installed
+ * <code>Filter</code> decides to discard it.
+ *
+ * @return the severity level below which all log messages will be discarded,
+ * or <code>null</code> if the logger inherits the threshold from
+ * its parent.
+ */
+ public Level getLevel()
+ {
+ synchronized (lock)
+ {
+ return level;
+ }
+ }
+
+ /**
+ * Returns whether or not a message of the specified level would be logged by
+ * this logger.
+ *
+ * @throws NullPointerException if <code>level</code> is <code>null</code>.
+ */
+ public boolean isLoggable(Level level)
+ {
+ synchronized (lock)
+ {
+ if (this.level != null)
+ return this.level.intValue() <= level.intValue();
+
+ if (parent != null)
+ return parent.isLoggable(level);
+ else
+ return false;
+ }
+ }
+
+ /**
+ * Sets the severity level threshold for this <code>Handler</code>. All log
+ * records with a lower severity level will be discarded immediately. A log
+ * record of the same or a higher level will be published unless an installed
+ * <code>Filter</code> decides to discard it.
+ *
+ * @param level the severity level below which all log messages will be
+ * discarded, or <code>null</code> to indicate that the logger
+ * should inherit the threshold from its parent.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void setLevel(Level level)
+ {
+ synchronized (lock)
+ {
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ this.level = level;
+ }
+ }
+
+ public Filter getFilter()
+ {
+ synchronized (lock)
+ {
+ return filter;
+ }
+ }
+
+ /**
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void setFilter(Filter filter) throws SecurityException
+ {
+ synchronized (lock)
+ {
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ this.filter = filter;
+ }
+ }
+
+ /**
+ * Returns the name of this logger.
+ *
+ * @return the name of this logger, or <code>null</code> if the logger is
+ * anonymous.
+ */
+ public String getName()
+ {
+ /*
+ * Note that the name of a logger cannot be changed during its lifetime, so
+ * no synchronization is needed.
+ */
+ return name;
+ }
+
+ /**
+ * Passes a record to registered handlers, provided the record is considered
+ * as loggable both by {@link #isLoggable(Level)} and a possibly installed
+ * custom {@link #setFilter(Filter) filter}.
+ * <p>
+ * If the logger has been configured to use parent handlers, the record will
+ * be forwarded to the parent of this logger in addition to being processed by
+ * the handlers registered with this logger.
+ * <p>
+ * The other logging methods in this class are convenience methods that merely
+ * create a new LogRecord and pass it to this method. Therefore, subclasses
+ * usually just need to override this single method for customizing the
+ * logging behavior.
+ *
+ * @param record the log record to be inspected and possibly forwarded.
+ */
+ public void log(LogRecord record)
+ {
+ synchronized (lock)
+ {
+ if (!isLoggable(record.getLevel()))
+ return;
+
+ if ((filter != null) && ! filter.isLoggable(record))
+ return;
+
+ /*
+ * If no logger name has been set for the log record, use the name of
+ * this logger.
+ */
+ if (record.getLoggerName() == null)
+ record.setLoggerName(name);
+
+ /*
+ * Avoid that some other thread is changing the logger hierarchy while
+ * we are traversing it.
+ */
+ synchronized (LogManager.getLogManager())
+ {
+ Logger curLogger = this;
+
+ do
+ {
+ /*
+ * The Sun J2SE 1.4 reference implementation seems to call the
+ * filter only for the logger whose log method is called, never
+ * for any of its parents. Also, parent loggers publish log
+ * record whatever their level might be. This is pretty weird,
+ * but GNU Classpath tries to be as compatible as possible to
+ * the reference implementation.
+ */
+ for (int i = 0; i < curLogger.handlers.length; i++)
+ curLogger.handlers[i].publish(record);
+
+ if (curLogger.getUseParentHandlers() == false)
+ break;
+
+ curLogger = curLogger.getParent();
+ }
+ while (parent != null);
+ }
+ }
+ }
+
+ public void log(Level level, String message)
+ {
+ if (isLoggable(level))
+ log(level, message, (Object[]) null);
+ }
+
+ public void log(Level level, String message, Object param)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(level))
+ {
+ StackTraceElement caller = getCallerStackFrame();
+ logp(level, caller != null ? caller.getClassName() : "<unknown>",
+ caller != null ? caller.getMethodName() : "<unknown>",
+ message, param);
+ }
+ }
+ }
+
+ public void log(Level level, String message, Object[] params)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(level))
+ {
+ StackTraceElement caller = getCallerStackFrame();
+ logp(level, caller != null ? caller.getClassName() : "<unknown>",
+ caller != null ? caller.getMethodName() : "<unknown>",
+ message, params);
+
+ }
+ }
+ }
+
+ public void log(Level level, String message, Throwable thrown)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(level))
+ {
+ StackTraceElement caller = getCallerStackFrame();
+ logp(level, caller != null ? caller.getClassName() : "<unknown>",
+ caller != null ? caller.getMethodName() : "<unknown>",
+ message, thrown);
+ }
+ }
+ }
+
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String message)
+ {
+ synchronized (lock)
+ {
+ logp(level, sourceClass, sourceMethod, message, (Object[]) null);
+ }
+ }
+
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String message, Object param)
+ {
+ synchronized (lock)
+ {
+ logp(level, sourceClass, sourceMethod, message, new Object[] { param });
+ }
+
+ }
+
+ private ResourceBundle findResourceBundle()
+ {
+ synchronized (lock)
+ {
+ if (resourceBundle != null)
+ return resourceBundle;
+
+ if (parent != null)
+ return parent.findResourceBundle();
+
+ return null;
+ }
+ }
+
+ private void logImpl(Level level, String sourceClass, String sourceMethod,
+ String message, Object[] params)
+ {
+ synchronized (lock)
+ {
+ LogRecord rec = new LogRecord(level, message);
+
+ rec.setResourceBundle(findResourceBundle());
+ rec.setSourceClassName(sourceClass);
+ rec.setSourceMethodName(sourceMethod);
+ rec.setParameters(params);
+
+ log(rec);
+ }
+ }
+
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String message, Object[] params)
+ {
+ synchronized (lock)
+ {
+ logImpl(level, sourceClass, sourceMethod, message, params);
+ }
+ }
+
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String message, Throwable thrown)
+ {
+ synchronized (lock)
+ {
+ LogRecord rec = new LogRecord(level, message);
+
+ rec.setResourceBundle(resourceBundle);
+ rec.setSourceClassName(sourceClass);
+ rec.setSourceMethodName(sourceMethod);
+ rec.setThrown(thrown);
+
+ log(rec);
+ }
+ }
+
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ String bundleName, String message)
+ {
+ synchronized (lock)
+ {
+ logrb(level, sourceClass, sourceMethod, bundleName, message,
+ (Object[]) null);
+ }
+ }
+
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ String bundleName, String message, Object param)
+ {
+ synchronized (lock)
+ {
+ logrb(level, sourceClass, sourceMethod, bundleName, message,
+ new Object[] { param });
+ }
+ }
+
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ String bundleName, String message, Object[] params)
+ {
+ synchronized (lock)
+ {
+ LogRecord rec = new LogRecord(level, message);
+
+ rec.setResourceBundleName(bundleName);
+ rec.setSourceClassName(sourceClass);
+ rec.setSourceMethodName(sourceMethod);
+ rec.setParameters(params);
+
+ log(rec);
+ }
+ }
+
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ String bundleName, String message, Throwable thrown)
+ {
+ synchronized (lock)
+ {
+ LogRecord rec = new LogRecord(level, message);
+
+ rec.setResourceBundleName(bundleName);
+ rec.setSourceClassName(sourceClass);
+ rec.setSourceMethodName(sourceMethod);
+ rec.setThrown(thrown);
+
+ log(rec);
+ }
+ }
+
+ public void entering(String sourceClass, String sourceMethod)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "ENTRY");
+ }
+ }
+
+ public void entering(String sourceClass, String sourceMethod, Object param)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", param);
+ }
+ }
+
+ public void entering(String sourceClass, String sourceMethod, Object[] params)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ {
+ CPStringBuilder buf = new CPStringBuilder(80);
+ buf.append("ENTRY");
+ for (int i = 0; i < params.length; i++)
+ {
+ buf.append(" {");
+ buf.append(i);
+ buf.append('}');
+ }
+
+ logp(Level.FINER, sourceClass, sourceMethod, buf.toString(), params);
+ }
+ }
+ }
+
+ public void exiting(String sourceClass, String sourceMethod)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "RETURN");
+ }
+ }
+
+ public void exiting(String sourceClass, String sourceMethod, Object result)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result);
+ }
+ }
+
+ public void throwing(String sourceClass, String sourceMethod, Throwable thrown)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "THROW", thrown);
+ }
+ }
+
+ /**
+ * Logs a message with severity level SEVERE, indicating a serious failure
+ * that prevents normal program execution. Messages at this level should be
+ * understandable to an inexperienced, non-technical end user. Ideally, they
+ * explain in simple words what actions the user can take in order to resolve
+ * the problem.
+ *
+ * @see Level#SEVERE
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void severe(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.SEVERE))
+ log(Level.SEVERE, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level WARNING, indicating a potential problem
+ * that does not prevent normal program execution. Messages at this level
+ * should be understandable to an inexperienced, non-technical end user.
+ * Ideally, they explain in simple words what actions the user can take in
+ * order to resolve the problem.
+ *
+ * @see Level#WARNING
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void warning(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.WARNING))
+ log(Level.WARNING, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level INFO. {@link Level#INFO} is intended for
+ * purely informational messages that do not indicate error or warning
+ * situations. In the default logging configuration, INFO messages will be
+ * written to the system console. For this reason, the INFO level should be
+ * used only for messages that are important to end users and system
+ * administrators. Messages at this level should be understandable to an
+ * inexperienced, non-technical user.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void info(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.INFO))
+ log(Level.INFO, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level CONFIG. {@link Level#CONFIG} is intended
+ * for static configuration messages, for example about the windowing
+ * environment, the operating system version, etc.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void config(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.CONFIG))
+ log(Level.CONFIG, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level FINE. {@link Level#FINE} is intended for
+ * messages that are relevant for developers using the component generating
+ * log messages. Examples include minor, recoverable failures, or possible
+ * inefficiencies.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void fine(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINE))
+ log(Level.FINE, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level FINER. {@link Level#FINER} is intended
+ * for rather detailed tracing, for example entering a method, returning from
+ * a method, or throwing an exception.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void finer(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ log(Level.FINER, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level FINEST. {@link Level#FINEST} is intended
+ * for highly detailed tracing, for example reaching a certain point inside
+ * the body of a method.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void finest(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINEST))
+ log(Level.FINEST, message);
+ }
+ }
+
+ /**
+ * Adds a handler to the set of handlers that get notified when a log record
+ * is to be published.
+ *
+ * @param handler the handler to be added.
+ * @throws NullPointerException if <code>handler</code> is <code>null</code>.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void addHandler(Handler handler) throws SecurityException
+ {
+ synchronized (lock)
+ {
+ if (handler == null)
+ throw new NullPointerException();
+
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ if (! handlerList.contains(handler))
+ {
+ handlerList.add(handler);
+ handlers = getHandlers();
+ }
+ }
+ }
+
+ /**
+ * Removes a handler from the set of handlers that get notified when a log
+ * record is to be published.
+ *
+ * @param handler the handler to be removed.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method {@link
+ * #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ * @throws NullPointerException if <code>handler</code> is <code>null</code>.
+ */
+ public void removeHandler(Handler handler) throws SecurityException
+ {
+ synchronized (lock)
+ {
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ if (handler == null)
+ throw new NullPointerException();
+
+ handlerList.remove(handler);
+ handlers = getHandlers();
+ }
+ }
+
+ /**
+ * Returns the handlers currently registered for this Logger. When a log
+ * record has been deemed as being loggable, it will be passed to all
+ * registered handlers for publication. In addition, if the logger uses parent
+ * handlers (see {@link #getUseParentHandlers() getUseParentHandlers} and
+ * {@link #setUseParentHandlers(boolean) setUseParentHandlers}, the log
+ * record will be passed to the parent's handlers.
+ */
+ public Handler[] getHandlers()
+ {
+ synchronized (lock)
+ {
+ /*
+ * We cannot return our internal handlers array because we do not have
+ * any guarantee that the caller would not change the array entries.
+ */
+ return (Handler[]) handlerList.toArray(new Handler[handlerList.size()]);
+ }
+ }
+
+ /**
+ * Returns whether or not this Logger forwards log records to handlers
+ * registered for its parent loggers.
+ *
+ * @return <code>false</code> if this Logger sends log records merely to
+ * Handlers registered with itself; <code>true</code> if this Logger
+ * sends log records not only to Handlers registered with itself, but
+ * also to those Handlers registered with parent loggers.
+ */
+ public boolean getUseParentHandlers()
+ {
+ synchronized (lock)
+ {
+ return useParentHandlers;
+ }
+ }
+
+ /**
+ * Sets whether or not this Logger forwards log records to handlers registered
+ * for its parent loggers.
+ *
+ * @param useParentHandlers <code>false</code> to let this Logger send log
+ * records merely to Handlers registered with itself;
+ * <code>true</code> to let this Logger send log records not only
+ * to Handlers registered with itself, but also to those Handlers
+ * registered with parent loggers.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void setUseParentHandlers(boolean useParentHandlers)
+ {
+ synchronized (lock)
+ {
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ this.useParentHandlers = useParentHandlers;
+ }
+ }
+
+ /**
+ * Returns the parent of this logger. By default, the parent is assigned by
+ * the LogManager by inspecting the logger's name.
+ *
+ * @return the parent of this logger (as detemined by the LogManager by
+ * inspecting logger names), the root logger if no other logger has a
+ * name which is a prefix of this logger's name, or <code>null</code>
+ * for the root logger.
+ */
+ public Logger getParent()
+ {
+ synchronized (lock)
+ {
+ return parent;
+ }
+ }
+
+ /**
+ * Sets the parent of this logger. Usually, applications do not call this
+ * method directly. Instead, the LogManager will ensure that the tree of
+ * loggers reflects the hierarchical logger namespace. Basically, this method
+ * should not be public at all, but the GNU implementation follows the API
+ * specification.
+ *
+ * @throws NullPointerException if <code>parent</code> is <code>null</code>.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void setParent(Logger parent)
+ {
+ synchronized (lock)
+ {
+ if (parent == null)
+ throw new NullPointerException();
+
+ if (this == root)
+ throw new IllegalArgumentException(
+ "the root logger can only have a null parent");
+
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ this.parent = parent;
+ }
+ }
+
+ /**
+ * Gets the StackTraceElement of the first class that is not this class. That
+ * should be the initial caller of a logging method.
+ *
+ * @return caller of the initial logging method or null if unknown.
+ */
+ private StackTraceElement getCallerStackFrame()
+ {
+ Throwable t = new Throwable();
+ StackTraceElement[] stackTrace = t.getStackTrace();
+ int index = 0;
+
+ // skip to stackentries until this class
+ while (index < stackTrace.length
+ && ! stackTrace[index].getClassName().equals(getClass().getName()))
+ index++;
+
+ // skip the stackentries of this class
+ while (index < stackTrace.length
+ && stackTrace[index].getClassName().equals(getClass().getName()))
+ index++;
+
+ return index < stackTrace.length ? stackTrace[index] : null;
+ }
+
+ /**
+ * Reset and close handlers attached to this logger. This function is package
+ * private because it must only be available to the LogManager.
+ */
+ void resetLogger()
+ {
+ for (int i = 0; i < handlers.length; i++)
+ {
+ handlers[i].close();
+ handlerList.remove(handlers[i]);
+ }
+ handlers = getHandlers();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/LoggingMXBean.java b/libjava/classpath/java/util/logging/LoggingMXBean.java
new file mode 100644
index 000000000..24d8834c7
--- /dev/null
+++ b/libjava/classpath/java/util/logging/LoggingMXBean.java
@@ -0,0 +1,85 @@
+/* LoggingMxBean.java -- Management interface for logging
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import java.util.List;
+
+/**
+ * This interface represents the management interface for logging.
+ * There is a single logging bean per VM instance, which can be
+ * retrieved via {@link LogManager#getLoggingMXBean()}.
+ *
+ * @since 1.5
+ */
+public interface LoggingMXBean
+{
+ /**
+ * Return the name of the logging level given the name of
+ * a logger. Returns null if no such logger exists.
+ * @param logger the logger's name
+ * @return the logging level's name, or null
+ */
+ String getLoggerLevel(String logger);
+
+ /**
+ * Return a list of all logger names.
+ */
+ List<String> getLoggerNames();
+
+ /**
+ * Return the name of the parent of the indicated logger.
+ * If no such logger exists, returns null. If the logger
+ * is the root logger, returns the empty string.
+ * @param logger the logger's name
+ * @return the name of the logger's parent, or null
+ */
+ String getParentLoggerName(String logger);
+
+ /**
+ * Sets the logging level for a particular logger.
+ *
+ * @param logger the name of the logger
+ * @param level the name of the new logging level, or null
+ * @throws IllegalArgumentException if the level is not
+ * recognized, or if the logger does not exist
+ * @throws SecurityException if access is denied;
+ * see {@link Logger#setLevel(Level)}
+ */
+ void setLoggerLevel(String logger, String level);
+}
diff --git a/libjava/classpath/java/util/logging/LoggingPermission.java b/libjava/classpath/java/util/logging/LoggingPermission.java
new file mode 100644
index 000000000..804fb9401
--- /dev/null
+++ b/libjava/classpath/java/util/logging/LoggingPermission.java
@@ -0,0 +1,75 @@
+/* LoggingPermission.java -- a class for logging permissions.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+public final class LoggingPermission
+ extends java.security.BasicPermission
+{
+ private static final long serialVersionUID = 63564341580231582L;
+
+ /**
+ * Creates a new LoggingPermission.
+ *
+ * @param name the name of the permission, which must be "control".
+ *
+ * @param actions the list of actions for the permission, which
+ * must be either <code>null</code> or an empty
+ * string.
+ *
+ * @exception IllegalArgumentException if <code>name</code>
+ * is not "control", or <code>actions</code> is
+ * neither <code>null</code> nor empty.
+ */
+ public LoggingPermission(String name, String actions)
+ {
+ super("control", "");
+
+ if (!"control".equals(name))
+ {
+ throw new IllegalArgumentException(
+ "name of LoggingPermission must be \"control\"");
+ }
+
+ if ((actions != null) && (actions.length() != 0))
+ {
+ throw new IllegalArgumentException(
+ "actions of LoggingPermissions must be null or empty");
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/logging/MemoryHandler.java b/libjava/classpath/java/util/logging/MemoryHandler.java
new file mode 100644
index 000000000..e5c258bbf
--- /dev/null
+++ b/libjava/classpath/java/util/logging/MemoryHandler.java
@@ -0,0 +1,345 @@
+/* MemoryHandler.java -- a class for buffering log messages in a memory buffer
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.logging;
+
+/**
+ * A <code>MemoryHandler</code> maintains a circular buffer of
+ * log records.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>MemoryHandler</code> is initialized.
+ * If a property is not defined, or if it has an invalid
+ * value, a default is taken without an exception being thrown.
+ *
+ * <ul>
+ * <li><code>java.util.MemoryHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.ALL</code>.</li>
+ * <li><code>java.util.MemoryHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ * <li><code>java.util.MemoryHandler.size</code> - specifies the
+ * maximum number of log records that are kept in the circular
+ * buffer. Default value: 1000.</li>
+ * <li><code>java.util.MemoryHandler.push</code> - specifies the
+ * <code>pushLevel</code>. Default value:
+ * <code>Level.SEVERE</code>.</li>
+ * <li><code>java.util.MemoryHandler.target</code> - specifies the
+ * name of a subclass of {@link Handler} that will be used as the
+ * target handler. There is no default value for this property;
+ * if it is not set, the no-argument MemoryHandler constructor
+ * will throw an exception.</li>
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class MemoryHandler
+ extends Handler
+{
+ /**
+ * The storage area used for buffering the unpushed log records in
+ * memory.
+ */
+ private final LogRecord[] buffer;
+
+
+ /**
+ * The current position in the circular buffer. For a new
+ * MemoryHandler, or immediately after {@link #push()} was called,
+ * the value of this variable is zero. Each call to {@link
+ * #publish(LogRecord)} will store the published LogRecord into
+ * <code>buffer[position]</code> before position is incremented by
+ * one. If position becomes greater than the size of the buffer, it
+ * is reset to zero.
+ */
+ private int position;
+
+
+ /**
+ * The number of log records which have been published, but not
+ * pushed yet to the target handler.
+ */
+ private int numPublished;
+
+
+ /**
+ * The push level threshold for this <code>Handler</code>. When a
+ * record is published whose severity level is greater than or equal
+ * to the <code>pushLevel</code> of this <code>MemoryHandler</code>,
+ * the {@link #push()} method will be invoked for pushing the buffer
+ * contents to the target <code>Handler</code>.
+ */
+ private Level pushLevel;
+
+
+ /**
+ * The Handler to which log records are forwarded for actual
+ * publication.
+ */
+ private final Handler target;
+
+
+ /**
+ * Constructs a <code>MemoryHandler</code> for keeping a circular
+ * buffer of LogRecords; the initial configuration is determined by
+ * the <code>LogManager</code> properties described above.
+ */
+ public MemoryHandler()
+ {
+ this((Handler) LogManager.getInstanceProperty(
+ "java.util.logging.MemoryHandler.target",
+ Handler.class, /* default */ null),
+ LogManager.getIntPropertyClamped(
+ "java.util.logging.MemoryHandler.size",
+ /* default */ 1000,
+ /* minimum value */ 1,
+ /* maximum value */ Integer.MAX_VALUE),
+ LogManager.getLevelProperty(
+ "java.util.logging.MemoryHandler.push",
+ /* default push level */ Level.SEVERE));
+ }
+
+
+ /**
+ * Constructs a <code>MemoryHandler</code> for keeping a circular
+ * buffer of LogRecords, given some parameters. The values of the
+ * other parameters are taken from LogManager properties, as
+ * described above.
+ *
+ * @param target the target handler that will receive those
+ * log records that are passed on for publication.
+ *
+ * @param size the number of log records that are kept in the buffer.
+ * The value must be a at least one.
+ *
+ * @param pushLevel the push level threshold for this
+ * <code>MemoryHandler</code>. When a record is published whose
+ * severity level is greater than or equal to
+ * <code>pushLevel</code>, the {@link #push()} method will be
+ * invoked in order to push the bufffer contents to
+ * <code>target</code>.
+ *
+ * @throws java.lang.IllegalArgumentException if <code>size</code>
+ * is negative or zero. The GNU implementation also throws
+ * an IllegalArgumentException if <code>target</code> or
+ * <code>pushLevel</code> are <code>null</code>, but the
+ * API specification does not prescribe what should happen
+ * in those cases.
+ */
+ public MemoryHandler(Handler target, int size, Level pushLevel)
+ {
+ if ((target == null) || (size <= 0) || (pushLevel == null))
+ throw new IllegalArgumentException();
+
+ buffer = new LogRecord[size];
+ this.pushLevel = pushLevel;
+ this.target = target;
+
+ setLevel(LogManager.getLevelProperty(
+ "java.util.logging.MemoryHandler.level",
+ /* default value */ Level.ALL));
+
+ setFilter((Filter) LogManager.getInstanceProperty(
+ "java.util.logging.MemoryHandler.filter",
+ /* must be instance of */ Filter.class,
+ /* default value */ null));
+ }
+
+
+ /**
+ * Stores a <code>LogRecord</code> in a fixed-size circular buffer,
+ * provided the record passes all tests for being loggable. If the
+ * buffer is full, the oldest record will be discarded.
+ *
+ * <p>If the record has a severity level which is greater than or
+ * equal to the <code>pushLevel</code> of this
+ * <code>MemoryHandler</code>, the {@link #push()} method will be
+ * invoked for pushing the buffer contents to the target
+ * <code>Handler</code>.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use use a {@link Logger}, which will create
+ * LogRecords and distribute them to registered handlers.
+ *
+ * @param record the log event to be published.
+ */
+ public void publish(LogRecord record)
+ {
+ if (!isLoggable(record))
+ return;
+
+ buffer[position] = record;
+ position = (position + 1) % buffer.length;
+ numPublished = numPublished + 1;
+
+ if (record.getLevel().intValue() >= pushLevel.intValue())
+ push();
+ }
+
+
+ /**
+ * Pushes the contents of the memory buffer to the target
+ * <code>Handler</code> and clears the buffer. Note that
+ * the target handler will discard those records that do
+ * not satisfy its own severity level threshold, or that are
+ * not considered loggable by an installed {@link Filter}.
+ *
+ * <p>In case of an I/O failure, the {@link ErrorManager} of the
+ * target <code>Handler</code> will be notified, but the caller of
+ * this method will not receive an exception.
+ */
+ public void push()
+ {
+ int i;
+
+ if (numPublished < buffer.length)
+ {
+ for (i = 0; i < position; i++)
+ target.publish(buffer[i]);
+ }
+ else
+ {
+ for (i = position; i < buffer.length; i++)
+ target.publish(buffer[i]);
+ for (i = 0; i < position; i++)
+ target.publish(buffer[i]);
+ }
+
+ numPublished = 0;
+ position = 0;
+ }
+
+
+ /**
+ * Forces any data that may have been buffered by the target
+ * <code>Handler</code> to the underlying output device, but
+ * does <em>not</em> push the contents of the circular memory
+ * buffer to the target handler.
+ *
+ * <p>In case of an I/O failure, the {@link ErrorManager} of the
+ * target <code>Handler</code> will be notified, but the caller of
+ * this method will not receive an exception.
+ *
+ * @see #push()
+ */
+ public void flush()
+ {
+ target.flush();
+ }
+
+
+ /**
+ * Closes this <code>MemoryHandler</code> and its associated target
+ * handler, discarding the contents of the memory buffer. However,
+ * any data that may have been buffered by the target
+ * <code>Handler</code> is forced to the underlying output device.
+ *
+ * <p>As soon as <code>close</code> has been called,
+ * a <code>Handler</code> should not be used anymore. Attempts
+ * to publish log records, to flush buffers, or to modify the
+ * <code>Handler</code> in any other way may throw runtime
+ * exceptions after calling <code>close</code>.</p>
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code> of
+ * the associated target <code>Handler</code> will be informed, but
+ * the caller of this method will not receive an exception.</p>
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @see #push()
+ */
+ public void close()
+ throws SecurityException
+ {
+ push();
+
+ /* This will check for LoggingPermission("control"). If the
+ * current security context does not grant this permission,
+ * push() has been executed, but this does not impose a
+ * security risk.
+ */
+ target.close();
+ }
+
+
+
+ /**
+ * Returns the push level threshold for this <code>Handler</code>.
+ * When a record is published whose severity level is greater
+ * than or equal to the <code>pushLevel</code> of this
+ * <code>MemoryHandler</code>, the {@link #push()} method will be
+ * invoked for pushing the buffer contents to the target
+ * <code>Handler</code>.
+ *
+ * @return the push level threshold for automatic pushing.
+ */
+ public Level getPushLevel()
+ {
+ return pushLevel;
+ }
+
+
+ /**
+ * Sets the push level threshold for this <code>Handler</code>.
+ * When a record is published whose severity level is greater
+ * than or equal to the <code>pushLevel</code> of this
+ * <code>MemoryHandler</code>, the {@link #push()} method will be
+ * invoked for pushing the buffer contents to the target
+ * <code>Handler</code>.
+ *
+ * @param pushLevel the push level threshold for automatic pushing.
+ *
+ * @exception SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @exception NullPointerException if <code>pushLevel</code> is
+ * <code>null</code>.
+ */
+ public void setPushLevel(Level pushLevel)
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Throws a NullPointerException if pushLevel is null. */
+ pushLevel.getClass();
+
+ this.pushLevel = pushLevel;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/SimpleFormatter.java b/libjava/classpath/java/util/logging/SimpleFormatter.java
new file mode 100644
index 000000000..da731f2b1
--- /dev/null
+++ b/libjava/classpath/java/util/logging/SimpleFormatter.java
@@ -0,0 +1,131 @@
+/* SimpleFormatter.java --
+ A class for formatting log records into short human-readable messages
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * A <code>SimpleFormatter</code> formats log records into
+ * short human-readable messages, typically one or two lines.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class SimpleFormatter
+ extends Formatter
+{
+ /**
+ * Constructs a SimpleFormatter.
+ */
+ public SimpleFormatter()
+ {
+ }
+
+
+ /**
+ * An instance of a DateFormatter that is used for formatting
+ * the time of a log record into a human-readable string,
+ * according to the rules of the current locale. The value
+ * is set after the first invocation of format, since it is
+ * common that a JVM will instantiate a SimpleFormatter without
+ * ever using it.
+ */
+ private DateFormat dateFormat;
+
+ /**
+ * The character sequence that is used to separate lines in the
+ * generated stream. Somewhat surprisingly, the Sun J2SE 1.4
+ * reference implementation always uses UNIX line endings, even on
+ * platforms that have different line ending conventions (i.e.,
+ * DOS). The GNU implementation does not replicate this bug.
+ *
+ * @see Sun bug parade, bug #4462871,
+ * "java.util.logging.SimpleFormatter uses hard-coded line separator".
+ */
+ static final String lineSep = System.getProperty("line.separator");
+
+
+ /**
+ * Formats a log record into a String.
+ *
+ * @param record the log record to be formatted.
+ *
+ * @return a short human-readable message, typically one or two
+ * lines. Lines are separated using the default platform line
+ * separator.
+ *
+ * @throws NullPointerException if <code>record</code>
+ * is <code>null</code>.
+ */
+ public String format(LogRecord record)
+ {
+ CPStringBuilder buf = new CPStringBuilder(180);
+
+ if (dateFormat == null)
+ dateFormat = DateFormat.getDateTimeInstance();
+
+ buf.append(dateFormat.format(new Date(record.getMillis())));
+ buf.append(' ');
+ buf.append(record.getSourceClassName());
+ buf.append(' ');
+ buf.append(record.getSourceMethodName());
+ buf.append(lineSep);
+
+ buf.append(record.getLevel());
+ buf.append(": ");
+ buf.append(formatMessage(record));
+
+ buf.append(lineSep);
+
+ Throwable throwable = record.getThrown();
+ if (throwable != null)
+ {
+ StringWriter sink = new StringWriter();
+ throwable.printStackTrace(new PrintWriter(sink, true));
+ buf.append(sink.toString());
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/SocketHandler.java b/libjava/classpath/java/util/logging/SocketHandler.java
new file mode 100644
index 000000000..3c17b9bbc
--- /dev/null
+++ b/libjava/classpath/java/util/logging/SocketHandler.java
@@ -0,0 +1,220 @@
+/* SocketHandler.java -- a class for publishing log messages to network sockets
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+
+/**
+ * A <code>SocketHandler</code> publishes log records to
+ * a TCP/IP socket.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>SocketHandler</code> is initialized.
+ * If a property is not defined, or if it has an invalid
+ * value, a default is taken without an exception being thrown.
+ *
+ * <ul>
+ *
+ * <li><code>java.util.SocketHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.ALL</code>.</li>
+ *
+ * <li><code>java.util.SocketHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ *
+ * <li><code>java.util.SocketHandler.formatter</code> - specifies
+ * the name of a Formatter class. Default value:
+ * <code>java.util.logging.XMLFormatter</code>.</li>
+ *
+ * <li><code>java.util.SocketHandler.encoding</code> - specifies
+ * the name of the character encoding. Default value:
+ * the default platform encoding.</li>
+ *
+ * <li><code>java.util.SocketHandler.host</code> - specifies
+ * the name of the host to which records are published.
+ * There is no default value for this property; if it is
+ * not set, the SocketHandler constructor will throw
+ * an exception.</li>
+ *
+ * <li><code>java.util.SocketHandler.port</code> - specifies
+ * the TCP/IP port to which records are published.
+ * There is no default value for this property; if it is
+ * not set, the SocketHandler constructor will throw
+ * an exception.</li>
+ *
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class SocketHandler
+ extends StreamHandler
+{
+ /**
+ * Constructs a <code>SocketHandler</code> that publishes log
+ * records to a TCP/IP socket. Tthe initial configuration is
+ * determined by the <code>LogManager</code> properties described
+ * above.
+ *
+ * @throws java.io.IOException if the connection to the specified
+ * network host and port cannot be established.
+ *
+ * @throws java.lang.IllegalArgumentException if either the
+ * <code>java.util.logging.SocketHandler.host</code>
+ * or <code>java.util.logging.SocketHandler.port</code>
+ * LogManager properties is not defined, or specifies
+ * an invalid value.
+ */
+ public SocketHandler()
+ throws java.io.IOException
+ {
+ this(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.host"),
+ getPortNumber());
+ }
+
+
+ /**
+ * Constructs a <code>SocketHandler</code> that publishes log
+ * records to a TCP/IP socket. With the exception of the internet
+ * host and port, the initial configuration is determined by the
+ * <code>LogManager</code> properties described above.
+ *
+ * @param host the Internet host to which log records will be
+ * forwarded.
+ *
+ * @param port the port at the host which will accept a request
+ * for a TCP/IP connection.
+ *
+ * @throws java.io.IOException if the connection to the specified
+ * network host and port cannot be established.
+ *
+ * @throws java.lang.IllegalArgumentException if either
+ * <code>host</code> or <code>port</code> specify
+ * an invalid value.
+ */
+ public SocketHandler(String host, int port)
+ throws java.io.IOException
+ {
+ super(createSocket(host, port),
+ "java.util.logging.SocketHandler",
+ /* default level */ Level.ALL,
+ /* formatter */ null,
+ /* default formatter */ XMLFormatter.class);
+ }
+
+
+ /**
+ * Retrieves the port number from the java.util.logging.SocketHandler.port
+ * LogManager property.
+ *
+ * @throws IllegalArgumentException if the property is not defined or
+ * does not specify an integer value.
+ */
+ private static int getPortNumber()
+ {
+ try {
+ return Integer.parseInt(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.port"));
+ } catch (Exception ex) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+
+ /**
+ * Creates an OutputStream for publishing log records to an Internet
+ * host and port. This private method is a helper for use by the
+ * constructor of SocketHandler.
+ *
+ * @param host the Internet host to which log records will be
+ * forwarded.
+ *
+ * @param port the port at the host which will accept a request
+ * for a TCP/IP connection.
+ *
+ * @throws java.io.IOException if the connection to the specified
+ * network host and port cannot be established.
+ *
+ * @throws java.lang.IllegalArgumentException if either
+ * <code>host</code> or <code>port</code> specify
+ * an invalid value.
+ */
+ private static java.io.OutputStream createSocket(String host, int port)
+ throws java.io.IOException, java.lang.IllegalArgumentException
+ {
+ java.net.Socket socket;
+
+ if ((host == null) || (port < 1))
+ throw new IllegalArgumentException();
+
+ socket = new java.net.Socket(host, port);
+
+ socket.shutdownInput();
+
+ /* The architecture of the logging framework provides replaceable
+ * formatters. Because these formatters perform their task by
+ * returning one single String for each LogRecord to be formatted,
+ * there is no need to buffer.
+ */
+ socket.setTcpNoDelay(true);
+
+ return socket.getOutputStream();
+ }
+
+
+ /**
+ * Publishes a <code>LogRecord</code> to the network socket,
+ * provided the record passes all tests for being loggable.
+ * In addition, all data that may have been buffered will
+ * be forced to the network stream.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use a {@link Logger} instance, which will
+ * create LogRecords and distribute them to registered handlers.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>SocketHandler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * @param record the log event to be published.
+ */
+ public void publish(LogRecord record)
+ {
+ super.publish(record);
+ flush();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/StreamHandler.java b/libjava/classpath/java/util/logging/StreamHandler.java
new file mode 100644
index 000000000..d74dfac0c
--- /dev/null
+++ b/libjava/classpath/java/util/logging/StreamHandler.java
@@ -0,0 +1,521 @@
+/* StreamHandler.java --
+ A class for publishing log messages to instances of java.io.OutputStream
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+/**
+ * A <code>StreamHandler</code> publishes <code>LogRecords</code> to
+ * a instances of <code>java.io.OutputStream</code>.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class StreamHandler
+ extends Handler
+{
+ private OutputStream out;
+ private Writer writer;
+
+
+ /**
+ * Indicates the current state of this StreamHandler. The value
+ * should be one of STATE_FRESH, STATE_PUBLISHED, or STATE_CLOSED.
+ */
+ private int streamState = STATE_FRESH;
+
+
+ /**
+ * streamState having this value indicates that the StreamHandler
+ * has been created, but the publish(LogRecord) method has not been
+ * called yet. If the StreamHandler has been constructed without an
+ * OutputStream, writer will be null, otherwise it is set to a
+ * freshly created OutputStreamWriter.
+ */
+ private static final int STATE_FRESH = 0;
+
+
+ /**
+ * streamState having this value indicates that the publish(LocRecord)
+ * method has been called at least once.
+ */
+ private static final int STATE_PUBLISHED = 1;
+
+
+ /**
+ * streamState having this value indicates that the close() method
+ * has been called.
+ */
+ private static final int STATE_CLOSED = 2;
+
+
+ /**
+ * Creates a <code>StreamHandler</code> without an output stream.
+ * Subclasses can later use {@link
+ * #setOutputStream(java.io.OutputStream)} to associate an output
+ * stream with this StreamHandler.
+ */
+ public StreamHandler()
+ {
+ this(null, null);
+ }
+
+
+ /**
+ * Creates a <code>StreamHandler</code> that formats log messages
+ * with the specified Formatter and publishes them to the specified
+ * output stream.
+ *
+ * @param out the output stream to which the formatted log messages
+ * are published.
+ *
+ * @param formatter the <code>Formatter</code> that will be used
+ * to format log messages.
+ */
+ public StreamHandler(OutputStream out, Formatter formatter)
+ {
+ this(out, "java.util.logging.StreamHandler", Level.INFO,
+ formatter, SimpleFormatter.class);
+ }
+
+
+ StreamHandler(
+ OutputStream out,
+ String propertyPrefix,
+ Level defaultLevel,
+ Formatter formatter, Class defaultFormatterClass)
+ {
+ this.level = LogManager.getLevelProperty(propertyPrefix + ".level",
+ defaultLevel);
+
+ this.filter = (Filter) LogManager.getInstanceProperty(
+ propertyPrefix + ".filter",
+ /* must be instance of */ Filter.class,
+ /* default: new instance of */ null);
+
+ if (formatter != null)
+ this.formatter = formatter;
+ else
+ this.formatter = (Formatter) LogManager.getInstanceProperty(
+ propertyPrefix + ".formatter",
+ /* must be instance of */ Formatter.class,
+ /* default: new instance of */ defaultFormatterClass);
+
+ try
+ {
+ String enc = LogManager.getLogManager().getProperty(propertyPrefix
+ + ".encoding");
+
+ /* make sure enc actually is a valid encoding */
+ if ((enc != null) && (enc.length() > 0))
+ new String(new byte[0], enc);
+
+ this.encoding = enc;
+ }
+ catch (Exception _)
+ {
+ }
+
+ if (out != null)
+ {
+ try
+ {
+ changeWriter(out, getEncoding());
+ }
+ catch (UnsupportedEncodingException uex)
+ {
+ /* This should never happen, since the validity of the encoding
+ * name has been checked above.
+ */
+ throw new RuntimeException(uex.getMessage());
+ }
+ }
+ }
+
+
+ private void checkOpen()
+ {
+ if (streamState == STATE_CLOSED)
+ throw new IllegalStateException(this.toString() + " has been closed");
+ }
+
+ private void checkFresh()
+ {
+ checkOpen();
+ if (streamState != STATE_FRESH)
+ throw new IllegalStateException("some log records have been published to " + this);
+ }
+
+
+ private void changeWriter(OutputStream out, String encoding)
+ throws UnsupportedEncodingException
+ {
+ OutputStreamWriter writer;
+
+ /* The logging API says that a null encoding means the default
+ * platform encoding. However, java.io.OutputStreamWriter needs
+ * another constructor for the default platform encoding,
+ * passing null would throw an exception.
+ */
+ if (encoding == null)
+ writer = new OutputStreamWriter(out);
+ else
+ writer = new OutputStreamWriter(out, encoding);
+
+ /* Closing the stream has side effects -- do this only after
+ * creating a new writer has been successful.
+ */
+ if ((streamState != STATE_FRESH) || (this.writer != null))
+ close();
+
+ this.writer = writer;
+ this.out = out;
+ this.encoding = encoding;
+ streamState = STATE_FRESH;
+ }
+
+
+ /**
+ * Sets the character encoding which this handler uses for publishing
+ * log records. The encoding of a <code>StreamHandler</code> must be
+ * set before any log records have been published.
+ *
+ * @param encoding the name of a character encoding, or <code>null</code>
+ * for the default encoding.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control the
+ * the logging infrastructure.
+ *
+ * @exception IllegalStateException if any log records have been
+ * published to this <code>StreamHandler</code> before. Please
+ * be aware that this is a pecularity of the GNU implementation.
+ * While the API specification indicates that it is an error
+ * if the encoding is set after records have been published,
+ * it does not mandate any specific behavior for that case.
+ */
+ public void setEncoding(String encoding)
+ throws SecurityException, UnsupportedEncodingException
+ {
+ /* The inherited implementation first checks whether the invoking
+ * code indeed has the permission to control the logging infra-
+ * structure, and throws a SecurityException if this was not the
+ * case.
+ *
+ * Next, it verifies that the encoding is supported and throws
+ * an UnsupportedEncodingExcpetion otherwise. Finally, it remembers
+ * the name of the encoding.
+ */
+ super.setEncoding(encoding);
+
+ checkFresh();
+
+ /* If out is null, setEncoding is being called before an output
+ * stream has been set. In that case, we need to check that the
+ * encoding is valid, and remember it if this is the case. Since
+ * this is exactly what the inherited implementation of
+ * Handler.setEncoding does, we can delegate.
+ */
+ if (out != null)
+ {
+ /* The logging API says that a null encoding means the default
+ * platform encoding. However, java.io.OutputStreamWriter needs
+ * another constructor for the default platform encoding, passing
+ * null would throw an exception.
+ */
+ if (encoding == null)
+ writer = new OutputStreamWriter(out);
+ else
+ writer = new OutputStreamWriter(out, encoding);
+ }
+ }
+
+
+ /**
+ * Changes the output stream to which this handler publishes
+ * logging records.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @throws NullPointerException if <code>out</code>
+ * is <code>null</code>.
+ */
+ protected void setOutputStream(OutputStream out)
+ throws SecurityException
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Throw a NullPointerException if out is null. */
+ out.getClass();
+
+ try
+ {
+ changeWriter(out, getEncoding());
+ }
+ catch (UnsupportedEncodingException ex)
+ {
+ /* This seems quite unlikely to happen, unless the underlying
+ * implementation of java.io.OutputStreamWriter changes its
+ * mind (at runtime) about the set of supported character
+ * encodings.
+ */
+ throw new RuntimeException(ex.getMessage());
+ }
+ }
+
+
+ /**
+ * Publishes a <code>LogRecord</code> to the associated output
+ * stream, provided the record passes all tests for being loggable.
+ * The <code>StreamHandler</code> will localize the message of the
+ * log record and substitute any message parameters.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use use a {@link Logger}, which will create
+ * LogRecords and distribute them to registered handlers.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * <p>If a log record is being published to a
+ * <code>StreamHandler</code> that has been closed earlier, the Sun
+ * J2SE 1.4 reference can be observed to silently ignore the
+ * call. The GNU implementation, however, intentionally behaves
+ * differently by informing the <code>ErrorManager</code> associated
+ * with this <code>StreamHandler</code>. Since the condition
+ * indicates a programming error, the programmer should be
+ * informed. It also seems extremely unlikely that any application
+ * would depend on the exact behavior in this rather obscure,
+ * erroneous case -- especially since the API specification does not
+ * prescribe what is supposed to happen.
+ *
+ * @param record the log event to be published.
+ */
+ public void publish(LogRecord record)
+ {
+ String formattedMessage;
+
+ if (!isLoggable(record))
+ return;
+
+ if (streamState == STATE_FRESH)
+ {
+ try
+ {
+ writer.write(formatter.getHead(this));
+ }
+ catch (java.io.IOException ex)
+ {
+ reportError(null, ex, ErrorManager.WRITE_FAILURE);
+ return;
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.GENERIC_FAILURE);
+ return;
+ }
+
+ streamState = STATE_PUBLISHED;
+ }
+
+ try
+ {
+ formattedMessage = formatter.format(record);
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.FORMAT_FAILURE);
+ return;
+ }
+
+ try
+ {
+ writer.write(formattedMessage);
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.WRITE_FAILURE);
+ }
+ }
+
+
+ /**
+ * Checks whether or not a <code>LogRecord</code> would be logged
+ * if it was passed to this <code>StreamHandler</code> for publication.
+ *
+ * <p>The <code>StreamHandler</code> implementation first checks
+ * whether a writer is present and the handler's level is greater
+ * than or equal to the severity level threshold. In a second step,
+ * if a {@link Filter} has been installed, its {@link
+ * Filter#isLoggable(LogRecord) isLoggable} method is
+ * invoked. Subclasses of <code>StreamHandler</code> can override
+ * this method to impose their own constraints.
+ *
+ * @param record the <code>LogRecord</code> to be checked.
+ *
+ * @return <code>true</code> if <code>record</code> would
+ * be published by {@link #publish(LogRecord) publish},
+ * <code>false</code> if it would be discarded.
+ *
+ * @see #setLevel(Level)
+ * @see #setFilter(Filter)
+ * @see Filter#isLoggable(LogRecord)
+ *
+ * @throws NullPointerException if <code>record</code> is
+ * <code>null</code>. */
+ public boolean isLoggable(LogRecord record)
+ {
+ return (writer != null) && super.isLoggable(record);
+ }
+
+
+ /**
+ * Forces any data that may have been buffered to the underlying
+ * output device.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * <p>If a <code>StreamHandler</code> that has been closed earlier
+ * is closed a second time, the Sun J2SE 1.4 reference can be
+ * observed to silently ignore the call. The GNU implementation,
+ * however, intentionally behaves differently by informing the
+ * <code>ErrorManager</code> associated with this
+ * <code>StreamHandler</code>. Since the condition indicates a
+ * programming error, the programmer should be informed. It also
+ * seems extremely unlikely that any application would depend on the
+ * exact behavior in this rather obscure, erroneous case --
+ * especially since the API specification does not prescribe what is
+ * supposed to happen.
+ */
+ public void flush()
+ {
+ try
+ {
+ checkOpen();
+ if (writer != null)
+ writer.flush();
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.FLUSH_FAILURE);
+ }
+ }
+
+
+ /**
+ * Closes this <code>StreamHandler</code> after having forced any
+ * data that may have been buffered to the underlying output
+ * device.
+ *
+ * <p>As soon as <code>close</code> has been called,
+ * a <code>Handler</code> should not be used anymore. Attempts
+ * to publish log records, to flush buffers, or to modify the
+ * <code>Handler</code> in any other way may throw runtime
+ * exceptions after calling <code>close</code>.</p>
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.</p>
+ *
+ * <p>If a <code>StreamHandler</code> that has been closed earlier
+ * is closed a second time, the Sun J2SE 1.4 reference can be
+ * observed to silently ignore the call. The GNU implementation,
+ * however, intentionally behaves differently by informing the
+ * <code>ErrorManager</code> associated with this
+ * <code>StreamHandler</code>. Since the condition indicates a
+ * programming error, the programmer should be informed. It also
+ * seems extremely unlikely that any application would depend on the
+ * exact behavior in this rather obscure, erroneous case --
+ * especially since the API specification does not prescribe what is
+ * supposed to happen.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public void close()
+ throws SecurityException
+ {
+ LogManager.getLogManager().checkAccess();
+
+ try
+ {
+ /* Although flush also calls checkOpen, it catches
+ * any exceptions and reports them to the ErrorManager
+ * as flush failures. However, we want to report
+ * a closed stream as a close failure, not as a
+ * flush failure here. Therefore, we call checkOpen()
+ * before flush().
+ */
+ checkOpen();
+ flush();
+
+ if (writer != null)
+ {
+ if (formatter != null)
+ {
+ /* Even if the StreamHandler has never published a record,
+ * it emits head and tail upon closing. An earlier version
+ * of the GNU Classpath implementation did not emitted
+ * anything. However, this had caused XML log files to be
+ * entirely empty instead of containing no log records.
+ */
+ if (streamState == STATE_FRESH)
+ writer.write(formatter.getHead(this));
+ if (streamState != STATE_CLOSED)
+ writer.write(formatter.getTail(this));
+ }
+ streamState = STATE_CLOSED;
+ writer.close();
+ }
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.CLOSE_FAILURE);
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/logging/XMLFormatter.java b/libjava/classpath/java/util/logging/XMLFormatter.java
new file mode 100644
index 000000000..da7c6ef29
--- /dev/null
+++ b/libjava/classpath/java/util/logging/XMLFormatter.java
@@ -0,0 +1,389 @@
+/* XMLFormatter.java --
+ A class for formatting log messages into a standard XML format
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.ResourceBundle;
+
+/**
+ * An <code>XMLFormatter</code> formats LogRecords into
+ * a standard XML format.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class XMLFormatter
+ extends Formatter
+{
+ /**
+ * Constructs a new XMLFormatter.
+ */
+ public XMLFormatter()
+ {
+ }
+
+
+ /**
+ * The character sequence that is used to separate lines in the
+ * generated XML stream. Somewhat surprisingly, the Sun J2SE 1.4
+ * reference implementation always uses UNIX line endings, even on
+ * platforms that have different line ending conventions (i.e.,
+ * DOS). The GNU Classpath implementation does not replicates this
+ * bug.
+ *
+ * See also the Sun bug parade, bug #4462871,
+ * "java.util.logging.SimpleFormatter uses hard-coded line separator".
+ */
+ private static final String lineSep = SimpleFormatter.lineSep;
+
+
+ /**
+ * A DateFormat for emitting time in the ISO 8601 format.
+ * Since the API specification of SimpleDateFormat does not talk
+ * about its thread-safety, we cannot share a singleton instance.
+ */
+ private final SimpleDateFormat iso8601
+ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+
+
+ /**
+ * Appends a line consisting of indentation, opening element tag,
+ * element content, closing element tag and line separator to
+ * a CPStringBuilder, provided that the element content is
+ * actually existing.
+ *
+ * @param buf the CPStringBuilder to which the line will be appended.
+ *
+ * @param indent the indentation level.
+ *
+ * @param tag the element tag name, for instance <code>method</code>.
+ *
+ * @param content the element content, or <code>null</code> to
+ * have no output whatsoever appended to <code>buf</code>.
+ */
+ private static void appendTag(CPStringBuilder buf, int indent,
+ String tag, String content)
+ {
+ int i;
+
+ if (content == null)
+ return;
+
+ for (i = 0; i < indent * 2; i++)
+ buf.append(' ');
+
+ buf.append("<");
+ buf.append(tag);
+ buf.append('>');
+
+ /* Append the content, but escape for XML by replacing
+ * '&', '<', '>' and all non-ASCII characters with
+ * appropriate escape sequences.
+ * The Sun J2SE 1.4 reference implementation does not
+ * escape non-ASCII characters. This is a bug in their
+ * implementation which has been reported in the Java
+ * bug parade as bug number (FIXME: Insert number here).
+ */
+ for (i = 0; i < content.length(); i++)
+ {
+ char c = content.charAt(i);
+ switch (c)
+ {
+ case '&':
+ buf.append("&amp;");
+ break;
+
+ case '<':
+ buf.append("&lt;");
+ break;
+
+ case '>':
+ buf.append("&gt;");
+ break;
+
+ default:
+ if (((c >= 0x20) && (c <= 0x7e))
+ || (c == /* line feed */ 10)
+ || (c == /* carriage return */ 13))
+ buf.append(c);
+ else
+ {
+ buf.append("&#");
+ buf.append((int) c);
+ buf.append(';');
+ }
+ break;
+ } /* switch (c) */
+ } /* for i */
+
+ buf.append("</");
+ buf.append(tag);
+ buf.append(">");
+ buf.append(lineSep);
+ }
+
+
+ /**
+ * Appends a line consisting of indentation, opening element tag,
+ * numeric element content, closing element tag and line separator
+ * to a CPStringBuilder.
+ *
+ * @param buf the CPStringBuilder to which the line will be appended.
+ *
+ * @param indent the indentation level.
+ *
+ * @param tag the element tag name, for instance <code>method</code>.
+ *
+ * @param content the element content.
+ */
+ private static void appendTag(CPStringBuilder buf, int indent,
+ String tag, long content)
+ {
+ appendTag(buf, indent, tag, Long.toString(content));
+ }
+
+
+ public String format(LogRecord record)
+ {
+ CPStringBuilder buf = new CPStringBuilder(400);
+ Level level = record.getLevel();
+ long millis = record.getMillis();
+ Object[] params = record.getParameters();
+ ResourceBundle bundle = record.getResourceBundle();
+ String message;
+
+ buf.append("<record>");
+ buf.append(lineSep);
+
+
+ appendTag(buf, 1, "date", iso8601.format(new Date(millis)));
+ appendTag(buf, 1, "millis", millis);
+ appendTag(buf, 1, "sequence", record.getSequenceNumber());
+ appendTag(buf, 1, "logger", record.getLoggerName());
+
+ if (level.isStandardLevel())
+ appendTag(buf, 1, "level", level.toString());
+ else
+ appendTag(buf, 1, "level", level.intValue());
+
+ appendTag(buf, 1, "class", record.getSourceClassName());
+ appendTag(buf, 1, "method", record.getSourceMethodName());
+ appendTag(buf, 1, "thread", record.getThreadID());
+
+ /* The Sun J2SE 1.4 reference implementation does not emit the
+ * message in localized form. This is in violation of the API
+ * specification. The GNU Classpath implementation intentionally
+ * replicates the buggy behavior of the Sun implementation, as
+ * different log files might be a big nuisance to users.
+ */
+ try
+ {
+ record.setResourceBundle(null);
+ message = formatMessage(record);
+ }
+ finally
+ {
+ record.setResourceBundle(bundle);
+ }
+ appendTag(buf, 1, "message", message);
+
+ /* The Sun J2SE 1.4 reference implementation does not
+ * emit key, catalog and param tags. This is in violation
+ * of the API specification. The Classpath implementation
+ * intentionally replicates the buggy behavior of the
+ * Sun implementation, as different log files might be
+ * a big nuisance to users.
+ *
+ * FIXME: File a bug report with Sun. Insert bug number here.
+ *
+ *
+ * key = record.getMessage();
+ * if (key == null)
+ * key = "";
+ *
+ * if ((bundle != null) && !key.equals(message))
+ * {
+ * appendTag(buf, 1, "key", key);
+ * appendTag(buf, 1, "catalog", record.getResourceBundleName());
+ * }
+ *
+ * if (params != null)
+ * {
+ * for (int i = 0; i < params.length; i++)
+ * appendTag(buf, 1, "param", params[i].toString());
+ * }
+ */
+
+ /* FIXME: We have no way to obtain the stacktrace before free JVMs
+ * support the corresponding method in java.lang.Throwable. Well,
+ * it would be possible to parse the output of printStackTrace,
+ * but this would be pretty kludgy. Instead, we postpose the
+ * implementation until Throwable has made progress.
+ */
+ Throwable thrown = record.getThrown();
+ if (thrown != null)
+ {
+ buf.append(" <exception>");
+ buf.append(lineSep);
+
+ /* The API specification is not clear about what exactly
+ * goes into the XML record for a thrown exception: It
+ * could be the result of getMessage(), getLocalizedMessage(),
+ * or toString(). Therefore, it was necessary to write a
+ * Mauve testlet and run it with the Sun J2SE 1.4 reference
+ * implementation. It turned out that the we need to call
+ * toString().
+ *
+ * FIXME: File a bug report with Sun, asking for clearer
+ * specs.
+ */
+ appendTag(buf, 2, "message", thrown.toString());
+
+ /* FIXME: The Logging DTD specifies:
+ *
+ * <!ELEMENT exception (message?, frame+)>
+ *
+ * However, java.lang.Throwable.getStackTrace() is
+ * allowed to return an empty array. So, what frame should
+ * be emitted for an empty stack trace? We probably
+ * should file a bug report with Sun, asking for the DTD
+ * to be changed.
+ */
+
+ buf.append(" </exception>");
+ buf.append(lineSep);
+ }
+
+
+ buf.append("</record>");
+ buf.append(lineSep);
+
+ return buf.toString();
+ }
+
+
+ /**
+ * Returns a string that handlers are supposed to emit before
+ * the first log record. The base implementation returns an
+ * empty string, but subclasses such as {@link XMLFormatter}
+ * override this method in order to provide a suitable header.
+ *
+ * @return a string for the header.
+ *
+ * @param h the handler which will prepend the returned
+ * string in front of the first log record. This method
+ * will inspect certain properties of the handler, for
+ * example its encoding, in order to construct the header.
+ */
+ public String getHead(Handler h)
+ {
+ CPStringBuilder buf;
+ String encoding;
+
+ buf = new CPStringBuilder(80);
+ buf.append("<?xml version=\"1.0\" encoding=\"");
+
+ encoding = h.getEncoding();
+
+ /* file.encoding is a system property with the Sun JVM, indicating
+ * the platform-default file encoding. Unfortunately, the API
+ * specification for java.lang.System.getProperties() does not
+ * list this property.
+ */
+ if (encoding == null)
+ encoding = System.getProperty("file.encoding");
+
+ /* Since file.encoding is not listed with the API specification of
+ * java.lang.System.getProperties(), there might be some VMs that
+ * do not define this system property. Therefore, we use UTF-8 as
+ * a reasonable default. Please note that if the platform encoding
+ * uses the same codepoints as US-ASCII for the US-ASCII character
+ * set (e.g, 65 for A), it does not matter whether we emit the
+ * wrong encoding into the XML header -- the GNU Classpath will
+ * emit XML escape sequences like &#1234; for any non-ASCII
+ * character. Virtually all character encodings use the same code
+ * points as US-ASCII for ASCII characters. Probably, EBCDIC is
+ * the only exception.
+ */
+ if (encoding == null)
+ encoding = "UTF-8";
+
+ /* On Windows XP localized for Swiss German (this is one of
+ * my [Sascha Brawer's] test machines), the default encoding
+ * has the canonical name "windows-1252". The "historical" name
+ * of this encoding is "Cp1252" (see the Javadoc for the class
+ * java.nio.charset.Charset for the distinction). Now, that class
+ * does have a method for mapping historical to canonical encoding
+ * names. However, if we used it here, we would be come dependent
+ * on java.nio.*, which was only introduced with J2SE 1.4.
+ * Thus, we do this little hack here. As soon as Classpath supports
+ * java.nio.charset.CharSet, this hack should be replaced by
+ * code that correctly canonicalizes the encoding name.
+ */
+ if ((encoding.length() > 2) && encoding.startsWith("Cp"))
+ encoding = "windows-" + encoding.substring(2);
+
+ buf.append(encoding);
+
+ buf.append("\" standalone=\"no\"?>");
+ buf.append(lineSep);
+
+ /* SYSTEM is not a fully qualified URL so that validating
+ * XML parsers do not need to connect to the Internet in
+ * order to read in a log file. See also the Sun Bug Parade,
+ * bug #4372790, "Logging APIs: need to use relative URL for XML
+ * doctype".
+ */
+ buf.append("<!DOCTYPE log SYSTEM \"logger.dtd\">");
+ buf.append(lineSep);
+ buf.append("<log>");
+ buf.append(lineSep);
+
+ return buf.toString();
+ }
+
+
+ public String getTail(Handler h)
+ {
+ return "</log>" + lineSep;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/package.html b/libjava/classpath/java/util/logging/package.html
new file mode 100644
index 000000000..31f0494fc
--- /dev/null
+++ b/libjava/classpath/java/util/logging/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util.logging package.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. -->
+
+<html>
+<head><title>GNU Classpath - java.util.logging</title></head>
+
+<body>
+<p>Utility classes for logging events.</p>
+
+</body>
+</html>