summaryrefslogtreecommitdiffstats
path: root/logging/src/main/java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commitfdb2704414a9ed92394ada0d1395e4db86889465 (patch)
tree9b591a4a50054274a197f02b3ccb51313681879f /logging/src/main/java
downloadlibcore-fdb2704414a9ed92394ada0d1395e4db86889465.zip
libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.gz
libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.bz2
Initial Contribution
Diffstat (limited to 'logging/src/main/java')
-rw-r--r--logging/src/main/java/java/util/logging/ConsoleHandler.java76
-rw-r--r--logging/src/main/java/java/util/logging/ErrorManager.java116
-rw-r--r--logging/src/main/java/java/util/logging/FileHandler.java652
-rw-r--r--logging/src/main/java/java/util/logging/Filter.java34
-rw-r--r--logging/src/main/java/java/util/logging/Formatter.java133
-rw-r--r--logging/src/main/java/java/util/logging/Handler.java420
-rw-r--r--logging/src/main/java/java/util/logging/Level.java360
-rw-r--r--logging/src/main/java/java/util/logging/LogManager.java634
-rw-r--r--logging/src/main/java/java/util/logging/LogRecord.java499
-rw-r--r--logging/src/main/java/java/util/logging/Logger.java1425
-rw-r--r--logging/src/main/java/java/util/logging/LoggingMXBean.java86
-rw-r--r--logging/src/main/java/java/util/logging/LoggingPermission.java68
-rw-r--r--logging/src/main/java/java/util/logging/MemoryHandler.java284
-rw-r--r--logging/src/main/java/java/util/logging/SimpleFormatter.java71
-rw-r--r--logging/src/main/java/java/util/logging/SocketHandler.java181
-rw-r--r--logging/src/main/java/java/util/logging/StreamHandler.java350
-rw-r--r--logging/src/main/java/java/util/logging/XMLFormatter.java210
-rw-r--r--logging/src/main/java/java/util/logging/logging.properties65
-rw-r--r--logging/src/main/java/java/util/logging/package.html9
-rw-r--r--logging/src/main/java/org/apache/harmony/logging/internal/nls/Messages.java132
-rw-r--r--logging/src/main/java/org/apache/harmony/logging/internal/nls/messages.properties52
21 files changed, 5857 insertions, 0 deletions
diff --git a/logging/src/main/java/java/util/logging/ConsoleHandler.java b/logging/src/main/java/java/util/logging/ConsoleHandler.java
new file mode 100644
index 0000000..9cea60f
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/ConsoleHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+/**
+ * A handler that writes log messages to the standard output stream
+ * <code>System.err</code>.
+ * <p>
+ * This handler reads the following properties from the log manager to
+ * initialize itself:
+ * <ul>
+ * <li>java.util.logging.ConsoleHandler.level specifies the logging level,
+ * defaults to <code>Level.INFO</code> if this property is not found or has an
+ * invalid value;
+ * <li>java.util.logging.ConsoleHandler.filter specifies the name of the filter
+ * class to be associated with this handler, defaults to <code>null</code> if
+ * this property is not found or has an invalid value;
+ * <li>java.util.logging.ConsoleHandler.formatter specifies the name of the
+ * formatter class to be associated with this handler, defaults to
+ * <code>java.util.logging.SimpleFormatter</code> if this property is not
+ * found or has an invalid value;
+ * <li>java.util.logging.ConsoleHandler.encoding specifies the encoding this
+ * handler will use to encode log messages, defaults to <code>null</code> if
+ * this property is not found or has an invalid value.
+ * </ul>
+ * </p>
+ * <p>
+ * This class is not thread-safe.
+ * </p>
+ *
+ */
+public class ConsoleHandler extends StreamHandler {
+
+ /**
+ * Constructs a <code>ConsoleHandler</code> object.
+ */
+ public ConsoleHandler() {
+ super(System.err);
+ }
+
+ /**
+ * Closes this handler. The <code>System.err</code> is flushed but not
+ * closed.
+ */
+ @Override
+ public void close() {
+ super.close(false);
+ }
+
+ /**
+ * Logs a record if necessary. A flush operation will be done.
+ *
+ * @param record the log record to be logged
+ */
+ @Override
+ public void publish(LogRecord record) {
+ super.publish(record);
+ super.flush();
+
+ }
+}
diff --git a/logging/src/main/java/java/util/logging/ErrorManager.java b/logging/src/main/java/java/util/logging/ErrorManager.java
new file mode 100644
index 0000000..0b12df1
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/ErrorManager.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * <p>
+ * An error reporting facility for {@link Handler} implementations to record any
+ * error that may happen during logging. <code>Handlers</code> should report
+ * errors to an <code>ErrorManager</code>, instead of throwing exceptions,
+ * which would interfere with the log issuer's execution.
+ * </p>
+ */
+public class ErrorManager {
+
+ /**
+ * The error code indicating a failure that does not fit in any of the
+ * specific types of failures that follow.
+ */
+ public static final int GENERIC_FAILURE = 0;
+
+ /**
+ * The error code indicating a failure when writing to an output stream.
+ */
+ public static final int WRITE_FAILURE = 1;
+
+ /**
+ * The error code indicating a failure when flushing an output stream.
+ */
+ public static final int FLUSH_FAILURE = 2;
+
+ /**
+ * The error code indicating a failure when closing an output stream.
+ */
+ public static final int CLOSE_FAILURE = 3;
+
+ /**
+ * The error code indicating a failure when opening an output stream.
+ */
+ public static final int OPEN_FAILURE = 4;
+
+ /**
+ * The error code indicating a failure when formatting the error messages.
+ */
+ public static final int FORMAT_FAILURE = 5;
+
+ @SuppressWarnings("nls")
+ private static final String[] FAILURES = new String[] { "GENERIC_FAILURE",
+ "WRITE_FAILURE", "FLUSH_FAILURE", "CLOSE_FAILURE", "OPEN_FAILURE",
+ "FORMAT_FAILURE" };
+
+ /**
+ * An indicator for determining if the error manager has been called at
+ * least once before.
+ */
+ private boolean called;
+
+ /**
+ * Constructs an instance of <code>ErrorManager</code>.
+ */
+ public ErrorManager() {
+ super();
+ }
+
+ /**
+ * <p>
+ * Reports an error using the given message, exception and error code. This
+ * implementation will write out the message to {@link System#err} on the
+ * first call and all subsequent calls are ignored. A subclass of this class
+ * should override this method.
+ * </p>
+ *
+ * @param message
+ * The error message, which may be <code>null</code>.
+ * @param exception
+ * The exception associated with the error, which may be
+ * <code>null</code>.
+ * @param errorCode
+ * The error code that identifies the type of error; see the
+ * constant fields on this class.
+ */
+ public void error(String message, Exception exception, int errorCode) {
+ synchronized (this) {
+ if (called) {
+ return;
+ }
+ called = true;
+ }
+ System.err.println(this.getClass().getName()
+ + ": " + FAILURES[errorCode]); //$NON-NLS-1$
+ if (message != null) {
+ // logging.1E=Error message - {0}
+ System.err.println(Messages.getString("logging.1E", message)); //$NON-NLS-1$
+ }
+ if (exception != null) {
+ // logging.1F=Exception - {0}
+ System.err.println(Messages.getString("logging.1F", exception)); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/logging/src/main/java/java/util/logging/FileHandler.java b/logging/src/main/java/java/util/logging/FileHandler.java
new file mode 100644
index 0000000..be21ebc
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/FileHandler.java
@@ -0,0 +1,652 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Hashtable;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * A <code>Handler</code> writes description of logging event into a specified
+ * file or a rotating set of files.
+ * <p>
+ * If a set of files are used, when a given amount of data has been written to
+ * one file, this file is closed, and another file is opened. The name of these
+ * files are generated by given name pattern, see below for details.
+ * </p>
+ * <p>
+ * By default the IO buffering mechanism is enabled, but when each log record is
+ * complete, it is flushed out.
+ * </p>
+ * <p>
+ * <code>XMLFormatter</code> is default formatter for <code>FileHandler</code>.
+ * </p>
+ * <p>
+ * <code>MemoryHandler</code> will read following <code>LogManager</code>
+ * properties for initialization, if given properties are not defined or has
+ * invalid values, default value will be used.
+ * <ul>
+ * <li>java.util.logging.FileHandler.level specifies the level for this
+ * <code>Handler</code>, defaults to <code>Level.ALL</code>.</li>
+ * <li>java.util.logging.FileHandler.filter specifies the <code>Filter</code>
+ * class name, defaults to no <code>Filter</code>.</li>
+ * <li>java.util.logging.FileHandler.formatter specifies the
+ * <code>Formatter</code> class, defaults to
+ * <code>java.util.logging.XMLFormatter</code>.</li>
+ * <li>java.util.logging.FileHandler.encoding specifies the character set
+ * encoding name, defaults to the default platform encoding.</li>
+ * <li>java.util.logging.FileHandler.limit specifies an maximum bytes to write
+ * to any one file, defaults to zero, which means no limit.</li>
+ * <li>java.util.logging.FileHandler.count specifies how many output files to
+ * rotate, defaults to 1.</li>
+ * <li>java.util.logging.FileHandler.pattern specifies name pattern for the
+ * output files. See below for details. Defaults to "%h/java%u.log".</li>
+ * <li>java.util.logging.FileHandler.append specifies whether this
+ * <code>FileHandler</code> should append onto existing files, defaults to
+ * false.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Name pattern is a string that may includes some special sub-strings, which
+ * will be replaced to generate output files:
+ * <ul>
+ * <li>"/" represents the local pathname separator</li>
+ * <li>"%t" represents the system temporary directory</li>
+ * <li>"%h" represents the home directory of current user, which is specified
+ * by "user.home" system property</li>
+ * <li>"%g" represents the generation number to distinguish rotated logs</li>
+ * <li>"%u" represents a unique number to resolve conflicts</li>
+ * <li>"%%" represents percent sign character '%'</li>
+ * </ul>
+ * </p>
+ * Normally, the generation numbers are not larger than given file count and
+ * follow the sequence 0, 1, 2.... If the file count is larger than one, but the
+ * generation field("%g") has not been specified in the pattern, then the
+ * generation number after a dot will be added to the end of the file name,
+ * </p>
+ * <p>
+ * The "%u" unique field is used to avoid conflicts and set to 0 at first. If
+ * one <code>FileHandler</code> tries to open the filename which is currently
+ * in use by another process, it will repeatedly increment the unique number
+ * field and try again. If the "%u" component has not been included in the file
+ * name pattern and some contention on a file does occur then a unique numerical
+ * value will be added to the end of the filename in question immediately to the
+ * right of a dot. The unique IDs for avoiding conflicts is only guaranteed to
+ * work reliably when using a local disk file system.
+ * </p>
+ *
+ */
+public class FileHandler extends StreamHandler {
+
+ private static final String LCK_EXT = ".lck"; //$NON-NLS-1$
+
+ private static final int DEFAULT_COUNT = 1;
+
+ private static final int DEFAULT_LIMIT = 0;
+
+ private static final boolean DEFAULT_APPEND = false;
+
+ private static final String DEFAULT_PATTERN = "%h/java%u.log"; //$NON-NLS-1$
+
+ // maintain all file locks hold by this process
+ private static final Hashtable<String, FileLock> allLocks = new Hashtable<String, FileLock>();
+
+ // the count of files which the output cycle through
+ private int count;
+
+ // the size limitation in byte of log file
+ private int limit;
+
+ // whether the FileHandler should open a existing file for output in append
+ // mode
+ private boolean append;
+
+ // the pattern for output file name
+ private String pattern;
+
+ // maintain a LogManager instance for convenience
+ private LogManager manager;
+
+ // output stream, which can measure the output file length
+ private MeasureOutputStream output;
+
+ // used output file
+ private File[] files;
+
+ // output file lock
+ FileLock lock = null;
+
+ // current output file name
+ String fileName = null;
+
+ // current unique ID
+ int uniqueID = -1;
+
+ /**
+ * Construct a <code>FileHandler</code> using <code>LogManager</code>
+ * properties or their default value
+ *
+ * @throws IOException
+ * if any IO exception happened
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to control this handler,
+ * required permissions include
+ * <code>LogPermission("control")</code> and other permission
+ * like <code>FilePermission("write")</code>, etc.
+ */
+ public FileHandler() throws IOException {
+ init(null, null, null, null);
+ }
+
+ // init properties
+ private void init(String p, Boolean a, Integer l, Integer c)
+ throws IOException {
+ // check access
+ manager = LogManager.getLogManager();
+ manager.checkAccess();
+ initProperties(p, a, l, c);
+ initOutputFiles();
+ }
+
+ private void initOutputFiles() throws FileNotFoundException, IOException {
+ while (true) {
+ // try to find a unique file which is not locked by other process
+ uniqueID++;
+ // FIXME: improve performance here
+ for (int generation = 0; generation < count; generation++) {
+ // cache all file names for rotation use
+ files[generation] = new File(parseFileName(generation));
+ }
+ fileName = files[0].getAbsolutePath();
+ synchronized (allLocks) {
+ /*
+ * if current process has held lock for this fileName continue
+ * to find next file
+ */
+ if (null != allLocks.get(fileName)) {
+ continue;
+ }
+ if (files[0].exists()
+ && (!append || files[0].length() >= limit)) {
+ for (int i = count - 1; i > 0; i--) {
+ if (files[i].exists()) {
+ files[i].delete();
+ }
+ files[i - 1].renameTo(files[i]);
+ }
+ }
+ FileOutputStream fileStream = new FileOutputStream(fileName
+ + LCK_EXT);
+ FileChannel channel = fileStream.getChannel();
+ /*
+ * if lock is unsupported and IOException thrown, just let the
+ * IOException throws out and exit otherwise it will go into an
+ * undead cycle
+ */
+ lock = channel.tryLock();
+ if (null == lock) {
+ try {
+ fileStream.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ continue;
+ }
+ allLocks.put(fileName, lock);
+ break;
+ }
+ }
+ // BEGIN android-modified
+ output = new MeasureOutputStream(
+ new BufferedOutputStream(
+ new FileOutputStream(fileName, append), 8192),
+ files[0].length());
+ // END android-modified
+ setOutputStream(output);
+ }
+
+ private void initProperties(String p, Boolean a, Integer l, Integer c) {
+ super.initProperties("ALL", null, "java.util.logging.XMLFormatter", //$NON-NLS-1$//$NON-NLS-2$
+ null);
+ String className = this.getClass().getName();
+ pattern = (null == p) ? getStringProperty(className + ".pattern", //$NON-NLS-1$
+ DEFAULT_PATTERN) : p;
+ if (null == pattern || "".equals(pattern)) { //$NON-NLS-1$
+ // logging.19=Pattern cannot be empty
+ throw new NullPointerException(Messages.getString("logging.19")); //$NON-NLS-1$
+ }
+ append = (null == a) ? getBooleanProperty(className + ".append", //$NON-NLS-1$
+ DEFAULT_APPEND) : a.booleanValue();
+ count = (null == c) ? getIntProperty(className + ".count", //$NON-NLS-1$
+ DEFAULT_COUNT) : c.intValue();
+ limit = (null == l) ? getIntProperty(className + ".limit", //$NON-NLS-1$
+ DEFAULT_LIMIT) : l.intValue();
+ count = count < 1 ? DEFAULT_COUNT : count;
+ limit = limit < 0 ? DEFAULT_LIMIT : limit;
+ files = new File[count];
+ }
+
+ void findNextGeneration() {
+ super.close();
+ for (int i = count - 1; i > 0; i--) {
+ if (files[i].exists()) {
+ files[i].delete();
+ }
+ files[i - 1].renameTo(files[i]);
+ }
+ try {
+ // BEGIN android-modified
+ output = new MeasureOutputStream(
+ new BufferedOutputStream(
+ new FileOutputStream(files[0]),
+ 8192));
+ // END android-modified
+ } catch (FileNotFoundException e1) {
+ // logging.1A=Error happened when open log file.
+ this.getErrorManager().error(Messages.getString("logging.1A"), //$NON-NLS-1$
+ e1, ErrorManager.OPEN_FAILURE);
+ }
+ setOutputStream(output);
+ }
+
+ /**
+ * Transform the pattern to the valid file name, replacing any patterns, and
+ * applying generation and uniqueID if present
+ *
+ * @param gen
+ * generation of this file
+ * @return transformed filename ready for use
+ */
+ private String parseFileName(int gen) {
+ int cur = 0;
+ int next = 0;
+ boolean hasUniqueID = false;
+ boolean hasGeneration = false;
+
+ // TODO privilege code?
+
+ String tempPath = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
+ boolean tempPathHasSepEnd = (tempPath == null ? false : tempPath
+ .endsWith(File.separator));
+
+ String homePath = System.getProperty("user.home"); //$NON-NLS-1$
+ boolean homePathHasSepEnd = (homePath == null ? false : homePath
+ .endsWith(File.separator));
+
+ StringBuilder sb = new StringBuilder();
+ pattern = pattern.replace('/', File.separatorChar);
+
+ char[] value = pattern.toCharArray();
+ while ((next = pattern.indexOf('%', cur)) >= 0) {
+ if (++next < pattern.length()) {
+ switch (value[next]) {
+ case 'g':
+ sb.append(value, cur, next - cur - 1).append(gen);
+ hasGeneration = true;
+ break;
+ case 'u':
+ sb.append(value, cur, next - cur - 1).append(uniqueID);
+ hasUniqueID = true;
+ break;
+ case 't':
+ /*
+ * we should probably try to do something cute here like
+ * lookahead for adjacent '/'
+ */
+ sb.append(value, cur, next - cur - 1).append(tempPath);
+ if (!tempPathHasSepEnd) {
+ sb.append(File.separator);
+ }
+ break;
+ case 'h':
+ sb.append(value, cur, next - cur - 1).append(homePath);
+ if (!homePathHasSepEnd) {
+ sb.append(File.separator);
+ }
+ break;
+ case '%':
+ sb.append(value, cur, next - cur - 1).append('%');
+ break;
+ default:
+ sb.append(value, cur, next - cur);
+ }
+ cur = ++next;
+ } else {
+ // fail silently
+ }
+ }
+
+ sb.append(value, cur, value.length - cur);
+
+ if (!hasGeneration && count > 1) {
+ sb.append(".").append(gen); //$NON-NLS-1$
+ }
+
+ if (!hasUniqueID && uniqueID > 0) {
+ sb.append(".").append(uniqueID); //$NON-NLS-1$
+ }
+
+ return sb.toString();
+ }
+
+ // get boolean LogManager property, if invalid value got, using default
+ // value
+ private boolean getBooleanProperty(String key, boolean defaultValue) {
+ String property = manager.getProperty(key);
+ if (null == property) {
+ return defaultValue;
+ }
+ boolean result = defaultValue;
+ if ("true".equalsIgnoreCase(property)) { //$NON-NLS-1$
+ result = true;
+ } else if ("false".equalsIgnoreCase(property)) { //$NON-NLS-1$
+ result = false;
+ }
+ return result;
+ }
+
+ // get String LogManager property, if invalid value got, using default value
+ private String getStringProperty(String key, String defaultValue) {
+ String property = manager.getProperty(key);
+ return property == null ? defaultValue : property;
+ }
+
+ // get int LogManager property, if invalid value got, using default value
+ private int getIntProperty(String key, int defaultValue) {
+ String property = manager.getProperty(key);
+ int result = defaultValue;
+ if (null != property) {
+ try {
+ result = Integer.parseInt(property);
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Construct a <code>FileHandler</code>, the given name pattern is used
+ * as output filename, the file limit is set to zero(no limit), and the file
+ * count is set to one, other configuration using <code>LogManager</code>
+ * properties or their default value
+ *
+ * This handler write to only one file and no amount limit.
+ *
+ * @param pattern
+ * the name pattern of output file
+ * @throws IOException
+ * if any IO exception happened
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to control this handler,
+ * required permissions include
+ * <code>LogPermission("control")</code> and other permission
+ * like <code>FilePermission("write")</code>, etc.
+ * @throws NullPointerException
+ * if the pattern is <code>null</code>.
+ * @throws IllegalArgumentException
+ * if the pattern is empty.
+ */
+ public FileHandler(String pattern) throws IOException {
+ if (pattern.equals("")) { //$NON-NLS-1$
+ // logging.19=Pattern cannot be empty
+ throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$
+ }
+ init(pattern, null, Integer.valueOf(DEFAULT_LIMIT), Integer
+ .valueOf(DEFAULT_COUNT));
+ }
+
+ /**
+ * Construct a <code>FileHandler</code>, the given name pattern is used
+ * as output filename, the file limit is set to zero(i.e. no limit applies),
+ * the file count is initialized to one, and the value of
+ * <code>append</code> becomes the new instance's append mode. Other
+ * configuration is done using <code>LogManager</code> properties.
+ *
+ * This handler write to only one file and no amount limit.
+ *
+ * @param pattern
+ * the name pattern of output file
+ * @param append
+ * the append mode
+ * @throws IOException
+ * if any IO exception happened
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to control this handler,
+ * required permissions include
+ * <code>LogPermission("control")</code> and other permission
+ * like <code>FilePermission("write")</code>, etc.
+ * @throws NullPointerException
+ * if the pattern is <code>null</code>.
+ * @throws IllegalArgumentException
+ * if the pattern is empty.
+ */
+ public FileHandler(String pattern, boolean append) throws IOException {
+ if (pattern.equals("")) { //$NON-NLS-1$
+ throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$
+ }
+
+ init(pattern, Boolean.valueOf(append), Integer.valueOf(DEFAULT_LIMIT),
+ Integer.valueOf(DEFAULT_COUNT));
+ }
+
+ /**
+ * Construct a <code>FileHandler</code>, the given name pattern is used
+ * as output filename, the file limit is set to given limit argument, and
+ * the file count is set to given count argument, other configuration using
+ * <code>LogManager</code> properties or their default value
+ *
+ * This handler is configured to write to a rotating set of count files,
+ * when the limit of bytes has been written to one output file, another file
+ * will be opened instead.
+ *
+ * @param pattern
+ * the name pattern of output file
+ * @param limit
+ * the data amount limit in bytes of one output file, cannot less
+ * than one
+ * @param count
+ * the maximum number of files can be used, cannot less than one
+ * @throws IOException
+ * if any IO exception happened
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to control this handler,
+ * required permissions include
+ * <code>LogPermission("control")</code> and other permission
+ * like <code>FilePermission("write")</code>, etc.
+ * @throws NullPointerException
+ * if pattern is <code>null</code>.
+ * @throws IllegalArgumentException
+ * if count<1, or limit<0
+ */
+ public FileHandler(String pattern, int limit, int count) throws IOException {
+ if (pattern.equals("")) { //$NON-NLS-1$
+ throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$
+ }
+ if (limit < 0 || count < 1) {
+ // logging.1B=The limit and count property must be larger than 0 and
+ // 1, respectively
+ throw new IllegalArgumentException(Messages.getString("logging.1B")); //$NON-NLS-1$
+ }
+ init(pattern, null, Integer.valueOf(limit), Integer.valueOf(count));
+ }
+
+ /**
+ * Construct a <code>FileHandler</code>, the given name pattern is used
+ * as output filename, the file limit is set to given limit argument, the
+ * file count is set to given count argument, and the append mode is set to
+ * given append argument, other configuration using <code>LogManager</code>
+ * properties or their default value
+ *
+ * This handler is configured to write to a rotating set of count files,
+ * when the limit of bytes has been written to one output file, another file
+ * will be opened instead.
+ *
+ * @param pattern
+ * the name pattern of output file
+ * @param limit
+ * the data amount limit in bytes of one output file, cannot less
+ * than one
+ * @param count
+ * the maximum number of files can be used, cannot less than one
+ * @param append
+ * the append mode
+ * @throws IOException
+ * if any IO exception happened
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to control this handler,
+ * required permissions include
+ * <code>LogPermission("control")</code> and other permission
+ * like <code>FilePermission("write")</code>, etc.
+ * @throws NullPointerException
+ * if pattern is <code>null</code>.
+ * @throws IllegalArgumentException
+ * if count<1, or limit<0
+ */
+ public FileHandler(String pattern, int limit, int count, boolean append)
+ throws IOException {
+ if (pattern.equals("")) { //$NON-NLS-1$
+ throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$
+ }
+ if (limit < 0 || count < 1) {
+ // logging.1B=The limit and count property must be larger than 0 and
+ // 1, respectively
+ throw new IllegalArgumentException(Messages.getString("logging.1B")); //$NON-NLS-1$
+ }
+ init(pattern, Boolean.valueOf(append), Integer.valueOf(limit), Integer
+ .valueOf(count));
+ }
+
+ /**
+ * Flush and close all opened files.
+ *
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to control this handler,
+ * required permissions include
+ * <code>LogPermission("control")</code> and other permission
+ * like <code>FilePermission("write")</code>, etc.
+ */
+ @Override
+ public void close() {
+ // release locks
+ super.close();
+ allLocks.remove(fileName);
+ try {
+ FileChannel channel = lock.channel();
+ lock.release();
+ channel.close();
+ File file = new File(fileName + LCK_EXT);
+ file.delete();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ /**
+ * Publish a <code>LogRecord</code>
+ *
+ * @param record
+ * the log record to be published
+ */
+ @Override
+ public void publish(LogRecord record) {
+ super.publish(record);
+ flush();
+ if (limit > 0 && output.getLength() >= limit) {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ findNextGeneration();
+ return null;
+ }
+ });
+ }
+ }
+
+ /**
+ * This output stream use decorator pattern to add measure feature to
+ * OutputStream which can detect the total size(in bytes) of output, the
+ * initial size can be set
+ */
+ static class MeasureOutputStream extends OutputStream {
+
+ OutputStream wrapped;
+
+ long length;
+
+ public MeasureOutputStream(OutputStream stream, long currentLength) {
+ wrapped = stream;
+ length = currentLength;
+ }
+
+ public MeasureOutputStream(OutputStream stream) {
+ this(stream, 0);
+ }
+
+ @Override
+ public void write(int oneByte) throws IOException {
+ wrapped.write(oneByte);
+ length++;
+ }
+
+ @Override
+ public void write(byte[] bytes) throws IOException {
+ wrapped.write(bytes);
+ length += bytes.length;
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ wrapped.write(b, off, len);
+ length += len;
+ }
+
+ @Override
+ public void close() throws IOException {
+ wrapped.close();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ wrapped.flush();
+ }
+
+ public long getLength() {
+ return length;
+ }
+
+ public void setLength(long newLength) {
+ length = newLength;
+ }
+ }
+}
diff --git a/logging/src/main/java/java/util/logging/Filter.java b/logging/src/main/java/java/util/logging/Filter.java
new file mode 100644
index 0000000..d96f3ce
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/Filter.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+/**
+ * <p>A Filter provides a mechanism for exercising fine-grained control over
+ * what records get logged.</p>
+ */
+public interface Filter {
+
+ /**
+ * <p>Checks the {@link LogRecord} to determine if it should be logged.</p>
+ *
+ * @param record The {@link LogRecord} to be checked.
+ * @return <code>true</code> if the supplied log record needs to be
+ * logged, otherwise <code>false</code>
+ */
+ boolean isLoggable(LogRecord record);
+}
diff --git a/logging/src/main/java/java/util/logging/Formatter.java b/logging/src/main/java/java/util/logging/Formatter.java
new file mode 100644
index 0000000..7321aa2
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/Formatter.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package java.util.logging;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+/**
+ * <code>Formatter</code> objects are used to format <code>LogRecord</code>
+ * objects into a string representation. Head and tail strings are sometime used
+ * to wrap a set of records. The <code>getHead</code> and <code>getTail</code>
+ * methods are presented for this purpose.
+ *
+ */
+public abstract class Formatter {
+
+ /*
+ * -------------------------------------------------------------------
+ * Constructors
+ * -------------------------------------------------------------------
+ */
+
+ /**
+ * Constructs a <code>Formatter</code> object.
+ */
+ protected Formatter() {
+ super();
+ }
+
+ /*
+ * -------------------------------------------------------------------
+ * Methods
+ * -------------------------------------------------------------------
+ */
+
+ /**
+ * Formats a <code>LogRecord</code> object into a string representation.
+ * The resulted string is usually localized and includes the message field
+ * of the supplied <code>LogRecord</code> object.
+ *
+ * @param r
+ * the log record to be formatted into a string
+ * @return the string resulted from the formatting
+ */
+ public abstract String format(LogRecord r);
+
+ /**
+ * Formats a <code>LogRecord</code> object into a localized string
+ * representation. This method can be regarded as a convenience for
+ * subclasses of <code>Formatter</code> to use.
+ * <p>
+ * The message string is firstly localized using the
+ * <code>ResourceBundle</code> object associated with the supplied
+ * <code>LogRecord</code>.
+ * </p>
+ *
+ * @param r
+ * the log record to be formatted
+ * @return the string resulted from the formatting
+ */
+ public String formatMessage(LogRecord r) {
+ String pattern = r.getMessage();
+ ResourceBundle rb = null;
+ // try to localize the message string first
+ if (null != (rb = r.getResourceBundle())) {
+ try {
+ pattern = rb.getString(pattern);
+ } catch (Exception e) {
+ pattern = r.getMessage();
+ }
+ }
+ if (null != pattern) {
+ Object[] params = r.getParameters();
+ /*
+ * if the message contains "{0", use java.text.MessageFormat to
+ * format the string
+ */
+ if (pattern.indexOf("{0") >= 0 && null != params //$NON-NLS-1$
+ && params.length > 0) {
+ try {
+ pattern = MessageFormat.format(pattern, params);
+ } catch (IllegalArgumentException e) {
+ pattern = r.getMessage();
+ }
+ }
+ }
+ return pattern;
+ }
+
+ /**
+ * Gets the head string used to wrap a set of log records. This base class
+ * always returns the empty string.
+ *
+ * @param h
+ * the target handler
+ * @return the head string used to wrap a set of log records
+ */
+ @SuppressWarnings("unused")
+ public String getHead(Handler h) {
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Gets the tail string used to wrap a set of log records. This base class
+ * always returns the empty string.
+ *
+ * @param h
+ * the target handler
+ * @return the tail string used to wrap a set of log records
+ */
+ @SuppressWarnings("unused")
+ public String getTail(Handler h) {
+ return ""; //$NON-NLS-1$
+ }
+
+}
+
diff --git a/logging/src/main/java/java/util/logging/Handler.java b/logging/src/main/java/java/util/logging/Handler.java
new file mode 100644
index 0000000..9bad459
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/Handler.java
@@ -0,0 +1,420 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package java.util.logging;
+
+import java.nio.charset.Charset;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * A <code>Handler</code> object accepts a logging request and exports the
+ * desired messages to a target, for example, a file, the console, etc. It can
+ * be disabled by setting its logging level to <code>Level.OFF</code>.
+ *
+ */
+public abstract class Handler {
+
+ /*
+ * -------------------------------------------------------------------
+ * Constants
+ * -------------------------------------------------------------------
+ */
+ private static final Level DEFAULT_LEVEL = Level.ALL;
+
+ /*
+ * -------------------------------------------------------------------
+ * Instance variables
+ * -------------------------------------------------------------------
+ */
+
+ // the error manager to report errors during logging
+ private ErrorManager errorMan;
+
+ // the character encoding used by this handler
+ private String encoding;
+
+ // the logging level
+ private Level level;
+
+ // the formatter used to export messages
+ private Formatter formatter;
+
+ // the filter used to filter undesired messages
+ private Filter filter;
+
+ // class name, used for property reading
+ private String prefix;
+
+ /*
+ * -------------------------------------------------------------------
+ * Constructors
+ * -------------------------------------------------------------------
+ */
+
+ /**
+ * Constructs a <code>Handler</code> object with a default error manager,
+ * the default encoding, and the default logging level
+ * <code>Level.ALL</code>. It has no filter and no formatter.
+ */
+ protected Handler() {
+ this.errorMan = new ErrorManager();
+ this.level = DEFAULT_LEVEL;
+ this.encoding = null;
+ this.filter = null;
+ this.formatter = null;
+ this.prefix = this.getClass().getName();
+ }
+
+ /*
+ * -------------------------------------------------------------------
+ * Methods
+ * -------------------------------------------------------------------
+ */
+
+ // get a instance from given class name, using Class.forName()
+ private Object getDefaultInstance(String className) {
+ Object result = null;
+ if (null == className) {
+ return result;
+ }
+ try {
+ result = Class.forName(className).newInstance();
+ } catch (Exception e) {
+ //ignore
+ }
+ return result;
+ }
+
+ // get a instance from given class name, using context classloader
+ private Object getCustomizeInstance(final String className)
+ throws Exception {
+ Class<?> c = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() {
+ public Class<?> run() throws Exception {
+ ClassLoader loader = Thread.currentThread()
+ .getContextClassLoader();
+ if (null == loader) {
+ loader = ClassLoader.getSystemClassLoader();
+ }
+ return loader.loadClass(className);
+ }
+ });
+ return c.newInstance();
+ }
+
+ // print error message in some format
+ void printInvalidPropMessage(String key, String value, Exception e) {
+ // logging.12=Invalid property value for
+ String msg = new StringBuilder().append(Messages.getString("logging.12")) //$NON-NLS-1$
+ .append(prefix).append(":").append(key).append("/").append( //$NON-NLS-1$//$NON-NLS-2$
+ value).toString();
+ errorMan.error(msg, e, ErrorManager.GENERIC_FAILURE);
+ }
+
+ /*
+ * init the common properties, including filter, level, formatter, and
+ * encoding
+ */
+ @SuppressWarnings("unused")
+ void initProperties(String defaultLevel, String defaultFilter,
+ String defaultFormatter, String defaultEncoding) {
+ LogManager manager = LogManager.getLogManager();
+
+ //set filter
+ final String filterName = manager.getProperty(prefix + ".filter"); //$NON-NLS-1$
+ if (null != filterName) {
+ try {
+ filter = (Filter) getCustomizeInstance(filterName);
+ } catch (Exception e1) {
+ printInvalidPropMessage("filter", filterName, e1); //$NON-NLS-1$
+ filter = (Filter) getDefaultInstance(defaultFilter);
+ }
+ } else {
+ filter = (Filter) getDefaultInstance(defaultFilter);
+ }
+
+ //set level
+ String levelName = manager.getProperty(prefix + ".level"); //$NON-NLS-1$
+ if (null != levelName) {
+ try {
+ level = Level.parse(levelName);
+ } catch (Exception e) {
+ printInvalidPropMessage("level", levelName, e); //$NON-NLS-1$
+ level = Level.parse(defaultLevel);
+ }
+ } else {
+ level = Level.parse(defaultLevel);
+ }
+
+ //set formatter
+ final String formatterName = manager.getProperty(prefix + ".formatter"); //$NON-NLS-1$
+ if (null != formatterName) {
+ try {
+ formatter = (Formatter) getCustomizeInstance(formatterName);
+ } catch (Exception e) {
+ printInvalidPropMessage("formatter", formatterName, e); //$NON-NLS-1$
+ formatter = (Formatter) getDefaultInstance(defaultFormatter);
+ }
+ } else {
+ formatter = (Formatter) getDefaultInstance(defaultFormatter);
+ }
+
+ //set encoding
+ final String encodingName = manager.getProperty(prefix + ".encoding"); //$NON-NLS-1$
+ try {
+ internalSetEncoding(encodingName);
+ } catch (UnsupportedEncodingException e) {
+ printInvalidPropMessage("encoding", encodingName, e); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Closes this handler. A flush operation will usually be performed and all
+ * the associated resources will be freed. Client applications should not
+ * use a handler after closing it.
+ *
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public abstract void close();
+
+ /**
+ * Flushes any buffered output.
+ */
+ public abstract void flush();
+
+ /**
+ * Accepts an actual logging request.
+ *
+ * @param record
+ * the log record to be logged
+ */
+ public abstract void publish(LogRecord record);
+
+ /**
+ * Gets the character encoding used by this handler.
+ *
+ * @return the character encoding used by this handler
+ */
+ public String getEncoding() {
+ return this.encoding;
+ }
+
+ /**
+ * Gets the error manager used by this handler to report errors during
+ * logging.
+ *
+ * @return the error manager used by this handler
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public ErrorManager getErrorManager() {
+ LogManager.getLogManager().checkAccess();
+ return this.errorMan;
+ }
+
+ /**
+ * Gets the filter used by this handler.
+ *
+ * @return the filter used by this handler
+ */
+ public Filter getFilter() {
+ return this.filter;
+ }
+
+ /**
+ * Gets the formatter used by this handler to format the logging messages.
+ *
+ * @return the formatter used by this handler
+ */
+ public Formatter getFormatter() {
+ return this.formatter;
+ }
+
+ /**
+ * Gets the logging level of this handler.
+ *
+ * @return the logging level of this handler
+ */
+ public Level getLevel() {
+ return this.level;
+ }
+
+ /**
+ * Determines whether the supplied log record need to be logged. The logging
+ * levels will be checked as well as the filter.
+ *
+ * @param record
+ * the log record to be checked
+ * @return <code>true</code> if the supplied log record need to be logged,
+ * otherwise <code>false</code>
+ */
+ public boolean isLoggable(LogRecord record) {
+ if (null == record) {
+ throw new NullPointerException();
+ }
+ if (this.level.intValue() == Level.OFF.intValue()) {
+ return false;
+ } else if (record.getLevel().intValue() >= this.level.intValue()) {
+ return null == this.filter || this.filter.isLoggable(record);
+ }
+ return false;
+ }
+
+ /**
+ * Report an error to the error manager associated with this handler.
+ *
+ * @param msg
+ * the error message
+ * @param ex
+ * the associated exception
+ * @param code
+ * the error code
+ */
+ protected void reportError(String msg, Exception ex, int code) {
+ this.errorMan.error(msg, ex, code);
+ }
+
+ /**
+ * Sets the character encoding used by this handler. A <code>null</code>
+ * value indicates the using of the default encoding. This internal method
+ * does not check security.
+ *
+ * @param newEncoding
+ * the character encoding to set
+ * @throws UnsupportedEncodingException
+ * If the specified encoding is not supported by the runtime.
+ */
+ void internalSetEncoding(String newEncoding)
+ throws UnsupportedEncodingException {
+ // accepts "null" because it indicates using default encoding
+ if (null == newEncoding) {
+ this.encoding = null;
+ } else {
+ if (Charset.isSupported(newEncoding)) {
+ this.encoding = newEncoding;
+ } else {
+ // logging.13=The encoding "{0}" is not supported.
+ throw new UnsupportedEncodingException(Messages.getString(
+ "logging.13", //$NON-NLS-1$
+ newEncoding));
+ }
+
+ }
+ }
+
+ /**
+ * Sets the character encoding used by this handler. A <code>null</code>
+ * value indicates the using of the default encoding.
+ *
+ * @param encoding
+ * the character encoding to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ * @throws UnsupportedEncodingException
+ * If the specified encoding is not supported by the runtime.
+ */
+ public void setEncoding(String encoding) throws SecurityException,
+ UnsupportedEncodingException {
+ LogManager.getLogManager().checkAccess();
+ internalSetEncoding(encoding);
+ }
+
+ /**
+ * Sets the error manager for this handler.
+ *
+ * @param em
+ * the error manager to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void setErrorManager(ErrorManager em) {
+ LogManager.getLogManager().checkAccess();
+ if (null == em) {
+ throw new NullPointerException();
+ }
+ this.errorMan = em;
+ }
+
+ /**
+ * Sets the filter to be used by this handler.
+ *
+ * @param newFilter
+ * the filter to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void setFilter(Filter newFilter) {
+ LogManager.getLogManager().checkAccess();
+ this.filter = newFilter;
+ }
+
+ /**
+ * Sets the formatter to be used by this handler. This internal method does
+ * not check security.
+ *
+ * @param newFormatter
+ * the formatter to set
+ */
+ void internalSetFormatter(Formatter newFormatter) {
+ if (null == newFormatter) {
+ throw new NullPointerException();
+ }
+ this.formatter = newFormatter;
+ }
+
+ /**
+ * Sets the formatter to be used by this handler.
+ *
+ * @param newFormatter
+ * the formatter to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void setFormatter(Formatter newFormatter) {
+ LogManager.getLogManager().checkAccess();
+ internalSetFormatter(newFormatter);
+ }
+
+ /**
+ * Sets the logging level of this handler.
+ *
+ * @param newLevel
+ * the logging level to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void setLevel(Level newLevel) {
+ if (null == newLevel) {
+ throw new NullPointerException();
+ }
+ LogManager.getLogManager().checkAccess();
+ this.level = newLevel;
+ }
+}
+
diff --git a/logging/src/main/java/java/util/logging/Level.java b/logging/src/main/java/java/util/logging/Level.java
new file mode 100644
index 0000000..bb39d51
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/Level.java
@@ -0,0 +1,360 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+import org.apache.harmony.kernel.vm.VM;
+
+/**
+ * <code>Level</code> objects are used to indicate the level of logging. There
+ * are a set of predefined logging levels, each associated with an integer
+ * value. Enabling a certain logging level also enables all logging levels with
+ * larger values.
+ * <p>
+ * The predefined levels in ascending order are FINEST, FINER, FINE, CONFIG,
+ * INFO, WARNING, SEVERE. There are two additional predefined levels, which are
+ * ALL and OFF. ALL indicates logging all messages, and OFF indicates logging no
+ * messages.
+ * </p>
+ *
+ */
+public class Level implements Serializable {
+
+ private static final long serialVersionUID = -8176160795706313070L;
+
+ private static final List<Level> levels = new ArrayList<Level>(9);
+
+ /**
+ * The OFF level provides no logging messages.
+ */
+ public static final Level OFF = new Level("OFF", Integer.MAX_VALUE); //$NON-NLS-1$
+
+ /**
+ * The SEVERE level indicates a severe failure.
+ */
+ public static final Level SEVERE = new Level("SEVERE", 1000); //$NON-NLS-1$
+
+ /**
+ * The WARNING level indicates a warning.
+ */
+ public static final Level WARNING = new Level("WARNING", 900); //$NON-NLS-1$
+
+ /**
+ * The INFO level indicates an informative message.
+ */
+ public static final Level INFO = new Level("INFO", 800); //$NON-NLS-1$
+
+ /**
+ * The CONFIG level indicates a static configuration message.
+ */
+ public static final Level CONFIG = new Level("CONFIG", 700); //$NON-NLS-1$
+
+ /**
+ * The FINE level provides tracing messages.
+ */
+ public static final Level FINE = new Level("FINE", 500); //$NON-NLS-1$
+
+ /**
+ * The FINER level provides more detailed tracing messages.
+ */
+ public static final Level FINER = new Level("FINER", 400); //$NON-NLS-1$
+
+ /**
+ * The FINEST level provides highly detailed tracing messages.
+ */
+ public static final Level FINEST = new Level("FINEST", 300); //$NON-NLS-1$
+
+ /**
+ * The ALL level provides all logging messages.
+ */
+ public static final Level ALL = new Level("ALL", Integer.MIN_VALUE); //$NON-NLS-1$
+
+ /**
+ * Parses a level name into a <code>Level</code> object.
+ *
+ * @param name
+ * the name of the desired level, which cannot be null
+ * @return a <code>Level</code> object with the specified name
+ * @throws NullPointerException
+ * if <code>name</code> is <code>null</code>.
+ * @throws IllegalArgumentException
+ * if <code>name</code> is not valid.
+ */
+ public static final Level parse(String name) {
+ if (name == null) {
+ // logging.1C=The 'name' parameter is null.
+ throw new NullPointerException(Messages.getString("logging.1C")); //$NON-NLS-1$
+ }
+
+ boolean isNameAnInt;
+ int nameAsInt;
+ try {
+ nameAsInt = Integer.parseInt(name);
+ isNameAnInt = true;
+ } catch (NumberFormatException e) {
+ nameAsInt = 0;
+ isNameAnInt = false;
+ }
+
+ synchronized (levels) {
+ for (Level level : levels) {
+ if (name.equals(level.getName())) {
+ return level;
+ }
+ }
+
+ if (isNameAnInt) {
+ /*
+ * Loop through levels a second time, so that the
+ * returned instance will be passed on the order of construction.
+ */
+ for (Level level : levels) {
+ if (nameAsInt == level.intValue()) {
+ return level;
+ }
+ }
+ }
+ }
+
+ if (!isNameAnInt) {
+ // logging.1D=Cannot parse this name: {0}
+ throw new IllegalArgumentException(Messages.getString("logging.1D", name)); //$NON-NLS-1$
+ }
+
+ return new Level(name, nameAsInt);
+ }
+
+ /**
+ * The name of this Level.
+ *
+ * @serial
+ */
+ private final String name;
+
+ /**
+ * The integer value indicating the level.
+ *
+ * @serial
+ */
+ private final int value;
+
+ /**
+ * The name of the resource bundle used to localize the level name.
+ *
+ * @serial
+ */
+ private final String resourceBundleName;
+
+ /**
+ * The resource bundle associated with this level, used to localize the
+ * level name.
+ */
+ private transient ResourceBundle rb;
+
+ /**
+ * Constructs an instance of <code>Level</code> taking the supplied name
+ * and level value.
+ *
+ * @param name name of the level
+ * @param level an integer value indicating the level
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ protected Level(String name, int level) {
+ this(name, level, null);
+ }
+
+ /**
+ * Constructs an instance of <code>Level</code> taking the supplied name
+ * and level value.
+ *
+ * @param name name of the level
+ * @param level an integer value indicating the level
+ * @param resourceBundleName the name of the resource bundle to use
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ protected Level(String name, int level, String resourceBundleName) {
+ if (name == null) {
+ // logging.1C=The 'name' parameter is null.
+ throw new NullPointerException(Messages.getString("logging.1C")); //$NON-NLS-1$
+ }
+ this.name = name;
+ this.value = level;
+ this.resourceBundleName = resourceBundleName;
+ if (resourceBundleName != null) {
+ try {
+ rb = ResourceBundle.getBundle(resourceBundleName,
+ // TODO Implement kernel.vm.VM.callerClassLoader, or find
+ // another way to get the caller class loader
+ // Locale.getDefault(), VM.callerClassLoader());
+ Locale.getDefault(), ClassLoader.getSystemClassLoader());
+ } catch (MissingResourceException e) {
+ rb = null;
+ }
+ }
+ synchronized (levels) {
+ levels.add(this);
+ }
+ }
+
+ /**
+ * Gets the name of this <code>Level</code>.
+ *
+ * @return the name of this <code>Level</code>
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Gets the name of the resource bundle associated with this
+ * <code>Level</code>.
+ *
+ * @return the name of the resource bundle associated with this
+ * <code>Level</code>
+ */
+ public String getResourceBundleName() {
+ return this.resourceBundleName;
+ }
+
+ /**
+ * Gets the integer value indicating this <code>Level</code>.
+ *
+ * @return the integer value indicating this <code>Level</code>
+ */
+ public final int intValue() {
+ return this.value;
+ }
+
+ /**
+ * <p>
+ * Serialization helper method to maintain singletons and add any new
+ * levels.
+ * </p>
+ *
+ * @return The resolved instance.
+ */
+ private Object readResolve() {
+ synchronized (levels) {
+ for (Level level : levels) {
+ if (value != level.value) {
+ continue;
+ }
+ if (!name.equals(name)) {
+ continue;
+ }
+ if (resourceBundleName == level.resourceBundleName) {
+ return level;
+ } else if (resourceBundleName != null
+ && resourceBundleName.equals(level.resourceBundleName)) {
+ return level;
+ }
+ }
+ // This is a new value, so add it.
+ levels.add(this);
+ return this;
+ }
+ }
+
+ /**
+ * <p>Serialization helper to setup transient resource bundle instance.</p>
+ * @param in The input stream to read the instance data from.
+ * @throws IOException if an IO error occurs.
+ * @throws ClassNotFoundException if a class is not found.
+ */
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ if (resourceBundleName != null) {
+ try {
+ rb = ResourceBundle.getBundle(resourceBundleName);
+ } catch (MissingResourceException e) {
+ rb = null;
+ }
+ }
+ }
+
+ /**
+ * Gets the localized name of this level. The default locale is used. If no
+ * resource bundle is associated with this <code>Level</code>, the
+ * original level name is returned.
+ *
+ * @return the localized name of this level
+ */
+ public String getLocalizedName() {
+ if (rb == null) {
+ return name;
+ }
+
+ try {
+ return rb.getString(name);
+ } catch (MissingResourceException e) {
+ return name;
+ }
+ }
+
+ /**
+ * Compares two <code>Level</code> objects for equality. They are
+ * considered to be equal if they have the same value.
+ *
+ * @param o the other object to be compared with
+ * @return <code>true</code> if this object equals to the supplied object,
+ * otherwise <code>false</code>
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof Level)) {
+ return false;
+ }
+
+ return ((Level) o).intValue() == this.value;
+ }
+
+ /**
+ * Returns the hash code of this <code>Level</code> object.
+ *
+ * @return the hash code of this <code>Level</code> object
+ */
+ @Override
+ public int hashCode() {
+ return this.value;
+ }
+
+ /**
+ * Returns the string representation of this <code>Level</code> object.
+ * Usually this will include its name.
+ *
+ * @return the string representation of this <code>Level</code> object
+ */
+ @Override
+ public final String toString() {
+ return this.name;
+ }
+
+}
diff --git a/logging/src/main/java/java/util/logging/LogManager.java b/logging/src/main/java/java/util/logging/LogManager.java
new file mode 100644
index 0000000..462b658
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/LogManager.java
@@ -0,0 +1,634 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+// BEGIN android-removed
+// import java.beans.PropertyChangeListener;
+// import java.beans.PropertyChangeSupport;
+// END android-removed
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+// BEGIN android-removed
+// import java.lang.management.ManagementFactory;
+// import java.lang.reflect.Method;
+// import javax.management.MBeanServer;
+// import javax.management.ObjectName;
+// import javax.management.ObjectInstance;
+// import javax.management.MalformedObjectNameException;
+// END android-removed
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * <code>LogManager</code> is used to maintain configuration properties of the
+ * logging framework, and to manage a hierarchical namespace of all named
+ * <code>Logger</code> objects.
+ * <p>
+ * There is only one global <code>LogManager</code> instance in the
+ * application, which can be get by calling static method
+ * <code>LogManager.getLogManager()</code>. This instance is created and
+ * initialized during class initialization and cannot be changed.
+ * </p>
+ * <p>
+ * The <code>LogManager</code> class can be specified by
+ * java.util.logging.manager system property, if the property is unavailable or
+ * invalid, the default class <code>java.util.logging.LogManager</code> will
+ * be used.
+ * </p>
+ * <p>
+ * When initialization, <code>LogManager</code> read its configuration from a
+ * properties file, which by default is the "lib/logging.properties" in the JRE
+ * directory.
+ * </p>
+ * <p>
+ * However, two optional system properties can be used to customize the initial
+ * configuration process of <code>LogManager</code>.
+ * <ul>
+ * <li>"java.util.logging.config.class"</li>
+ * <li>"java.util.logging.config.file"</li>
+ * </ul>
+ * </p>
+ * <p>
+ * These two properties can be set in three ways, by the Preferences API, by the
+ * "java" command line property definitions, or by system property definitions
+ * passed to JNI_CreateJavaVM.
+ * </p>
+ * <p>
+ * The "java.util.logging.config.class" should specifies a class name. If it is
+ * set, this given class will be loaded and instantiated during
+ * <code>LogManager</code> initialization, so that this object's default
+ * constructor can read the initial configuration and define properties for
+ * <code>LogManager</code>.
+ * </p>
+ * <p>
+ * If "java.util.logging.config.class" property is not set, or it is invalid, or
+ * some exception is thrown during the instantiation, then the
+ * "java.util.logging.config.file" system property can be used to specify a
+ * properties file. The <code>LogManager</code> will read initial
+ * configuration from this file.
+ * </p>
+ * <p>
+ * If neither of these properties is defined, or some exception is thrown
+ * during these two properties using, the <code>LogManager</code> will read
+ * its initial configuration from default properties file, as described above.
+ * </p>
+ * <p>
+ * The global logging properties may include:
+ * <ul>
+ * <li>"handlers". This property's values should be a list of class names for
+ * handler classes separated by whitespace, these classes must be subclasses of
+ * <code>Handler</code> and each must have a default constructor, these
+ * classes will be loaded, instantiated and registered as handlers on the root
+ * <code>Logger</code> (the <code>Logger</code> named ""). These
+ * <code>Handler</code>s maybe initialized lazily.</li>
+ * <li>"config". The property defines a list of class names separated by
+ * whitespace. Each class must have a default constructor, in which it can
+ * update the logging configuration, such as levels, handlers, or filters for
+ * some logger, etc. These classes will be loaded and instantiated during
+ * <code>LogManager</code> configuration</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This class, together with any handler and configuration classes associated
+ * with it, <b>must</b> be loaded from the system classpath when
+ * <code>LogManager</code> configuration occurs.
+ * </p>
+ * <p>
+ * Besides global properties, the properties for loggers and Handlers can be
+ * specified in the property files. The names of these properties will start
+ * with the complete dot separated names for the handlers or loggers.
+ * </p>
+ * <p>
+ * In the <code>LogManager</code>'s hierarchical namespace,
+ * <code>Loggers</code> are organized based on their dot separated names. For
+ * example, "x.y.z" is child of "x.y".
+ * </p>
+ * <p>
+ * Levels for <code>Loggers</code> can be defined by properties whose name end
+ * with ".level". Thus "alogger.level" defines a level for the logger named as
+ * "alogger" and for all its children in the naming hierarchy. Log levels
+ * properties are read and applied in the same order as they are specified in
+ * the property file. The root logger's level can be defined by the property
+ * named as ".level".
+ * </p>
+ * <p>
+ * All methods on this type can be taken as being thread safe.
+ * </p>
+ *
+ */
+public class LogManager {
+ /*
+ * -------------------------------------------------------------------
+ * Class variables
+ * -------------------------------------------------------------------
+ */
+
+ // The line separator of the underlying OS
+ // Use privileged code to read the line.separator system property
+ private static final String lineSeparator =
+ getPrivilegedSystemProperty("line.separator"); //$NON-NLS-1$
+
+ // The shared logging permission
+ private static final LoggingPermission perm = new LoggingPermission(
+ "control", null); //$NON-NLS-1$
+
+ // the singleton instance
+ static LogManager manager;
+
+ /**
+ * <p>The String value of the {@link LoggingMXBean}'s ObjectName.</p>
+ */
+ public static final String LOGGING_MXBEAN_NAME =
+ "java.util.logging:type=Logging"; //$NON-NLS-1$
+
+// BENGIN android-removed
+// /**
+// * Get the <code>LoggingMXBean</code> instance
+// *
+// * @return the <code>LoggingMXBean</code> instance
+// */
+// public static LoggingMXBean getLoggingMXBean() {
+// try {
+// ObjectName loggingMXBeanName = new ObjectName(LOGGING_MXBEAN_NAME);
+// MBeanServer platformBeanServer =
+// ManagementFactory.getPlatformMBeanServer();
+// Set loggingMXBeanSet = platformBeanServer.queryMBeans(
+// loggingMXBeanName, null);
+//
+// if (loggingMXBeanSet.size() != 1) {
+// // logging.21=There Can Be Only One logging MX bean.
+// throw new AssertionError(Messages.getString("logging.21"));
+// }
+//
+// Iterator i = loggingMXBeanSet.iterator();
+// ObjectInstance loggingMXBeanOI = (ObjectInstance) i.next();
+// String lmxbcn = loggingMXBeanOI.getClassName();
+// Class lmxbc = Class.forName(lmxbcn);
+// Method giMethod = lmxbc.getDeclaredMethod("getInstance");
+// giMethod.setAccessible(true);
+// LoggingMXBean lmxb = (LoggingMXBean)
+// giMethod.invoke(null, new Object[] {});
+//
+// return lmxb;
+// } catch (Exception e) {
+// //TODO
+// //e.printStackTrace();
+// }
+// // logging.22=Exception occurred while getting the logging MX bean.
+// throw new AssertionError(Messages.getString("logging.22")); //$NON-NLS-1$
+// }
+// END android-removed
+
+ /*
+ * -------------------------------------------------------------------
+ * Instance variables
+ * -------------------------------------------------------------------
+ */
+ //FIXME: use weak reference to avoid heap memory leak
+ private Hashtable<String, Logger> loggers;
+
+ // the configuration properties
+ private Properties props;
+
+ // the property change listener
+// BEGIN android-removed
+// private PropertyChangeSupport listeners;
+// END android-removed
+
+ /*
+ * -------------------------------------------------------------------
+ * Global initialization
+ * -------------------------------------------------------------------
+ */
+
+ static {
+ // init LogManager singleton instance
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ String className = System.getProperty(
+ "java.util.logging.manager"); //$NON-NLS-1$
+
+ if (null != className) {
+ manager = (LogManager) getInstanceByClass(className);
+ }
+ if (null == manager) {
+ manager = new LogManager();
+ }
+
+ // read configuration
+ try {
+ manager.readConfiguration();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // if global logger has been initialized, set root as its parent
+ Logger root = new Logger("", null); //$NON-NLS-1$
+ root.setLevel(Level.INFO);
+ Logger.global.setParent(root);
+
+ manager.addLogger(root);
+ manager.addLogger(Logger.global);
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Default constructor. This is not public because there should be only one
+ * <code>LogManager</code> instance, which can be get by
+ * <code>LogManager.getLogManager(</code>. This is protected so that
+ * application can subclass the object.
+ */
+ protected LogManager() {
+ loggers = new Hashtable<String, Logger>();
+ props = new Properties();
+// BEGIN android-removed
+// listeners = new PropertyChangeSupport(this);
+// END android-removed
+ // add shutdown hook to ensure that the associated resource will be
+ // freed when JVM exits
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ reset();
+ }
+ });
+ return null;
+ }
+ });
+ }
+
+ /*
+ * -------------------------------------------------------------------
+ * Methods
+ * -------------------------------------------------------------------
+ */
+ /*
+ * Package private utilities Returns the line separator of the underlying
+ * OS.
+ */
+ static String getSystemLineSeparator() {
+ return lineSeparator;
+ }
+
+ /**
+ * Check that the caller has <code>LoggingPermission("control")</code> so
+ * that it is trusted to modify the configuration for logging framework. If
+ * the check passes, just return, otherwise <code>SecurityException</code>
+ * will be thrown.
+ *
+ * @throws SecurityException
+ * if there is a security manager in operation and the invoker
+ * of this method does not have the required security permission
+ * <code>LoggingPermission("control")</code>
+ */
+ public void checkAccess() {
+ if (null != System.getSecurityManager()) {
+ System.getSecurityManager().checkPermission(perm);
+ }
+ }
+
+ /**
+ * Add a given logger into the hierarchical namespace. The
+ * <code>Logger.addLogger()</code> factory methods call this method to add
+ * newly created Logger. This returns false if a logger with the given name
+ * has existed in the namespace
+ * <p>
+ * Note that the <code>LogManager</code> may only retain weak references
+ * to registered loggers. In order to prevent <code>Logger</code> objects
+ * from being unexpectedly garbage collected it is necessary for
+ * <i>applications</i> to maintain references to them.
+ * </p>
+ *
+ * @param logger
+ * the logger to be added
+ * @return true if the given logger is added into the namespace
+ * successfully, false if the logger of given name has existed in
+ * the namespace
+ */
+ public synchronized boolean addLogger(Logger logger) {
+ String name = logger.getName();
+ if (null != loggers.get(name)) {
+ return false;
+ }
+ addToFamilyTree(logger, name);
+ loggers.put(name, logger);
+ logger.setManager(this);
+ return true;
+ }
+
+
+ private void addToFamilyTree(Logger logger, String name) {
+ Logger parent = null;
+ // find parent
+ int lastSeparator;
+ String parentName = name;
+ while ((lastSeparator = parentName.lastIndexOf('.')) != -1) {
+ parentName = parentName.substring(0, lastSeparator);
+ parent = loggers.get(parentName);
+ if (parent != null) {
+ logger.internalSetParent(parent);
+ break;
+ } else if (getProperty(parentName+".level") != null || //$NON-NLS-1$
+ getProperty(parentName+".handlers") != null) { //$NON-NLS-1$
+ parent = Logger.getLogger(parentName);
+ logger.internalSetParent(parent);
+ break;
+ }
+ }
+ if (parent == null && null != (parent = loggers.get(""))) { //$NON-NLS-1$
+ logger.internalSetParent(parent);
+ }
+
+ // find children
+ //TODO: performance can be improved here?
+ Collection<Logger> allLoggers = loggers.values();
+ for (Logger child : allLoggers) {
+ Logger oldParent = child.getParent();
+ if (parent == oldParent
+ && (name.length() == 0 || child.getName().startsWith(
+ name + '.'))) {
+ child.setParent(logger);
+ if (null != oldParent) {
+ //-- remove from old parent as the parent has been changed
+ oldParent.removeChild(child);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the logger with the given name
+ *
+ * @param name
+ * name of logger
+ * @return logger with given name, or null if nothing is found
+ */
+ public synchronized Logger getLogger(String name) {
+ return loggers.get(name);
+ }
+
+ /**
+ * Get a <code>Enumeration</code> of all registered logger names
+ *
+ * @return enumeration of registered logger names
+ */
+ public synchronized Enumeration<String> getLoggerNames() {
+ return loggers.keys();
+ }
+
+ /**
+ * Get the global <code>LogManager</code> instance
+ *
+ * @return the global <code>LogManager</code> instance
+ */
+ public static LogManager getLogManager() {
+ return manager;
+ }
+
+ /**
+ * Get the value of property with given name
+ *
+ * @param name
+ * the name of property
+ * @return the value of property
+ */
+ public String getProperty(String name) {
+ return props.getProperty(name);
+ }
+
+ /**
+ * Re-initialize the properties and configuration. The initialization process
+ * is same as the <code>LogManager</code> instantiation.
+ * <p>
+ * A <code>PropertyChangeEvent</code> must be fired.
+ * </p>
+ *
+ * @throws IOException
+ * if any IO related problems happened
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to perform this action
+ */
+ public void readConfiguration() throws IOException {
+ checkAccess();
+ // check config class
+ String configClassName = System.getProperty(
+ "java.util.logging.config.class"); //$NON-NLS-1$
+ if (null == configClassName || null == getInstanceByClass(configClassName)) {
+ // if config class failed, check config file
+ String configFile = System.getProperty(
+ "java.util.logging.config.file"); //$NON-NLS-1$
+
+ if (null == configFile) {
+ // if cannot find configFile, use default logging.properties
+ configFile = new StringBuilder().append(
+ System.getProperty("java.home")).append(File.separator) //$NON-NLS-1$
+ .append("lib").append(File.separator).append( //$NON-NLS-1$
+ "logging.properties").toString(); //$NON-NLS-1$
+ }
+
+ InputStream input = null;
+ try {
+ // BEGIN android-removed
+ // input = new BufferedInputStream(new FileInputStream(configFile));
+ // END android-removed
+
+ // BEGIN android-added
+ try {
+ input = new BufferedInputStream(new FileInputStream(configFile), 8192);
+ } catch (Exception ex) {
+ // consult fixed resource as a last resort
+ input = new BufferedInputStream(getClass().getResourceAsStream("logging.properties"), 8192);
+ }
+ // END android-added
+ readConfigurationImpl(input);
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (Exception e) {// ignore
+ }
+ }
+ }
+ }
+ }
+
+ // use privilege code to get system property
+ static String getPrivilegedSystemProperty(final String key) {
+ return AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty(key);
+ }
+ });
+ }
+
+ // use SystemClassLoader to load class from system classpath
+ static Object getInstanceByClass(final String className) {
+ try {
+ Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass(
+ className);
+ return clazz.newInstance();
+ } catch (Exception e) {
+ try {
+ Class<?> clazz = Thread.currentThread()
+ .getContextClassLoader().loadClass(className);
+ return clazz.newInstance();
+ } catch (Exception innerE) {
+ //logging.20=Loading class "{0}" failed
+ System.err.println(Messages.getString(
+ "logging.20", className)); //$NON-NLS-1$
+ System.err.println(innerE);
+ return null;
+ }
+ }
+
+ }
+
+ // actual initialization process from a given input stream
+ private synchronized void readConfigurationImpl(InputStream ins)
+ throws IOException {
+ reset();
+ props.load(ins);
+
+ // parse property "config" and apply setting
+ String configs = props.getProperty("config"); //$NON-NLS-1$
+ if (null != configs) {
+ StringTokenizer st = new StringTokenizer(configs, " "); //$NON-NLS-1$
+ while (st.hasMoreTokens()) {
+ String configerName = st.nextToken();
+ getInstanceByClass(configerName);
+ }
+ }
+
+ // set levels for logger
+ Collection<Logger> allLoggers = loggers.values();
+ for(Logger logger : allLoggers){
+ String property = props.getProperty(
+ logger.getName()+".level"); //$NON-NLS-1$
+ if(null != property){
+ logger.setLevel(Level.parse(property));
+ }
+ }
+// BEGIN android-removed
+// listeners.firePropertyChange(null, null, null);
+// END android-removed
+ }
+
+
+ /**
+ * Re-initialize the properties and configuration from the given
+ * <code>InputStream</code>
+ * <p>
+ * A <code>PropertyChangeEvent</code> must be fired.
+ * </p>
+ *
+ * @param ins
+ * the input stream.
+ * @throws IOException
+ * if any IO related problems happened
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to perform this action
+ */
+ public void readConfiguration(InputStream ins) throws IOException {
+ checkAccess();
+ readConfigurationImpl(ins);
+ }
+
+ /**
+ * Reset configuration.
+ * <p>
+ * All handlers are closed and removed from any named loggers. All loggers'
+ * level is set to null, except the root logger's level is set to
+ * <code>Level.INFO</code>.
+ * </p>
+ *
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to perform this action
+ */
+ public void reset() {
+ checkAccess();
+ props = new Properties();
+ Enumeration<String> names = getLoggerNames();
+ while(names.hasMoreElements()){
+ String name = names.nextElement();
+ Logger logger = getLogger(name);
+ if(logger != null){
+ logger.reset();
+ }
+ }
+ Logger root = loggers.get(""); //$NON-NLS-1$
+ if (null != root) {
+ root.setLevel(Level.INFO);
+ }
+ }
+
+ /**
+ * Add a <code>PropertyChangeListener</code>, which will be invoked when
+ * the properties are reread.
+ *
+ * @param l
+ * the <code>PropertyChangeListener</code> to be added
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to perform this action
+ */
+// BEGIN android-removed
+// public void addPropertyChangeListener(IPropertyChangeListener l) {
+// if(l == null){
+// throw new NullPointerException();
+// }
+// checkAccess();
+// listeners.addPropertyChangeListener(l);
+// }
+// END android-removed
+
+ /**
+ * Remove a <code>PropertyChangeListener</code>, do nothing if the given
+ * listener is not found.
+ *
+ * @param l
+ * the <code>PropertyChangeListener</code> to be removed
+ * @throws SecurityException
+ * if security manager exists and it determines that caller does
+ * not have the required permissions to perform this action
+ */
+// BEGIN android-removed
+// public void removePropertyChangeListener(IPropertyChangeListener l) {
+// checkAccess();
+// listeners.removePropertyChangeListener(l);
+// }
+// END android-removed
+}
diff --git a/logging/src/main/java/java/util/logging/LogRecord.java b/logging/src/main/java/java/util/logging/LogRecord.java
new file mode 100644
index 0000000..e133797
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/LogRecord.java
@@ -0,0 +1,499 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * A <code>LogRecord</code> object represents a logging request. It is passed
+ * between the logging framework and individual logging handlers. Client
+ * applications should not modify a <code>LogRecord</code> object that has
+ * been passed into the logging framework.
+ * <p>
+ * The <code>LogRecord</code> class will infer the source method name and
+ * source class name the first time they are accessed if the client application
+ * didn't specify them explicitly. This automatic inference is based on the
+ * analysis of the call stack and is not guaranteed to be precise. Client
+ * applications should force the initialization of these two fields by calling
+ * <code>getSourceClassName</code> or <code>getSourceMethodName</code> if
+ * they expect to use them after passing the <code>LogRecord</code> object to
+ * another thread or transmitting it over RMI.
+ * </p>
+ *
+ */
+public class LogRecord implements Serializable {
+
+ private static final long serialVersionUID = 5372048053134512534L;
+
+ // The major byte used in serialization.
+ private static final int MAJOR = 1;
+
+ // The minor byte used in serialization.
+ private static final int MINOR = 4;
+
+ // Store the current value for the sequence number.
+ private static long currentSequenceNumber = 0;
+
+ // Store the id for each thread.
+ private static ThreadLocal<Integer> currentThreadId = new ThreadLocal<Integer>();
+
+ // The base id as the starting point for thread ID allocation.
+ private static int initThreadId = 0;
+
+ /**
+ * The logging level.
+ *
+ * @serial
+ */
+ private Level level;
+
+ /**
+ * The sequence number.
+ *
+ * @serial
+ */
+ private long sequenceNumber;
+
+ /**
+ * The name of the class that issued the logging call.
+ *
+ * @serial
+ */
+ private String sourceClassName;
+
+ /**
+ * The name of the method that issued the logging call.
+ *
+ * @serial
+ */
+ private String sourceMethodName;
+
+ /**
+ * The original message text.
+ *
+ * @serial
+ */
+ private String message;
+
+ /**
+ * The ID of the thread that issued the logging call.
+ *
+ * @serial
+ */
+ private int threadID;
+
+ /**
+ * The time that the event occurred, in milliseconds since 1970.
+ *
+ * @serial
+ */
+ private long millis;
+
+ /**
+ * The associated <code>Throwable</code> object if any.
+ *
+ * @serial
+ */
+ private Throwable thrown;
+
+ /**
+ * The name of the source logger.
+ *
+ * @serial
+ */
+ private String loggerName;
+
+ /**
+ * The name of the resource bundle used to localize the log message.
+ *
+ * @serial
+ */
+ private String resourceBundleName;
+
+ // The associated resource bundle if any.
+ private transient ResourceBundle resourceBundle;
+
+ // The parameters.
+ private transient Object[] parameters;
+
+ // If the source method and source class has been initialized
+ private transient boolean sourceInited;
+
+ /**
+ * Constructs a <code>LogRecord</code> object using the supplied the
+ * logging level and message. The millis property is set to the current
+ * time. The sequence property is set to a new unique value, allocated in
+ * increasing order within a VM. The thread ID is set to a unique value for
+ * the current thread. All other properties are set to <code>null</code>.
+ *
+ * @param level
+ * the logging level which may not be null
+ * @param msg
+ * the raw message
+ */
+ public LogRecord(Level level, String msg) {
+ if (null == level) {
+ // logging.4=The 'level' parameter is null.
+ throw new NullPointerException(Messages.getString("logging.4")); //$NON-NLS-1$
+ }
+ this.level = level;
+ this.message = msg;
+ this.millis = System.currentTimeMillis();
+
+ synchronized (LogRecord.class) {
+ this.sequenceNumber = currentSequenceNumber++;
+ Integer id = currentThreadId.get();
+ if (null == id) {
+ this.threadID = initThreadId;
+ currentThreadId.set(Integer.valueOf(initThreadId++));
+ } else {
+ this.threadID = id.intValue();
+ }
+ }
+
+ this.sourceClassName = null;
+ this.sourceMethodName = null;
+ this.loggerName = null;
+ this.parameters = null;
+ this.resourceBundle = null;
+ this.resourceBundleName = null;
+ this.thrown = null;
+ }
+
+ /**
+ * Gets the logging level.
+ *
+ * @return the logging level
+ */
+ public Level getLevel() {
+ return level;
+ }
+
+ /**
+ * Sets the logging level.
+ *
+ * @param level
+ * the level to set
+ */
+ public void setLevel(Level level) {
+ if (null == level) {
+ // logging.4=The 'level' parameter is null.
+ throw new NullPointerException(Messages.getString("logging.4")); //$NON-NLS-1$
+ }
+ this.level = level;
+ }
+
+ /**
+ * Gets the name of the logger.
+ *
+ * @return the logger name
+ */
+ public String getLoggerName() {
+ return loggerName;
+ }
+
+ /**
+ * Sets the name of the logger.
+ *
+ * @param loggerName
+ * the logger name to set
+ */
+ public void setLoggerName(String loggerName) {
+ this.loggerName = loggerName;
+ }
+
+ /**
+ * Gets the raw message.
+ *
+ * @return the raw message
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Sets the raw message.
+ *
+ * @param message
+ * the raw message to set
+ */
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ /**
+ * Gets the time that the event occurred, in milliseconds since 1970.
+ *
+ * @return the time that the event occurred, in milliseconds since 1970
+ */
+ public long getMillis() {
+ return millis;
+ }
+
+ /**
+ * Sets the time that the event occurred, in milliseconds since 1970.
+ *
+ * @param millis
+ * the time that the event occurred, in milliseconds since 1970
+ */
+ public void setMillis(long millis) {
+ this.millis = millis;
+ }
+
+ /**
+ * Gets the parameters.
+ *
+ * @return the array of parameters
+ */
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Sets the parameters.
+ *
+ * @param parameters
+ * the array of parameters to set
+ */
+ public void setParameters(Object[] parameters) {
+ this.parameters = parameters;
+ }
+
+ /**
+ * Gets the resource bundle used to localize the raw message during
+ * formatting.
+ *
+ * @return the associated resource bundle
+ */
+ public ResourceBundle getResourceBundle() {
+ return resourceBundle;
+ }
+
+ /**
+ * Sets the resource bundle used to localize the raw message during
+ * formatting.
+ *
+ * @param resourceBundle
+ * the resource bundle to set
+ */
+ public void setResourceBundle(ResourceBundle resourceBundle) {
+ this.resourceBundle = resourceBundle;
+ }
+
+ /**
+ * Gets the name of the resource bundle.
+ *
+ * @return the name of the resource bundle
+ */
+ public String getResourceBundleName() {
+ return resourceBundleName;
+ }
+
+ /**
+ * Sets the name of the resource bundle.
+ *
+ * @param resourceBundleName
+ * the name of the resource bundle to set
+ */
+ public void setResourceBundleName(String resourceBundleName) {
+ this.resourceBundleName = resourceBundleName;
+ }
+
+ /**
+ * Gets the sequence number.
+ *
+ * @return the sequence number
+ */
+ public long getSequenceNumber() {
+ return sequenceNumber;
+ }
+
+ /**
+ * Sets the sequence number. It is usually unnecessary to call this method
+ * to change the sequence number because the number is allocated when this
+ * instance is constructed.
+ *
+ * @param sequenceNumber
+ * the sequence number to set
+ */
+ public void setSequenceNumber(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ /**
+ * Gets the name of the class that issued the logging call.
+ *
+ * @return the name of the class that issued the logging call
+ */
+ public String getSourceClassName() {
+ initSource();
+ return sourceClassName;
+ }
+
+ /*
+ * Init the sourceClass and sourceMethod fields.
+ */
+ private void initSource() {
+ if (!sourceInited) {
+ StackTraceElement[] elements = (new Throwable()).getStackTrace();
+ int i = 0;
+ String current = null;
+ FINDLOG: for (; i < elements.length; i++) {
+ current = elements[i].getClassName();
+ if (current.equals(Logger.class.getName())) {
+ break FINDLOG;
+ }
+ }
+ while(++i<elements.length && elements[i].getClassName().equals(current)) {
+ // do nothing
+ }
+ if (i < elements.length) {
+ this.sourceClassName = elements[i].getClassName();
+ this.sourceMethodName = elements[i].getMethodName();
+ }
+ sourceInited = true;
+ }
+ }
+
+ /**
+ * Sets the name of the class that issued the logging call.
+ *
+ * @param sourceClassName
+ * the name of the class that issued the logging call
+ */
+ public void setSourceClassName(String sourceClassName) {
+ sourceInited = true;
+ this.sourceClassName = sourceClassName;
+ }
+
+ /**
+ * Gets the name of the method that issued the logging call.
+ *
+ * @return the name of the method that issued the logging call
+ */
+ public String getSourceMethodName() {
+ initSource();
+ return sourceMethodName;
+ }
+
+ /**
+ * Sets the name of the method that issued the logging call.
+ *
+ * @param sourceMethodName
+ * the name of the method that issued the logging call
+ */
+ public void setSourceMethodName(String sourceMethodName) {
+ sourceInited = true;
+ this.sourceMethodName = sourceMethodName;
+ }
+
+ /**
+ * Gets the ID of the thread originating the message.
+ *
+ * @return the ID of the thread originating the message
+ */
+ public int getThreadID() {
+ return threadID;
+ }
+
+ /**
+ * Sets the ID of the thread originating the message.
+ *
+ * @param threadID
+ * the ID of the thread originating the message
+ */
+ public void setThreadID(int threadID) {
+ this.threadID = threadID;
+ }
+
+ /**
+ * Gets the <code>Throwable</code> object associated with this log record.
+ *
+ * @return the <code>Throwable</code> object associated with this log
+ * record
+ */
+ public Throwable getThrown() {
+ return thrown;
+ }
+
+ /**
+ * Sets the <code>Throwable</code> object associated with this log record.
+ *
+ * @param thrown
+ * the <code>Throwable</code> object associated with this log
+ * record
+ */
+ public void setThrown(Throwable thrown) {
+ this.thrown = thrown;
+ }
+
+ /*
+ * Customized serialization.
+ */
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeByte(MAJOR);
+ out.writeByte(MINOR);
+ if (null == parameters) {
+ out.writeInt(-1);
+ } else {
+ out.writeInt(parameters.length);
+ for (Object element : parameters) {
+ out.writeObject(null == element ? null : element.toString());
+ }
+ }
+ }
+
+ /*
+ * Customized deserialization.
+ */
+ private void readObject(ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ byte major = in.readByte();
+ byte minor = in.readByte();
+ //only check MAJOR version
+ if (major != MAJOR) {
+ // logging.5=Different version - {0}.{1}
+ throw new IOException(Messages.getString("logging.5", major, minor)); //$NON-NLS-1$
+ }
+
+ int length = in.readInt();
+ if (length >= 0) {
+ parameters = new Object[length];
+ for (int i = 0; i < parameters.length; i++) {
+ parameters[i] = in.readObject();
+ }
+ }
+ if (null != resourceBundleName) {
+ try {
+ resourceBundle = Logger.loadResourceBundle(resourceBundleName);
+ } catch (MissingResourceException e) {
+ // Cannot find the specified resource bundle
+ resourceBundle = null;
+ }
+ }
+ }
+}
diff --git a/logging/src/main/java/java/util/logging/Logger.java b/logging/src/main/java/java/util/logging/Logger.java
new file mode 100644
index 0000000..fbe32ed
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/Logger.java
@@ -0,0 +1,1425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * Loggers are used to log records to certain outputs, including file, console,
+ * etc. They use various handlers to actually do the output-dependent
+ * operations.
+ * <p>
+ * Client applications can get named loggers by calling the methods
+ * <code>getLogger</code>. They can also get anonymous loggers by calling the
+ * methods <code>getAnonymousLogger</code>. Named loggers are organized in a
+ * namespace hierarchy managed by a log manager. The naming convention is
+ * usually the same as java package's naming convention, i.e., using
+ * dot-separated strings. Anonymous loggers do not belong to any namespace.
+ * </p>
+ * <p>
+ * Loggers "inherit" log level setting from their parent if its own level is set
+ * to <code>null</code>. This is also true for the resource bundle. The
+ * logger's resource bundle is used to localize the log messages if no resource
+ * bundle name is given when a log method is called. If
+ * <code>getUseParentHandlers</code> is <code>true</code>, loggers also
+ * inherit their parent's handlers. Here "inherit" only means the "behaviors"
+ * are inherited. The internal fields value will not change, for example,
+ * <code>getLevel()</code> still returns <code>null</code>.
+ * </p>
+ * <p>
+ * When loading a given resource bundle, the logger first tries to use the
+ * context classloader. If that fails, it tries the system classloader. And if
+ * that still fails, it searches up the class stack and uses each class's
+ * classloader to try to locate the resource bundle.
+ * </p>
+ * <p>
+ * Some log methods accept log requests that do not specify the source class and
+ * source method. In these cases, the logging framework will automatically infer
+ * the calling class and method, but not guaranteed to be accurate.
+ * </p>
+ * <p>
+ * Once a <code>LogRecord</code> object has been passed into the logging
+ * framework, it is owned by the logging framework and the client applications
+ * should not use it any longer.
+ * </p>
+ * <p>
+ * All methods of this class are thread-safe.
+ * </p>
+ *
+ * @see LogManager
+ */
+public class Logger {
+
+ /**
+ * The global logger is provided as convenience for casual use.
+ */
+ public final static Logger global = new Logger("global", null); //$NON-NLS-1$
+
+ // the name of this logger
+ private volatile String name;
+
+ // the parent logger of this logger
+ private Logger parent;
+
+ // the logging level of this logger
+ private volatile Level levelObjVal;
+
+ // the logging level as int of this logger
+ private volatile int levelIntVal;
+
+ // the filter
+ private Filter filter;
+
+ // the name of the resource bundle used to localize logging messages
+ private String resBundleName;
+
+ // the loaded resource bundle according to the specified name
+ private ResourceBundle resBundle;
+
+ // the handlers attached to this logger
+ private List<Handler> handlers;
+
+ /*
+ * flag indicating whether to notify parent's handlers on receiving a log
+ * request
+ */
+ private boolean notifyParentHandlers;
+
+ // flag indicating whether this logger is named or anonymous
+ private boolean isNamed;
+
+ private List<Logger> childs;
+
+ private LogManager manager;
+
+ private volatile boolean handlerInited;
+
+
+ /*
+ * -------------------------------------------------------------------
+ * Constructors
+ * -------------------------------------------------------------------
+ */
+
+ /**
+ * Constructs a <code>Logger</code> object with the supplied name and
+ * resource bundle name.
+ *
+ * @param name
+ * the name of this logger, may be null for anonymous loggers
+ * @param resourceBundleName
+ * the name of the resource bundle used to localize logging
+ * messages, may be null
+ * @throws MissingResourceException
+ * If the specified resource bundle can not be loaded.
+ */
+ protected Logger(String name, String resourceBundleName) {
+ // try to load the specified resource bundle first
+ if (null == resourceBundleName) {
+ this.resBundleName = null;
+ this.resBundle = null;
+ } else {
+ this.resBundle = loadResourceBundle(resourceBundleName);
+ this.resBundleName = resourceBundleName;
+ }
+ this.name = name;
+ this.parent = null;
+ this.filter = null;
+ this.childs = new ArrayList<Logger>();
+ this.notifyParentHandlers = true;
+ // any logger is not anonymous by default
+ this.isNamed = true;
+
+ //-- 'null' means that level will be inherited from parent (see getLevel)
+ //-- Level.INFO is default level if we don't set it. It will be
+ //-- changed to parent level or to configLevel after adding to the
+ //-- family tree. As of this, actually, setting to Level.INFO is
+ //-- not needed here.
+ this.levelObjVal = null;
+ this.levelIntVal = Level.INFO.intValue();
+ }
+
+ //-- should be called under the lm lock
+ private void setLevelImpl(Level newLevel) {
+ // update levels for the whole hierarchy
+ int oldVal = levelIntVal;
+ levelObjVal = newLevel;
+ if (null == newLevel) {
+ levelIntVal = null != parent
+ ? parent.levelIntVal
+ : Level.INFO.intValue();
+ } else {
+ levelIntVal = newLevel.intValue();
+ }
+ if (oldVal != levelIntVal) {
+ forceChildsToInherit();
+ }
+ }
+
+ //-- should be called under the lm lock
+ private void forceChildsToInherit() {
+ for (Logger child : childs) {
+ if (null == child.levelObjVal) { // should inherit
+ child.setLevelImpl(null);
+ }
+ }
+ }
+
+ /*
+ * -------------------------------------------------------------------
+ * Methods
+ * -------------------------------------------------------------------
+ */
+
+ /**
+ * Load the specified resource bundle, use privileged code.
+ *
+ * @param resourceBundleName
+ * the name of the resource bundle to load, cannot be null
+ * @return the loaded resource bundle.
+ * @throws MissingResourceException
+ * If the specified resource bundle can not be loaded.
+ */
+ static ResourceBundle loadResourceBundle(String resourceBundleName) {
+ // try context class loader to load the resource
+ ClassLoader cl = AccessController.doPrivileged(
+ new PrivilegedAction<ClassLoader>() {
+ public ClassLoader run() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ });
+ if (null != cl) {
+ try {
+ return ResourceBundle.getBundle(resourceBundleName, Locale
+ .getDefault(), cl);
+ } catch (MissingResourceException e) {
+ // Failed to load using context classloader, ignore
+ }
+ }
+ // try system class loader to load the resource
+ cl = AccessController.doPrivileged(
+ new PrivilegedAction<ClassLoader>() {
+ public ClassLoader run() {
+ return ClassLoader.getSystemClassLoader();
+ }
+ });
+ if (null != cl) {
+ try {
+ return ResourceBundle.getBundle(resourceBundleName, Locale
+ .getDefault(), cl);
+ } catch (MissingResourceException e) {
+ // Failed to load using system classloader, ignore
+ }
+ }
+ // try all class loaders up the class stack
+ final Class<?>[] classes = AccessController
+ .doPrivileged(new PrivilegedAction<Class<?>[]>() {
+ public Class<?>[] run() {
+ return (new PrivateSecurityManager())
+ .privateGetClassContext();
+ }
+ });
+ // the first class, which is PrivateSecurityManager, is skipped
+ for (int i = 1; i < classes.length; i++) {
+ final int index = i;
+ try {
+ cl = AccessController.doPrivileged(
+ new PrivilegedAction<ClassLoader>() {
+ public ClassLoader run() {
+ return classes[index].getClassLoader();
+ }
+ });
+ if (null == cl) {
+ continue;
+ }
+ return ResourceBundle.getBundle(resourceBundleName, Locale
+ .getDefault(), cl);
+ } catch (MissingResourceException e) {
+ // Failed to load using the current class's classloader, ignore
+ }
+ }
+ // logging.8=Failed to load the specified resource bundle "{0}".
+ throw new MissingResourceException(Messages.getString("logging.8", //$NON-NLS-1$
+ resourceBundleName), resourceBundleName, null);
+ }
+
+ /**
+ * Gets an anonymous logger to use internally in a thread. Anonymous loggers
+ * are not registered in the log manager's namespace. No security checks
+ * will be performed when updating an anonymous logger's control settings
+ * so that they can be used in applets.
+ * <p>
+ * Anonymous loggers' parent is set to be the root logger. This enables
+ * them to inherit default logging level and handlers from the root logger.
+ * </p>
+ *
+ * @return a new instance of anonymous logger
+ */
+ public static Logger getAnonymousLogger() {
+ return getAnonymousLogger(null);
+ }
+
+ /**
+ * Gets an anonymous logger to use internally in a thread. Anonymous loggers
+ * are not registered in the log manager's namespace. No security checks
+ * will be performed when updating an anonymous logger's control settings
+ * so that they can be used in applets.
+ * <p>
+ * Anonymous loggers' parent is set to be the root logger. This enables
+ * them to inherit default logging level and handlers from the root logger.
+ * </p>
+ *
+ * @param resourceBundleName
+ * the name of the resource bundle used to localize log messages
+ * @return a new instance of anonymous logger
+ * @throws MissingResourceException
+ * If the specified resource bundle can not be loaded.
+ */
+ public static Logger getAnonymousLogger(String resourceBundleName) {
+ final Logger l = new Logger(null, resourceBundleName);
+ l.isNamed = false;
+ l.internalSetParent(LogManager.getLogManager().getLogger("")); //$NON-NLS-1$
+ return l;
+ }
+
+ /*
+ * Check whether the same resource bundle has been specified.
+ * Synchronize to ensure the consistency between resource bundle
+ * and its name.
+ */
+ private static void updateResourceBundle(Logger l, String resourceBundleName) {
+ synchronized (l) {
+ if (null == l.getResourceBundleName()) {
+ if(null == resourceBundleName){
+ return;
+ }
+ /*
+ * load the resource bundle if none is specified
+ * before
+ */
+ l.resBundle = loadResourceBundle(resourceBundleName);
+ l.resBundleName = resourceBundleName;
+ } else if (!l.getResourceBundleName().equals(resourceBundleName)) {
+ /*
+ * throw exception if the specified resource bundles
+ * are inconsistent with each other, i.e., different
+ * names
+ */
+ // logging.9=The specified resource bundle name "{0}" is
+ // inconsistent with the existing one "{1}".
+ throw new IllegalArgumentException(Messages.getString(
+ "logging.9", //$NON-NLS-1$
+ resourceBundleName, l.getResourceBundleName()));
+ }
+ }
+ }
+
+ /*
+ * Gets a named logger associated with the supplied resource bundle. This
+ * method accepts null resource bundle name. The method body is synchronized
+ * on the instance of the LogManager to insure the consistency of the whole
+ * operation.
+ */
+ private static Logger getLoggerWithRes(String name,
+ String resourceBundleName, boolean hasResourceName) {
+ LogManager man = LogManager.getLogManager();
+ Logger l = null;
+ synchronized (man) {
+ // Try to find an existing logger with the specified name
+ l = man.getLogger(name);
+ // If no existing logger with the same name, create a new one
+ if (null == l) {
+ l = new Logger(name, resourceBundleName);
+ man.addLogger(l);
+ return l;
+ }
+ }
+ if (hasResourceName) {
+ updateResourceBundle(l, resourceBundleName);
+ }
+ return l;
+ }
+
+ /**
+ * Gets a named logger. The returned logger may already exist, or may be
+ * newly created. If the latter, its level will be set to the configured
+ * level according to the <code>LogManager</code>'s properties if any.
+ *
+ * @param name
+ * the name of the logger to get, cannot be null
+ * @return a named logger
+ * @throws MissingResourceException
+ * If the specified resource bundle can not be loaded.
+ */
+ public static Logger getLogger(String name) {
+ return getLoggerWithRes(name, null, false);
+ }
+
+ /**
+ * Gets a named logger associated with the supplied resource bundle. The
+ * resource bundle will be used to localize logging messages.
+ *
+ * @param name
+ * the name of the logger to get, cannot be null
+ * @param resourceBundleName
+ * the name of the resource bundle, may be null
+ * @return a named logger
+ */
+ public static Logger getLogger(String name, String resourceBundleName) {
+ return getLoggerWithRes(name, resourceBundleName, true);
+ }
+
+ /**
+ * Adds a handler to this logger. The handler will be fed with log records
+ * received by this logger.
+ *
+ * @param handler
+ * the handler object to add, cannot be null
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void addHandler(Handler handler) {
+ if (null == handler) {
+ // logging.A=The 'handler' parameter is null.
+ throw new NullPointerException(Messages.getString("logging.A")); //$NON-NLS-1$
+ }
+ // Anonymous loggers can always add handlers
+ if (this.isNamed) {
+ LogManager.getLogManager().checkAccess();
+ }
+ initHandler();
+ synchronized(this){
+ this.handlers.add(handler);
+ }
+ }
+
+ /*
+ * Be cautious to avoid deadlock when using this method, it gets lock on manager
+ * at first, and then gets lock on this Logger, so any methods should not hold
+ * lock on this Logger when invoking this method.
+ */
+ private void initHandler() {
+ if(!handlerInited){
+ synchronized (this) {
+ if (!handlerInited) {
+ // BEGIN android-added
+ /*
+ * Force LogManager to be initialized, since its
+ * class init code performs necessary one-time setup.
+ */
+ LogManager.getLogManager();
+ // END android-added
+
+ if (handlers == null) {
+ handlers = new ArrayList<Handler>();
+ }
+ if (manager == null) {
+ return;
+ }
+
+ String handlerStr = manager
+ .getProperty("".equals(name) ? "handlers" : name + ".handlers"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (null == handlerStr) {
+ return;
+ }
+ StringTokenizer st = new StringTokenizer(handlerStr, " "); //$NON-NLS-1$
+ while (st.hasMoreTokens()) {
+ String handlerName = st.nextToken();
+
+ // BEGIN android-changed: deal with non-existing handler
+ try {
+ Handler handler = (Handler) LogManager
+ .getInstanceByClass(handlerName);
+ handlers.add(handler);
+ String level = manager.getProperty(handlerName
+ + ".level"); //$NON-NLS-1$
+ if (null != level) {
+ handler.setLevel(Level.parse(level));
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ // END android-changed
+
+ }
+ handlerInited = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets all the handlers associated with this logger.
+ *
+ * @return an array of all the handlers associated with this logger
+ */
+ public Handler[] getHandlers() {
+ initHandler();
+ synchronized(this){
+ return handlers.toArray(new Handler[handlers.size()]);
+ }
+ }
+
+ /**
+ * Removes a handler for this logger. If the specified handler does not
+ * exist, this method has no effect.
+ *
+ * @param handler
+ * the handler to be removed, cannot be null
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void removeHandler(Handler handler) {
+ // Anonymous loggers can always remove handlers
+ if (this.isNamed) {
+ LogManager.getLogManager().checkAccess();
+ }
+ if (null == handler) {
+ return;
+ }
+ initHandler();
+ synchronized(this){
+ this.handlers.remove(handler);
+ }
+ }
+
+ /**
+ * Gets the filter used by this logger.
+ *
+ * @return the filter used by this logger
+ */
+ public Filter getFilter() {
+ return this.filter;
+ }
+
+ /**
+ * Sets the filter used by this logger.
+ *
+ * @param newFilter
+ * the filter to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void setFilter(Filter newFilter) {
+ // Anonymous loggers can always set the filter
+ if (this.isNamed) {
+ LogManager.getLogManager().checkAccess();
+ }
+ filter = newFilter;
+ }
+
+ /**
+ * Gets the logging level of this logger.
+ *
+ * @return the logging level of this logger
+ */
+ public Level getLevel() {
+ return levelObjVal;
+ }
+
+ /**
+ * Sets the logging level for this logger. A <code>null</code> level
+ * indicates this logger will inherit its parent's level.
+ *
+ * @param newLevel
+ * the logging level to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void setLevel(Level newLevel) {
+ // Anonymous loggers can always set the level
+ if (this.isNamed) {
+ LogManager.getLogManager().checkAccess();
+ }
+ synchronized (LogManager.getLogManager()) {
+ setLevelImpl(newLevel);
+ }
+ }
+
+ /**
+ * Gets the flag which indicates whether to use parent's handlers to publish
+ * incoming log records, potentially recursively up the namespace.
+ *
+ * @return <code>true</code> if set to use parent's handlers, otherwise
+ * <code>false</code>
+ */
+ public boolean getUseParentHandlers() {
+ return this.notifyParentHandlers;
+ }
+
+ /**
+ * Sets the flag which indicates whether to use parent's handlers to publish
+ * incoming log records, potentially recursively up the namespace.
+ *
+ * @param notifyParentHandlers
+ * the flag whether to use parent's handlers
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void setUseParentHandlers(boolean notifyParentHandlers) {
+ // Anonymous loggers can always set the useParentHandlers flag
+ if (this.isNamed) {
+ LogManager.getLogManager().checkAccess();
+ }
+ this.notifyParentHandlers = notifyParentHandlers;
+ }
+
+ /**
+ * Gets the parent of this logger in the namespace.
+ *
+ * @return the parent of this logger in the namespace
+ */
+ public Logger getParent() {
+ return parent;
+ }
+
+ /**
+ * Sets the parent of this logger in the namespace. This method should
+ * usually be used by the <code>LogManager</code> object only. This
+ * method does not check security.
+ *
+ * @param newParent
+ * the parent logger to set
+ */
+ void internalSetParent(Logger newParent) {
+ //All hierarchy related modifications should get LogManager lock at first
+ synchronized(LogManager.getLogManager()){
+ parent = newParent;
+ // -- update level after setting a parent.
+ // -- if level == null we should inherit the parent's level
+ if (null == levelObjVal) {
+ setLevelImpl(levelObjVal);
+ }
+ newParent.addChild(this);
+ }
+ }
+
+ /**
+ * Sets the parent of this logger in the namespace. This method should
+ * usually be used by the <code>LogManager</code> object only.
+ *
+ * @param parent
+ * the parent logger to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ public void setParent(Logger parent) {
+ if (null == parent) {
+ // logging.B=The 'parent' parameter is null.
+ throw new NullPointerException(Messages.getString("logging.B")); //$NON-NLS-1$
+ }
+ // even anonymous loggers are checked
+ LogManager.getLogManager().checkAccess();
+ internalSetParent(parent);
+ }
+
+ final void addChild(Logger logger) {
+ childs.add(logger);
+ }
+
+ final void removeChild(Logger child) {
+ childs.remove(child);
+ }
+
+
+ /**
+ * Gets the name of this logger.
+ *
+ * @return the name of this logger
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Gets the loaded resource bundle used by this logger to localize logging
+ * messages. If it's null, the parent's resource bundle will be inherited.
+ *
+ * @return the loaded resource bundle used by this logger
+ */
+ public ResourceBundle getResourceBundle() {
+ return this.resBundle;
+ }
+
+ /**
+ * Gets the name of the loaded resource bundle used by this logger to
+ * localize logging messages. If it's null, the parent's resource bundle
+ * name will be inherited.
+ *
+ * @return the name of the loaded resource bundle used by this logger
+ */
+ public String getResourceBundleName() {
+ return this.resBundleName;
+ }
+
+ /**
+ * This method is for compatibility. Tests written to the reference
+ * implementation API imply that the isLoggable() method is not called
+ * directly. This behavior is important because subclass may override
+ * isLoggable() method, so that affect the result of log methods.
+ */
+ private boolean internalIsLoggable(Level l) {
+ int effectiveLevel = levelIntVal;
+ if (effectiveLevel == Level.OFF.intValue()) {
+ // always return false if the effective level is off
+ return false;
+ }
+ return l.intValue() >= effectiveLevel;
+ }
+
+ /**
+ * Determines whether this logger will actually log messages of the
+ * specified level. The effective level used to do the determination may be
+ * inherited from its parent. The default level is <code>Level.INFO</code>.
+ *
+ * @param l
+ * the level to check
+ * @return <code>true</code> if this logger will actually log this level,
+ * otherwise <code>false</code>
+ */
+ public boolean isLoggable(Level l) {
+ return internalIsLoggable(l);
+ }
+
+ /*
+ * Sets the resource bundle and its name for a supplied LogRecord object.
+ * This method first tries to use this logger's resource bundle if any,
+ * otherwise try to inherit from this logger's parent, recursively up the
+ * namespace. Synchronize to ensure the consistency between resource bundle
+ * and its name.
+ */
+ private void setResourceBundle(LogRecord record) {
+ if (null != this.resBundleName) {
+ record.setResourceBundle(this.resBundle);
+ record.setResourceBundleName(this.resBundleName);
+ } else {
+ Logger anyParent = this.parent;
+ // no need to synchronize here, because if resBundleName
+ // is not null, there is no chance to modify it
+ while (null != anyParent) {
+ if (null != anyParent.resBundleName) {
+ record.setResourceBundle(anyParent.resBundle);
+ record.setResourceBundleName(anyParent.resBundleName);
+ return;
+ }
+ anyParent = anyParent.parent;
+ }
+ }
+ }
+
+ /**
+ * Logs a message indicating entering a method. A log record with log level
+ * <code>Level.FINER</code>, log message "ENTRY", and the specified
+ * source class name and source method name is submitted for logging.
+ *
+ * @param sourceClass
+ * the calling class name
+ * @param sourceMethod
+ * the method name
+ */
+ public void entering(String sourceClass, String sourceMethod) {
+ if (internalIsLoggable(Level.FINER)) {
+ LogRecord record = new LogRecord(Level.FINER, "ENTRY"); //$NON-NLS-1$
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message indicating entering a method. A log record with log level
+ * <code>Level.FINER</code>, log message "ENTRY", and the specified
+ * source class name and source method name and one parameter is submitted
+ * for logging.
+ *
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param param
+ * the parameter for the method call
+ */
+ public void entering(String sourceClass, String sourceMethod, Object param) {
+ if (internalIsLoggable(Level.FINER)) {
+ LogRecord record = new LogRecord(Level.FINER, "ENTRY" + " {0}"); //$NON-NLS-1$ //$NON-NLS-2$
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setParameters(new Object[] { param });
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message indicating entering a method. A log record with log level
+ * <code>Level.FINER</code>, log message "ENTRY", and the specified
+ * source class name and source method name and parameters is submitted for
+ * logging.
+ *
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param params
+ * an array of parameters for the method call
+ */
+ public void entering(String sourceClass, String sourceMethod,
+ Object[] params) {
+ if (internalIsLoggable(Level.FINER)) {
+ String msg = "ENTRY"; //$NON-NLS-1$
+ if (null != params) {
+ StringBuilder msgBuffer = new StringBuilder("ENTRY"); //$NON-NLS-1$
+ for (int i = 0; i < params.length; i++) {
+ msgBuffer.append(" {" + i + "}"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ msg = msgBuffer.toString();
+ }
+ LogRecord record = new LogRecord(Level.FINER, msg);
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setParameters(params);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message indicating existing a method. A log record with log level
+ * <code>Level.FINER</code>, log message "RETURN", and the specified
+ * source class name and source method name is submitted for logging.
+ *
+ * @param sourceClass
+ * the calling class name
+ * @param sourceMethod
+ * the method name
+ */
+ public void exiting(String sourceClass, String sourceMethod) {
+ if (internalIsLoggable(Level.FINER)) {
+ LogRecord record = new LogRecord(Level.FINER, "RETURN"); //$NON-NLS-1$
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message indicating exiting a method. A log record with log level
+ * <code>Level.FINER</code>, log message "RETURN", and the specified
+ * source class name and source method name and return value is submitted
+ * for logging.
+ *
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param result
+ * the return value of the method call
+ */
+ public void exiting(String sourceClass, String sourceMethod, Object result) {
+ if (internalIsLoggable(Level.FINER)) {
+ LogRecord record = new LogRecord(Level.FINER, "RETURN" + " {0}"); //$NON-NLS-1$ //$NON-NLS-2$
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setParameters(new Object[] { result });
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message indicating throwing an exception. A log record with log
+ * level <code>Level.FINER</code>, log message "THROW", and the specified
+ * source class name and source method name and <code>Throwable</code>
+ * object is submitted for logging.
+ *
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param thrown
+ * the <code>Throwable</code> object
+ */
+ public void throwing(String sourceClass, String sourceMethod,
+ Throwable thrown) {
+ if (internalIsLoggable(Level.FINER)) {
+ LogRecord record = new LogRecord(Level.FINER, "THROW"); //$NON-NLS-1$
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setThrown(thrown);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of level <code>Level.SEVERE</code>.
+ *
+ * @param msg
+ * the message to log
+ */
+ public void severe(String msg) {
+ if (internalIsLoggable(Level.SEVERE)) {
+ LogRecord record = new LogRecord(Level.SEVERE, msg);
+ record.setLoggerName(this.name);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of level <code>Level.WARNING</code>.
+ *
+ * @param msg
+ * the message to log
+ */
+ public void warning(String msg) {
+ if (internalIsLoggable(Level.WARNING)) {
+ LogRecord record = new LogRecord(Level.WARNING, msg);
+ record.setLoggerName(this.name);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of level <code>Level.INFO</code>.
+ *
+ * @param msg
+ * the message to log
+ */
+ public void info(String msg) {
+ if (internalIsLoggable(Level.INFO)) {
+ LogRecord record = new LogRecord(Level.INFO, msg);
+ record.setLoggerName(this.name);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of level <code>Level.CONFIG</code>.
+ *
+ * @param msg
+ * the message to log
+ */
+ public void config(String msg) {
+ if (internalIsLoggable(Level.CONFIG)) {
+ LogRecord record = new LogRecord(Level.CONFIG, msg);
+ record.setLoggerName(this.name);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of level <code>Level.FINE</code>.
+ *
+ * @param msg
+ * the message to log
+ */
+ public void fine(String msg) {
+ if (internalIsLoggable(Level.FINE)) {
+ LogRecord record = new LogRecord(Level.FINE, msg);
+ record.setLoggerName(this.name);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of level <code>Level.FINER</code>.
+ *
+ * @param msg
+ * the message to log
+ */
+ public void finer(String msg) {
+ if (internalIsLoggable(Level.FINER)) {
+ LogRecord record = new LogRecord(Level.FINER, msg);
+ record.setLoggerName(this.name);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of level <code>Level.FINEST</code>.
+ *
+ * @param msg
+ * the message to log
+ */
+ public void finest(String msg) {
+ if (internalIsLoggable(Level.FINEST)) {
+ LogRecord record = new LogRecord(Level.FINEST, msg);
+ record.setLoggerName(this.name);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the specified level.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param msg
+ * the message to log
+ */
+ public void log(Level logLevel, String msg) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ record.setLoggerName(this.name);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the specified level with the supplied parameter.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param msg
+ * the message to log
+ * @param param
+ * the parameter associated with the event that need to be logged
+ */
+ public void log(Level logLevel, String msg, Object param) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ record.setLoggerName(this.name);
+ record.setParameters(new Object[] { param });
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the specified level with the supplied parameter array.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param msg
+ * the message to log
+ * @param params
+ * the parameter array associated with the event that need to be
+ * logged
+ */
+ public void log(Level logLevel, String msg, Object[] params) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ record.setLoggerName(this.name);
+ record.setParameters(params);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the specified level with the supplied
+ * <code>Throwable</code> object.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param msg
+ * the message to log
+ * @param thrown
+ * the <code>Throwable</code> object associated with the event
+ * that need to be logged
+ */
+ public void log(Level logLevel, String msg, Throwable thrown) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ record.setLoggerName(this.name);
+ record.setThrown(thrown);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a given log record. Only those with a logging level no lower than
+ * this logger's level will be submitted to this logger's handlers for
+ * logging. If <code>getUseParentHandlers()</code> is <code>true</code>,
+ * the log record will also be submitted to the parent logger's handlers,
+ * potentially recursively up the namespace.
+ * <p>
+ * Since all other log methods call this method to actually perform the
+ * logging action, subclasses of this class can override this method to
+ * catch all logging activities.
+ * </p>
+ *
+ * @param record
+ * the log record to be logged
+ */
+ public void log(LogRecord record) {
+ if (internalIsLoggable(record.getLevel())) {
+ // apply the filter if any
+ Filter f = filter;
+ if (null != f && !f.isLoggable(record)) {
+ return;
+ }
+ initHandler();
+ /*
+ * call the handlers of this logger, throw any exception that
+ * occurs
+ */
+ Handler[] allHandlers = getHandlers();
+ for (Handler element : allHandlers) {
+ element.publish(record);
+ }
+ // call the parent's handlers if set useParentHandlers
+ Logger temp = this;
+ Logger theParent = temp.parent;
+ while (theParent != null && temp.getUseParentHandlers()) {
+ Handler[] ha = theParent.getHandlers();
+ for (Handler element : ha) {
+ element.publish(record);
+ }
+ temp = theParent;
+ theParent = temp.parent;
+ }
+ }
+ }
+
+ /**
+ * Logs a message of the given level with the specified source class name
+ * and source method name.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param msg
+ * the message to be logged
+ */
+ public void logp(Level logLevel, String sourceClass, String sourceMethod,
+ String msg) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the given level with the specified source class name
+ * and source method name and parameter.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param msg
+ * the message to be logged
+ * @param param
+ * the parameter associated with the event that need to be logged
+ */
+ public void logp(Level logLevel, String sourceClass, String sourceMethod,
+ String msg, Object param) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setParameters(new Object[] { param });
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the given level with the specified source class name
+ * and source method name and parameter array.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param msg
+ * the message to be logged
+ * @param params
+ * the parameter array associated with the event that need to be
+ * logged
+ */
+ public void logp(Level logLevel, String sourceClass, String sourceMethod,
+ String msg, Object[] params) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setParameters(params);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the given level with the specified source class name
+ * and source method name and <code>Throwable</code> object.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param msg
+ * the message to be logged
+ * @param thrown
+ * the <code>Throwable</code> object
+ */
+ public void logp(Level logLevel, String sourceClass, String sourceMethod,
+ String msg, Throwable thrown) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setThrown(thrown);
+ setResourceBundle(record);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the given level with the specified source class name
+ * and source method name, using the given resource bundle to localize the
+ * message.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param bundleName
+ * the name of the resource bundle, used to localize the message
+ * @param msg
+ * the message to be logged
+ */
+ public void logrb(Level logLevel, String sourceClass, String sourceMethod,
+ String bundleName, String msg) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ if (null != bundleName) {
+ try {
+ record.setResourceBundle(loadResourceBundle(bundleName));
+ } catch (MissingResourceException e) {
+ // ignore
+ }
+ record.setResourceBundleName(bundleName);
+ }
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the given level with the specified source class name
+ * and source method name and parameter, using the given resource bundle to
+ * localize the message.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param bundleName
+ * the name of the resource bundle, used to localize the message
+ * @param msg
+ * the message to be logged
+ * @param param
+ * the parameter associated with the event that need to be logged
+ */
+ public void logrb(Level logLevel, String sourceClass, String sourceMethod,
+ String bundleName, String msg, Object param) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ if (null != bundleName) {
+ try {
+ record.setResourceBundle(loadResourceBundle(bundleName));
+ } catch (MissingResourceException e) {
+ // ignore
+ }
+ record.setResourceBundleName(bundleName);
+ }
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setParameters(new Object[] { param });
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the given level with the specified source class name
+ * and source method name and parameter array, using the given resource
+ * bundle to localize the message.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param bundleName
+ * the name of the resource bundle, used to localize the message
+ * @param msg
+ * the message to be logged
+ * @param params
+ * the parameter array associated with the event that need to be
+ * logged
+ */
+ public void logrb(Level logLevel, String sourceClass, String sourceMethod,
+ String bundleName, String msg, Object[] params) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ if (null != bundleName) {
+ try {
+ record.setResourceBundle(loadResourceBundle(bundleName));
+ } catch (MissingResourceException e) {
+ // ignore
+ }
+ record.setResourceBundleName(bundleName);
+ }
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setParameters(params);
+ log(record);
+ }
+ }
+
+ /**
+ * Logs a message of the given level with the specified source class name
+ * and source method name and <code>Throwable</code> object, using the
+ * given resource bundle to localize the message.
+ *
+ * @param logLevel
+ * the level of the given message
+ * @param sourceClass
+ * the source class name
+ * @param sourceMethod
+ * the source method name
+ * @param bundleName
+ * the name of the resource bundle, used to localize the message
+ * @param msg
+ * the message to be logged
+ * @param thrown
+ * the <code>Throwable</code> object
+ */
+ public void logrb(Level logLevel, String sourceClass, String sourceMethod,
+ String bundleName, String msg, Throwable thrown) {
+ if (internalIsLoggable(logLevel)) {
+ LogRecord record = new LogRecord(logLevel, msg);
+ if (null != bundleName) {
+ try {
+ record.setResourceBundle(loadResourceBundle(bundleName));
+ } catch (MissingResourceException e) {
+ // ignore
+ }
+ record.setResourceBundleName(bundleName);
+ }
+ record.setLoggerName(this.name);
+ record.setSourceClassName(sourceClass);
+ record.setSourceMethodName(sourceMethod);
+ record.setThrown(thrown);
+ log(record);
+ }
+ }
+
+ /*
+ * This security manager is used to access the class context.
+ */
+ static class PrivateSecurityManager extends SecurityManager {
+ public Class<?>[] privateGetClassContext() {
+ return super.getClassContext();
+ }
+ }
+
+ void setManager(LogManager manager) {
+ if(this.manager != manager){
+ this.manager = manager;
+ handlerInited = false;
+ }
+ //init level here, but let handlers be for lazy loading
+ String configedLevel = manager.getProperty(name+ ".level"); //$NON-NLS-1$
+ if (null != configedLevel) {
+ try {
+ setLevel(Level.parse(configedLevel));
+ } catch (IllegalArgumentException e) {
+ //ignore
+ }
+ }
+ }
+
+ synchronized void reset() {
+ levelObjVal = null;
+ levelIntVal = Level.INFO.intValue();
+ if(handlers != null){
+ for (Handler element : handlers) {
+ // close all handlers, when unknown exceptions happen,
+ // ignore them and go on
+ try {
+ element.close();
+ } catch (Exception e) {
+ // Ignored.
+ }
+ }
+ handlers.clear();
+ }
+ handlerInited = false;
+ }
+}
+
diff --git a/logging/src/main/java/java/util/logging/LoggingMXBean.java b/logging/src/main/java/java/util/logging/LoggingMXBean.java
new file mode 100644
index 0000000..71ea65d
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/LoggingMXBean.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+import java.util.List;
+
+/**
+ * <p>
+ * The management interface for the logging sub-system.
+ * </p>
+ *
+ * <p>
+ * ObjectName =
+ * {@link LogManager#LOGGING_MXBEAN_NAME java.util.logging:type=Logging}
+ * </p>
+ *
+ * @since 1.5
+ */
+public interface LoggingMXBean {
+ /**
+ * <p>
+ * Gets the String value of the logging level of a logger. An empty String
+ * is returned when the logger's level is defined by its parent.
+ * </p>
+ *
+ * @param loggerName The name of the logger lookup.
+ * @return A String if the logger was found, otherwise <code>null</code>.
+ * @see Level#getName()
+ */
+ String getLoggerLevel(String loggerName);
+
+ /**
+ * <p>
+ * Gets a list of all currently registered logger's names. This is performed
+ * using the {@link LogManager#getLoggerNames()}.
+ * </p>
+ *
+ * @return A List of String instances.
+ */
+ List<String> getLoggerNames();
+
+ /**
+ * <p>
+ * Gets the name of the parent logger of a logger. If the logger doesn't
+ * exist then <code>null</code> is returned. If the logger is the root
+ * logger, then an empty String is returned.
+ * </p>
+ *
+ * @param loggerName The name of the logger to lookup.
+ * @return A String if the logger was found, otherwise <code>null</code>.
+ */
+ String getParentLoggerName(String loggerName);
+
+ /**
+ * <p>
+ * Sets the log level of a logger.
+ * </p>
+ *
+ * @param loggerName The name of the logger to set the level on, which must
+ * not be <code>null</code>.
+ * @param levelName The level to set on the logger, which may be
+ * <code>null</code>.
+ * @throws IllegalArgumentException if <code>loggerName</code> is not a
+ * registered logger or if <code>levelName</code> is not null and
+ * an invalid value.
+ * @throws SecurityException if a security manager exists and the caller
+ * doesn't have LoggingPermission("control").
+ * @see Level#parse(String)
+ */
+ void setLoggerLevel(String loggerName, String levelName);
+}
diff --git a/logging/src/main/java/java/util/logging/LoggingPermission.java b/logging/src/main/java/java/util/logging/LoggingPermission.java
new file mode 100644
index 0000000..6962127
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/LoggingPermission.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package java.util.logging;
+
+import java.io.Serializable;
+import java.security.BasicPermission;
+import java.security.Guard;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * The permission required to control the logging when run with a
+ * <code>SecurityManager</code>.
+ *
+ */
+public final class LoggingPermission extends BasicPermission implements Guard,
+ Serializable {
+
+ //for serialization compatibility with J2SE 1.4.2
+ private static final long serialVersionUID =63564341580231582L;
+
+
+ /*
+ * -------------------------------------------------------------------
+ * Constructors
+ * -------------------------------------------------------------------
+ */
+
+ /**
+ * Constructs a <code>LoggingPermission</code> object required to control
+ * the logging.
+ *
+ * @param name
+ * Currently must be "control".
+ * @param actions
+ * Currently must be either <code>null</code> or the empty
+ * string.
+ */
+ public LoggingPermission(String name, String actions) {
+ super(name, actions);
+ if (!"control".equals(name)) { //$NON-NLS-1$
+ // logging.6=Name must be "control".
+ throw new IllegalArgumentException(Messages.getString("logging.6")); //$NON-NLS-1$
+ }
+ if (null != actions && !"".equals(actions)) { //$NON-NLS-1$
+ // logging.7=Actions must be either null or the empty string.
+ throw new IllegalArgumentException(Messages.getString("logging.7")); //$NON-NLS-1$
+ }
+ }
+
+}
+
diff --git a/logging/src/main/java/java/util/logging/MemoryHandler.java b/logging/src/main/java/java/util/logging/MemoryHandler.java
new file mode 100644
index 0000000..ca96dc5
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/MemoryHandler.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util.logging;
+
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+
+/**
+ * A <code>Handler</code> put the description of log events into a cycled memory
+ * buffer.
+ * <p>
+ * Mostly this <code>MemoryHandler</code> just puts the given <code>LogRecord</code>
+ * into the internal buffer and doesn't perform any formatting or any other process.
+ * When the buffer is full, the earliest buffered records will be discarded.
+ * </p>
+ * <p>
+ * Every <code>MemoryHandler</code> has a target handler, and push action can be
+ * triggered so that all buffered records will be output to the target handler
+ * and normally the latter will publish the records. After the push action, the
+ * buffer will be cleared.
+ * </p>
+ * <p>
+ * The push action can be triggered in three ways:
+ * <ul>
+ * <li>The push method is called explicitly</li>
+ * <li>When a new <code>LogRecord</code> is put into the internal buffer, and it has a level which is not less than the specified push level.</li>
+ * <li>A subclass extends this <code>MemoryHandler</code> and call push method implicitly according to some criteria.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * <code>MemoryHandler</code> will read following <code>LogManager</code>
+ * properties for initialization, if given properties are not defined or has
+ * invalid values, default value will be used.
+ * <ul>
+ * <li>java.util.logging.MemoryHandler.level specifies the level for this
+ * <code>Handler</code>, defaults to <code>Level.ALL</code>.</li>
+ * <li>java.util.logging.MemoryHandler.filter specifies the <code>Filter</code>
+ * class name, defaults to no <code>Filter</code>.</li>
+ * <li>java.util.logging.MemoryHandler.size specifies the buffer size in number
+ * of <code>LogRecord</code>, defaults to 1000.</li>
+ * <li>java.util.logging.MemoryHandler.push specifies the push level, defaults
+ * to level.SEVERE.</li>
+ * <li>java.util.logging.MemoryHandler.target specifies the class of the target
+ * <code>Handler</code>, no default value, which means this property must be
+ * specified either by property setting or by constructor.</li>
+ * </ul>
+ * </p>
+ *
+ */
+public class MemoryHandler extends Handler {
+
+ //default maximum buffered number of LogRecord
+ private static final int DEFAULT_SIZE = 1000;
+ //target handler
+ private Handler target;
+
+ //buffer size
+ private int size = DEFAULT_SIZE;
+
+ //push level
+ private Level push = Level.SEVERE;
+
+ //LogManager instance for convenience
+ private final LogManager manager = LogManager.getLogManager();
+
+ //buffer
+ private LogRecord[] buffer;
+
+ //current position in buffer
+ private int cursor;
+
+ /**
+ * Default constructor, construct and init a <code>MemoryHandler</code> using
+ * <code>LogManager</code> properties or default values
+ */
+ public MemoryHandler() {
+ super();
+ String className = this.getClass().getName();
+ //init target
+ final String targetName = manager.getProperty(className+".target"); //$NON-NLS-1$
+ try {
+ Class<?> targetClass = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>(){
+ public Class<?> run() throws Exception{
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if(loader == null){
+ loader = ClassLoader.getSystemClassLoader();
+ }
+ return loader.loadClass(targetName);
+ }
+ });
+ target = (Handler) targetClass.newInstance();
+ } catch (Exception e) {
+ // logging.10=Cannot load target handler:{0}
+ throw new RuntimeException(Messages.getString("logging.10", //$NON-NLS-1$
+ targetName));
+ }
+ //init size
+ String sizeString = manager.getProperty(className+".size"); //$NON-NLS-1$
+ if (null != sizeString) {
+ try {
+ size = Integer.parseInt(sizeString);
+ if(size <= 0){
+ size = DEFAULT_SIZE;
+ }
+ } catch (Exception e) {
+ printInvalidPropMessage(className+".size", sizeString, e); //$NON-NLS-1$
+ }
+ }
+ //init push level
+ String pushName = manager.getProperty(className+".push"); //$NON-NLS-1$
+ if (null != pushName) {
+ try {
+ push = Level.parse(pushName);
+ } catch (Exception e) {
+ printInvalidPropMessage(className+".push", pushName, e); //$NON-NLS-1$
+ }
+ }
+ //init other properties which are common for all Handler
+ initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$
+ buffer = new LogRecord[size];
+ }
+
+ /**
+ * Construct and init a <code>MemoryHandler</code> using given target, size
+ * and push level, other properties using <code>LogManager</code> properties
+ * or default values
+ *
+ * @param target
+ * the given <code>Handler</code> to output
+ * @param size the maximum number of buffered <code>LogRecord</code>
+ * @param pushLevel
+ * the push level
+ * @throws IllegalArgumentException
+ * if size<=0
+ */
+ public MemoryHandler(Handler target, int size, Level pushLevel) {
+ if (size <= 0) {
+ // logging.11=Size must be positive.
+ throw new IllegalArgumentException(Messages.getString("logging.11")); //$NON-NLS-1$
+ }
+ target.getLevel();
+ pushLevel.intValue();
+ this.target = target;
+ this.size = size;
+ this.push = pushLevel;
+ initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$
+ buffer = new LogRecord[size];
+ }
+
+ /**
+ * Close this handler and target handler, free all associated resources
+ *
+ * @throws SecurityException
+ * if security manager exists and it determines that caller
+ * does not have the required permissions to control this handler
+ */
+ @Override
+ public void close() {
+ manager.checkAccess();
+ target.close();
+ setLevel(Level.OFF);
+ }
+
+ /**
+ * Call target handler to flush any buffered output.
+ *
+ * Note that this doesn't cause this <code>MemoryHandler</code> to push.
+ *
+ */
+ @Override
+ public void flush() {
+ target.flush();
+ }
+
+ /**
+ * Put a given <code>LogRecord</code> into internal buffer.
+ *
+ * If given record is not loggable, just return. Otherwise it is stored in
+ * the buffer. Furthermore if the record's level is not less than the push
+ * level, the push action is triggered to output all the buffered records
+ * to the target handler, and the target handler will publish them.
+ *
+ * @param record the log record.
+ */
+ @Override
+ public synchronized void publish(LogRecord record) {
+ if (!isLoggable(record)) {
+ return;
+ }
+ if (cursor >= size) {
+ cursor = 0;
+ }
+ buffer[cursor++] = record;
+ if (record.getLevel().intValue() >= push.intValue()) {
+ push();
+ }
+ return;
+ }
+
+ /**
+ * Return the push level.
+ *
+ * @return the push level
+ */
+ public Level getPushLevel() {
+ return push;
+ }
+
+ /**
+ * <p>Check if given <code>LogRecord</code> would be put into this
+ * <code>MemoryHandler</code>'s internal buffer.
+ * </p>
+ * <p>
+ * The given <code>LogRecord</code> is loggable if and only if it has
+ * appropriate level and it pass any associated filter's check.
+ * </p>
+ * <p>
+ * Note that the push level is not used for this check.
+ * </p>
+ * @param record
+ * the given <code>LogRecord</code>
+ * @return if the given <code>LogRecord</code> should be logged
+ */
+ @Override
+ public boolean isLoggable(LogRecord record) {
+ return super.isLoggable(record);
+ }
+
+ /**
+ * Triggers a push action to output all buffered records to the target handler,
+ * and the target handler will publish them. Then the buffer is cleared.
+ */
+ public void push() {
+ for (int i = cursor; i < size; i++) {
+ if(null != buffer[i]) {
+ target.publish(buffer[i]);
+ }
+ buffer[i] = null;
+ }
+ for (int i = 0; i < cursor; i++) {
+ if(null != buffer[i]) {
+ target.publish(buffer[i]);
+ }
+ buffer[i] = null;
+ }
+ cursor = 0;
+ }
+
+ /**
+ * Set the push level. The push level is used to check the push action
+ * triggering. When a new <code>LogRecord</code> is put into the internal
+ * buffer and its level is not less than the push level, the push action
+ * will be triggered. Note that set new push level won't trigger push action.
+ *
+ * @param newLevel
+ * the new level to set
+ * @throws SecurityException
+ * if security manager exists and it determines that caller
+ * does not have the required permissions to control this handler
+ */
+ public void setPushLevel(Level newLevel) {
+ manager.checkAccess();
+ newLevel.intValue();
+ this.push = newLevel;
+ }
+}
diff --git a/logging/src/main/java/java/util/logging/SimpleFormatter.java b/logging/src/main/java/java/util/logging/SimpleFormatter.java
new file mode 100644
index 0000000..cfccda0
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/SimpleFormatter.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package java.util.logging;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.Date;
+
+/**
+ * <code>SimpleFormatter</code> can be used to print a summary of the
+ * information contained in a <code>LogRecord</code> object in a human
+ * readable format.
+ *
+ */
+public class SimpleFormatter extends Formatter {
+ /**
+ * Constructs a <code>SimpleFormatter</code> object.
+ */
+ public SimpleFormatter() {
+ super();
+ }
+
+ @Override
+ public String format(LogRecord r) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(MessageFormat.format("{0, date} {0, time} ", //$NON-NLS-1$
+ new Object[] { new Date(r.getMillis()) }));
+ sb.append(r.getSourceClassName()).append(" "); //$NON-NLS-1$
+ sb.append(r.getSourceMethodName()).append(LogManager.getSystemLineSeparator()); //$NON-NLS-1$
+ sb.append(r.getLevel().getName()).append(": "); //$NON-NLS-1$
+ sb.append(formatMessage(r)).append(LogManager.getSystemLineSeparator());
+ if (null != r.getThrown()) {
+ sb.append("Throwable occurred: "); //$NON-NLS-1$
+ Throwable t = r.getThrown();
+ PrintWriter pw = null;
+ try {
+ StringWriter sw = new StringWriter();
+ pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ sb.append(sw.toString());
+ } finally {
+ if(pw != null){
+ try {
+ pw.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+ }
+ return sb.toString();
+ }
+}
+
diff --git a/logging/src/main/java/java/util/logging/SocketHandler.java b/logging/src/main/java/java/util/logging/SocketHandler.java
new file mode 100644
index 0000000..3d9ab29
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/SocketHandler.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package java.util.logging;
+
+import java.net.Socket;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * A handler that writes log messages to a socket connection.
+ * <p>
+ * This handler reads the following properties from the log manager to
+ * initialize itself:
+ * <ul>
+ * <li>java.util.logging.ConsoleHandler.level specifies the logging level,
+ * defaults to <code>Level.ALL</code> if this property is not found or has an
+ * invalid value;
+ * <li>java.util.logging.SocketHandler.filter specifies the name of the filter
+ * class to be associated with this handler, defaults to <code>null</code> if
+ * this property is not found or has an invalid value;
+ * <li>java.util.logging.SocketHandler.formatter specifies the name of the
+ * formatter class to be associated with this handler, defaults to
+ * <code>java.util.logging.XMLFormatter</code> if this property is not found
+ * or has an invalid value;
+ * <li>java.util.logging.SocketHandler.encoding specifies the encoding this
+ * handler will use to encode log messages, defaults to <code>null</code> if
+ * this property is not found or has an invalid value.
+ * <li>java.util.logging.SocketHandler.host specifies the name of the host that
+ * this handler should connect to. There's no default value for this property.
+ * <li>java.util.logging.SocketHandler.encoding specifies the port number that
+ * this handler should connect to. There's no default value for this property.
+ * </ul>
+ * </p>
+ * <p>
+ * This handler buffers the outgoing messages, but flushes each time a log
+ * record has been published.
+ * </p>
+ * <p>
+ * This class is not thread-safe.
+ * </p>
+ *
+ */
+public class SocketHandler extends StreamHandler {
+ // default level
+ private static final String DEFAULT_LEVEL = "ALL"; //$NON-NLS-1$
+
+ // default formatter
+ private static final String DEFAULT_FORMATTER = "java.util.logging.XMLFormatter"; //$NON-NLS-1$
+
+ // the socket connection
+ private Socket socket;
+
+ /**
+ * Constructs a <code>SocketHandler</code> object using the properties
+ * read by the log manager, including the host name and port number.
+ *
+ * @throws IOException
+ * If failed to connect to the specified host and port.
+ * @throws IllegalArgumentException
+ * If the host name or port number is illegal.
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission to control this handler.
+ */
+ public SocketHandler() throws IOException {
+ super(DEFAULT_LEVEL, null, DEFAULT_FORMATTER, null);
+ initSocket(LogManager.getLogManager().getProperty(
+ "java.util.logging.SocketHandler.host"), LogManager //$NON-NLS-1$
+ .getLogManager().getProperty(
+ "java.util.logging.SocketHandler.port")); //$NON-NLS-1$
+ }
+
+ /**
+ * Constructs a <code>SocketHandler</code> object using the specified host
+ * name and port number together with other properties read by the log
+ * manager.
+ *
+ * @param host
+ * the host name
+ * @param port
+ * the port number
+ * @throws IOException
+ * If failed to connect to the specified host and port.
+ * @throws IllegalArgumentException
+ * If the host name or port number is illegal.
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission to control this handler.
+ */
+ public SocketHandler(String host, int port) throws IOException {
+ super(DEFAULT_LEVEL, null, DEFAULT_FORMATTER, null);
+ initSocket(host, String.valueOf(port));
+ }
+
+ // Initialize the socket connection and prepare the output stream
+ private void initSocket(String host, String port) throws IOException {
+ // check the validity of the host name
+ if (null == host || "".equals(host)) { //$NON-NLS-1$
+ // logging.C=Illegal host argument.
+ throw new IllegalArgumentException(Messages.getString("logging.C")); //$NON-NLS-1$
+ }
+ // check the validity of the port number
+ int p = 0;
+ try {
+ p = Integer.parseInt(port);
+ } catch (NumberFormatException e) {
+ // logging.D=Illegal port argument.
+ throw new IllegalArgumentException(Messages.getString("logging.D")); //$NON-NLS-1$
+ }
+ if (p <= 0) {
+ // logging.D=Illegal port argument.
+ throw new IllegalArgumentException(Messages.getString("logging.D")); //$NON-NLS-1$
+ }
+ // establish the network connection
+ try {
+ this.socket = new Socket(host, p);
+ } catch (IOException e) {
+ // logging.E=Failed to establish the network connection.
+ getErrorManager().error(Messages.getString("logging.E"), e, //$NON-NLS-1$
+ ErrorManager.OPEN_FAILURE);
+ throw e;
+ }
+ // BEGIN android-modified
+ super.internalSetOutputStream(new BufferedOutputStream(this.socket
+ .getOutputStream(), 8192));
+ // END android-modified
+ }
+
+ /**
+ * Closes this handler. The network connection to the host is also closed.
+ *
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission to control this handler.
+ */
+ @Override
+ public void close() {
+ try {
+ super.close();
+ if (null != this.socket) {
+ this.socket.close();
+ this.socket = null;
+ }
+ } catch (Exception e) {
+ // logging.F=Exception occurred when closing the socket handler.
+ getErrorManager().error(Messages.getString("logging.F"), e, //$NON-NLS-1$
+ ErrorManager.CLOSE_FAILURE);
+ }
+ }
+
+ /**
+ * Logs a record if necessary. A flush operation will be done afterwards.
+ *
+ * @param record
+ * the log record to be logged
+ */
+ @Override
+ public void publish(LogRecord record) {
+ super.publish(record);
+ super.flush();
+ }
+
+}
diff --git a/logging/src/main/java/java/util/logging/StreamHandler.java b/logging/src/main/java/java/util/logging/StreamHandler.java
new file mode 100644
index 0000000..fb48c2a
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/StreamHandler.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package java.util.logging;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import org.apache.harmony.logging.internal.nls.Messages;
+
+/**
+ * A <code>StreamHandler</code> object writes log messages to an output
+ * stream, that is, objects of the class <code>java.io.OutputStream</code>.
+ * <p>
+ * A <code>StreamHandler</code> object reads the following properties from the
+ * log manager to initialize itself:
+ * <ul>
+ * <li>java.util.logging.StreamHandler.level specifies the logging level,
+ * defaults to <code>Level.INFO</code> if this property is not found or has an
+ * invalid value;
+ * <li>java.util.logging.StreamHandler.filter specifies the name of the filter
+ * class to be associated with this handler, defaults to <code>null</code> if
+ * this property is not found or has an invalid value;
+ * <li>java.util.logging.StreamHandler.formatter specifies the name of the
+ * formatter class to be associated with this handler, defaults to
+ * <code>java.util.logging.SimpleFormatter</code> if this property is not
+ * found or has an invalid value;
+ * <li>java.util.logging.StreamHandler.encoding specifies the encoding this
+ * handler will use to encode log messages, defaults to <code>null</code> if
+ * this property is not found or has an invalid value.
+ * </ul>
+ * </p>
+ * <p>
+ * This class is not thread-safe.
+ * </p>
+ *
+ */
+public class StreamHandler extends Handler {
+ // the output stream this handler writes to
+ private OutputStream os;
+
+ // the writer that writes to the output stream
+ private Writer writer;
+
+ // the flag indicating whether the writer has been initialized
+ private boolean writerNotInitialized;
+
+ /**
+ * Constructs a <code>StreamHandler</code> object. The new stream handler
+ * does not have an associated output stream.
+ */
+ public StreamHandler() {
+ initProperties("INFO", null, "java.util.logging.SimpleFormatter", //$NON-NLS-1$//$NON-NLS-2$
+ null);
+ this.os = null;
+ this.writer = null;
+ this.writerNotInitialized = true;
+ }
+
+ /**
+ * Constructs a <code>StreamHandler</code> object with the supplied output
+ * stream. Default properties are read.
+ *
+ * @param os
+ * the output stream this handler writes to
+ */
+ StreamHandler(OutputStream os) {
+ this();
+ this.os = os;
+ }
+
+ /**
+ * Constructs a <code>StreamHandler</code> object. Specified default
+ * values will be used if the corresponding properties are found in log
+ * manager's properties.
+ */
+ StreamHandler(String defaultLevel, String defaultFilter,
+ String defaultFormatter, String defaultEncoding) {
+ initProperties(defaultLevel, defaultFilter, defaultFormatter,
+ defaultEncoding);
+ this.os = null;
+ this.writer = null;
+ this.writerNotInitialized = true;
+ }
+
+ /**
+ * Constructs a <code>StreamHandler</code> object with the supplied output
+ * stream and formatter.
+ *
+ * @param os
+ * the output stream this handler writes to
+ * @param formatter
+ * the formatter this handler uses to format the output
+ */
+ public StreamHandler(OutputStream os, Formatter formatter) {
+ this();
+ if (os == null) {
+ // logging.2=The OutputStream parameter is null
+ throw new NullPointerException(Messages.getString("logging.2")); //$NON-NLS-1$
+ }
+ if (formatter == null) {
+ // logging.3=The Formatter parameter is null.
+ throw new NullPointerException(Messages.getString("logging.3")); //$NON-NLS-1$
+ }
+ this.os = os;
+ internalSetFormatter(formatter);
+ }
+
+ // initialize the writer
+ private void initializeWritter() {
+ this.writerNotInitialized = false;
+ if (null == getEncoding()) {
+ this.writer = new OutputStreamWriter(this.os);
+ } else {
+ try {
+ this.writer = new OutputStreamWriter(this.os, getEncoding());
+ } catch (UnsupportedEncodingException e) {
+ /*
+ * Should not happen because it's checked in
+ * super.initProperties().
+ */
+ }
+ }
+ write(getFormatter().getHead(this));
+ }
+
+ // Write a string to the output stream.
+ private void write(String s) {
+ try {
+ this.writer.write(s);
+ } catch (Exception e) {
+ // logging.14=Exception occurred when writing to the output stream.
+ getErrorManager().error(Messages.getString("logging.14"), e, //$NON-NLS-1$
+ ErrorManager.WRITE_FAILURE);
+ }
+ }
+
+ /**
+ * Sets the output stream this handler writes to. Note it does nothing else.
+ *
+ * @param newOs
+ * the new output stream
+ */
+ void internalSetOutputStream(OutputStream newOs) {
+ this.os = newOs;
+ }
+
+
+ /**
+ * Sets the output stream this handler writes to. If there's an existing
+ * output stream, the tail string of the associated formatter will be
+ * written to it. Then it will be flushed and closed.
+ *
+ * @param os
+ * the new output stream
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ protected void setOutputStream(OutputStream os) {
+ if (null == os) {
+ throw new NullPointerException();
+ }
+ LogManager.getLogManager().checkAccess();
+ close(true);
+ this.writer = null;
+ this.os = os;
+ this.writerNotInitialized = true;
+ }
+
+ /**
+ * Sets the character encoding used by this handler. A <code>null</code>
+ * value indicates the using of the default encoding.
+ *
+ * @param encoding
+ * the character encoding to set
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ * @throws UnsupportedEncodingException
+ * If the specified encoding is not supported by the runtime.
+ */
+ @Override
+ public void setEncoding(String encoding) throws SecurityException,
+ UnsupportedEncodingException {
+ //flush first before set new encoding
+ this.flush();
+ super.setEncoding(encoding);
+ // renew writer only if the writer exists
+ if (null != this.writer) {
+ if (null == getEncoding()) {
+ this.writer = new OutputStreamWriter(this.os);
+ } else {
+ try {
+ this.writer = new OutputStreamWriter(this.os, getEncoding());
+ } catch (UnsupportedEncodingException e) {
+ /*
+ * Should not happen because it's checked in
+ * super.initProperties().
+ */
+ throw new AssertionError(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Closes this handler, but the underlying output stream is only closed when
+ * <code>closeStream</code> is <code>true</code>. Security is not checked.
+ *
+ * @param closeStream
+ * whether to close the underlying output stream
+ */
+ void close(boolean closeStream) {
+ if (null != this.os) {
+ if (this.writerNotInitialized) {
+ initializeWritter();
+ }
+ write(getFormatter().getTail(this));
+ try {
+ this.writer.flush();
+ if (closeStream) {
+ this.writer.close();
+ this.writer = null;
+ this.os = null;
+ }
+ } catch (Exception e) {
+ // logging.15=Exception occurred when closing the output stream.
+ getErrorManager().error(Messages.getString("logging.15"), e, //$NON-NLS-1$
+ ErrorManager.CLOSE_FAILURE);
+ }
+ }
+ }
+
+ /**
+ * Closes this handler. The tail string of the formatter associated with
+ * this handler will be written out. A flush operation a subsequent close
+ * operation will then be performed upon the outputstream. Client
+ * applications should not use a handler after closing it.
+ *
+ * @throws SecurityException
+ * If a security manager determines that the caller does not
+ * have the required permission.
+ */
+ @Override
+ public void close() {
+ LogManager.getLogManager().checkAccess();
+ close(true);
+ }
+
+ /**
+ * Flushes any buffered output.
+ */
+ @Override
+ public void flush() {
+ if (null != this.os) {
+ try {
+ if (null != this.writer) {
+ this.writer.flush();
+ } else {
+ this.os.flush();
+ }
+ } catch (Exception e) {
+ // logging.16=Exception occurred while flushing the output stream.
+ getErrorManager().error(Messages.getString("logging.16"), //$NON-NLS-1$
+ e, ErrorManager.FLUSH_FAILURE);
+ }
+ }
+ }
+
+ /**
+ * Accepts an actual logging request. The log record will be formatted and
+ * written to the output stream if the following three conditions are met:
+ * <ul>
+ * <li>the supplied log record has at least the required logging level;
+ * <li>the supplied log record passes the filter associated with this
+ * handler if any;
+ * <li>the output stream associated with this handler is not
+ * <code>null</code>.
+ * </ul>
+ * If it is the first time a log record need to be written out, the head
+ * string of the formatter associated with this handler will be written out
+ * first.
+ *
+ * @param record
+ * the log record to be logged
+ */
+ @Override
+ public synchronized void publish(LogRecord record) {
+ try {
+ if (this.isLoggable(record)) {
+ if (this.writerNotInitialized) {
+ initializeWritter();
+ }
+ String msg = null;
+ try {
+ msg = getFormatter().format(record);
+ } catch (Exception e) {
+ // logging.17=Exception occurred while formatting the log record.
+ getErrorManager().error(Messages.getString("logging.17"), //$NON-NLS-1$
+ e, ErrorManager.FORMAT_FAILURE);
+ }
+ write(msg);
+ }
+ } catch (Exception e) {
+ // logging.18=Exception occurred while logging the record.
+ getErrorManager().error(Messages.getString("logging.18"), e, //$NON-NLS-1$
+ ErrorManager.GENERIC_FAILURE);
+ }
+ }
+
+ /**
+ * Determines whether the supplied log record need to be logged. The logging
+ * levels will be checked as well as the filter. The output stream of this
+ * handler is also checked. If it's null, this method returns false.
+ *
+ * @param record
+ * the log record to be checked
+ * @return <code>true</code> if the supplied log record need to be logged,
+ * otherwise <code>false</code>
+ */
+ @Override
+ public boolean isLoggable(LogRecord record) {
+ if (null == record) {
+ return false;
+ }
+ if (null != this.os && super.isLoggable(record)) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/logging/src/main/java/java/util/logging/XMLFormatter.java b/logging/src/main/java/java/util/logging/XMLFormatter.java
new file mode 100644
index 0000000..f3ff280
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/XMLFormatter.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package java.util.logging;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.ResourceBundle;
+
+/**
+ * Format a given <code>LogRecord</code> into string represents XML. The DTD specified in
+ * Appendix A to Java Logging APIs specification is used.
+ *
+ * <code>XMLFormatter</code> uses given <code>Handler</code>'s encoding if has, otherwise
+ * uses default platform encoding instead. However, the UTF-8 is recommended encoding.
+ *
+ */
+public class XMLFormatter extends Formatter {
+
+ private static final String lineSeperator = LogManager
+ .getSystemLineSeparator();
+
+ private static final String indent = " "; //$NON-NLS-1$
+
+ /**
+ * Default constructor
+ */
+ public XMLFormatter() {
+ super();
+ }
+
+ /**
+ * Format a <code>LogRecord</code> into string which represents XML
+ *
+ * @param r the given LogRecord instance to be formatted
+ * @return string which represents XML
+ */
+ @Override
+ public String format(LogRecord r) {
+ //call a method of LogRecord to ensure not null
+ long time = r.getMillis();
+ //format to date
+ String date = MessageFormat.format("{0, date} {0, time}", //$NON-NLS-1$
+ new Object[] { new Date(time) });
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(("<record>")).append(lineSeperator); //$NON-NLS-1$
+ sb.append(indent).append(("<date>")).append(date).append(("</date>")) //$NON-NLS-1$ //$NON-NLS-2$
+ .append(lineSeperator);
+ sb.append(indent).append(("<millis>")).append(time).append( //$NON-NLS-1$
+ ("</millis>")).append(lineSeperator); //$NON-NLS-1$
+ sb.append(indent).append(("<sequence>")).append(r.getSequenceNumber()) //$NON-NLS-1$
+ .append(("</sequence>")).append(lineSeperator); //$NON-NLS-1$
+ if (null != r.getLoggerName()) {
+ sb.append(indent).append(("<logger>")).append(r.getLoggerName()) //$NON-NLS-1$
+ .append(("</logger>")).append(lineSeperator); //$NON-NLS-1$
+ }
+ sb.append(indent).append(("<level>")).append(r.getLevel().getName()) //$NON-NLS-1$
+ .append(("</level>")).append(lineSeperator); //$NON-NLS-1$
+ if (null != r.getSourceClassName()) {
+ sb.append(indent).append(("<class>")) //$NON-NLS-1$
+ .append(r.getSourceClassName()).append(("</class>")) //$NON-NLS-1$
+ .append(lineSeperator);
+ }
+ if (null != r.getSourceMethodName()) {
+ sb.append(indent).append(("<method>")).append( //$NON-NLS-1$
+ r.getSourceMethodName()).append(("</method>")).append( //$NON-NLS-1$
+ lineSeperator);
+ }
+ sb.append(indent).append(("<thread>")).append(r.getThreadID()).append( //$NON-NLS-1$
+ ("</thread>")).append(lineSeperator); //$NON-NLS-1$
+ formatMessages(r, sb);
+ Object[] params;
+ if ((params = r.getParameters()) != null) {
+ for (Object element : params) {
+ sb.append(indent).append(("<param>")).append(element).append( //$NON-NLS-1$
+ ("</param>")).append(lineSeperator); //$NON-NLS-1$
+ }
+ }
+ formatThrowable(r, sb);
+ sb.append(("</record>")).append(lineSeperator); //$NON-NLS-1$
+ return sb.toString();
+ }
+
+ private void formatMessages(LogRecord r, StringBuilder sb) {
+ //get localized message if has, but don't call Formatter.formatMessage to parse pattern string
+ ResourceBundle rb = r.getResourceBundle();
+ String pattern = r.getMessage();
+ if (null != rb && null != pattern) {
+ String message;
+ try {
+ message = rb.getString(pattern);
+ } catch (Exception e) {
+ message = null;
+ }
+
+ if (message == null) {
+ message = pattern;
+ sb.append(indent).append(("<message>")).append(message).append( //$NON-NLS-1$
+ ("</message>")).append(lineSeperator); //$NON-NLS-1$
+ } else {
+ sb.append(indent).append(("<message>")).append(message).append( //$NON-NLS-1$
+ ("</message>")).append(lineSeperator); //$NON-NLS-1$
+ sb.append(indent).append(("<key>")).append(pattern).append( //$NON-NLS-1$
+ ("</key>")).append(lineSeperator); //$NON-NLS-1$
+ sb.append(indent).append(("<catalog>")).append( //$NON-NLS-1$
+ r.getResourceBundleName()).append(("</catalog>")) //$NON-NLS-1$
+ .append(lineSeperator);
+ }
+ } else if(null != pattern){
+ sb.append(indent).append(("<message>")).append(pattern).append( //$NON-NLS-1$
+ ("</message>")).append(lineSeperator); //$NON-NLS-1$
+ } else{
+ sb.append(indent).append(("<message/>")); //$NON-NLS-1$
+ }
+ }
+
+ private void formatThrowable(LogRecord r, StringBuilder sb) {
+ Throwable t;
+ if ((t = r.getThrown()) != null) {
+ sb.append(indent).append("<exception>").append(lineSeperator); //$NON-NLS-1$
+ sb.append(indent).append(indent).append("<message>").append( //$NON-NLS-1$
+ t.toString()).append("</message>").append(lineSeperator); //$NON-NLS-1$
+ //format throwable's stack trace
+ StackTraceElement[] elements = t.getStackTrace();
+ for (StackTraceElement e : elements) {
+ sb.append(indent).append(indent).append("<frame>").append( //$NON-NLS-1$
+ lineSeperator);
+ sb.append(indent).append(indent).append(indent).append(
+ "<class>").append(e.getClassName()).append("</class>") //$NON-NLS-1$//$NON-NLS-2$
+ .append(lineSeperator);
+ sb.append(indent).append(indent).append(indent).append(
+ "<method>").append(e.getMethodName()).append( //$NON-NLS-1$
+ "</method>").append(lineSeperator); //$NON-NLS-1$
+ sb.append(indent).append(indent).append(indent)
+ .append("<line>").append(e.getLineNumber()).append( //$NON-NLS-1$
+ "</line>").append(lineSeperator); //$NON-NLS-1$
+ sb.append(indent).append(indent).append("</frame>").append( //$NON-NLS-1$
+ lineSeperator);
+ }
+ sb.append(indent).append("</exception>").append(lineSeperator); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Return the header string for XML, use given handler's encoding if has,
+ * other wise use default platform encoding
+ *
+ * @param h the given handler
+ * @return the header string for XML
+ */
+ @Override
+ public String getHead(Handler h) {
+ String encoding = null;
+ if(null != h) {
+ encoding = h.getEncoding();
+ }
+ if (null == encoding) {
+ encoding = getSystemProperty("file.encoding"); //$NON-NLS-1$
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("<?xml version=\"1.0\" encoding=\"").append(encoding).append( //$NON-NLS-1$
+ "\" standalone=\"no\"?>").append(lineSeperator); //$NON-NLS-1$
+ sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">").append(lineSeperator); //$NON-NLS-1$
+ sb.append(("<log>")); //$NON-NLS-1$
+ return sb.toString();
+ }
+
+ /**
+ * Return the tail string for XML
+ *
+ * @param h the given handler
+ * @return the tail string for XML
+ */
+ @Override
+ @SuppressWarnings("unused")
+ public String getTail(Handler h) {
+ return "</log>"; //$NON-NLS-1$
+ }
+
+ //use privilege code to get system property
+ private static String getSystemProperty(final String key) {
+ return AccessController.doPrivileged(
+ new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty(key);
+ }
+ });
+ }
+
+}
+
+
diff --git a/logging/src/main/java/java/util/logging/logging.properties b/logging/src/main/java/java/util/logging/logging.properties
new file mode 100644
index 0000000..f99fe3f
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/logging.properties
@@ -0,0 +1,65 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#------------------------------------------------------------------------------
+# Default logging property file.
+# This file is used by java.util.logging package as default settings, users can
+# specify another file instead with java.util.logging.config.file system
+# property, this property can be set via the Preference API, or as VM arguments
+# passed to "java" command, or as property definition passed to JNI_CreateJavaVM.
+# You can refer to JavaDoc of java.util.logging package for more information
+# about this file.
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# Global settings
+#------------------------------------------------------------------------------
+
+# Specify default level for global logger, the event whose level is below won't
+# be logged. You can specify level for every logger, otherwise the level of parent
+# logger will be used. You can also set the level for every handler, as below for
+# java.util.logging.ConsoleHandler.
+.level=INFO
+
+# Specify handler classes list, these classes will be instantiated during the
+# logging framework initialization. The list should be white space separated.
+# For example, use the line below to add SocketHandler. Note that the handler
+# classes must be in the classpath.
+#
+# handlers=java.util.logging.ConsoleHandler java.util.logging.SocketHandler
+#
+handlers=java.util.logging.ConsoleHandler
+
+# Specify a class names list, these classes' default constructor will be executed
+# during logging package initialization, which may contain some code to set the
+# logging configuration. The list should be white space separated, and the
+# classes must be in the classpath.
+#
+# config=
+
+
+#------------------------------------------------------------------------------
+# Handler settings
+#------------------------------------------------------------------------------
+
+# The properties below are samples for handler settings.
+#java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
+#java.util.logging.ConsoleHandler.level=INFO
+#java.util.logging.FileHandler.limit=100000
+#java.util.logging.FileHandler.count=1
+#java.util.logging.FileHandler.formatter=java.util.logging.XMLFormatter
+#java.util.logging.FileHandler.pattern=%h/java%u.log
+
diff --git a/logging/src/main/java/java/util/logging/package.html b/logging/src/main/java/java/util/logging/package.html
new file mode 100644
index 0000000..4cc90c9
--- /dev/null
+++ b/logging/src/main/java/java/util/logging/package.html
@@ -0,0 +1,9 @@
+<html>
+ <body>
+ <p>
+ This package allows to add logging to any application. It
+ supports different levels of importance of a message that needs to be
+ logged. Later the output can be filtered by this level.
+ </p>
+ </body>
+</html>
diff --git a/logging/src/main/java/org/apache/harmony/logging/internal/nls/Messages.java b/logging/src/main/java/org/apache/harmony/logging/internal/nls/Messages.java
new file mode 100644
index 0000000..e052773
--- /dev/null
+++ b/logging/src/main/java/org/apache/harmony/logging/internal/nls/Messages.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL.
+ * All changes made to this file manually will be overwritten
+ * if this tool runs again. Better make changes in the template file.
+ */
+
+package org.apache.harmony.logging.internal.nls;
+
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.apache.harmony.kernel.vm.VM;
+import org.apache.harmony.luni.util.MsgHelp;
+
+/**
+ * This class retrieves strings from a resource bundle and returns them,
+ * formatting them with MessageFormat when required.
+ * <p>
+ * It is used by the system classes to provide national language support, by
+ * looking up messages in the <code>
+ * org.apache.harmony.logging.internal.nls.messages
+ * </code>
+ * resource bundle. Note that if this file is not available, or an invalid key
+ * is looked up, or resource bundle support is not available, the key itself
+ * will be returned as the associated message. This means that the <em>KEY</em>
+ * should a reasonable human-readable (english) string.
+ *
+ */
+public class Messages {
+
+ private static final String sResource =
+ "org.apache.harmony.logging.internal.nls.messages"; //$NON-NLS-1$
+
+ /**
+ * Retrieves a message which has no arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg) {
+ return MsgHelp.getString(sResource, msg);
+ }
+
+ /**
+ * Retrieves a message which takes 1 argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * Object the object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg) {
+ return getString(msg, new Object[] { arg });
+ }
+
+ /**
+ * Retrieves a message which takes 1 integer argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * int the integer to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, int arg) {
+ return getString(msg, new Object[] { Integer.toString(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 1 character argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * char the character to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, char arg) {
+ return getString(msg, new Object[] { String.valueOf(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 2 arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg1
+ * Object an object to insert in the formatted output.
+ * @param arg2
+ * Object another object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg1, Object arg2) {
+ return getString(msg, new Object[] { arg1, arg2 });
+ }
+
+ /**
+ * Retrieves a message which takes several arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param args
+ * Object[] the objects to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object[] args) {
+ return MsgHelp.getString(sResource, msg, args);
+ }
+}
diff --git a/logging/src/main/java/org/apache/harmony/logging/internal/nls/messages.properties b/logging/src/main/java/org/apache/harmony/logging/internal/nls/messages.properties
new file mode 100644
index 0000000..da3945e
--- /dev/null
+++ b/logging/src/main/java/org/apache/harmony/logging/internal/nls/messages.properties
@@ -0,0 +1,52 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# messages for EN locale
+logging.0=This method is not currently implemented.
+logging.1=Invalid level name: {0}.
+logging.10=Cannot load target handler:{0}
+logging.11=Size must be positive.
+logging.12=Invalid property value for
+logging.13=The encoding "{0}" is not supported.
+logging.14=Exception occurred when writing to the output stream.
+logging.15=Exception occurred when closing the output stream.
+logging.16=Exception occurred while flushing the output stream.
+logging.17=Exception occurred while formatting the log record.
+logging.18=Exception occurred while logging the record.
+logging.19=Pattern cannot be empty
+logging.1A=Error happened when open log file.
+logging.1B=The limit and count property must be larger than 0 and 1, respectively
+logging.1C=The 'name' parameter is null.
+logging.1D=Cannot parse this name: {0}
+logging.1E=Error message - {0}
+logging.1F=Exception - {0}
+logging.2=The OutputStream parameter is null
+logging.20=Loading class "{0}" failed
+logging.21=There Can Be Only One logging MX bean.
+logging.22=Exception occurred while getting the logging MX bean.
+logging.3=The Formatter parameter is null.
+logging.4=The 'level' parameter is null.
+logging.5=Different version - {0}.{1}
+logging.6=Name must be "control".
+logging.7=Actions must be either null or the empty string.
+logging.8=Failed to load the specified resource bundle "{0}".
+logging.9=The specified resource bundle name "{0}" is inconsistent with the existing one "{1}".
+logging.A=The 'handler' parameter is null.
+logging.B=The 'parent' parameter is null.
+logging.C=Illegal host argument.
+logging.D=Illegal port argument.
+logging.E=Failed to establish the network connection.
+logging.F=Exception occured when closing the socket handler.