diff -r 000000000000 -r 724f3e1ea53e emul/compact/src/main/java/java/util/logging/LogRecord.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/logging/LogRecord.java Sat Sep 07 13:51:24 2013 +0200 @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.logging; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.io.*; + +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; + +/** + * LogRecord objects are used to pass logging requests between + * the logging framework and individual log Handlers. + *

+ * When a LogRecord is passed into the logging framework it + * logically belongs to the framework and should no longer be + * used or updated by the client application. + *

+ * Note that if the client application has not specified an + * explicit source method name and source class name, then the + * LogRecord class will infer them automatically when they are + * first accessed (due to a call on getSourceMethodName or + * getSourceClassName) by analyzing the call stack. Therefore, + * if a logging Handler wants to pass off a LogRecord to another + * thread, or to transmit it over RMI, and if it wishes to subsequently + * obtain method name or class name information it should call + * one of getSourceClassName or getSourceMethodName to force + * the values to be filled in. + *

+ * Serialization notes: + *

+ * + * @since 1.4 + */ + +public class LogRecord implements java.io.Serializable { + private static final AtomicLong globalSequenceNumber + = new AtomicLong(0); + + /** + * The default value of threadID will be the current thread's + * thread id, for ease of correlation, unless it is greater than + * MIN_SEQUENTIAL_THREAD_ID, in which case we try harder to keep + * our promise to keep threadIDs unique by avoiding collisions due + * to 32-bit wraparound. Unfortunately, LogRecord.getThreadID() + * returns int, while Thread.getId() returns long. + */ + private static final int MIN_SEQUENTIAL_THREAD_ID = Integer.MAX_VALUE / 2; + + private static final AtomicInteger nextThreadId + = new AtomicInteger(MIN_SEQUENTIAL_THREAD_ID); + + private static final ThreadLocal threadIds = new ThreadLocal<>(); + + /** + * @serial Logging message level + */ + private Level level; + + /** + * @serial Sequence number + */ + private long sequenceNumber; + + /** + * @serial Class that issued logging call + */ + private String sourceClassName; + + /** + * @serial Method that issued logging call + */ + private String sourceMethodName; + + /** + * @serial Non-localized raw message text + */ + private String message; + + /** + * @serial Thread ID for thread that issued logging call. + */ + private int threadID; + + /** + * @serial Event time in milliseconds since 1970 + */ + private long millis; + + /** + * @serial The Throwable (if any) associated with log message + */ + private Throwable thrown; + + /** + * @serial Name of the source Logger. + */ + private String loggerName; + + /** + * @serial Resource bundle name to localized log message. + */ + private String resourceBundleName; + + private transient boolean needToInferCaller; + private transient Object parameters[]; + private transient ResourceBundle resourceBundle; + + /** + * Returns the default value for a new LogRecord's threadID. + */ + private int defaultThreadID() { + long tid = Thread.currentThread().getId(); + if (tid < MIN_SEQUENTIAL_THREAD_ID) { + return (int) tid; + } else { + Integer id = threadIds.get(); + if (id == null) { + id = nextThreadId.getAndIncrement(); + threadIds.set(id); + } + return id; + } + } + + /** + * Construct a LogRecord with the given level and message values. + *

+ * The sequence property will be initialized with a new unique value. + * These sequence values are allocated in increasing order within a VM. + *

+ * The millis property will be initialized to the current time. + *

+ * The thread ID property will be initialized with a unique ID for + * the current thread. + *

+ * All other properties will be initialized to "null". + * + * @param level a logging level value + * @param msg the raw non-localized logging message (may be null) + */ + public LogRecord(Level level, String msg) { + // Make sure level isn't null, by calling random method. + level.getClass(); + this.level = level; + message = msg; + // Assign a thread ID and a unique sequence number. + sequenceNumber = globalSequenceNumber.getAndIncrement(); + threadID = defaultThreadID(); + millis = System.currentTimeMillis(); + needToInferCaller = true; + } + + /** + * Get the source Logger's name. + * + * @return source logger name (may be null) + */ + public String getLoggerName() { + return loggerName; + } + + /** + * Set the source Logger's name. + * + * @param name the source logger name (may be null) + */ + public void setLoggerName(String name) { + loggerName = name; + } + + /** + * Get the localization resource bundle + *

+ * This is the ResourceBundle that should be used to localize + * the message string before formatting it. The result may + * be null if the message is not localizable, or if no suitable + * ResourceBundle is available. + */ + public ResourceBundle getResourceBundle() { + return resourceBundle; + } + + /** + * Set the localization resource bundle. + * + * @param bundle localization bundle (may be null) + */ + public void setResourceBundle(ResourceBundle bundle) { + resourceBundle = bundle; + } + + /** + * Get the localization resource bundle name + *

+ * This is the name for the ResourceBundle that should be + * used to localize the message string before formatting it. + * The result may be null if the message is not localizable. + */ + public String getResourceBundleName() { + return resourceBundleName; + } + + /** + * Set the localization resource bundle name. + * + * @param name localization bundle name (may be null) + */ + public void setResourceBundleName(String name) { + resourceBundleName = name; + } + + /** + * Get the logging message level, for example Level.SEVERE. + * @return the logging message level + */ + public Level getLevel() { + return level; + } + + /** + * Set the logging message level, for example Level.SEVERE. + * @param level the logging message level + */ + public void setLevel(Level level) { + if (level == null) { + throw new NullPointerException(); + } + this.level = level; + } + + /** + * Get the sequence number. + *

+ * Sequence numbers are normally assigned in the LogRecord + * constructor, which assigns unique sequence numbers to + * each new LogRecord in increasing order. + * @return the sequence number + */ + public long getSequenceNumber() { + return sequenceNumber; + } + + /** + * Set the sequence number. + *

+ * Sequence numbers are normally assigned in the LogRecord constructor, + * so it should not normally be necessary to use this method. + */ + public void setSequenceNumber(long seq) { + sequenceNumber = seq; + } + + /** + * Get the name of the class that (allegedly) issued the logging request. + *

+ * Note that this sourceClassName is not verified and may be spoofed. + * This information may either have been provided as part of the + * logging call, or it may have been inferred automatically by the + * logging framework. In the latter case, the information may only + * be approximate and may in fact describe an earlier call on the + * stack frame. + *

+ * May be null if no information could be obtained. + * + * @return the source class name + */ + public String getSourceClassName() { + if (needToInferCaller) { + inferCaller(); + } + return sourceClassName; + } + + /** + * Set the name of the class that (allegedly) issued the logging request. + * + * @param sourceClassName the source class name (may be null) + */ + public void setSourceClassName(String sourceClassName) { + this.sourceClassName = sourceClassName; + needToInferCaller = false; + } + + /** + * Get the name of the method that (allegedly) issued the logging request. + *

+ * Note that this sourceMethodName is not verified and may be spoofed. + * This information may either have been provided as part of the + * logging call, or it may have been inferred automatically by the + * logging framework. In the latter case, the information may only + * be approximate and may in fact describe an earlier call on the + * stack frame. + *

+ * May be null if no information could be obtained. + * + * @return the source method name + */ + public String getSourceMethodName() { + if (needToInferCaller) { + inferCaller(); + } + return sourceMethodName; + } + + /** + * Set the name of the method that (allegedly) issued the logging request. + * + * @param sourceMethodName the source method name (may be null) + */ + public void setSourceMethodName(String sourceMethodName) { + this.sourceMethodName = sourceMethodName; + needToInferCaller = false; + } + + /** + * Get the "raw" log message, before localization or formatting. + *

+ * May be null, which is equivalent to the empty string "". + *

+ * This message may be either the final text or a localization key. + *

+ * During formatting, if the source logger has a localization + * ResourceBundle and if that ResourceBundle has an entry for + * this message string, then the message string is replaced + * with the localized value. + * + * @return the raw message string + */ + public String getMessage() { + return message; + } + + /** + * Set the "raw" log message, before localization or formatting. + * + * @param message the raw message string (may be null) + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * Get the parameters to the log message. + * + * @return the log message parameters. May be null if + * there are no parameters. + */ + public Object[] getParameters() { + return parameters; + } + + /** + * Set the parameters to the log message. + * + * @param parameters the log message parameters. (may be null) + */ + public void setParameters(Object parameters[]) { + this.parameters = parameters; + } + + /** + * Get an identifier for the thread where the message originated. + *

+ * This is a thread identifier within the Java VM and may or + * may not map to any operating system ID. + * + * @return thread ID + */ + public int getThreadID() { + return threadID; + } + + /** + * Set an identifier for the thread where the message originated. + * @param threadID the thread ID + */ + public void setThreadID(int threadID) { + this.threadID = threadID; + } + + /** + * Get event time in milliseconds since 1970. + * + * @return event time in millis since 1970 + */ + public long getMillis() { + return millis; + } + + /** + * Set event time. + * + * @param millis event time in millis since 1970 + */ + public void setMillis(long millis) { + this.millis = millis; + } + + /** + * Get any throwable associated with the log record. + *

+ * If the event involved an exception, this will be the + * exception object. Otherwise null. + * + * @return a throwable + */ + public Throwable getThrown() { + return thrown; + } + + /** + * Set a throwable associated with the log event. + * + * @param thrown a throwable (may be null) + */ + public void setThrown(Throwable thrown) { + this.thrown = thrown; + } + + private static final long serialVersionUID = 5372048053134512534L; + + /** + * @serialData Default fields, followed by a two byte version number + * (major byte, followed by minor byte), followed by information on + * the log record parameter array. If there is no parameter array, + * then -1 is written. If there is a parameter array (possible of zero + * length) then the array length is written as an integer, followed + * by String values for each parameter. If a parameter is null, then + * a null String is written. Otherwise the output of Object.toString() + * is written. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + // We have to call defaultWriteObject first. + out.defaultWriteObject(); + + // Write our version number. + out.writeByte(1); + out.writeByte(0); + if (parameters == null) { + out.writeInt(-1); + return; + } + out.writeInt(parameters.length); + // Write string values for the parameters. + for (int i = 0; i < parameters.length; i++) { + if (parameters[i] == null) { + out.writeObject(null); + } else { + out.writeObject(parameters[i].toString()); + } + } + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + // We have to call defaultReadObject first. + in.defaultReadObject(); + + // Read version number. + byte major = in.readByte(); + byte minor = in.readByte(); + if (major != 1) { + throw new IOException("LogRecord: bad version: " + major + "." + minor); + } + int len = in.readInt(); + if (len == -1) { + parameters = null; + } else { + parameters = new Object[len]; + for (int i = 0; i < parameters.length; i++) { + parameters[i] = in.readObject(); + } + } + // If necessary, try to regenerate the resource bundle. + if (resourceBundleName != null) { + try { + resourceBundle = ResourceBundle.getBundle(resourceBundleName); + } catch (MissingResourceException ex) { + // This is not a good place to throw an exception, + // so we simply leave the resourceBundle null. + resourceBundle = null; + } + } + + needToInferCaller = false; + } + + // Private method to infer the caller's class and method names + private void inferCaller() { + needToInferCaller = false; + JavaLangAccess access = SharedSecrets.getJavaLangAccess(); + Throwable throwable = new Throwable(); + int depth = access.getStackTraceDepth(throwable); + + boolean lookingForLogger = true; + for (int ix = 0; ix < depth; ix++) { + // Calling getStackTraceElement directly prevents the VM + // from paying the cost of building the entire stack frame. + StackTraceElement frame = + access.getStackTraceElement(throwable, ix); + String cname = frame.getClassName(); + boolean isLoggerImpl = isLoggerImplFrame(cname); + if (lookingForLogger) { + // Skip all frames until we have found the first logger frame. + if (isLoggerImpl) { + lookingForLogger = false; + } + } else { + if (!isLoggerImpl) { + // skip reflection call + if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) { + // We've found the relevant frame. + setSourceClassName(cname); + setSourceMethodName(frame.getMethodName()); + return; + } + } + } + } + // We haven't found a suitable frame, so just punt. This is + // OK as we are only committed to making a "best effort" here. + } + + private boolean isLoggerImplFrame(String cname) { + // the log record could be created for a platform logger + return (cname.equals("java.util.logging.Logger") || + cname.startsWith("java.util.logging.LoggingProxyImpl") || + cname.startsWith("sun.util.logging.")); + } +}