8140364: JEP 264 Platform Logger API and Service Implementation
Summary: Initial implementation for JEP 264 Platform Logger API and Service
Reviewed-by: mchung, psandoz, rriggs
--- a/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java Fri Nov 20 19:26:16 2015 +0100
@@ -348,6 +348,19 @@
* {@code java.util.spi.LocaleServiceProvider}</a> for more
* information.</td>
* </tr>
+ *
+ * <tr>
+ * <td>loggerFinder</td>
+ * <td>This {@code RuntimePermission} is required to be granted to
+ * classes which subclass or call methods on
+ * {@code java.lang.System.LoggerFinder}. The permission is
+ * checked during invocation of the abstract base class constructor, as
+ * well as on the invocation of its public methods.
+ * This permission ensures trust in classes which provide loggers
+ * to system classes.</td>
+ * <td>See {@link java.lang.System.LoggerFinder java.lang.System.LoggerFinder}
+ * for more information.</td>
+ * </tr>
* </table>
*
* @implNote
--- a/jdk/src/java.base/share/classes/java/lang/System.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/System.java Fri Nov 20 19:26:16 2015 +0100
@@ -30,13 +30,14 @@
import java.security.AccessControlContext;
import java.util.Properties;
import java.util.PropertyPermission;
-import java.util.StringTokenizer;
import java.util.Map;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.security.AllPermission;
import java.nio.channels.Channel;
import java.nio.channels.spi.SelectorProvider;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
import sun.nio.ch.Interruptible;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
@@ -45,6 +46,9 @@
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.JavaLangAccess;;
import jdk.internal.misc.SharedSecrets;;
+import jdk.internal.logger.LoggerFinderLoader;
+import jdk.internal.logger.LazyLoggers;
+import jdk.internal.logger.LocalizedLoggerWrapper;
/**
* The <code>System</code> class contains several useful class fields
@@ -944,6 +948,648 @@
}
/**
+ * {@code System.Logger} instances log messages that will be
+ * routed to the underlying logging framework the {@link System.LoggerFinder
+ * LoggerFinder} uses.
+ * <p>
+ * {@code System.Logger} instances are typically obtained from
+ * the {@link java.lang.System System} class, by calling
+ * {@link java.lang.System#getLogger(java.lang.String) System.getLogger(loggerName)}
+ * or {@link java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle)
+ * System.getLogger(loggerName, bundle)}.
+ *
+ * @see java.lang.System#getLogger(java.lang.String)
+ * @see java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle)
+ * @see java.lang.System.LoggerFinder
+ *
+ * @since 9
+ *
+ */
+ public interface Logger {
+
+ /**
+ * System {@linkplain Logger loggers} levels.
+ * <p>
+ * A level has a {@linkplain #getName() name} and {@linkplain
+ * #getSeverity() severity}.
+ * Level values are {@link #ALL}, {@link #TRACE}, {@link #DEBUG},
+ * {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #OFF},
+ * by order of increasing severity.
+ * <br>
+ * {@link #ALL} and {@link #OFF}
+ * are simple markers with severities mapped respectively to
+ * {@link java.lang.Integer#MIN_VALUE Integer.MIN_VALUE} and
+ * {@link java.lang.Integer#MAX_VALUE Integer.MAX_VALUE}.
+ * <p>
+ * <b>Severity values and Mapping to {@code java.util.logging.Level}.</b>
+ * <p>
+ * {@linkplain System.Logger.Level System logger levels} are mapped to
+ * {@linkplain java.util.logging.Level java.util.logging levels}
+ * of corresponding severity.
+ * <br>The mapping is as follows:
+ * <br><br>
+ * <table border="1">
+ * <caption>System.Logger Severity Level Mapping</caption>
+ * <tr><td><b>System.Logger Levels</b></td>
+ * <td>{@link Logger.Level#ALL ALL}</td>
+ * <td>{@link Logger.Level#TRACE TRACE}</td>
+ * <td>{@link Logger.Level#DEBUG DEBUG}</td>
+ * <td>{@link Logger.Level#INFO INFO}</td>
+ * <td>{@link Logger.Level#WARNING WARNING}</td>
+ * <td>{@link Logger.Level#ERROR ERROR}</td>
+ * <td>{@link Logger.Level#OFF OFF}</td>
+ * </tr>
+ * <tr><td><b>java.util.logging Levels</b></td>
+ * <td>{@link java.util.logging.Level#ALL ALL}</td>
+ * <td>{@link java.util.logging.Level#FINER FINER}</td>
+ * <td>{@link java.util.logging.Level#FINE FINE}</td>
+ * <td>{@link java.util.logging.Level#INFO INFO}</td>
+ * <td>{@link java.util.logging.Level#WARNING WARNING}</td>
+ * <td>{@link java.util.logging.Level#SEVERE SEVERE}</td>
+ * <td>{@link java.util.logging.Level#OFF OFF}</td>
+ * </tr>
+ * </table>
+ *
+ * @since 9
+ *
+ * @see java.lang.System.LoggerFinder
+ * @see java.lang.System.Logger
+ */
+ public enum Level {
+
+ // for convenience, we're reusing java.util.logging.Level int values
+ // the mapping logic in sun.util.logging.PlatformLogger depends
+ // on this.
+ /**
+ * A marker to indicate that all levels are enabled.
+ * This level {@linkplain #getSeverity() severity} is
+ * {@link Integer#MIN_VALUE}.
+ */
+ ALL(Integer.MIN_VALUE), // typically mapped to/from j.u.l.Level.ALL
+ /**
+ * {@code TRACE} level: usually used to log diagnostic information.
+ * This level {@linkplain #getSeverity() severity} is
+ * {@code 400}.
+ */
+ TRACE(400), // typically mapped to/from j.u.l.Level.FINER
+ /**
+ * {@code DEBUG} level: usually used to log debug information traces.
+ * This level {@linkplain #getSeverity() severity} is
+ * {@code 500}.
+ */
+ DEBUG(500), // typically mapped to/from j.u.l.Level.FINEST/FINE/CONFIG
+ /**
+ * {@code INFO} level: usually used to log information messages.
+ * This level {@linkplain #getSeverity() severity} is
+ * {@code 800}.
+ */
+ INFO(800), // typically mapped to/from j.u.l.Level.INFO
+ /**
+ * {@code WARNING} level: usually used to log warning messages.
+ * This level {@linkplain #getSeverity() severity} is
+ * {@code 900}.
+ */
+ WARNING(900), // typically mapped to/from j.u.l.Level.WARNING
+ /**
+ * {@code ERROR} level: usually used to log error messages.
+ * This level {@linkplain #getSeverity() severity} is
+ * {@code 1000}.
+ */
+ ERROR(1000), // typically mapped to/from j.u.l.Level.SEVERE
+ /**
+ * A marker to indicate that all levels are disabled.
+ * This level {@linkplain #getSeverity() severity} is
+ * {@link Integer#MAX_VALUE}.
+ */
+ OFF(Integer.MAX_VALUE); // typically mapped to/from j.u.l.Level.OFF
+
+ private final int severity;
+
+ private Level(int severity) {
+ this.severity = severity;
+ }
+
+ /**
+ * Returns the name of this level.
+ * @return this level {@linkplain #name()}.
+ */
+ public final String getName() {
+ return name();
+ }
+
+ /**
+ * Returns the severity of this level.
+ * A higher severity means a more severe condition.
+ * @return this level severity.
+ */
+ public final int getSeverity() {
+ return severity;
+ }
+ }
+
+ /**
+ * Returns the name of this logger.
+ *
+ * @return the logger name.
+ */
+ public String getName();
+
+ /**
+ * Checks if a message of the given level would be logged by
+ * this logger.
+ *
+ * @param level the log message level.
+ * @return {@code true} if the given log message level is currently
+ * being logged.
+ *
+ * @throws NullPointerException if {@code level} is {@code null}.
+ */
+ public boolean isLoggable(Level level);
+
+ /**
+ * Logs a message.
+ *
+ * @implSpec The default implementation for this method calls
+ * {@code this.log(level, (ResourceBundle)null, msg, (Object[])null);}
+ *
+ * @param level the log message level.
+ * @param msg the string message (or a key in the message catalog, if
+ * this logger is a {@link
+ * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)
+ * localized logger}); can be {@code null}.
+ *
+ * @throws NullPointerException if {@code level} is {@code null}.
+ */
+ public default void log(Level level, String msg) {
+ log(level, (ResourceBundle) null, msg, (Object[]) null);
+ }
+
+ /**
+ * Logs a lazily supplied message.
+ * <p>
+ * If the logger is currently enabled for the given log message level
+ * then a message is logged that is the result produced by the
+ * given supplier function. Otherwise, the supplier is not operated on.
+ *
+ * @implSpec When logging is enabled for the given level, the default
+ * implementation for this method calls
+ * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), (Object[])null);}
+ *
+ * @param level the log message level.
+ * @param msgSupplier a supplier function that produces a message.
+ *
+ * @throws NullPointerException if {@code level} is {@code null},
+ * or {@code msgSupplier} is {@code null}.
+ */
+ public default void log(Level level, Supplier<String> msgSupplier) {
+ Objects.requireNonNull(msgSupplier);
+ if (isLoggable(Objects.requireNonNull(level))) {
+ log(level, (ResourceBundle) null, msgSupplier.get(), (Object[]) null);
+ }
+ }
+
+ /**
+ * Logs a message produced from the given object.
+ * <p>
+ * If the logger is currently enabled for the given log message level then
+ * a message is logged that, by default, is the result produced from
+ * calling toString on the given object.
+ * Otherwise, the object is not operated on.
+ *
+ * @implSpec When logging is enabled for the given level, the default
+ * implementation for this method calls
+ * {@code this.log(level, (ResourceBundle)null, obj.toString(), (Object[])null);}
+ *
+ * @param level the log message level.
+ * @param obj the object to log.
+ *
+ * @throws NullPointerException if {@code level} is {@code null}, or
+ * {@code obj} is {@code null}.
+ */
+ public default void log(Level level, Object obj) {
+ Objects.requireNonNull(obj);
+ if (isLoggable(Objects.requireNonNull(level))) {
+ this.log(level, (ResourceBundle) null, obj.toString(), (Object[]) null);
+ }
+ }
+
+ /**
+ * Logs a message associated with a given throwable.
+ *
+ * @implSpec The default implementation for this method calls
+ * {@code this.log(level, (ResourceBundle)null, msg, thrown);}
+ *
+ * @param level the log message level.
+ * @param msg the string message (or a key in the message catalog, if
+ * this logger is a {@link
+ * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)
+ * localized logger}); can be {@code null}.
+ * @param thrown a {@code Throwable} associated with the log message;
+ * can be {@code null}.
+ *
+ * @throws NullPointerException if {@code level} is {@code null}.
+ */
+ public default void log(Level level, String msg, Throwable thrown) {
+ this.log(level, null, msg, thrown);
+ }
+
+ /**
+ * Logs a lazily supplied message associated with a given throwable.
+ * <p>
+ * If the logger is currently enabled for the given log message level
+ * then a message is logged that is the result produced by the
+ * given supplier function. Otherwise, the supplier is not operated on.
+ *
+ * @implSpec When logging is enabled for the given level, the default
+ * implementation for this method calls
+ * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), thrown);}
+ *
+ * @param level one of the log message level identifiers.
+ * @param msgSupplier a supplier function that produces a message.
+ * @param thrown a {@code Throwable} associated with log message;
+ * can be {@code null}.
+ *
+ * @throws NullPointerException if {@code level} is {@code null}, or
+ * {@code msgSupplier} is {@code null}.
+ */
+ public default void log(Level level, Supplier<String> msgSupplier,
+ Throwable thrown) {
+ Objects.requireNonNull(msgSupplier);
+ if (isLoggable(Objects.requireNonNull(level))) {
+ this.log(level, null, msgSupplier.get(), thrown);
+ }
+ }
+
+ /**
+ * Logs a message with an optional list of parameters.
+ *
+ * @implSpec The default implementation for this method calls
+ * {@code this.log(level, (ResourceBundle)null, format, params);}
+ *
+ * @param level one of the log message level identifiers.
+ * @param format the string message format in {@link
+ * java.text.MessageFormat} format, (or a key in the message
+ * catalog, if this logger is a {@link
+ * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)
+ * localized logger}); can be {@code null}.
+ * @param params an optional list of parameters to the message (may be
+ * none).
+ *
+ * @throws NullPointerException if {@code level} is {@code null}.
+ */
+ public default void log(Level level, String format, Object... params) {
+ this.log(level, null, format, params);
+ }
+
+ /**
+ * Logs a localized message associated with a given throwable.
+ * <p>
+ * If the given resource bundle is non-{@code null}, the {@code msg}
+ * string is localized using the given resource bundle.
+ * Otherwise the {@code msg} string is not localized.
+ *
+ * @param level the log message level.
+ * @param bundle a resource bundle to localize {@code msg}; can be
+ * {@code null}.
+ * @param msg the string message (or a key in the message catalog,
+ * if {@code bundle} is not {@code null}); can be {@code null}.
+ * @param thrown a {@code Throwable} associated with the log message;
+ * can be {@code null}.
+ *
+ * @throws NullPointerException if {@code level} is {@code null}.
+ */
+ public void log(Level level, ResourceBundle bundle, String msg,
+ Throwable thrown);
+
+ /**
+ * Logs a message with resource bundle and an optional list of
+ * parameters.
+ * <p>
+ * If the given resource bundle is non-{@code null}, the {@code format}
+ * string is localized using the given resource bundle.
+ * Otherwise the {@code format} string is not localized.
+ *
+ * @param level the log message level.
+ * @param bundle a resource bundle to localize {@code format}; can be
+ * {@code null}.
+ * @param format the string message format in {@link
+ * java.text.MessageFormat} format, (or a key in the message
+ * catalog if {@code bundle} is not {@code null}); can be {@code null}.
+ * @param params an optional list of parameters to the message (may be
+ * none).
+ *
+ * @throws NullPointerException if {@code level} is {@code null}.
+ */
+ public void log(Level level, ResourceBundle bundle, String format,
+ Object... params);
+
+
+ }
+
+ /**
+ * The {@code LoggerFinder} service is responsible for creating, managing,
+ * and configuring loggers to the underlying framework it uses.
+ * <p>
+ * A logger finder is a concrete implementation of this class that has a
+ * zero-argument constructor and implements the abstract methods defined
+ * by this class.
+ * The loggers returned from a logger finder are capable of routing log
+ * messages to the logging backend this provider supports.
+ * A given invocation of the Java Runtime maintains a single
+ * system-wide LoggerFinder instance that is loaded as follows:
+ * <ul>
+ * <li>First it finds any custom {@code LoggerFinder} provider
+ * using the {@link java.util.ServiceLoader} facility with the
+ * {@linkplain ClassLoader#getSystemClassLoader() system class
+ * loader}.</li>
+ * <li>If no {@code LoggerFinder} provider is found, the system default
+ * {@code LoggerFinder} implementation will be used.</li>
+ * </ul>
+ * <p>
+ * An application can replace the logging backend
+ * <i>even when the java.logging module is present</i>, by simply providing
+ * and declaring an implementation of the {@link LoggerFinder} service.
+ * <p>
+ * <b>Default Implementation</b>
+ * <p>
+ * The system default {@code LoggerFinder} implementation uses
+ * {@code java.util.logging} as the backend framework when the
+ * {@code java.logging} module is present.
+ * It returns a {@linkplain System.Logger logger} instance
+ * that will route log messages to a {@link java.util.logging.Logger
+ * java.util.logging.Logger}. Otherwise, if {@code java.logging} is not
+ * present, the default implementation will return a simple logger
+ * instance that will route log messages of {@code INFO} level and above to
+ * the console ({@code System.err}).
+ * <p>
+ * <b>Logging Configuration</b>
+ * <p>
+ * {@linkplain Logger Logger} instances obtained from the
+ * {@code LoggerFinder} factory methods are not directly configurable by
+ * the application. Configuration is the responsibility of the underlying
+ * logging backend, and usually requires using APIs specific to that backend.
+ * <p>For the default {@code LoggerFinder} implementation
+ * using {@code java.util.logging} as its backend, refer to
+ * {@link java.util.logging java.util.logging} for logging configuration.
+ * For the default {@code LoggerFinder} implementation returning simple loggers
+ * when the {@code java.logging} module is absent, the configuration
+ * is implementation dependent.
+ * <p>
+ * Usually an application that uses a logging framework will log messages
+ * through a logger facade defined (or supported) by that framework.
+ * Applications that wish to use an external framework should log
+ * through the facade associated with that framework.
+ * <p>
+ * A system class that needs to log messages will typically obtain
+ * a {@link System.Logger} instance to route messages to the logging
+ * framework selected by the application.
+ * <p>
+ * Libraries and classes that only need loggers to produce log messages
+ * should not attempt to configure loggers by themselves, as that
+ * would make them dependent from a specific implementation of the
+ * {@code LoggerFinder} service.
+ * <p>
+ * In addition, when a security manager is present, loggers provided to
+ * system classes should not be directly configurable through the logging
+ * backend without requiring permissions.
+ * <br>
+ * It is the responsibility of the provider of
+ * the concrete {@code LoggerFinder} implementation to ensure that
+ * these loggers are not configured by untrusted code without proper
+ * permission checks, as configuration performed on such loggers usually
+ * affects all applications in the same Java Runtime.
+ * <p>
+ * <b>Message Levels and Mapping to backend levels</b>
+ * <p>
+ * A logger finder is responsible for mapping from a {@code
+ * System.Logger.Level} to a level supported by the logging backend it uses.
+ * <br>The default LoggerFinder using {@code java.util.logging} as the backend
+ * maps {@code System.Logger} levels to
+ * {@linkplain java.util.logging.Level java.util.logging} levels
+ * of corresponding severity - as described in {@link Logger.Level
+ * Logger.Level}.
+ *
+ * @see java.lang.System
+ * @see java.lang.System.Logger
+ *
+ * @since 9
+ */
+ public static abstract class LoggerFinder {
+ /**
+ * The {@code RuntimePermission("loggerFinder")} is
+ * necessary to subclass and instantiate the {@code LoggerFinder} class,
+ * as well as to obtain loggers from an instance of that class.
+ */
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+
+ /**
+ * Creates a new instance of {@code LoggerFinder}.
+ *
+ * @implNote It is recommended that a {@code LoggerFinder} service
+ * implementation does not perform any heavy initialization in its
+ * constructor, in order to avoid possible risks of deadlock or class
+ * loading cycles during the instantiation of the service provider.
+ *
+ * @throws SecurityException if a security manager is present and its
+ * {@code checkPermission} method doesn't allow the
+ * {@code RuntimePermission("loggerFinder")}.
+ */
+ protected LoggerFinder() {
+ this(checkPermission());
+ }
+
+ private LoggerFinder(Void unused) {
+ // nothing to do.
+ }
+
+ private static Void checkPermission() {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ return null;
+ }
+
+ /**
+ * Returns an instance of {@link Logger Logger}
+ * for the given {@code caller}.
+ *
+ * @param name the name of the logger.
+ * @param caller the class for which the logger is being requested;
+ * can be {@code null}.
+ *
+ * @return a {@link Logger logger} suitable for the given caller's
+ * use.
+ * @throws NullPointerException if {@code name} is {@code null} or
+ * {@code caller} is {@code null}.
+ * @throws SecurityException if a security manager is present and its
+ * {@code checkPermission} method doesn't allow the
+ * {@code RuntimePermission("loggerFinder")}.
+ */
+ public abstract Logger getLogger(String name, /* Module */ Class<?> caller);
+
+ /**
+ * Returns a localizable instance of {@link Logger Logger}
+ * for the given {@code caller}.
+ * The returned logger will use the provided resource bundle for
+ * message localization.
+ *
+ * @implSpec By default, this method calls {@link
+ * #getLogger(java.lang.String, java.lang.Class)
+ * this.getLogger(name, caller)} to obtain a logger, then wraps that
+ * logger in a {@link Logger} instance where all methods that do not
+ * take a {@link ResourceBundle} as parameter are redirected to one
+ * which does - passing the given {@code bundle} for
+ * localization. So for instance, a call to {@link
+ * Logger#log(Level, String) Logger.log(Level.INFO, msg)}
+ * will end up as a call to {@link
+ * Logger#log(Level, ResourceBundle, String, Object...)
+ * Logger.log(Level.INFO, bundle, msg, (Object[])null)} on the wrapped
+ * logger instance.
+ * Note however that by default, string messages returned by {@link
+ * java.util.function.Supplier Supplier<String>} will not be
+ * localized, as it is assumed that such strings are messages which are
+ * already constructed, rather than keys in a resource bundle.
+ * <p>
+ * An implementation of {@code LoggerFinder} may override this method,
+ * for example, when the underlying logging backend provides its own
+ * mechanism for localizing log messages, then such a
+ * {@code LoggerFinder} would be free to return a logger
+ * that makes direct use of the mechanism provided by the backend.
+ *
+ * @param name the name of the logger.
+ * @param bundle a resource bundle; can be {@code null}.
+ * @param caller the class for which the logger is being requested.
+ * @return an instance of {@link Logger Logger} which will use the
+ * provided resource bundle for message localization.
+ *
+ * @throws NullPointerException if {@code name} is {@code null} or
+ * {@code caller} is {@code null}.
+ * @throws SecurityException if a security manager is present and its
+ * {@code checkPermission} method doesn't allow the
+ * {@code RuntimePermission("loggerFinder")}.
+ */
+ public Logger getLocalizedLogger(String name, ResourceBundle bundle,
+ /* Module */ Class<?> caller) {
+ return new LocalizedLoggerWrapper<>(getLogger(name, caller), bundle);
+ }
+
+ /**
+ * Returns the {@code LoggerFinder} instance. There is one
+ * single system-wide {@code LoggerFinder} instance in
+ * the Java Runtime. See the class specification of how the
+ * {@link LoggerFinder LoggerFinder} implementation is located and
+ * loaded.
+
+ * @return the {@link LoggerFinder LoggerFinder} instance.
+ * @throws SecurityException if a security manager is present and its
+ * {@code checkPermission} method doesn't allow the
+ * {@code RuntimePermission("loggerFinder")}.
+ */
+ public static LoggerFinder getLoggerFinder() {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ return accessProvider();
+ }
+
+
+ private static volatile LoggerFinder service;
+ static LoggerFinder accessProvider() {
+ // We do not need to synchronize: LoggerFinderLoader will
+ // always return the same instance, so if we don't have it,
+ // just fetch it again.
+ if (service == null) {
+ PrivilegedAction<LoggerFinder> pa =
+ () -> LoggerFinderLoader.getLoggerFinder();
+ service = AccessController.doPrivileged(pa, null,
+ LOGGERFINDER_PERMISSION);
+ }
+ return service;
+ }
+
+ }
+
+
+ /**
+ * Returns an instance of {@link Logger Logger} for the caller's
+ * use.
+ *
+ * @implSpec
+ * Instances returned by this method route messages to loggers
+ * obtained by calling {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class)
+ * LoggerFinder.getLogger(name, caller)}.
+ *
+ * @apiNote
+ * This method may defer calling the {@link
+ * LoggerFinder#getLogger(java.lang.String, java.lang.Class)
+ * LoggerFinder.getLogger} method to create an actual logger supplied by
+ * the logging backend, for instance, to allow loggers to be obtained during
+ * the system initialization time.
+ *
+ * @param name the name of the logger.
+ * @return an instance of {@link Logger} that can be used by the calling
+ * class.
+ * @throws NullPointerException if {@code name} is {@code null}.
+ */
+ @CallerSensitive
+ public static Logger getLogger(String name) {
+ Objects.requireNonNull(name);
+ final Class<?> caller = Reflection.getCallerClass();
+ return LazyLoggers.getLogger(name, caller);
+ }
+
+ /**
+ * Returns a localizable instance of {@link Logger
+ * Logger} for the caller's use.
+ * The returned logger will use the provided resource bundle for message
+ * localization.
+ *
+ * @implSpec
+ * The returned logger will perform message localization as specified
+ * by {@link LoggerFinder#getLocalizedLogger(java.lang.String,
+ * java.util.ResourceBundle, java.lang.Class)
+ * LoggerFinder.getLocalizedLogger(name, bundle, caller}.
+ *
+ * @apiNote
+ * This method is intended to be used after the system is fully initialized.
+ * This method may trigger the immediate loading and initialization
+ * of the {@link LoggerFinder} service, which may cause issues if the
+ * Java Runtime is not ready to initialize the concrete service
+ * implementation yet.
+ * System classes which may be loaded early in the boot sequence and
+ * need to log localized messages should create a logger using
+ * {@link #getLogger(java.lang.String)} and then use the log methods that
+ * take a resource bundle as parameter.
+ *
+ * @param name the name of the logger.
+ * @param bundle a resource bundle.
+ * @return an instance of {@link Logger} which will use the provided
+ * resource bundle for message localization.
+ * @throws NullPointerException if {@code name} is {@code null} or
+ * {@code bundle} is {@code null}.
+ */
+ @CallerSensitive
+ public static Logger getLogger(String name, ResourceBundle bundle) {
+ final ResourceBundle rb = Objects.requireNonNull(bundle);
+ Objects.requireNonNull(name);
+ final Class<?> caller = Reflection.getCallerClass();
+ final SecurityManager sm = System.getSecurityManager();
+ // We don't use LazyLoggers if a resource bundle is specified.
+ // Bootstrap sensitive classes in the JDK do not use resource bundles
+ // when logging. This could be revisited later, if it needs to.
+ if (sm != null) {
+ return AccessController.doPrivileged((PrivilegedAction<Logger>)
+ () -> LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller),
+ null,
+ LoggerFinder.LOGGERFINDER_PERMISSION);
+ }
+ return LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller);
+ }
+
+ /**
* Terminates the currently running Java Virtual Machine. The
* argument serves as a status code; by convention, a nonzero status
* code indicates abnormal termination.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/AbstractLoggerWrapper.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.logger;
+
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * An implementation of {@link System.Logger System.Logger}
+ * that redirects all calls to a wrapped instance of {@link
+ * System.Logger System.Logger}
+ *
+ * @param <L> Type of the wrapped Logger: {@code Logger} or
+ * an extension of that interface.
+ *
+ */
+abstract class AbstractLoggerWrapper<L extends Logger>
+ implements Logger, PlatformLogger.Bridge, PlatformLogger.ConfigurableBridge {
+
+ AbstractLoggerWrapper() { }
+
+ abstract L wrapped();
+
+ abstract PlatformLogger.Bridge platformProxy();
+
+ L getWrapped() {
+ return wrapped();
+ }
+
+ @Override
+ public final String getName() {
+ return wrapped().getName();
+ }
+
+ // -----------------------------------------------------------------
+ // Generic methods taking a Level as parameter
+ // -----------------------------------------------------------------
+
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return wrapped().isLoggable(level);
+ }
+
+ @Override
+ public void log(Level level, String msg) {
+ wrapped().log(level, msg);
+ }
+
+ @Override
+ public void log(Level level,
+ Supplier<String> msgSupplier) {
+ wrapped().log(level, msgSupplier);
+ }
+
+ @Override
+ public void log(Level level, Object obj) {
+ wrapped().log(level, obj);
+ }
+
+ @Override
+ public void log(Level level,
+ String msg, Throwable thrown) {
+ wrapped().log(level, msg, thrown);
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ wrapped().log(level, msgSupplier, thrown);
+ }
+
+ @Override
+ public void log(Level level,
+ String format, Object... params) {
+ wrapped().log(level, format, params);
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ wrapped().log(level, bundle, key, thrown);
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle,
+ String format, Object... params) {
+ wrapped().log(level, bundle, format, params);
+ }
+
+ // ---------------------------------------------------------
+ // Methods from PlatformLogger.Bridge
+ // ---------------------------------------------------------
+
+ @Override
+ public boolean isLoggable(PlatformLogger.Level level) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) return isLoggable(level.systemLevel());
+ else return platformProxy.isLoggable(level);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ return platformProxy == null || platformProxy.isEnabled();
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) {
+ wrapped().log(level.systemLevel(), msg);
+ } else {
+ platformProxy.log(level, msg);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) {
+ wrapped().log(level.systemLevel(), msg, thrown);
+ } else {
+ platformProxy.log(level, msg, thrown);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg, Object... params) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) {
+ wrapped().log(level.systemLevel(), msg, params);
+ } else {
+ platformProxy.log(level, msg, params);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) {
+ wrapped().log(level.systemLevel(),msgSupplier);
+ } else {
+ platformProxy.log(level,msgSupplier);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) {
+ wrapped().log(level.systemLevel(), msgSupplier, thrown);
+ } else {
+ platformProxy.log(level, thrown, msgSupplier);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) {
+ if (sourceClass == null && sourceMethod == null) { // best effort
+ wrapped().log(level.systemLevel(), msg);
+ } else {
+ Level systemLevel = level.systemLevel();
+ Logger wrapped = wrapped();
+ if (wrapped.isLoggable(systemLevel)) {
+ sourceClass = sourceClass == null ? "" : sourceClass;
+ sourceMethod = sourceMethod == null ? "" : sourceMethod;
+ msg = msg == null ? "" : msg;
+ wrapped.log(systemLevel, String.format("[%s %s] %s",
+ sourceClass, sourceMethod, msg));
+ }
+ }
+ } else {
+ platformProxy.logp(level, sourceClass, sourceMethod, msg);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Supplier<String> msgSupplier) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) { // best effort
+ if (sourceClass == null && sourceMethod == null) {
+ wrapped().log(level.systemLevel(), msgSupplier);
+ } else {
+ Level systemLevel = level.systemLevel();
+ Logger wrapped = wrapped();
+ if (wrapped.isLoggable(systemLevel)) {
+ final String sClass = sourceClass == null ? "" : sourceClass;
+ final String sMethod = sourceMethod == null ? "" : sourceMethod;
+ wrapped.log(systemLevel, () -> String.format("[%s %s] %s",
+ sClass, sMethod, msgSupplier.get()));
+ }
+ }
+ } else {
+ platformProxy.logp(level, sourceClass, sourceMethod, msgSupplier);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Object... params) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) { // best effort
+ if (sourceClass == null && sourceMethod == null) {
+ wrapped().log(level.systemLevel(), msg, params);
+ } else {
+ Level systemLevel = level.systemLevel();
+ Logger wrapped = wrapped();
+ if (wrapped.isLoggable(systemLevel)) {
+ sourceClass = sourceClass == null ? "" : sourceClass;
+ sourceMethod = sourceMethod == null ? "" : sourceMethod;
+ msg = msg == null ? "" : msg;
+ wrapped.log(systemLevel, String.format("[%s %s] %s",
+ sourceClass, sourceMethod, msg), params);
+ }
+ }
+ } else {
+ platformProxy.logp(level, sourceClass, sourceMethod, msg, params);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Throwable thrown) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) { // best effort
+ if (sourceClass == null && sourceMethod == null) {
+ wrapped().log(level.systemLevel(), msg, thrown);
+ } else {
+ Level systemLevel = level.systemLevel();
+ Logger wrapped = wrapped();
+ if (wrapped.isLoggable(systemLevel)) {
+ sourceClass = sourceClass == null ? "" : sourceClass;
+ sourceMethod = sourceMethod == null ? "" : sourceMethod;
+ msg = msg == null ? "" : msg;
+ wrapped.log(systemLevel, String.format("[%s %s] %s",
+ sourceClass, sourceMethod, msg), thrown);
+ }
+ }
+ } else {
+ platformProxy.logp(level, sourceClass, sourceMethod, msg, thrown);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) { // best effort
+ if (sourceClass == null && sourceMethod == null) {
+ wrapped().log(level.systemLevel(), msgSupplier, thrown);
+ } else {
+ Level systemLevel = level.systemLevel();
+ Logger wrapped = wrapped();
+ if (wrapped.isLoggable(systemLevel)) {
+ final String sClass = sourceClass == null ? "" : sourceClass;
+ final String sMethod = sourceMethod == null ? "" : sourceMethod;
+ wrapped.log(systemLevel, () -> String.format("[%s %s] %s",
+ sClass, sMethod, msgSupplier.get()), thrown);
+ }
+ }
+ } else {
+ platformProxy.logp(level, sourceClass, sourceMethod,
+ thrown, msgSupplier);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle,
+ String msg, Object... params) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) { // best effort
+ if (bundle != null || sourceClass == null && sourceMethod == null) {
+ wrapped().log(level.systemLevel(), bundle, msg, params);
+ } else {
+ Level systemLevel = level.systemLevel();
+ Logger wrapped = wrapped();
+ if (wrapped.isLoggable(systemLevel)) {
+ sourceClass = sourceClass == null ? "" : sourceClass;
+ sourceMethod = sourceMethod == null ? "" : sourceMethod;
+ msg = msg == null ? "" : msg;
+ wrapped.log(systemLevel, bundle, String.format("[%s %s] %s",
+ sourceClass, sourceMethod, msg), params);
+ }
+ }
+ } else {
+ platformProxy.logrb(level, sourceClass, sourceMethod,
+ bundle, msg, params);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String msg,
+ Throwable thrown) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) { // best effort
+ if (bundle != null || sourceClass == null && sourceMethod == null) {
+ wrapped().log(level.systemLevel(), bundle, msg, thrown);
+ } else {
+ Level systemLevel = level.systemLevel();
+ Logger wrapped = wrapped();
+ if (wrapped.isLoggable(systemLevel)) {
+ sourceClass = sourceClass == null ? "" : sourceClass;
+ sourceMethod = sourceMethod == null ? "" : sourceMethod;
+ msg = msg == null ? "" : msg;
+ wrapped.log(systemLevel, bundle, String.format("[%s %s] %s",
+ sourceClass, sourceMethod, msg), thrown);
+ }
+ }
+ } else {
+ platformProxy.logrb(level, sourceClass, sourceMethod, bundle,
+ msg, thrown);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+ String msg, Throwable thrown) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) {
+ wrapped().log(level.systemLevel(), bundle, msg, thrown);
+ } else {
+ platformProxy.logrb(level, bundle, msg, thrown);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+ String msg, Object... params) {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ if (platformProxy == null) {
+ wrapped().log(level.systemLevel(), bundle, msg, params);
+ } else {
+ platformProxy.logrb(level, bundle, msg, params);
+ }
+ }
+
+
+ @Override
+ public LoggerConfiguration getLoggerConfiguration() {
+ final PlatformLogger.Bridge platformProxy = platformProxy();
+ return platformProxy == null ? null
+ : PlatformLogger.ConfigurableBridge
+ .getLoggerConfiguration(platformProxy);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.logger;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.ServiceLoader;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import sun.misc.InnocuousThread;
+import sun.misc.VM;
+import sun.util.logging.PlatformLogger;
+import jdk.internal.logger.LazyLoggers.LazyLoggerAccessor;
+
+/**
+ * The BootstrapLogger class handles all the logic needed by Lazy Loggers
+ * to delay the creation of System.Logger instances until the VM is booted.
+ * By extension - it also contains the logic that will delay the creation
+ * of JUL Loggers until the LogManager is initialized by the application, in
+ * the common case where JUL is the default and there is no custom JUL
+ * configuration.
+ *
+ * A BootstrapLogger instance is both a Logger and a
+ * PlatformLogger.Bridge instance, which will put all Log messages in a queue
+ * until the VM is booted.
+ * Once the VM is booted, it obtain the real System.Logger instance from the
+ * LoggerFinder and flushes the message to the queue.
+ *
+ * There are a few caveat:
+ * - the queue may not be flush until the next message is logged after
+ * the VM is booted
+ * - while the BootstrapLogger is active, the default implementation
+ * for all convenience methods is used
+ * - PlatformLogger.setLevel calls are ignored
+ *
+ *
+ */
+public final class BootstrapLogger implements Logger, PlatformLogger.Bridge,
+ PlatformLogger.ConfigurableBridge {
+
+ // We use the BootstrapExecutors class to submit delayed messages
+ // to an independent InnocuousThread which will ensure that
+ // delayed log events will be clearly identified as messages that have
+ // been delayed during the boot sequence.
+ private static class BootstrapExecutors implements ThreadFactory {
+
+ // Maybe that should be made configurable with system properties.
+ static final long KEEP_EXECUTOR_ALIVE_SECONDS = 30;
+
+ // The BootstrapMessageLoggerTask is a Runnable which keeps
+ // a hard ref to the ExecutorService that owns it.
+ // This ensure that the ExecutorService is not gc'ed until the thread
+ // has stopped running.
+ private static class BootstrapMessageLoggerTask implements Runnable {
+ ExecutorService owner;
+ Runnable run;
+ public BootstrapMessageLoggerTask(ExecutorService owner, Runnable r) {
+ this.owner = owner;
+ this.run = r;
+ }
+ @Override
+ public void run() {
+ try {
+ run.run();
+ } finally {
+ owner = null; // allow the ExecutorService to be gced.
+ }
+ }
+ }
+
+ private static volatile WeakReference<ExecutorService> executorRef;
+ private static ExecutorService getExecutor() {
+ WeakReference<ExecutorService> ref = executorRef;
+ ExecutorService executor = ref == null ? null : ref.get();
+ if (executor != null) return executor;
+ synchronized (BootstrapExecutors.class) {
+ ref = executorRef;
+ executor = ref == null ? null : ref.get();
+ if (executor == null) {
+ executor = new ThreadPoolExecutor(0, 1,
+ KEEP_EXECUTOR_ALIVE_SECONDS, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<>(), new BootstrapExecutors());
+ }
+ // The executor service will be elligible for gc
+ // KEEP_EXECUTOR_ALIVE_SECONDS seconds (30s)
+ // after the execution of its last pending task.
+ executorRef = new WeakReference<>(executor);
+ return executorRef.get();
+ }
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ ExecutorService owner = getExecutor();
+ Thread thread = AccessController.doPrivileged(new PrivilegedAction<Thread>() {
+ @Override
+ public Thread run() {
+ Thread t = new InnocuousThread(new BootstrapMessageLoggerTask(owner, r));
+ t.setName("BootstrapMessageLoggerTask-"+t.getName());
+ return t;
+ }
+ }, null, new RuntimePermission("enableContextClassLoaderOverride"));
+ thread.setDaemon(true);
+ return thread;
+ }
+
+ static void submit(Runnable r) {
+ getExecutor().execute(r);
+ }
+
+ // This is used by tests.
+ static void join(Runnable r) {
+ try {
+ getExecutor().submit(r).get();
+ } catch (InterruptedException | ExecutionException ex) {
+ // should not happen
+ throw new RuntimeException(ex);
+ }
+ }
+
+ // This is used by tests.
+ static void awaitPendingTasks() {
+ WeakReference<ExecutorService> ref = executorRef;
+ ExecutorService executor = ref == null ? null : ref.get();
+ if (ref == null) {
+ synchronized(BootstrapExecutors.class) {
+ ref = executorRef;
+ executor = ref == null ? null : ref.get();
+ }
+ }
+ if (executor != null) {
+ // since our executor uses a FIFO and has a single thread
+ // then awaiting the execution of its pending tasks can be done
+ // simply by registering a new task and waiting until it
+ // completes. This of course would not work if we were using
+ // several threads, but we don't.
+ join(()->{});
+ }
+ }
+
+ // This is used by tests.
+ static boolean isAlive() {
+ WeakReference<ExecutorService> ref = executorRef;
+ ExecutorService executor = ref == null ? null : ref.get();
+ if (executor != null) return true;
+ synchronized (BootstrapExecutors.class) {
+ ref = executorRef;
+ executor = ref == null ? null : ref.get();
+ return executor != null;
+ }
+ }
+
+ // The pending log event queue. The first event is the head, and
+ // new events are added at the tail
+ static LogEvent head, tail;
+
+ static void enqueue(LogEvent event) {
+ if (event.next != null) return;
+ synchronized (BootstrapExecutors.class) {
+ if (event.next != null) return;
+ event.next = event;
+ if (tail == null) {
+ head = tail = event;
+ } else {
+ tail.next = event;
+ tail = event;
+ }
+ }
+ }
+
+ static void flush() {
+ LogEvent event;
+ // drain the whole queue
+ synchronized(BootstrapExecutors.class) {
+ event = head;
+ head = tail = null;
+ }
+ while(event != null) {
+ LogEvent.log(event);
+ synchronized(BootstrapExecutors.class) {
+ LogEvent prev = event;
+ event = (event.next == event ? null : event.next);
+ prev.next = null;
+ }
+ }
+ }
+ }
+
+ // The accessor in which this logger is temporarily set.
+ final LazyLoggerAccessor holder;
+
+ BootstrapLogger(LazyLoggerAccessor holder) {
+ this.holder = holder;
+ }
+
+ // Temporary data object storing log events
+ // It would be nice to use a Consumer<Logger> instead of a LogEvent.
+ // This way we could simply do things like:
+ // push((logger) -> logger.log(level, msg));
+ // Unfortunately, if we come to here it means we are in the bootsraping
+ // phase where using lambdas is not safe yet - so we have to use a
+ // a data object instead...
+ //
+ static final class LogEvent {
+ // only one of these two levels should be non null
+ final Level level;
+ final PlatformLogger.Level platformLevel;
+ final BootstrapLogger bootstrap;
+
+ final ResourceBundle bundle;
+ final String msg;
+ final Throwable thrown;
+ final Object[] params;
+ final Supplier<String> msgSupplier;
+ final String sourceClass;
+ final String sourceMethod;
+ final long timeMillis;
+ final long nanoAdjustment;
+
+ // because logging a message may entail calling toString() on
+ // the parameters etc... we need to store the context of the
+ // caller who logged the message - so that we can reuse it when
+ // we finally log the message.
+ final AccessControlContext acc;
+
+ // The next event in the queue
+ LogEvent next;
+
+ private LogEvent(BootstrapLogger bootstrap, Level level,
+ ResourceBundle bundle, String msg,
+ Throwable thrown, Object[] params) {
+ this.acc = AccessController.getContext();
+ this.timeMillis = System.currentTimeMillis();
+ this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
+ this.level = level;
+ this.platformLevel = null;
+ this.bundle = bundle;
+ this.msg = msg;
+ this.msgSupplier = null;
+ this.thrown = thrown;
+ this.params = params;
+ this.sourceClass = null;
+ this.sourceMethod = null;
+ this.bootstrap = bootstrap;
+ }
+
+ private LogEvent(BootstrapLogger bootstrap, Level level,
+ Supplier<String> msgSupplier,
+ Throwable thrown, Object[] params) {
+ this.acc = AccessController.getContext();
+ this.timeMillis = System.currentTimeMillis();
+ this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
+ this.level = level;
+ this.platformLevel = null;
+ this.bundle = null;
+ this.msg = null;
+ this.msgSupplier = msgSupplier;
+ this.thrown = thrown;
+ this.params = params;
+ this.sourceClass = null;
+ this.sourceMethod = null;
+ this.bootstrap = bootstrap;
+ }
+
+ private LogEvent(BootstrapLogger bootstrap,
+ PlatformLogger.Level platformLevel,
+ String sourceClass, String sourceMethod,
+ ResourceBundle bundle, String msg,
+ Throwable thrown, Object[] params) {
+ this.acc = AccessController.getContext();
+ this.timeMillis = System.currentTimeMillis();
+ this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
+ this.level = null;
+ this.platformLevel = platformLevel;
+ this.bundle = bundle;
+ this.msg = msg;
+ this.msgSupplier = null;
+ this.thrown = thrown;
+ this.params = params;
+ this.sourceClass = sourceClass;
+ this.sourceMethod = sourceMethod;
+ this.bootstrap = bootstrap;
+ }
+
+ private LogEvent(BootstrapLogger bootstrap,
+ PlatformLogger.Level platformLevel,
+ String sourceClass, String sourceMethod,
+ Supplier<String> msgSupplier,
+ Throwable thrown, Object[] params) {
+ this.acc = AccessController.getContext();
+ this.timeMillis = System.currentTimeMillis();
+ this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
+ this.level = null;
+ this.platformLevel = platformLevel;
+ this.bundle = null;
+ this.msg = null;
+ this.msgSupplier = msgSupplier;
+ this.thrown = thrown;
+ this.params = params;
+ this.sourceClass = sourceClass;
+ this.sourceMethod = sourceMethod;
+ this.bootstrap = bootstrap;
+ }
+
+ // Log this message in the given logger. Do not call directly.
+ // Use LogEvent.log(LogEvent, logger) instead.
+ private void log(Logger logger) {
+ assert platformLevel == null && level != null;
+ //new Exception("logging delayed message").printStackTrace();
+ if (msgSupplier != null) {
+ if (thrown != null) {
+ logger.log(level, msgSupplier, thrown);
+ } else {
+ logger.log(level, msgSupplier);
+ }
+ } else {
+ // BootstrapLoggers are never localized so we can safely
+ // use the method that takes a ResourceBundle parameter
+ // even when that resource bundle is null.
+ if (thrown != null) {
+ logger.log(level, bundle, msg, thrown);
+ } else {
+ logger.log(level, bundle, msg, params);
+ }
+ }
+ }
+
+ // Log this message in the given logger. Do not call directly.
+ // Use LogEvent.doLog(LogEvent, logger) instead.
+ private void log(PlatformLogger.Bridge logger) {
+ assert platformLevel != null && level == null;
+ if (sourceClass == null) {
+ if (msgSupplier != null) {
+ if (thrown != null) {
+ logger.log(platformLevel, thrown, msgSupplier);
+ } else {
+ logger.log(platformLevel, msgSupplier);
+ }
+ } else {
+ // BootstrapLoggers are never localized so we can safely
+ // use the method that takes a ResourceBundle parameter
+ // even when that resource bundle is null.
+ if (thrown != null) {
+ logger.logrb(platformLevel, bundle, msg, thrown);
+ } else {
+ logger.logrb(platformLevel, bundle, msg, params);
+ }
+ }
+ } else {
+ if (msgSupplier != null) {
+ if (thrown != null) {
+ logger.log(platformLevel, sourceClass, sourceMethod, thrown, msgSupplier);
+ } else {
+ logger.log(platformLevel,sourceClass, sourceMethod, msgSupplier);
+ }
+ } else {
+ // BootstrapLoggers are never localized so we can safely
+ // use the method that takes a ResourceBundle parameter
+ // even when that resource bundle is null.
+ if (thrown != null) {
+ logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, thrown);
+ } else {
+ logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, params);
+ }
+ }
+ }
+ }
+
+ // non default methods from Logger interface
+ static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
+ ResourceBundle bundle, String key, Throwable thrown) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), bundle, key,
+ thrown, null);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
+ ResourceBundle bundle, String format, Object[] params) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), bundle, format,
+ null, params);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
+ Supplier<String> msgSupplier, Throwable thrown) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level),
+ Objects.requireNonNull(msgSupplier), thrown, null);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
+ Supplier<String> msgSupplier) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level),
+ Objects.requireNonNull(msgSupplier), null, null);
+ }
+ static void log(LogEvent log, Logger logger) {
+ final SecurityManager sm = System.getSecurityManager();
+ // not sure we can actually use lambda here. We may need to create
+ // an anonymous class. Although if we reach here, then it means
+ // the VM is booted.
+ if (sm == null || log.acc == null) {
+ BootstrapExecutors.submit(() -> log.log(logger));
+ } else {
+ BootstrapExecutors.submit(() ->
+ AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ log.log(logger); return null;
+ }, log.acc));
+ }
+ }
+
+ // non default methods from PlatformLogger.Bridge interface
+ static LogEvent valueOf(BootstrapLogger bootstrap,
+ PlatformLogger.Level level, String msg) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), null, null, null,
+ msg, null, null);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+ String msg, Throwable thrown) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), null, null, null, msg, thrown, null);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+ String msg, Object[] params) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), null, null, null, msg, null, params);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+ Supplier<String> msgSupplier) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), null, null, msgSupplier, null, null);
+ }
+ static LogEvent vaueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+ Supplier<String> msgSupplier,
+ Throwable thrown) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), null, null,
+ msgSupplier, thrown, null);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+ String sourceClass, String sourceMethod,
+ ResourceBundle bundle, String msg, Object[] params) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), sourceClass,
+ sourceMethod, bundle, msg, null, params);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+ String sourceClass, String sourceMethod,
+ ResourceBundle bundle, String msg, Throwable thrown) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), sourceClass,
+ sourceMethod, bundle, msg, thrown, null);
+ }
+ static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+ String sourceClass, String sourceMethod,
+ Supplier<String> msgSupplier, Throwable thrown) {
+ return new LogEvent(Objects.requireNonNull(bootstrap),
+ Objects.requireNonNull(level), sourceClass,
+ sourceMethod, msgSupplier, thrown, null);
+ }
+ static void log(LogEvent log, PlatformLogger.Bridge logger) {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm == null || log.acc == null) {
+ log.log(logger);
+ } else {
+ // not sure we can actually use lambda here. We may need to create
+ // an anonymous class. Although if we reach here, then it means
+ // the VM is booted.
+ AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ log.log(logger); return null;
+ }, log.acc);
+ }
+ }
+
+ static void log(LogEvent event) {
+ event.bootstrap.flush(event);
+ }
+
+ }
+
+ // Push a log event at the end of the pending LogEvent queue.
+ void push(LogEvent log) {
+ BootstrapExecutors.enqueue(log);
+ // if the queue has been flushed just before we entered
+ // the synchronized block we need to flush it again.
+ checkBootstrapping();
+ }
+
+ // Flushes the queue of pending LogEvents to the logger.
+ void flush(LogEvent event) {
+ assert event.bootstrap == this;
+ if (event.platformLevel != null) {
+ PlatformLogger.Bridge concrete = holder.getConcretePlatformLogger(this);
+ LogEvent.log(event, concrete);
+ } else {
+ Logger concrete = holder.getConcreteLogger(this);
+ LogEvent.log(event, concrete);
+ }
+ }
+
+ /**
+ * The name of this logger. This is the name of the actual logger for which
+ * this logger acts as a temporary proxy.
+ * @return The logger name.
+ */
+ @Override
+ public String getName() {
+ return holder.name;
+ }
+
+ /**
+ * Check whether the VM is still bootstrapping, and if not, arranges
+ * for this logger's holder to create the real logger and flush the
+ * pending event queue.
+ * @return true if the VM is still bootstrapping.
+ */
+ boolean checkBootstrapping() {
+ if (isBooted()) {
+ BootstrapExecutors.flush();
+ return false;
+ }
+ return true;
+ }
+
+ // ----------------------------------
+ // Methods from Logger
+ // ----------------------------------
+
+ @Override
+ public boolean isLoggable(Level level) {
+ if (checkBootstrapping()) {
+ return level.getSeverity() >= Level.INFO.getSeverity();
+ } else {
+ final Logger spi = holder.wrapped();
+ return spi.isLoggable(level);
+ }
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, bundle, key, thrown));
+ } else {
+ final Logger spi = holder.wrapped();
+ spi.log(level, bundle, key, thrown);
+ }
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, bundle, format, params));
+ } else {
+ final Logger spi = holder.wrapped();
+ spi.log(level, bundle, format, params);
+ }
+ }
+
+ @Override
+ public void log(Level level, String msg, Throwable thrown) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, null, msg, thrown));
+ } else {
+ final Logger spi = holder.wrapped();
+ spi.log(level, msg, thrown);
+ }
+ }
+
+ @Override
+ public void log(Level level, String format, Object... params) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, null, format, params));
+ } else {
+ final Logger spi = holder.wrapped();
+ spi.log(level, format, params);
+ }
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, msgSupplier));
+ } else {
+ final Logger spi = holder.wrapped();
+ spi.log(level, msgSupplier);
+ }
+ }
+
+ @Override
+ public void log(Level level, Object obj) {
+ if (checkBootstrapping()) {
+ Logger.super.log(level, obj);
+ } else {
+ final Logger spi = holder.wrapped();
+ spi.log(level, obj);
+ }
+ }
+
+ @Override
+ public void log(Level level, String msg) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, null, msg, (Object[])null));
+ } else {
+ final Logger spi = holder.wrapped();
+ spi.log(level, msg);
+ }
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, msgSupplier, thrown));
+ } else {
+ final Logger spi = holder.wrapped();
+ spi.log(level, msgSupplier, thrown);
+ }
+ }
+
+ // ----------------------------------
+ // Methods from PlatformLogger.Bridge
+ // ----------------------------------
+
+ @Override
+ public boolean isLoggable(PlatformLogger.Level level) {
+ if (checkBootstrapping()) {
+ return level.intValue() >= PlatformLogger.Level.INFO.intValue();
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ return spi.isLoggable(level);
+ }
+ }
+
+ @Override
+ public boolean isEnabled() {
+ if (checkBootstrapping()) {
+ return true;
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ return spi.isEnabled();
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, msg));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.log(level, msg);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, msg, thrown));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.log(level, msg, thrown);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg, Object... params) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, msg, params));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.log(level, msg, params);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, msgSupplier));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.log(level, msgSupplier);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ if (checkBootstrapping()) {
+ push(LogEvent.vaueOf(this, level, msgSupplier, thrown));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.log(level, thrown, msgSupplier);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null,
+ msg, (Object[])null));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logp(level, sourceClass, sourceMethod, msg);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Supplier<String> msgSupplier) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, null));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logp(level, sourceClass, sourceMethod, msgSupplier);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Object... params) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, params));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logp(level, sourceClass, sourceMethod, msg, params);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Throwable thrown) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, thrown));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logp(level, sourceClass, sourceMethod, msg, thrown);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, thrown));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logp(level, sourceClass, sourceMethod, thrown, msgSupplier);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String msg, Object... params) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, params));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logrb(level, sourceClass, sourceMethod, bundle, msg, params);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, thrown));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logrb(level, sourceClass, sourceMethod, bundle, msg, thrown);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+ String msg, Object... params) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, null, null, bundle, msg, params));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logrb(level, bundle, msg, params);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, ResourceBundle bundle, String msg, Throwable thrown) {
+ if (checkBootstrapping()) {
+ push(LogEvent.valueOf(this, level, null, null, bundle, msg, thrown));
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ spi.logrb(level, bundle, msg, thrown);
+ }
+ }
+
+ @Override
+ public LoggerConfiguration getLoggerConfiguration() {
+ if (checkBootstrapping()) {
+ // This practically means that PlatformLogger.setLevel()
+ // calls will be ignored if the VM is still bootstrapping. We could
+ // attempt to fix that but is it worth it?
+ return PlatformLogger.ConfigurableBridge.super.getLoggerConfiguration();
+ } else {
+ final PlatformLogger.Bridge spi = holder.platform();
+ return PlatformLogger.ConfigurableBridge.getLoggerConfiguration(spi);
+ }
+ }
+
+ // This BooleanSupplier is a hook for tests - so that we can simulate
+ // what would happen before the VM is booted.
+ private static volatile BooleanSupplier isBooted;
+ public static boolean isBooted() {
+ if (isBooted != null) return isBooted.getAsBoolean();
+ else return VM.isBooted();
+ }
+
+ // A bit of black magic. We try to find out the nature of the logging
+ // backend without actually loading it.
+ private static enum LoggingBackend {
+ // There is no LoggerFinder and JUL is not present
+ NONE(true),
+
+ // There is no LoggerFinder, but we have found a
+ // JdkLoggerFinder installed (which means JUL is present),
+ // and we haven't found any custom configuration for JUL.
+ // Until LogManager is initialized we can use a simple console
+ // logger.
+ JUL_DEFAULT(false),
+
+ // Same as above, except that we have found a custom configuration
+ // for JUL. We cannot use the simple console logger in this case.
+ JUL_WITH_CONFIG(true),
+
+ // We have found a custom LoggerFinder.
+ CUSTOM(true);
+
+ final boolean useLoggerFinder;
+ private LoggingBackend(boolean useLoggerFinder) {
+ this.useLoggerFinder = useLoggerFinder;
+ }
+ };
+
+ // The purpose of this class is to delay the initialization of
+ // the detectedBackend field until it is actually read.
+ // We do not want this field to get initialized if VM.isBooted() is false.
+ private static final class DetectBackend {
+ static final LoggingBackend detectedBackend;
+ static {
+ detectedBackend = AccessController.doPrivileged(new PrivilegedAction<LoggingBackend>() {
+ @Override
+ public LoggingBackend run() {
+ final Iterator<LoggerFinder> iterator =
+ ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader())
+ .iterator();
+ if (iterator.hasNext()) {
+ return LoggingBackend.CUSTOM; // Custom Logger Provider is registered
+ }
+ // No custom logger provider: we will be using the default
+ // backend.
+ final Iterator<DefaultLoggerFinder> iterator2 =
+ ServiceLoader.loadInstalled(DefaultLoggerFinder.class)
+ .iterator();
+ if (iterator2.hasNext()) {
+ // LoggingProviderImpl is registered. The default
+ // implementation is java.util.logging
+ String cname = System.getProperty("java.util.logging.config.class");
+ String fname = System.getProperty("java.util.logging.config.file");
+ return (cname != null || fname != null)
+ ? LoggingBackend.JUL_WITH_CONFIG
+ : LoggingBackend.JUL_DEFAULT;
+ } else {
+ // SimpleLogger is used
+ return LoggingBackend.NONE;
+ }
+ }
+ });
+
+ }
+ }
+
+ // We will use temporary SimpleConsoleLoggers if
+ // the logging backend is JUL, there is no custom config,
+ // and the LogManager has not been initialized yet.
+ private static boolean useTemporaryLoggers() {
+ // being paranoid: this should already have been checked
+ if (!isBooted()) return true;
+ return DetectBackend.detectedBackend == LoggingBackend.JUL_DEFAULT
+ && !logManagerConfigured;
+ }
+
+ // We will use lazy loggers if:
+ // - the VM is not yet booted
+ // - the logging backend is a custom backend
+ // - the logging backend is JUL, there is no custom config,
+ // and the LogManager has not been initialized yet.
+ public static synchronized boolean useLazyLoggers() {
+ return !BootstrapLogger.isBooted()
+ || DetectBackend.detectedBackend == LoggingBackend.CUSTOM
+ || useTemporaryLoggers();
+ }
+
+ // Called by LazyLoggerAccessor. This method will determine whether
+ // to create a BootstrapLogger (if the VM is not yet booted),
+ // a SimpleConsoleLogger (if JUL is the default backend and there
+ // is no custom JUL configuration and LogManager is not yet initialized),
+ // or a logger returned by the loaded LoggerFinder (all other cases).
+ static Logger getLogger(LazyLoggerAccessor accessor) {
+ if (!BootstrapLogger.isBooted()) {
+ return new BootstrapLogger(accessor);
+ } else {
+ boolean temporary = useTemporaryLoggers();
+ if (temporary) {
+ // JUL is the default backend, there is no custom configuration,
+ // LogManager has not been used.
+ synchronized(BootstrapLogger.class) {
+ if (useTemporaryLoggers()) {
+ return makeTemporaryLogger(accessor);
+ }
+ }
+ }
+ // Already booted. Return the real logger.
+ return accessor.createLogger();
+ }
+ }
+
+
+ // If the backend is JUL, and there is no custom configuration, and
+ // nobody has attempted to call LogManager.getLogManager() yet, then
+ // we can temporarily substitute JUL Logger with SimpleConsoleLoggers,
+ // which avoids the cost of actually loading up the LogManager...
+ // The TemporaryLoggers class has the logic to create such temporary
+ // loggers, and to possibly replace them with real JUL loggers if
+ // someone calls LogManager.getLogManager().
+ static final class TemporaryLoggers implements
+ Function<LazyLoggerAccessor, SimpleConsoleLogger> {
+
+ // all accesses must be synchronized on the outer BootstrapLogger.class
+ final Map<LazyLoggerAccessor, SimpleConsoleLogger> temporaryLoggers =
+ new HashMap<>();
+
+ // all accesses must be synchronized on the outer BootstrapLogger.class
+ // The temporaryLoggers map will be cleared when LogManager is initialized.
+ boolean cleared;
+
+ @Override
+ // all accesses must be synchronized on the outer BootstrapLogger.class
+ public SimpleConsoleLogger apply(LazyLoggerAccessor t) {
+ if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
+ return SimpleConsoleLogger.makeSimpleLogger(t.getLoggerName(), true);
+ }
+
+ // all accesses must be synchronized on the outer BootstrapLogger.class
+ SimpleConsoleLogger get(LazyLoggerAccessor a) {
+ if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
+ return temporaryLoggers.computeIfAbsent(a, this);
+ }
+
+ // all accesses must be synchronized on the outer BootstrapLogger.class
+ Map<LazyLoggerAccessor, SimpleConsoleLogger> drainTemporaryLoggers() {
+ if (temporaryLoggers.isEmpty()) return null;
+ if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
+ final Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors = new HashMap<>(temporaryLoggers);
+ temporaryLoggers.clear();
+ cleared = true;
+ return accessors;
+ }
+
+ static void resetTemporaryLoggers(Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors) {
+ // When the backend is JUL we want to force the creation of
+ // JUL loggers here: some tests are expecting that the
+ // PlatformLogger will create JUL loggers as soon as the
+ // LogManager is initialized.
+ //
+ // If the backend is not JUL then we can delay the re-creation
+ // of the wrapped logger until they are next accessed.
+ //
+ final LoggingBackend detectedBackend = DetectBackend.detectedBackend;
+ final boolean lazy = detectedBackend != LoggingBackend.JUL_DEFAULT
+ && detectedBackend != LoggingBackend.JUL_WITH_CONFIG;
+ for (Map.Entry<LazyLoggerAccessor, SimpleConsoleLogger> a : accessors.entrySet()) {
+ a.getKey().release(a.getValue(), !lazy);
+ }
+ }
+
+ // all accesses must be synchronized on the outer BootstrapLogger.class
+ static final TemporaryLoggers INSTANCE = new TemporaryLoggers();
+ }
+
+ static synchronized Logger makeTemporaryLogger(LazyLoggerAccessor a) {
+ // accesses to TemporaryLoggers is synchronized on BootstrapLogger.class
+ return TemporaryLoggers.INSTANCE.get(a);
+ }
+
+ private static volatile boolean logManagerConfigured;
+
+ private static synchronized Map<LazyLoggerAccessor, SimpleConsoleLogger>
+ releaseTemporaryLoggers() {
+ // first check whether there's a chance that we have used
+ // temporary loggers; Will be false if logManagerConfigured is already
+ // true.
+ final boolean clearTemporaryLoggers = useTemporaryLoggers();
+
+ // then sets the flag that tells that the log manager is configured
+ logManagerConfigured = true;
+
+ // finally replace all temporary loggers by real JUL loggers
+ if (clearTemporaryLoggers) {
+ // accesses to TemporaryLoggers is synchronized on BootstrapLogger.class
+ return TemporaryLoggers.INSTANCE.drainTemporaryLoggers();
+ } else {
+ return null;
+ }
+ }
+
+ public static void redirectTemporaryLoggers() {
+ // This call is synchronized on BootstrapLogger.class.
+ final Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors =
+ releaseTemporaryLoggers();
+
+ // We will now reset the logger accessors, triggering the
+ // (possibly lazy) replacement of any temporary logger by the
+ // real logger returned from the loaded LoggerFinder.
+ if (accessors != null) {
+ TemporaryLoggers.resetTemporaryLoggers(accessors);
+ }
+
+ BootstrapExecutors.flush();
+ }
+
+ // Hook for tests which need to wait until pending messages
+ // are processed.
+ static void awaitPendingTasks() {
+ BootstrapExecutors.awaitPendingTasks();
+ }
+ static boolean isAlive() {
+ return BootstrapExecutors.isAlive();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/DefaultLoggerFinder.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.logger;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.ref.ReferenceQueue;
+import java.util.Collection;
+import java.util.ResourceBundle;
+
+/**
+ * Internal Service Provider Interface (SPI) that makes it possible to use
+ * {@code java.util.logging} as backend when the {@link
+ * sun.util.logging.internal.LoggingProviderImpl
+ * sun.util.logging.internal.LoggingProviderImpl} is present.
+ * <p>
+ * The JDK default implementation of the {@link LoggerFinder} will
+ * attempt to locate and load an {@linkplain
+ * java.util.ServiceLoader#loadInstalled(java.lang.Class) installed}
+ * implementation of the {@code DefaultLoggerFinder}. If {@code java.util.logging}
+ * is present, this will usually resolve to an instance of {@link
+ * sun.util.logging.internal.LoggingProviderImpl sun.util.logging.internal.LoggingProviderImpl}.
+ * Otherwise, if no concrete service provider is declared for
+ * {@code DefaultLoggerFinder}, the default implementation provided by this class
+ * will be used.
+ * <p>
+ * When the {@link sun.util.logging.internal.LoggingProviderImpl
+ * sun.util.logging.internal.LoggingProviderImpl} is not present then the
+ * default implementation provided by this class is to use a simple logger
+ * that will log messages whose level is INFO and above to the console.
+ * These simple loggers are not configurable.
+ * <p>
+ * When configuration is needed, an application should either link with
+ * {@code java.util.logging} - and use the {@code java.util.logging} for
+ * configuration, or link with {@link LoggerFinder another implementation}
+ * of the {@link LoggerFinder}
+ * that provides the necessary configuration.
+ *
+ * @apiNote Programmers are not expected to call this class directly.
+ * Instead they should rely on the static methods defined by {@link
+ * java.lang.System java.lang.System} or {@link sun.util.logging.PlatformLogger
+ * sun.util.logging.PlatformLogger}.
+ *
+ * @see java.lang.System.LoggerFinder
+ * @see jdk.internal.logger
+ * @see sun.util.logging.internal
+ *
+ */
+public class DefaultLoggerFinder extends LoggerFinder {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+
+ /**
+ * Creates a new instance of DefaultLoggerFinder.
+ * @throws SecurityException if the calling code does not have the
+ * {@code RuntimePermission("loggerFinder")}
+ */
+ protected DefaultLoggerFinder() {
+ this(checkPermission());
+ }
+
+ private DefaultLoggerFinder(Void unused) {
+ // nothing to do.
+ }
+
+ private static Void checkPermission() {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ return null;
+ }
+
+ // SharedLoggers is a default cache of loggers used when JUL is not
+ // present - in that case we use instances of SimpleConsoleLogger which
+ // cannot be directly configure through public APIs.
+ //
+ // We can therefore afford to simply maintain two domains - one for the
+ // system, and one for the application.
+ //
+ static final class SharedLoggers {
+ private final Map<String, Reference<Logger>> loggers =
+ new HashMap<>();
+ private final ReferenceQueue<Logger> queue = new ReferenceQueue<>();
+
+ synchronized Logger get(Function<String, Logger> loggerSupplier, final String name) {
+ Reference<? extends Logger> ref = loggers.get(name);
+ Logger w = ref == null ? null : ref.get();
+ if (w == null) {
+ w = loggerSupplier.apply(name);
+ loggers.put(name, new WeakReference<>(w, queue));
+ }
+
+ // Remove stale mapping...
+ Collection<Reference<Logger>> values = null;
+ while ((ref = queue.poll()) != null) {
+ if (values == null) values = loggers.values();
+ values.remove(ref);
+ }
+ return w;
+ }
+
+
+ final static SharedLoggers system = new SharedLoggers();
+ final static SharedLoggers application = new SharedLoggers();
+ }
+
+ @Override
+ public final Logger getLogger(String name, /* Module */ Class<?> caller) {
+ checkPermission();
+ return demandLoggerFor(name, caller);
+ }
+
+ @Override
+ public final Logger getLocalizedLogger(String name, ResourceBundle bundle,
+ /* Module */ Class<?> caller) {
+ return super.getLocalizedLogger(name, bundle, caller);
+ }
+
+
+
+ /**
+ * Returns a {@link Logger logger} suitable for the caller usage.
+ *
+ * @implSpec The default implementation for this method is to return a
+ * simple logger that will print all messages of INFO level and above
+ * to the console. That simple logger is not configurable.
+ *
+ * @param name The name of the logger.
+ * @param caller The class on behalf of which the logger is created.
+ * @return A {@link Logger logger} suitable for the application usage.
+ * @throws SecurityException if the calling code does not have the
+ * {@code RuntimePermission("loggerFinder")}.
+ */
+ protected Logger demandLoggerFor(String name, /* Module */ Class<?> caller) {
+ checkPermission();
+ if (caller.getClassLoader() == null) {
+ return SharedLoggers.system.get(SimpleConsoleLogger::makeSimpleLogger, name);
+ } else {
+ return SharedLoggers.application.get(SimpleConsoleLogger::makeSimpleLogger, name);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.logger;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.function.BiFunction;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+import sun.misc.VM;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * This class is a factory for Lazy Loggers; only system loggers can be
+ * Lazy Loggers.
+ */
+public final class LazyLoggers {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+
+ private LazyLoggers() {
+ throw new InternalError();
+ }
+
+ /**
+ * This class is used to hold the factories that a Lazy Logger will use
+ * to create (or map) its wrapped logger.
+ * @param <L> {@link Logger} or a subclass of {@link Logger}.
+ */
+ private static final class LazyLoggerFactories<L extends Logger> {
+
+ /**
+ * A factory method to create an SPI logger.
+ * Usually, this will be something like LazyLoggers::getSystemLogger.
+ */
+ final BiFunction<String, Class<?>, L> loggerSupplier;
+
+
+ public LazyLoggerFactories(BiFunction<String, Class<?>, L> loggerSupplier) {
+ this(Objects.requireNonNull(loggerSupplier),
+ (Void)null);
+ }
+
+ private LazyLoggerFactories(BiFunction<String, Class<?>, L> loggerSupplier,
+ Void unused) {
+ this.loggerSupplier = loggerSupplier;
+ }
+
+ }
+
+ static interface LoggerAccessor {
+ /**
+ * The logger name.
+ * @return The name of the logger that is / will be lazily created.
+ */
+ public String getLoggerName();
+
+ /**
+ * Returns the wrapped logger object.
+ * @return the wrapped logger object.
+ */
+ public Logger wrapped();
+
+ /**
+ * A PlatformLogger.Bridge view of the wrapped logger object.
+ * @return A PlatformLogger.Bridge view of the wrapped logger object.
+ */
+ public PlatformLogger.Bridge platform();
+ }
+
+ /**
+ * The LazyLoggerAccessor class holds all the logic that delays the creation
+ * of the SPI logger until such a time that the VM is booted and the logger
+ * is actually used for logging.
+ *
+ * This class uses the services of the BootstrapLogger class to instantiate
+ * temporary loggers if appropriate.
+ */
+ static final class LazyLoggerAccessor implements LoggerAccessor {
+
+ // The factories that will be used to create the logger lazyly
+ final LazyLoggerFactories<? extends Logger> factories;
+
+ // We need to pass the actual caller when creating the logger.
+ private final WeakReference<Class<?>> callerRef;
+
+ // The name of the logger that will be created lazyly
+ final String name;
+ // The plain logger SPI object - null until it is accessed for the
+ // first time.
+ private volatile Logger w;
+ // A PlatformLogger.Bridge view of w.
+ private volatile PlatformLogger.Bridge p;
+
+
+ private LazyLoggerAccessor(String name,
+ LazyLoggerFactories<? extends Logger> factories,
+ Class<?> caller) {
+ this(Objects.requireNonNull(name), Objects.requireNonNull(factories),
+ Objects.requireNonNull(caller), null);
+ }
+
+ private LazyLoggerAccessor(String name,
+ LazyLoggerFactories<? extends Logger> factories,
+ Class<?> caller, Void unused) {
+ this.name = name;
+ this.factories = factories;
+ this.callerRef = new WeakReference<Class<?>>(caller);
+ }
+
+ /**
+ * The logger name.
+ * @return The name of the logger that is / will be lazily created.
+ */
+ @Override
+ public String getLoggerName() {
+ return name;
+ }
+
+ // must be called in synchronized block
+ // set wrapped logger if not set
+ private void setWrappedIfNotSet(Logger wrapped) {
+ if (w == null) {
+ w = wrapped;
+ }
+ }
+
+ /**
+ * Returns the logger SPI object, creating it if 'w' is still null.
+ * @return the logger SPI object.
+ */
+ public Logger wrapped() {
+ Logger wrapped = w;
+ if (wrapped != null) return wrapped;
+ // Wrapped logger not created yet: create it.
+ // BootstrapLogger has the logic to decide whether to invoke the
+ // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger)
+ // logger.
+ wrapped = BootstrapLogger.getLogger(this);
+ synchronized(this) {
+ // if w has already been in between, simply drop 'wrapped'.
+ setWrappedIfNotSet(wrapped);
+ return w;
+ }
+ }
+
+ /**
+ * A PlatformLogger.Bridge view of the wrapped logger.
+ * @return A PlatformLogger.Bridge view of the wrapped logger.
+ */
+ public PlatformLogger.Bridge platform() {
+ // We can afford to return the platform view of the previous
+ // logger - if that view is not null.
+ // Because that view will either be the BootstrapLogger, which
+ // will redirect to the new wrapper properly, or the temporary
+ // logger - which in effect is equivalent to logging something
+ // just before the application initialized LogManager.
+ PlatformLogger.Bridge platform = p;
+ if (platform != null) return platform;
+ synchronized (this) {
+ if (w != null) {
+ if (p == null) p = PlatformLogger.Bridge.convert(w);
+ return p;
+ }
+ }
+ // If we reach here it means that the wrapped logger may not
+ // have been created yet: attempt to create it.
+ // BootstrapLogger has the logic to decide whether to invoke the
+ // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger)
+ // logger.
+ final Logger wrapped = BootstrapLogger.getLogger(this);
+ synchronized(this) {
+ // if w has already been set, simply drop 'wrapped'.
+ setWrappedIfNotSet(wrapped);
+ if (p == null) p = PlatformLogger.Bridge.convert(w);
+ return p;
+ }
+ }
+
+ /**
+ * Makes this accessor release a temporary logger.
+ * This method is called
+ * by BootstrapLogger when JUL is the default backend and LogManager
+ * is initialized, in order to replace temporary SimpleConsoleLoggers by
+ * real JUL loggers. See BootstrapLogger for more details.
+ * If {@code replace} is {@code true}, then this method will force
+ * the accessor to eagerly recreate its wrapped logger.
+ * Note: passing {@code replace=false} is no guarantee that the
+ * method will not actually replace the released logger.
+ * @param temporary The temporary logger too be released.
+ * @param replace Whether the released logger should be eagerly
+ * replaced.
+ */
+ void release(SimpleConsoleLogger temporary, boolean replace) {
+ PlatformLogger.ConfigurableBridge.LoggerConfiguration conf =
+ PlatformLogger.ConfigurableBridge.getLoggerConfiguration(temporary);
+ PlatformLogger.Level level = conf != null
+ ? conf.getPlatformLevel()
+ : null;
+ synchronized (this) {
+ if (this.w == temporary) {
+ this.w = null; this.p = null;
+ }
+ }
+ PlatformLogger.Bridge platform = replace || level != null
+ ? this.platform() : null;
+
+ if (level != null) {
+ conf = (platform != null && platform != temporary)
+ ? PlatformLogger.ConfigurableBridge.getLoggerConfiguration(platform)
+ : null;
+ if (conf != null) conf.setPlatformLevel(level);
+ }
+ }
+
+ /**
+ * Replace 'w' by the real SPI logger and flush the log messages pending
+ * in the temporary 'bootstrap' Logger. Called by BootstrapLogger when
+ * this accessor's bootstrap logger is accessed and BootstrapLogger
+ * notices that the VM is no longer booting.
+ * @param bootstrap This accessor's bootstrap logger (usually this is 'w').
+ */
+ Logger getConcreteLogger(BootstrapLogger bootstrap) {
+ assert VM.isBooted();
+ synchronized(this) {
+ // another thread may have already invoked flush()
+ if (this.w == bootstrap) {
+ this.w = null; this.p = null;
+ }
+ }
+ return this.wrapped();
+ }
+
+ PlatformLogger.Bridge getConcretePlatformLogger(BootstrapLogger bootstrap) {
+ assert VM.isBooted();
+ synchronized(this) {
+ // another thread may have already invoked flush()
+ if (this.w == bootstrap) {
+ this.w = null; this.p = null;
+ }
+ }
+ return this.platform();
+ }
+
+ // Creates the wrapped logger by invoking the SPI.
+ Logger createLogger() {
+ final Class<?> caller = callerRef.get();
+ if (caller == null) {
+ throw new IllegalStateException("The class for which this logger"
+ + " was created has been garbage collected");
+ }
+ return this.factories.loggerSupplier.apply(name, caller);
+ }
+
+ /**
+ * Creates a new lazy logger accessor for the named logger. The given
+ * factories will be use when it becomes necessary to actually create
+ * the logger.
+ * @param <T> An interface that extends {@link Logger}.
+ * @param name The logger name.
+ * @param factories The factories that should be used to create the
+ * wrapped logger.
+ * @return A new LazyLoggerAccessor.
+ */
+ public static LazyLoggerAccessor makeAccessor(String name,
+ LazyLoggerFactories<? extends Logger> factories, Class<?> caller) {
+ return new LazyLoggerAccessor(name, factories, caller);
+ }
+
+ }
+
+ /**
+ * An implementation of {@link Logger} that redirects all calls to a wrapped
+ * instance of {@code Logger}.
+ */
+ private static class LazyLoggerWrapper
+ extends AbstractLoggerWrapper<Logger> {
+
+ final LoggerAccessor loggerAccessor;
+
+ public LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier) {
+ this(Objects.requireNonNull(loggerSinkSupplier), (Void)null);
+ }
+
+ private LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier,
+ Void unused) {
+ this.loggerAccessor = loggerSinkSupplier;
+ }
+
+ @Override
+ final Logger wrapped() {
+ return loggerAccessor.wrapped();
+ }
+
+ @Override
+ PlatformLogger.Bridge platformProxy() {
+ return loggerAccessor.platform();
+ }
+
+ }
+
+ // Do not expose this outside of this package.
+ private static volatile LoggerFinder provider = null;
+ private static LoggerFinder accessLoggerFinder() {
+ if (provider == null) {
+ // no need to lock: it doesn't matter if we call
+ // getLoggerFinder() twice - since LoggerFinder already caches
+ // the result.
+ // This is just an optimization to avoid the cost of calling
+ // doPrivileged every time.
+ final SecurityManager sm = System.getSecurityManager();
+ provider = sm == null ? LoggerFinder.getLoggerFinder() :
+ AccessController.doPrivileged(
+ (PrivilegedAction<LoggerFinder>)LoggerFinder::getLoggerFinder);
+ }
+ return provider;
+ }
+
+ // Avoid using lambda here as lazy loggers could be created early
+ // in the bootstrap sequence...
+ private static final BiFunction<String, Class<?>, Logger> loggerSupplier =
+ new BiFunction<>() {
+ @Override
+ public Logger apply(String name, Class<?> caller) {
+ return LazyLoggers.getLoggerFromFinder(name, caller);
+ }
+ };
+
+ private static final LazyLoggerFactories<Logger> factories =
+ new LazyLoggerFactories<>(loggerSupplier);
+
+
+
+ // A concrete implementation of Logger that delegates to a System.Logger,
+ // but only creates the System.Logger instance lazily when it's used for
+ // the first time.
+ // The JdkLazyLogger uses a LazyLoggerAccessor objects, which relies
+ // on the logic embedded in BootstrapLogger to avoid loading the concrete
+ // logger provider until the VM has finished booting.
+ //
+ private static final class JdkLazyLogger extends LazyLoggerWrapper {
+ JdkLazyLogger(String name, Class<?> caller) {
+ this(LazyLoggerAccessor.makeAccessor(name, factories, caller),
+ (Void)null);
+ }
+ private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) {
+ super(holder);
+ }
+ }
+
+ /**
+ * Gets a logger from the LoggerFinder. Creates the actual concrete
+ * logger.
+ * @param name name of the logger
+ * @param caller class on behalf of which the logger is created
+ * @return The logger returned by the LoggerFinder.
+ */
+ static Logger getLoggerFromFinder(String name, Class<?> caller) {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm == null) {
+ return accessLoggerFinder().getLogger(name, caller);
+ } else {
+ return AccessController.doPrivileged((PrivilegedAction<Logger>)
+ () -> {return accessLoggerFinder().getLogger(name, caller);},
+ null, LOGGERFINDER_PERMISSION);
+ }
+ }
+
+ /**
+ * Returns a (possibly lazy) Logger for the caller.
+ *
+ * @param name the logger name
+ * @param caller The class on behalf of which the logger is created.
+ * If the caller is not loaded from the Boot ClassLoader,
+ * the LoggerFinder is accessed and the logger returned
+ * by {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class)}
+ * is returned to the caller directly.
+ * Otherwise, the logger returned by
+ * {@link #getLazyLogger(java.lang.String, java.lang.Class)}
+ * is returned to the caller.
+ *
+ * @return a (possibly lazy) Logger instance.
+ */
+ public static final Logger getLogger(String name, Class<?> caller) {
+ if (caller.getClassLoader() == null) {
+ return getLazyLogger(name, caller);
+ } else {
+ return getLoggerFromFinder(name, caller);
+ }
+ }
+
+ /**
+ * Returns a (possibly lazy) Logger suitable for system classes.
+ * Whether the returned logger is lazy or not depend on the result
+ * returned by {@link BootstrapLogger#useLazyLoggers()}.
+ *
+ * @param name the logger name
+ * @param caller the class on behalf of which the logger is created.
+ * @return a (possibly lazy) Logger instance.
+ */
+ public static final Logger getLazyLogger(String name, Class<?> caller) {
+
+ // BootstrapLogger has the logic to determine whether a LazyLogger
+ // should be used. Usually, it is worth it only if:
+ // - the VM is not yet booted
+ // - or, the backend is JUL and there is no configuration
+ // - or, the backend is a custom backend, as we don't know what
+ // that is going to load...
+ // So if for instance the VM is booted and we use JUL with a custom
+ // configuration, we're not going to delay the creation of loggers...
+ final boolean useLazyLogger = BootstrapLogger.useLazyLoggers();
+ if (useLazyLogger) {
+ return new JdkLazyLogger(name, caller);
+ } else {
+ // Directly invoke the LoggerFinder.
+ return getLoggerFromFinder(name, caller);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LocalizedLoggerWrapper.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.logger;
+
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+
+/**
+ * This implementation of {@link Logger} redirects all logging method
+ * calls to calls to {@code log(Level, String, ResourceBundle, ...)}
+ * methods, passing the Logger's ResourceBundle as parameter.
+ * So for instance a call to {@link Logger#log(Level, String)
+ * log(Level.INFO, msg)} will be redirected
+ * to a call to {@link #log(java.lang.System.Logger.Level,
+ * java.util.ResourceBundle, java.lang.String, java.lang.Object...)
+ * this.log(Level.INFO, this.bundle, msg, (Object[]) null)}.
+ * <p>
+ * Note that methods that take a {@link Supplier Supplier<String>}
+ * or an Object are not redirected. It is assumed that a string returned
+ * by a {@code Supplier<String>} is already localized, or cannot be localized.
+ *
+ * @param <L> Type of the wrapped Logger: {@code Logger} or an
+ * extension of the {@code Logger} interface.
+ */
+public class LocalizedLoggerWrapper<L extends Logger> extends LoggerWrapper<L> {
+
+ private final ResourceBundle bundle;
+
+ public LocalizedLoggerWrapper(L wrapped, ResourceBundle bundle) {
+ super(wrapped);
+ this.bundle = bundle;
+ }
+
+ public final ResourceBundle getBundle() {
+ return bundle;
+ }
+
+ // We assume that messages returned by Supplier<String> and Object are
+ // either already localized or not localizable. To be evaluated.
+
+ // -----------------------------------------------------------------
+ // Generic methods taking a Level as parameter
+ // -----------------------------------------------------------------
+
+ @Override
+ public final void log(Level level, String msg) {
+ log(level, bundle, msg, (Object[]) null);
+ }
+
+ @Override
+ public final void log(Level level,
+ String msg, Throwable thrown) {
+ log(level, bundle, msg, thrown);
+ }
+
+ @Override
+ public final void log(Level level,
+ String format, Object... params) {
+ log(level, bundle, format, params);
+ }
+
+ @Override
+ public final void log(Level level, Object obj) {
+ wrapped.log(level, obj);
+ }
+
+ @Override
+ public final void log(Level level, Supplier<String> msgSupplier) {
+ wrapped.log(level, msgSupplier);
+ }
+
+ @Override
+ public final void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ wrapped.log(level, msgSupplier, thrown);
+ }
+
+ @Override
+ public final void log(Level level, ResourceBundle bundle, String format, Object... params) {
+ wrapped.log(level, bundle, format, params);
+ }
+
+ @Override
+ public final void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ wrapped.log(level, bundle, key, thrown);
+ }
+
+ @Override
+ public final boolean isLoggable(Level level) {
+ return wrapped.isLoggable(level);
+ }
+
+ // Override methods from PlatformLogger.Bridge that don't take a
+ // resource bundle...
+
+ @Override
+ public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ String key) {
+ logrb(level, sourceClass, sourceMethod, bundle, key, (Object[]) null);
+ }
+
+ @Override
+ public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ String key, Throwable thrown) {
+ logrb(level, sourceClass, sourceMethod, bundle, key, thrown);
+ }
+
+ @Override
+ public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ String key, Object... params) {
+ logrb(level, sourceClass, sourceMethod, bundle, key, params);
+ }
+
+ @Override
+ public final void log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable thrown) {
+ logrb(level, bundle, msg, thrown);
+ }
+
+ @Override
+ public final void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+ logrb(level, bundle, msg, (Object[]) null);
+ }
+
+ @Override
+ public final void log(sun.util.logging.PlatformLogger.Level level, String format, Object... params) {
+ logrb(level, bundle, format, params);
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.logger;
+
+import java.io.FilePermission;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import sun.security.util.SecurityConstants;
+
+/**
+ * Helper class used to load the {@link java.lang.System.LoggerFinder}.
+ */
+public final class LoggerFinderLoader {
+ private static volatile System.LoggerFinder service;
+ private static final Object lock = new int[0];
+ static final Permission CLASSLOADER_PERMISSION =
+ SecurityConstants.GET_CLASSLOADER_PERMISSION;
+ static final Permission READ_PERMISSION =
+ new FilePermission("<<ALL FILES>>",
+ SecurityConstants.FILE_READ_ACTION);
+ public static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+
+ // This is used to control how the LoggerFinderLoader handles
+ // errors when instantiating the LoggerFinder provider.
+ // ERROR => throws ServiceConfigurationError
+ // WARNING => Do not fail, use plain default (simple logger) implementation,
+ // prints warning on console. (this is the default)
+ // DEBUG => Do not fail, use plain default (simple logger) implementation,
+ // prints warning and exception stack trace on console.
+ // QUIET => Do not fail and stay silent.
+ private static enum ErrorPolicy { ERROR, WARNING, DEBUG, QUIET };
+
+ // This class is static and cannot be instantiated.
+ private LoggerFinderLoader() {
+ throw new InternalError("LoggerFinderLoader cannot be instantiated");
+ }
+
+
+ // Return the loaded LoggerFinder, or load it if not already loaded.
+ private static System.LoggerFinder service() {
+ if (service != null) return service;
+ synchronized(lock) {
+ if (service != null) return service;
+ service = loadLoggerFinder();
+ }
+ // Since the LoggerFinder is already loaded - we can stop using
+ // temporary loggers.
+ BootstrapLogger.redirectTemporaryLoggers();
+ return service;
+ }
+
+ // Get configuration error policy
+ private static ErrorPolicy configurationErrorPolicy() {
+ final PrivilegedAction<String> getConfigurationErrorPolicy =
+ () -> System.getProperty("jdk.logger.finder.error");
+ String errorPolicy = AccessController.doPrivileged(getConfigurationErrorPolicy);
+ if (errorPolicy == null || errorPolicy.isEmpty()) {
+ return ErrorPolicy.WARNING;
+ }
+ try {
+ return ErrorPolicy.valueOf(errorPolicy.toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException x) {
+ return ErrorPolicy.WARNING;
+ }
+ }
+
+ // Whether multiple provider should be considered as an error.
+ // This is further submitted to the configuration error policy.
+ private static boolean ensureSingletonProvider() {
+ final PrivilegedAction<Boolean> ensureSingletonProvider =
+ () -> Boolean.getBoolean("jdk.logger.finder.singleton");
+ return AccessController.doPrivileged(ensureSingletonProvider);
+ }
+
+ private static Iterator<System.LoggerFinder> findLoggerFinderProviders() {
+ final Iterator<System.LoggerFinder> iterator;
+ if (System.getSecurityManager() == null) {
+ iterator = ServiceLoader.load(System.LoggerFinder.class,
+ ClassLoader.getSystemClassLoader()).iterator();
+ } else {
+ final PrivilegedAction<Iterator<System.LoggerFinder>> pa =
+ () -> ServiceLoader.load(System.LoggerFinder.class,
+ ClassLoader.getSystemClassLoader()).iterator();
+ iterator = AccessController.doPrivileged(pa, null,
+ LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION,
+ READ_PERMISSION);
+ }
+ return iterator;
+ }
+
+ // Loads the LoggerFinder using ServiceLoader. If no LoggerFinder
+ // is found returns the default (possibly JUL based) implementation
+ private static System.LoggerFinder loadLoggerFinder() {
+ System.LoggerFinder result;
+ try {
+ // Iterator iterates with the access control context stored
+ // at ServiceLoader creation time.
+ final Iterator<System.LoggerFinder> iterator =
+ findLoggerFinderProviders();
+ if (iterator.hasNext()) {
+ result = iterator.next();
+ if (iterator.hasNext() && ensureSingletonProvider()) {
+ throw new ServiceConfigurationError(
+ "More than on LoggerFinder implementation");
+ }
+ } else {
+ result = loadDefaultImplementation();
+ }
+ } catch (Error | RuntimeException x) {
+ // next caller will get the plain default impl (not linked
+ // to java.util.logging)
+ service = result = new DefaultLoggerFinder();
+ ErrorPolicy errorPolicy = configurationErrorPolicy();
+ if (errorPolicy == ErrorPolicy.ERROR) {
+ // rethrow any exception as a ServiceConfigurationError.
+ if (x instanceof Error) {
+ throw x;
+ } else {
+ throw new ServiceConfigurationError(
+ "Failed to instantiate LoggerFinder provider; Using default.", x);
+ }
+ } else if (errorPolicy != ErrorPolicy.QUIET) {
+ // if QUIET just silently use the plain default impl
+ // otherwise, log a warning, possibly adding the exception
+ // stack trace (if DEBUG is specified).
+ SimpleConsoleLogger logger =
+ new SimpleConsoleLogger("jdk.internal.logger", false);
+ logger.log(System.Logger.Level.WARNING,
+ "Failed to instantiate LoggerFinder provider; Using default.");
+ if (errorPolicy == ErrorPolicy.DEBUG) {
+ logger.log(System.Logger.Level.WARNING,
+ "Exception raised trying to instantiate LoggerFinder", x);
+ }
+ }
+ }
+ return result;
+ }
+
+ private static System.LoggerFinder loadDefaultImplementation() {
+ final SecurityManager sm = System.getSecurityManager();
+ final Iterator<DefaultLoggerFinder> iterator;
+ if (sm == null) {
+ iterator = ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator();
+ } else {
+ // We use limited do privileged here - the minimum set of
+ // permissions required to 'see' the META-INF/services resources
+ // seems to be CLASSLOADER_PERMISSION and READ_PERMISSION.
+ // Note that do privileged is required because
+ // otherwise the SecurityManager will prevent the ServiceLoader
+ // from seeing the installed provider.
+ PrivilegedAction<Iterator<DefaultLoggerFinder>> pa = () ->
+ ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator();
+ iterator = AccessController.doPrivileged(pa, null,
+ LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION,
+ READ_PERMISSION);
+ }
+ DefaultLoggerFinder result = null;
+ try {
+ // Iterator iterates with the access control context stored
+ // at ServiceLoader creation time.
+ if (iterator.hasNext()) {
+ result = iterator.next();
+ }
+ } catch (RuntimeException x) {
+ throw new ServiceConfigurationError(
+ "Failed to instantiate default LoggerFinder", x);
+ }
+ if (result == null) {
+ result = new DefaultLoggerFinder();
+ }
+ return result;
+ }
+
+ public static System.LoggerFinder getLoggerFinder() {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ return service();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerWrapper.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.logger;
+
+import java.util.Objects;
+import java.lang.System.Logger;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * An implementation of {@link Logger} that redirects all calls to a wrapped
+ instance of Logger.
+ *
+ * @param <L> Type of the wrapped Logger: {@code Logger} or an
+ * extension of that interface.
+ */
+public class LoggerWrapper<L extends Logger> extends AbstractLoggerWrapper<L> {
+
+ final L wrapped;
+ final PlatformLogger.Bridge platformProxy;
+
+ public LoggerWrapper(L wrapped) {
+ this(Objects.requireNonNull(wrapped), (Void)null);
+ }
+
+ LoggerWrapper(L wrapped, Void unused) {
+ this.wrapped = wrapped;
+ this.platformProxy = (wrapped instanceof PlatformLogger.Bridge) ?
+ (PlatformLogger.Bridge) wrapped : null;
+ }
+
+ @Override
+ public final L wrapped() {
+ return wrapped;
+ }
+
+ @Override
+ public final PlatformLogger.Bridge platformProxy() {
+ return platformProxy;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.logger;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.time.ZonedDateTime;
+import java.util.ResourceBundle;
+import java.util.function.Function;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.function.Supplier;
+import jdk.internal.misc.JavaLangAccess;
+import jdk.internal.misc.SharedSecrets;
+import sun.util.logging.PlatformLogger;
+import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration;
+
+/**
+ * A simple console logger to emulate the behavior of JUL loggers when
+ * in the default configuration. SimpleConsoleLoggers are also used when
+ * JUL is not present and no DefaultLoggerFinder is installed.
+ */
+public class SimpleConsoleLogger extends LoggerConfiguration
+ implements Logger, PlatformLogger.Bridge, PlatformLogger.ConfigurableBridge {
+
+ static final PlatformLogger.Level DEFAULT_LEVEL = PlatformLogger.Level.INFO;
+
+ final String name;
+ volatile PlatformLogger.Level level;
+ final boolean usePlatformLevel;
+ SimpleConsoleLogger(String name, boolean usePlatformLevel) {
+ this.name = name;
+ this.usePlatformLevel = usePlatformLevel;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ private Enum<?> logLevel(PlatformLogger.Level level) {
+ return usePlatformLevel ? level : level.systemLevel();
+ }
+
+ private Enum<?> logLevel(Level level) {
+ return usePlatformLevel ? PlatformLogger.toPlatformLevel(level) : level;
+ }
+
+ // ---------------------------------------------------
+ // From Logger
+ // ---------------------------------------------------
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return isLoggable(PlatformLogger.toPlatformLevel(level));
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ if (isLoggable(level)) {
+ if (bundle != null) {
+ key = bundle.getString(key);
+ }
+ publish(getCallerInfo(), logLevel(level), key, thrown);
+ }
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+ if (isLoggable(level)) {
+ if (bundle != null) {
+ format = bundle.getString(format);
+ }
+ publish(getCallerInfo(), logLevel(level), format, params);
+ }
+ }
+
+ // ---------------------------------------------------
+ // From PlatformLogger.Bridge
+ // ---------------------------------------------------
+
+ @Override
+ public boolean isLoggable(PlatformLogger.Level level) {
+ final PlatformLogger.Level effectiveLevel = effectiveLevel();
+ return level != PlatformLogger.Level.OFF
+ && level.ordinal() >= effectiveLevel.ordinal();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return level != PlatformLogger.Level.OFF;
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(), logLevel(level), msg);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(), logLevel(level), msg, thrown);
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, String msg, Object... params) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(), logLevel(level), msg, params);
+ }
+ }
+
+ private PlatformLogger.Level effectiveLevel() {
+ if (level == null) return DEFAULT_LEVEL;
+ return level;
+ }
+
+ @Override
+ public PlatformLogger.Level getPlatformLevel() {
+ return level;
+ }
+
+ @Override
+ public void setPlatformLevel(PlatformLogger.Level newLevel) {
+ level = newLevel;
+ }
+
+ @Override
+ public LoggerConfiguration getLoggerConfiguration() {
+ return this;
+ }
+
+ /**
+ * Default platform logging support - output messages to System.err -
+ * equivalent to ConsoleHandler with SimpleFormatter.
+ */
+ static PrintStream outputStream() {
+ return System.err;
+ }
+
+ // Returns the caller's class and method's name; best effort
+ // if cannot infer, return the logger's name.
+ private String getCallerInfo() {
+ String sourceClassName = null;
+ String sourceMethodName = null;
+
+ JavaLangAccess access = SharedSecrets.getJavaLangAccess();
+ Throwable throwable = new Throwable();
+ int depth = access.getStackTraceDepth(throwable);
+
+ String logClassName = "sun.util.logging.PlatformLogger";
+ String simpleLoggerClassName = "jdk.internal.logger.SimpleConsoleLogger";
+ 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.
+ final StackTraceElement frame =
+ access.getStackTraceElement(throwable, ix);
+ final String cname = frame.getClassName();
+ if (lookingForLogger) {
+ // Skip all frames until we have found the first logger frame.
+ if (cname.equals(logClassName) || cname.equals(simpleLoggerClassName)) {
+ lookingForLogger = false;
+ }
+ } else {
+ if (skipLoggingFrame(cname)) continue;
+ if (!cname.equals(logClassName) && !cname.equals(simpleLoggerClassName)) {
+ // We've found the relevant frame.
+ sourceClassName = cname;
+ sourceMethodName = frame.getMethodName();
+ break;
+ }
+ }
+ }
+
+ if (sourceClassName != null) {
+ return sourceClassName + " " + sourceMethodName;
+ } else {
+ return name;
+ }
+ }
+
+ private String getCallerInfo(String sourceClassName, String sourceMethodName) {
+ if (sourceClassName == null) return name;
+ if (sourceMethodName == null) return sourceClassName;
+ return sourceClassName + " " + sourceMethodName;
+ }
+
+ private String toString(Throwable thrown) {
+ String throwable = "";
+ if (thrown != null) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ pw.println();
+ thrown.printStackTrace(pw);
+ pw.close();
+ throwable = sw.toString();
+ }
+ return throwable;
+ }
+
+ private synchronized String format(Enum<?> level,
+ String msg, Throwable thrown, String callerInfo) {
+
+ ZonedDateTime zdt = ZonedDateTime.now();
+ String throwable = toString(thrown);
+
+ return String.format(Formatting.formatString,
+ zdt,
+ callerInfo,
+ name,
+ level.name(),
+ msg,
+ throwable);
+ }
+
+ // publish accepts both PlatformLogger Levels and LoggerFinder Levels.
+ private void publish(String callerInfo, Enum<?> level, String msg) {
+ outputStream().print(format(level, msg, null, callerInfo));
+ }
+ // publish accepts both PlatformLogger Levels and LoggerFinder Levels.
+ private void publish(String callerInfo, Enum<?> level, String msg, Throwable thrown) {
+ outputStream().print(format(level, msg, thrown, callerInfo));
+ }
+ // publish accepts both PlatformLogger Levels and LoggerFinder Levels.
+ private void publish(String callerInfo, Enum<?> level, String msg, Object... params) {
+ msg = params == null || params.length == 0 ? msg
+ : Formatting.formatMessage(msg, params);
+ outputStream().print(format(level, msg, null, callerInfo));
+ }
+
+ public static SimpleConsoleLogger makeSimpleLogger(String name, boolean usePlatformLevel) {
+ return new SimpleConsoleLogger(name, usePlatformLevel);
+ }
+
+ public static SimpleConsoleLogger makeSimpleLogger(String name) {
+ return new SimpleConsoleLogger(name, false);
+ }
+
+ public static String getSimpleFormat(Function<String, String> defaultPropertyGetter) {
+ return Formatting.getSimpleFormat(defaultPropertyGetter);
+ }
+
+ public static boolean skipLoggingFrame(String cname) {
+ return Formatting.skipLoggingFrame(cname);
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(), logLevel(level), msgSupplier.get());
+ }
+ }
+
+ @Override
+ public void log(PlatformLogger.Level level, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(), logLevel(level), msgSupplier.get(), thrown);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Supplier<String> msgSupplier) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get());
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ String msg, Object... params) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Throwable thrown) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown);
+ }
+ }
+
+ @Override
+ public void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) {
+ if (isLoggable(level)) {
+ publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get(), thrown);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String key, Object... params) {
+ if (isLoggable(level)) {
+ String msg = bundle == null ? key : bundle.getString(key);
+ publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String key, Throwable thrown) {
+ if (isLoggable(level)) {
+ String msg = bundle == null ? key : bundle.getString(key);
+ publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+ String key, Object... params) {
+ if (isLoggable(level)) {
+ String msg = bundle == null ? key : bundle.getString(key);
+ publish(getCallerInfo(), logLevel(level), msg, params);
+ }
+ }
+
+ @Override
+ public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ if (isLoggable(level)) {
+ String msg = bundle == null ? key : bundle.getString(key);
+ publish(getCallerInfo(), logLevel(level), msg, thrown);
+ }
+ }
+
+ private static final class Formatting {
+ static final String DEFAULT_FORMAT =
+ "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n";
+ static final String FORMAT_PROP_KEY =
+ "java.util.logging.SimpleFormatter.format";
+ static final String formatString = getSimpleFormat(null);
+
+ // Make it easier to wrap Logger...
+ static private final String[] skips;
+ static {
+ String additionalPkgs = AccessController.doPrivileged(
+ (PrivilegedAction<String>)
+ () -> System.getProperty("jdk.logger.packages"));
+ skips = additionalPkgs == null ? new String[0] : additionalPkgs.split(",");
+
+ }
+
+ static boolean skipLoggingFrame(String cname) {
+ // skip logging/logger infrastructure
+
+ // fast escape path: all the prefixes below start with 's' or 'j' and
+ // have more than 12 characters.
+ char c = cname.length() < 12 ? 0 : cname.charAt(0);
+ if (c == 's') {
+ // skip internal machinery classes
+ if (cname.startsWith("sun.util.logging.")) return true;
+ if (cname.startsWith("sun.reflect.")) return true;
+ if (cname.startsWith("sun.rmi.runtime.Log")) return true;
+ } else if (c == 'j') {
+ // Message delayed at Bootstrap: no need to go further up.
+ if (cname.startsWith("jdk.internal.logger.BootstrapLogger$LogEvent")) return false;
+ // skip public machinery classes
+ if (cname.startsWith("jdk.internal.logger.")) return true;
+ if (cname.startsWith("java.util.logging.")) return true;
+ if (cname.startsWith("java.lang.System$Logger")) return true;
+ if (cname.startsWith("java.lang.reflect.")) return true;
+ if (cname.startsWith("java.lang.invoke.MethodHandle")) return true;
+ if (cname.startsWith("java.lang.invoke.LambdaForm")) return true;
+ if (cname.startsWith("java.security.AccessController")) return true;
+ }
+
+ // check additional prefixes if any are specified.
+ if (skips.length > 0) {
+ for (int i=0; i<skips.length; i++) {
+ if (!skips[i].isEmpty() && cname.startsWith(skips[i])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ static String getSimpleFormat(Function<String, String> defaultPropertyGetter) {
+ // Using a lambda here causes
+ // jdk/test/java/lang/invoke/lambda/LogGeneratedClassesTest.java
+ // to fail - because that test has a testcase which somehow references
+ // PlatformLogger and counts the number of generated lambda classes
+ // So we explicitely use new PrivilegedAction<String> here.
+ String format =
+ AccessController.doPrivileged(new PrivilegedAction<String>() {
+ @Override
+ public String run() {
+ return System.getProperty(FORMAT_PROP_KEY);
+ }
+ });
+ if (format == null && defaultPropertyGetter != null) {
+ format = defaultPropertyGetter.apply(FORMAT_PROP_KEY);
+ }
+ if (format != null) {
+ try {
+ // validate the user-defined format string
+ String.format(format, ZonedDateTime.now(), "", "", "", "", "");
+ } catch (IllegalArgumentException e) {
+ // illegal syntax; fall back to the default format
+ format = DEFAULT_FORMAT;
+ }
+ } else {
+ format = DEFAULT_FORMAT;
+ }
+ return format;
+ }
+
+
+ // Copied from java.util.logging.Formatter.formatMessage
+ static String formatMessage(String format, Object... parameters) {
+ // Do the formatting.
+ try {
+ if (parameters == null || parameters.length == 0) {
+ // No parameters. Just return format string.
+ return format;
+ }
+ // Is it a java.text style format?
+ // Ideally we could match with
+ // Pattern.compile("\\{\\d").matcher(format).find())
+ // However the cost is 14% higher, so we cheaply check for
+ //
+ boolean isJavaTestFormat = false;
+ final int len = format.length();
+ for (int i=0; i<len-2; i++) {
+ final char c = format.charAt(i);
+ if (c == '{') {
+ final int d = format.charAt(i+1);
+ if (d >= '0' && d <= '9') {
+ isJavaTestFormat = true;
+ break;
+ }
+ }
+ }
+ if (isJavaTestFormat) {
+ return java.text.MessageFormat.format(format, parameters);
+ }
+ return format;
+ } catch (Exception ex) {
+ // Formatting failed: use format string.
+ return format;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/package-info.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+/**
+ * <b>[JDK INTERNAL]</b>
+ * The {@code jdk.internal.logger} package defines an internal provider
+ * whose default naive implementation is replaced by the {@code java.logging}
+ * module when the {@code java.logging} module is present.
+ * <p>
+ * <b>Default Implementation</b>
+ * <p>
+ * The JDK default implementation of the System.LoggerFinder will attempt to
+ * load an installed instance of the {@link jdk.internal.logger.DefaultLoggerFinder}
+ * defined in this package.
+ * When the {@code java.util.logging} package is present, this will usually
+ * resolve to an instance of {@link sun.util.logging.internal.LoggingProviderImpl} -
+ * which provides an implementation of the Logger whose backend is a
+ * {@link java.util.logging.Logger java.util.logging.Logger}.
+ * Configuration can thus be performed by direct access to the regular
+ * {@code java.util.logging} APIs,
+ * using {@link java.util.logging.Logger java.util.logging.Logger} and
+ * {@link java.util.logging.LogManager} to access and configure the backend
+ * Loggers.
+ * <br>
+ * If however {@code java.util.logging} is not linked with the application, then
+ * the default implementation will return a simple logger that will print out
+ * all log messages of INFO level and above to the console ({@code System.err}),
+ * as implemented by the base {@link jdk.internal.logger.DefaultLoggerFinder} class.
+ * <p>
+ * <b>Message Levels and Mapping to java.util.logging</b>
+ * <p>
+ * The {@link java.lang.System.LoggerFinder} class documentation describe how
+ * {@linkplain java.lang.System.Logger.Level System.Logger levels} are mapped
+ * to {@linkplain java.util.logging.Level JUL levels} when {@code
+ * java.util.logging} is the backend.
+ *
+ * @see jdk.internal.logger.DefaultLoggerFinder
+ * @see sun.util.logging.internal.LoggingProviderImpl
+ * @see java.lang.System.LoggerFinder
+ * @see java.lang.System.Logger
+ * @see sun.util.logging.PlatformLogger.Bridge
+ * @see sun.util.logging.internal
+ *
+ * @since 1.9
+ */
+package jdk.internal.logger;
--- a/jdk/src/java.base/share/classes/sun/util/logging/LoggingProxy.java Fri Nov 20 15:34:12 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2009, 2013, 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 sun.util.logging;
-
-/**
- * A proxy interface for the java.util.logging support.
- *
- * @see sun.util.logging.LoggingSupport
- */
-public interface LoggingProxy {
- // Methods to bridge java.util.logging.Logger methods
- public Object getLogger(String name);
-
- public Object getLevel(Object logger);
-
- public void setLevel(Object logger, Object newLevel);
-
- public boolean isLoggable(Object logger, Object level);
-
- public void log(Object logger, Object level, String msg);
-
- public void log(Object logger, Object level, String msg, Throwable t);
-
- public void log(Object logger, Object level, String msg, Object... params);
-
- // Methods to bridge java.util.logging.LoggingMXBean methods
- public java.util.List<String> getLoggerNames();
-
- public String getLoggerLevel(String loggerName);
-
- public void setLoggerLevel(String loggerName, String levelName);
-
- public String getParentLoggerName(String loggerName);
-
- // Methods to bridge Level.parse() and Level.getName() method
- public Object parseLevel(String levelName);
-
- public String getLevelName(Object level);
-
- public int getLevelValue(Object level);
-
- // return the logging property
- public String getProperty(String key);
-}
--- a/jdk/src/java.base/share/classes/sun/util/logging/LoggingSupport.java Fri Nov 20 15:34:12 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
-/*
- * Copyright (c) 2009, 2015, 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 sun.util.logging;
-
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.time.ZonedDateTime;
-
-/**
- * Internal API to support JRE implementation to detect if the java.util.logging
- * support is available but with no dependency on the java.util.logging
- * classes. This LoggingSupport class provides several static methods to
- * access the java.util.logging functionality that requires the caller
- * to ensure that the logging support is {@linkplain #isAvailable available}
- * before invoking it.
- *
- * @see sun.util.logging.PlatformLogger if you want to log messages even
- * if the logging support is not available
- */
-public class LoggingSupport {
- private LoggingSupport() { }
-
- private static final LoggingProxy proxy =
- AccessController.doPrivileged(new PrivilegedAction<LoggingProxy>() {
- public LoggingProxy run() {
- try {
- // create a LoggingProxyImpl instance when
- // java.util.logging classes exist
- Class<?> c = Class.forName("java.util.logging.LoggingProxyImpl", true, null);
- Field f = c.getDeclaredField("INSTANCE");
- f.setAccessible(true);
- return (LoggingProxy) f.get(null);
- } catch (ClassNotFoundException cnf) {
- return null;
- } catch (NoSuchFieldException e) {
- throw new AssertionError(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }});
-
- /**
- * Returns true if java.util.logging support is available.
- */
- public static boolean isAvailable() {
- return proxy != null;
- }
-
- private static void ensureAvailable() {
- if (proxy == null)
- throw new AssertionError("Should not here");
- }
-
- public static java.util.List<String> getLoggerNames() {
- ensureAvailable();
- return proxy.getLoggerNames();
- }
- public static String getLoggerLevel(String loggerName) {
- ensureAvailable();
- return proxy.getLoggerLevel(loggerName);
- }
-
- public static void setLoggerLevel(String loggerName, String levelName) {
- ensureAvailable();
- proxy.setLoggerLevel(loggerName, levelName);
- }
-
- public static String getParentLoggerName(String loggerName) {
- ensureAvailable();
- return proxy.getParentLoggerName(loggerName);
- }
-
- public static Object getLogger(String name) {
- ensureAvailable();
- return proxy.getLogger(name);
- }
-
- public static Object getLevel(Object logger) {
- ensureAvailable();
- return proxy.getLevel(logger);
- }
-
- public static void setLevel(Object logger, Object newLevel) {
- ensureAvailable();
- proxy.setLevel(logger, newLevel);
- }
-
- public static boolean isLoggable(Object logger, Object level) {
- ensureAvailable();
- return proxy.isLoggable(logger,level);
- }
-
- public static void log(Object logger, Object level, String msg) {
- ensureAvailable();
- proxy.log(logger, level, msg);
- }
-
- public static void log(Object logger, Object level, String msg, Throwable t) {
- ensureAvailable();
- proxy.log(logger, level, msg, t);
- }
-
- public static void log(Object logger, Object level, String msg, Object... params) {
- ensureAvailable();
- proxy.log(logger, level, msg, params);
- }
-
- public static Object parseLevel(String levelName) {
- ensureAvailable();
- return proxy.parseLevel(levelName);
- }
-
- public static String getLevelName(Object level) {
- ensureAvailable();
- return proxy.getLevelName(level);
- }
-
- public static int getLevelValue(Object level) {
- ensureAvailable();
- return proxy.getLevelValue(level);
- }
-
- // Since JDK 9, logging uses java.time to get more precise time stamps.
- // It is possible to configure the simple format to print nano seconds (.%1$tN)
- // by specifying:
- // java.util.logging.SimpleFormatter.format=%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS.%1$tN %1$Tp %2$s%n%4$s: %5$s%6$s%n
- // in the logging configuration
- private static final String DEFAULT_FORMAT =
- "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n";
-
- private static final String FORMAT_PROP_KEY = "java.util.logging.SimpleFormatter.format";
- public static String getSimpleFormat() {
- return getSimpleFormat(true);
- }
-
- // useProxy if true will cause initialization of
- // java.util.logging and read its configuration
- static String getSimpleFormat(boolean useProxy) {
- String format =
- AccessController.doPrivileged(
- new PrivilegedAction<String>() {
- public String run() {
- return System.getProperty(FORMAT_PROP_KEY);
- }
- });
-
- if (useProxy && proxy != null && format == null) {
- format = proxy.getProperty(FORMAT_PROP_KEY);
- }
-
- if (format != null) {
- try {
- // validate the user-defined format string
- String.format(format, ZonedDateTime.now(), "", "", "", "", "");
- } catch (IllegalArgumentException e) {
- // illegal syntax; fall back to the default format
- format = DEFAULT_FORMAT;
- }
- } else {
- format = DEFAULT_FORMAT;
- }
- return format;
- }
-
-}
--- a/jdk/src/java.base/share/classes/sun/util/logging/PlatformLogger.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/util/logging/PlatformLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -27,20 +27,13 @@
package sun.util.logging;
import java.lang.ref.WeakReference;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.time.Clock;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
-import jdk.internal.misc.JavaLangAccess;
-import jdk.internal.misc.SharedSecrets;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import jdk.internal.logger.LazyLoggers;
+import jdk.internal.logger.LoggerWrapper;
/**
* Platform logger provides an API for the JRE components to log
@@ -56,18 +49,28 @@
* the stack frame information issuing the log message.
*
* When the logging facility is enabled (at startup or runtime),
- * the java.util.logging.Logger will be created for each platform
+ * the backend logger will be created for each platform
* logger and all log messages will be forwarded to the Logger
* to handle.
*
+ * The PlatformLogger uses an underlying PlatformLogger.Bridge instance
+ * obtained by calling {@link PlatformLogger.Bridge#convert PlatformLogger.Bridge.convert(}
+ * {@link jdk.internal.logger.LazyLoggers#getLazyLogger(java.lang.String, java.lang.Class)
+ * jdk.internal.logger.LazyLoggers#getLazyLogger(name, PlatformLogger.class))}.
+ *
* Logging facility is "enabled" when one of the following
* conditions is met:
- * 1) a system property "java.util.logging.config.class" or
- * "java.util.logging.config.file" is set
- * 2) java.util.logging.LogManager or java.util.logging.Logger
- * is referenced that will trigger the logging initialization.
+ * 1) ServiceLoader.load({@link java.lang.System.LoggerFinder LoggerFinder.class},
+ * ClassLoader.getSystemClassLoader()).iterator().hasNext().
+ * 2) ServiceLoader.loadInstalled({@link jdk.internal.logger.DefaultLoggerFinder}).iterator().hasNext(),
+ * and 2.1) a system property "java.util.logging.config.class" or
+ * "java.util.logging.config.file" is set
+ * or 2.2) java.util.logging.LogManager or java.util.logging.Logger
+ * is referenced that will trigger the logging initialization.
*
* Default logging configuration:
+ *
+ * No LoggerFinder service implementation declared
* global logging level = INFO
* handlers = java.util.logging.ConsoleHandler
* java.util.logging.ConsoleHandler.level = INFO
@@ -84,71 +87,84 @@
* The platform loggers are designed for JDK developers use and
* this limitation can be workaround with setting
* -Djava.util.logging.config.file system property.
+ * <br>
+ * Calling PlatformLogger.setLevel will not work when there is a custom
+ * LoggerFinder installed - and as a consequence {@link #setLevel setLevel}
+ * is now deprecated.
*
* @since 1.7
*/
public class PlatformLogger {
- // The integer values must match that of {@code java.util.logging.Level}
- // objects.
- private static final int OFF = Integer.MAX_VALUE;
- private static final int SEVERE = 1000;
- private static final int WARNING = 900;
- private static final int INFO = 800;
- private static final int CONFIG = 700;
- private static final int FINE = 500;
- private static final int FINER = 400;
- private static final int FINEST = 300;
- private static final int ALL = Integer.MIN_VALUE;
-
/**
* PlatformLogger logging levels.
*/
public static enum Level {
// The name and value must match that of {@code java.util.logging.Level}s.
// Declare in ascending order of the given value for binary search.
- ALL,
- FINEST,
- FINER,
- FINE,
- CONFIG,
- INFO,
- WARNING,
- SEVERE,
- OFF;
+ ALL(System.Logger.Level.ALL),
+ FINEST(System.Logger.Level.TRACE),
+ FINER(System.Logger.Level.TRACE),
+ FINE(System.Logger.Level.DEBUG),
+ CONFIG(System.Logger.Level.DEBUG),
+ INFO(System.Logger.Level.INFO),
+ WARNING(System.Logger.Level.WARNING),
+ SEVERE(System.Logger.Level.ERROR),
+ OFF(System.Logger.Level.OFF);
- /**
- * Associated java.util.logging.Level lazily initialized in
- * JavaLoggerProxy's static initializer only once
- * when java.util.logging is available and enabled.
- * Only accessed by JavaLoggerProxy.
- */
- /* java.util.logging.Level */ Object javaLevel;
+ final System.Logger.Level systemLevel;
+ Level(System.Logger.Level systemLevel) {
+ this.systemLevel = systemLevel;
+ }
+
+ // The integer values must match that of {@code java.util.logging.Level}
+ // objects.
+ private static final int SEVERITY_OFF = Integer.MAX_VALUE;
+ private static final int SEVERITY_SEVERE = 1000;
+ private static final int SEVERITY_WARNING = 900;
+ private static final int SEVERITY_INFO = 800;
+ private static final int SEVERITY_CONFIG = 700;
+ private static final int SEVERITY_FINE = 500;
+ private static final int SEVERITY_FINER = 400;
+ private static final int SEVERITY_FINEST = 300;
+ private static final int SEVERITY_ALL = Integer.MIN_VALUE;
// ascending order for binary search matching the list of enum constants
private static final int[] LEVEL_VALUES = new int[] {
- PlatformLogger.ALL, PlatformLogger.FINEST, PlatformLogger.FINER,
- PlatformLogger.FINE, PlatformLogger.CONFIG, PlatformLogger.INFO,
- PlatformLogger.WARNING, PlatformLogger.SEVERE, PlatformLogger.OFF
+ SEVERITY_ALL, SEVERITY_FINEST, SEVERITY_FINER,
+ SEVERITY_FINE, SEVERITY_CONFIG, SEVERITY_INFO,
+ SEVERITY_WARNING, SEVERITY_SEVERE, SEVERITY_OFF
};
+ public System.Logger.Level systemLevel() {
+ return systemLevel;
+ }
+
public int intValue() {
return LEVEL_VALUES[this.ordinal()];
}
- static Level valueOf(int level) {
+ /**
+ * Maps a severity value to an effective logger level.
+ * @param level The severity of the messages that should be
+ * logged with a logger set to the returned level.
+ * @return The effective logger level, which is the nearest Level value
+ * whose severity is greater or equal to the given level.
+ * For level > SEVERE (OFF excluded), return SEVERE.
+ */
+ public static Level valueOf(int level) {
switch (level) {
// ordering per the highest occurrences in the jdk source
// finest, fine, finer, info first
- case PlatformLogger.FINEST : return Level.FINEST;
- case PlatformLogger.FINE : return Level.FINE;
- case PlatformLogger.FINER : return Level.FINER;
- case PlatformLogger.INFO : return Level.INFO;
- case PlatformLogger.WARNING : return Level.WARNING;
- case PlatformLogger.CONFIG : return Level.CONFIG;
- case PlatformLogger.SEVERE : return Level.SEVERE;
- case PlatformLogger.OFF : return Level.OFF;
- case PlatformLogger.ALL : return Level.ALL;
+ case SEVERITY_FINEST : return Level.FINEST;
+ case SEVERITY_FINE : return Level.FINE;
+ case SEVERITY_FINER : return Level.FINER;
+ case SEVERITY_INFO : return Level.INFO;
+ case SEVERITY_WARNING : return Level.WARNING;
+ case SEVERITY_CONFIG : return Level.CONFIG;
+ case SEVERITY_SEVERE : return Level.SEVERE;
+ case SEVERITY_OFF : return Level.OFF;
+ case SEVERITY_ALL : return Level.ALL;
}
// return the nearest Level value >= the given level,
// for level > SEVERE, return SEVERE and exclude OFF
@@ -157,39 +173,110 @@
}
}
- private static final Level DEFAULT_LEVEL = Level.INFO;
- private static boolean loggingEnabled;
- static {
- loggingEnabled = AccessController.doPrivileged(
- new PrivilegedAction<>() {
- public Boolean run() {
- String cname = System.getProperty("java.util.logging.config.class");
- String fname = System.getProperty("java.util.logging.config.file");
- return (cname != null || fname != null);
- }
- });
+ /**
+ *
+ * The PlatformLogger.Bridge interface is implemented by the System.Logger
+ * objects returned by our default JUL provider - so that JRE classes using
+ * PlatformLogger see no difference when JUL is the actual backend.
+ *
+ * PlatformLogger is now only a thin adaptation layer over the same
+ * loggers than returned by java.lang.System.getLogger(String name).
+ *
+ * The recommendation for JRE classes going forward is to use
+ * java.lang.System.getLogger(String name), which will
+ * use Lazy Loggers when possible and necessary.
+ *
+ */
+ public static interface Bridge {
+
+ /**
+ * Gets the name for this platform logger.
+ * @return the name of the platform logger.
+ */
+ public String getName();
+
+ /**
+ * Returns true if a message of the given level would actually
+ * be logged by this logger.
+ * @param level the level
+ * @return whether a message of that level would be logged
+ */
+ public boolean isLoggable(Level level);
+ public boolean isEnabled();
- // force loading of all JavaLoggerProxy (sub)classes to make JIT de-optimizations
- // less probable. Don't initialize JavaLoggerProxy class since
- // java.util.logging may not be enabled.
- try {
- Class.forName("sun.util.logging.PlatformLogger$DefaultLoggerProxy",
- false,
- PlatformLogger.class.getClassLoader());
- Class.forName("sun.util.logging.PlatformLogger$JavaLoggerProxy",
- false, // do not invoke class initializer
- PlatformLogger.class.getClassLoader());
- } catch (ClassNotFoundException ex) {
- throw new InternalError(ex);
+ public void log(Level level, String msg);
+ public void log(Level level, String msg, Throwable thrown);
+ public void log(Level level, String msg, Object... params);
+ public void log(Level level, Supplier<String> msgSupplier);
+ public void log(Level level, Throwable thrown, Supplier<String> msgSupplier);
+ public void logp(Level level, String sourceClass, String sourceMethod, String msg);
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ Supplier<String> msgSupplier);
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String msg, Object... params);
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String msg, Throwable thrown);
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ Throwable thrown, Supplier<String> msgSupplier);
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ ResourceBundle bundle, String msg, Object... params);
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ ResourceBundle bundle, String msg, Throwable thrown);
+ public void logrb(Level level, ResourceBundle bundle, String msg,
+ Object... params);
+ public void logrb(Level level, ResourceBundle bundle, String msg,
+ Throwable thrown);
+
+
+ public static Bridge convert(System.Logger logger) {
+ if (logger instanceof PlatformLogger.Bridge) {
+ return (Bridge) logger;
+ } else {
+ return new LoggerWrapper<>(logger);
+ }
+ }
+ }
+
+ /**
+ * The {@code PlatformLogger.ConfigurableBridge} interface is used to
+ * implement the deprecated {@link PlatformLogger#setLevel} method.
+ *
+ * PlatformLogger is now only a thin adaptation layer over the same
+ * loggers than returned by java.lang.System.getLogger(String name).
+ *
+ * The recommendation for JRE classes going forward is to use
+ * java.lang.System.getLogger(String name), which will
+ * use Lazy Loggers when possible and necessary.
+ *
+ */
+ public static interface ConfigurableBridge {
+
+ public abstract class LoggerConfiguration {
+ public abstract Level getPlatformLevel();
+ public abstract void setPlatformLevel(Level level);
+ }
+
+ public default LoggerConfiguration getLoggerConfiguration() {
+ return null;
+ }
+
+ public static LoggerConfiguration getLoggerConfiguration(PlatformLogger.Bridge logger) {
+ if (logger instanceof PlatformLogger.ConfigurableBridge) {
+ return ((ConfigurableBridge) logger).getLoggerConfiguration();
+ } else {
+ return null;
+ }
}
}
// Table of known loggers. Maps names to PlatformLoggers.
- private static Map<String,WeakReference<PlatformLogger>> loggers =
+ private static final Map<String,WeakReference<PlatformLogger>> loggers =
new HashMap<>();
/**
* Returns a PlatformLogger of a given name.
+ * @param name the name of the logger
+ * @return a PlatformLogger
*/
public static synchronized PlatformLogger getLogger(String name) {
PlatformLogger log = null;
@@ -198,56 +285,31 @@
log = ref.get();
}
if (log == null) {
- log = new PlatformLogger(name);
+ log = new PlatformLogger(PlatformLogger.Bridge.convert(
+ // We pass PlatformLogger.class rather than the actual caller
+ // because we want PlatformLoggers to be system loggers: we
+ // won't need to resolve any resource bundles anyway.
+ // Note: Many unit tests depend on the fact that
+ // PlatformLogger.getLoggerFromFinder is not caller sensitive.
+ LazyLoggers.getLazyLogger(name, PlatformLogger.class)));
loggers.put(name, new WeakReference<>(log));
}
return log;
}
- /**
- * Initialize java.util.logging.Logger objects for all platform loggers.
- * This method is called from LogManager.readPrimordialConfiguration().
- */
- public static synchronized void redirectPlatformLoggers() {
- if (loggingEnabled || !LoggingSupport.isAvailable()) return;
-
- loggingEnabled = true;
- for (Map.Entry<String, WeakReference<PlatformLogger>> entry : loggers.entrySet()) {
- WeakReference<PlatformLogger> ref = entry.getValue();
- PlatformLogger plog = ref.get();
- if (plog != null) {
- plog.redirectToJavaLoggerProxy();
- }
- }
- }
-
- /**
- * Creates a new JavaLoggerProxy and redirects the platform logger to it
- */
- private void redirectToJavaLoggerProxy() {
- DefaultLoggerProxy lp = DefaultLoggerProxy.class.cast(this.loggerProxy);
- JavaLoggerProxy jlp = new JavaLoggerProxy(lp.name, lp.level);
- // the order of assignments is important
- this.javaLoggerProxy = jlp; // isLoggable checks javaLoggerProxy if set
- this.loggerProxy = jlp;
- }
-
- // DefaultLoggerProxy may be replaced with a JavaLoggerProxy object
- // when the java.util.logging facility is enabled
- private volatile LoggerProxy loggerProxy;
- // javaLoggerProxy is only set when the java.util.logging facility is enabled
- private volatile JavaLoggerProxy javaLoggerProxy;
- private PlatformLogger(String name) {
- if (loggingEnabled) {
- this.loggerProxy = this.javaLoggerProxy = new JavaLoggerProxy(name);
- } else {
- this.loggerProxy = new DefaultLoggerProxy(name);
- }
+ // The system loggerProxy returned by LazyLoggers
+ // This may be a lazy logger - see jdk.internal.logger.LazyLoggers,
+ // or may be a Logger instance (or a wrapper thereof).
+ //
+ private final PlatformLogger.Bridge loggerProxy;
+ private PlatformLogger(PlatformLogger.Bridge loggerProxy) {
+ this.loggerProxy = loggerProxy;
}
/**
* A convenience method to test if the logger is turned off.
* (i.e. its level is OFF).
+ * @return whether the logger is turned off.
*/
public boolean isEnabled() {
return loggerProxy.isEnabled();
@@ -255,22 +317,24 @@
/**
* Gets the name for this platform logger.
+ * @return the name of the platform logger.
*/
public String getName() {
- return loggerProxy.name;
+ return loggerProxy.getName();
}
/**
* Returns true if a message of the given level would actually
* be logged by this logger.
+ * @param level the level
+ * @return whether a message of that level would be logged
*/
public boolean isLoggable(Level level) {
if (level == null) {
throw new NullPointerException();
}
- // performance-sensitive method: use two monomorphic call-sites
- JavaLoggerProxy jlp = javaLoggerProxy;
- return jlp != null ? jlp.isLoggable(level) : loggerProxy.isLoggable(level);
+
+ return loggerProxy.isLoggable(level);
}
/**
@@ -281,13 +345,15 @@
* @return this PlatformLogger's level
*/
public Level level() {
- return loggerProxy.getLevel();
+ final ConfigurableBridge.LoggerConfiguration spi =
+ PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);
+ return spi == null ? null : spi.getPlatformLevel();
}
/**
* Set the log level specifying which message levels will be
* logged by this logger. Message levels lower than this
- * value will be discarded. The level value {@link #OFF}
+ * value will be discarded. The level value {@link Level#OFF}
* can be used to turn off logging.
* <p>
* If the new level is null, it means that this node should
@@ -295,366 +361,153 @@
* (non-null) level value.
*
* @param newLevel the new value for the log level (may be null)
+ * @deprecated Platform Loggers should not be configured programmatically.
+ * This method will not work if a custom {@link
+ * java.lang.System.LoggerFinder} is installed.
*/
+ @Deprecated
public void setLevel(Level newLevel) {
- loggerProxy.setLevel(newLevel);
+ final ConfigurableBridge.LoggerConfiguration spi =
+ PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);;
+ if (spi != null) {
+ spi.setPlatformLevel(newLevel);
+ }
}
/**
* Logs a SEVERE message.
+ * @param msg the message
*/
public void severe(String msg) {
- loggerProxy.doLog(Level.SEVERE, msg);
+ loggerProxy.log(Level.SEVERE, msg, (Object[])null);
}
public void severe(String msg, Throwable t) {
- loggerProxy.doLog(Level.SEVERE, msg, t);
+ loggerProxy.log(Level.SEVERE, msg, t);
}
public void severe(String msg, Object... params) {
- loggerProxy.doLog(Level.SEVERE, msg, params);
+ loggerProxy.log(Level.SEVERE, msg, params);
}
/**
* Logs a WARNING message.
+ * @param msg the message
*/
public void warning(String msg) {
- loggerProxy.doLog(Level.WARNING, msg);
+ loggerProxy.log(Level.WARNING, msg, (Object[])null);
}
public void warning(String msg, Throwable t) {
- loggerProxy.doLog(Level.WARNING, msg, t);
+ loggerProxy.log(Level.WARNING, msg, t);
}
public void warning(String msg, Object... params) {
- loggerProxy.doLog(Level.WARNING, msg, params);
+ loggerProxy.log(Level.WARNING, msg, params);
}
/**
* Logs an INFO message.
+ * @param msg the message
*/
public void info(String msg) {
- loggerProxy.doLog(Level.INFO, msg);
+ loggerProxy.log(Level.INFO, msg, (Object[])null);
}
public void info(String msg, Throwable t) {
- loggerProxy.doLog(Level.INFO, msg, t);
+ loggerProxy.log(Level.INFO, msg, t);
}
public void info(String msg, Object... params) {
- loggerProxy.doLog(Level.INFO, msg, params);
+ loggerProxy.log(Level.INFO, msg, params);
}
/**
* Logs a CONFIG message.
+ * @param msg the message
*/
public void config(String msg) {
- loggerProxy.doLog(Level.CONFIG, msg);
+ loggerProxy.log(Level.CONFIG, msg, (Object[])null);
}
public void config(String msg, Throwable t) {
- loggerProxy.doLog(Level.CONFIG, msg, t);
+ loggerProxy.log(Level.CONFIG, msg, t);
}
public void config(String msg, Object... params) {
- loggerProxy.doLog(Level.CONFIG, msg, params);
+ loggerProxy.log(Level.CONFIG, msg, params);
}
/**
* Logs a FINE message.
+ * @param msg the message
*/
public void fine(String msg) {
- loggerProxy.doLog(Level.FINE, msg);
+ loggerProxy.log(Level.FINE, msg, (Object[])null);
}
public void fine(String msg, Throwable t) {
- loggerProxy.doLog(Level.FINE, msg, t);
+ loggerProxy.log(Level.FINE, msg, t);
}
public void fine(String msg, Object... params) {
- loggerProxy.doLog(Level.FINE, msg, params);
+ loggerProxy.log(Level.FINE, msg, params);
}
/**
* Logs a FINER message.
+ * @param msg the message
*/
public void finer(String msg) {
- loggerProxy.doLog(Level.FINER, msg);
+ loggerProxy.log(Level.FINER, msg, (Object[])null);
}
public void finer(String msg, Throwable t) {
- loggerProxy.doLog(Level.FINER, msg, t);
+ loggerProxy.log(Level.FINER, msg, t);
}
public void finer(String msg, Object... params) {
- loggerProxy.doLog(Level.FINER, msg, params);
+ loggerProxy.log(Level.FINER, msg, params);
}
/**
* Logs a FINEST message.
+ * @param msg the message
*/
public void finest(String msg) {
- loggerProxy.doLog(Level.FINEST, msg);
+ loggerProxy.log(Level.FINEST, msg, (Object[])null);
}
public void finest(String msg, Throwable t) {
- loggerProxy.doLog(Level.FINEST, msg, t);
+ loggerProxy.log(Level.FINEST, msg, t);
}
public void finest(String msg, Object... params) {
- loggerProxy.doLog(Level.FINEST, msg, params);
- }
-
- /**
- * Abstract base class for logging support, defining the API and common field.
- */
- private abstract static class LoggerProxy {
- final String name;
-
- protected LoggerProxy(String name) {
- this.name = name;
- }
-
- abstract boolean isEnabled();
-
- abstract Level getLevel();
- abstract void setLevel(Level newLevel);
-
- abstract void doLog(Level level, String msg);
- abstract void doLog(Level level, String msg, Throwable thrown);
- abstract void doLog(Level level, String msg, Object... params);
-
- abstract boolean isLoggable(Level level);
+ loggerProxy.log(Level.FINEST, msg, params);
}
-
- private static final class DefaultLoggerProxy extends LoggerProxy {
- /**
- * Default platform logging support - output messages to System.err -
- * equivalent to ConsoleHandler with SimpleFormatter.
- */
- private static PrintStream outputStream() {
- return System.err;
- }
-
- volatile Level effectiveLevel; // effective level (never null)
- volatile Level level; // current level set for this node (may be null)
-
- DefaultLoggerProxy(String name) {
- super(name);
- this.effectiveLevel = deriveEffectiveLevel(null);
- this.level = null;
- }
-
- boolean isEnabled() {
- return effectiveLevel != Level.OFF;
- }
-
- Level getLevel() {
- return level;
- }
-
- void setLevel(Level newLevel) {
- Level oldLevel = level;
- if (oldLevel != newLevel) {
- level = newLevel;
- effectiveLevel = deriveEffectiveLevel(newLevel);
- }
- }
-
- void doLog(Level level, String msg) {
- if (isLoggable(level)) {
- outputStream().print(format(level, msg, null));
- }
- }
-
- void doLog(Level level, String msg, Throwable thrown) {
- if (isLoggable(level)) {
- outputStream().print(format(level, msg, thrown));
- }
- }
-
- void doLog(Level level, String msg, Object... params) {
- if (isLoggable(level)) {
- String newMsg = formatMessage(msg, params);
- outputStream().print(format(level, newMsg, null));
- }
- }
-
- boolean isLoggable(Level level) {
- Level effectiveLevel = this.effectiveLevel;
- return level.intValue() >= effectiveLevel.intValue() && effectiveLevel != Level.OFF;
- }
-
- // derive effective level (could do inheritance search like j.u.l.Logger)
- private Level deriveEffectiveLevel(Level level) {
- return level == null ? DEFAULT_LEVEL : level;
- }
+ // ------------------------------------
+ // Maps used for Level conversion
+ // ------------------------------------
- // Copied from java.util.logging.Formatter.formatMessage
- private String formatMessage(String format, Object... parameters) {
- // Do the formatting.
- try {
- if (parameters == null || parameters.length == 0) {
- // No parameters. Just return format string.
- return format;
- }
- // Is it a java.text style format?
- // Ideally we could match with
- // Pattern.compile("\\{\\d").matcher(format).find())
- // However the cost is 14% higher, so we cheaply check for
- // 1 of the first 4 parameters
- if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 ||
- format.indexOf("{2") >=0|| format.indexOf("{3") >=0) {
- return java.text.MessageFormat.format(format, parameters);
- }
- return format;
- } catch (Exception ex) {
- // Formatting failed: use format string.
- return format;
- }
- }
-
- private static final String formatString =
- LoggingSupport.getSimpleFormat(false); // don't check logging.properties
- private final ZoneId zoneId = ZoneId.systemDefault();
- private synchronized String format(Level level, String msg, Throwable thrown) {
- ZonedDateTime zdt = ZonedDateTime.now(zoneId);
- String throwable = "";
- if (thrown != null) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- pw.println();
- thrown.printStackTrace(pw);
- pw.close();
- throwable = sw.toString();
- }
+ // This map is indexed by java.util.spi.Logger.Level.ordinal() and returns
+ // a PlatformLogger.Level
+ //
+ // ALL, TRACE, DEBUG, INFO, WARNING, ERROR, OFF
+ private static final Level[] spi2platformLevelMapping = {
+ Level.ALL, // mapped from ALL
+ Level.FINER, // mapped from TRACE
+ Level.FINE, // mapped from DEBUG
+ Level.INFO, // mapped from INFO
+ Level.WARNING, // mapped from WARNING
+ Level.SEVERE, // mapped from ERROR
+ Level.OFF // mapped from OFF
+ };
- return String.format(formatString,
- zdt,
- getCallerInfo(),
- name,
- level.name(),
- msg,
- throwable);
- }
-
- // Returns the caller's class and method's name; best effort
- // if cannot infer, return the logger's name.
- private String getCallerInfo() {
- String sourceClassName = null;
- String sourceMethodName = null;
-
- JavaLangAccess access = SharedSecrets.getJavaLangAccess();
- Throwable throwable = new Throwable();
- int depth = access.getStackTraceDepth(throwable);
-
- String logClassName = "sun.util.logging.PlatformLogger";
- 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();
- if (lookingForLogger) {
- // Skip all frames until we have found the first logger frame.
- if (cname.equals(logClassName)) {
- lookingForLogger = false;
- }
- } else {
- if (!cname.equals(logClassName)) {
- // We've found the relevant frame.
- sourceClassName = cname;
- sourceMethodName = frame.getMethodName();
- break;
- }
- }
- }
-
- if (sourceClassName != null) {
- return sourceClassName + " " + sourceMethodName;
- } else {
- return name;
- }
- }
+ public static Level toPlatformLevel(java.lang.System.Logger.Level level) {
+ if (level == null) return null;
+ assert level.ordinal() < spi2platformLevelMapping.length;
+ return spi2platformLevelMapping[level.ordinal()];
}
- /**
- * JavaLoggerProxy forwards all the calls to its corresponding
- * java.util.logging.Logger object.
- */
- private static final class JavaLoggerProxy extends LoggerProxy {
- // initialize javaLevel fields for mapping from Level enum -> j.u.l.Level object
- static {
- for (Level level : Level.values()) {
- level.javaLevel = LoggingSupport.parseLevel(level.name());
- }
- }
-
- private final /* java.util.logging.Logger */ Object javaLogger;
-
- JavaLoggerProxy(String name) {
- this(name, null);
- }
-
- JavaLoggerProxy(String name, Level level) {
- super(name);
- this.javaLogger = LoggingSupport.getLogger(name);
- if (level != null) {
- // level has been updated and so set the Logger's level
- LoggingSupport.setLevel(javaLogger, level.javaLevel);
- }
- }
-
- void doLog(Level level, String msg) {
- LoggingSupport.log(javaLogger, level.javaLevel, msg);
- }
-
- void doLog(Level level, String msg, Throwable t) {
- LoggingSupport.log(javaLogger, level.javaLevel, msg, t);
- }
-
- void doLog(Level level, String msg, Object... params) {
- if (!isLoggable(level)) {
- return;
- }
- // only pass String objects to the j.u.l.Logger which may
- // be created by untrusted code
- int len = (params != null) ? params.length : 0;
- Object[] sparams = new String[len];
- for (int i = 0; i < len; i++) {
- sparams [i] = String.valueOf(params[i]);
- }
- LoggingSupport.log(javaLogger, level.javaLevel, msg, sparams);
- }
-
- boolean isEnabled() {
- return LoggingSupport.isLoggable(javaLogger, Level.OFF.javaLevel);
- }
-
- /**
- * Returns the PlatformLogger.Level mapped from j.u.l.Level
- * set in the logger. If the j.u.l.Logger is set to a custom Level,
- * this method will return the nearest Level.
- */
- Level getLevel() {
- Object javaLevel = LoggingSupport.getLevel(javaLogger);
- if (javaLevel == null) return null;
-
- try {
- return Level.valueOf(LoggingSupport.getLevelName(javaLevel));
- } catch (IllegalArgumentException e) {
- return Level.valueOf(LoggingSupport.getLevelValue(javaLevel));
- }
- }
-
- void setLevel(Level level) {
- LoggingSupport.setLevel(javaLogger, level == null ? null : level.javaLevel);
- }
-
- boolean isLoggable(Level level) {
- return LoggingSupport.isLoggable(javaLogger, level.javaLevel);
- }
- }
}
--- a/jdk/src/java.desktop/share/classes/sun/font/FontUtilities.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.desktop/share/classes/sun/font/FontUtilities.java Fri Nov 20 19:26:16 2015 +0100
@@ -72,6 +72,8 @@
static {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @SuppressWarnings("deprecation") // PlatformLogger.setLevel is deprecated.
+ @Override
public Object run() {
String osName = System.getProperty("os.name", "unknownOS");
isSolaris = osName.startsWith("SunOS");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.logging/share/classes/META-INF/services/jdk.internal.logger.DefaultLoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1 @@
+sun.util.logging.internal.LoggingProviderImpl
--- a/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java Fri Nov 20 19:26:16 2015 +0100
@@ -43,6 +43,7 @@
import jdk.internal.misc.JavaAWTAccess;
import jdk.internal.misc.SharedSecrets;
import sun.misc.ManagedLocalsThread;
+import sun.util.logging.internal.LoggingProviderImpl;
/**
* There is a single global LogManager object that is used to
@@ -436,7 +437,8 @@
readConfiguration();
// Platform loggers begin to delegate to java.util.logging.Logger
- sun.util.logging.PlatformLogger.redirectPlatformLoggers();
+ jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers();
+
} catch (Exception ex) {
assert false : "Exception raised while reading logging configuration: " + ex;
}
@@ -1481,7 +1483,7 @@
* <p>
* Any {@linkplain #addConfigurationListener registered configuration
* listener} will be invoked after the properties are read.
- * <p>
+ *
* @apiNote This {@code readConfiguration} method should only be used for
* initializing the configuration during LogManager initialization or
* used with the "java.util.logging.config.class" property.
@@ -2363,7 +2365,8 @@
}
}
- static final Permission controlPermission = new LoggingPermission("control", null);
+ static final Permission controlPermission =
+ new LoggingPermission("control", null);
void checkPermission() {
SecurityManager sm = System.getSecurityManager();
@@ -2607,4 +2610,69 @@
if (t instanceof RuntimeException) throw (RuntimeException)t;
}
+ /**
+ * This class allows the {@link LoggingProviderImpl} to demand loggers on
+ * behalf of system and application classes.
+ */
+ private static final class LoggingProviderAccess
+ implements LoggingProviderImpl.LogManagerAccess,
+ PrivilegedAction<Void> {
+
+ private LoggingProviderAccess() {
+ }
+
+ /**
+ * Demands a logger on behalf of the given {@code caller}.
+ * <p>
+ * If a named logger suitable for the given caller is found
+ * returns it.
+ * Otherwise, creates a new logger suitable for the given caller.
+ *
+ * @param name The logger name.
+ * @param caller The caller on which behalf the logger is created/retrieved.
+ * @return A logger for the given {@code caller}.
+ *
+ * @throws NullPointerException if {@code name} is {@code null}
+ * or {@code caller} is {@code null}.
+ * @throws IllegalArgumentException if {@code manager} is not the default
+ * LogManager.
+ * @throws SecurityException if a security manager is present and the
+ * calling code doesn't have the
+ * {@link LoggingPermission LoggingPermission("demandLogger", null)}.
+ */
+ @Override
+ public Logger demandLoggerFor(LogManager manager, String name, /* Module */ Class<?> caller) {
+ if (manager != getLogManager()) {
+ // having LogManager as parameter just ensures that the
+ // caller will have initialized the LogManager before reaching
+ // here.
+ throw new IllegalArgumentException("manager");
+ }
+ Objects.requireNonNull(name);
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(controlPermission);
+ }
+ if (caller.getClassLoader() == null) {
+ return manager.demandSystemLogger(name,
+ Logger.SYSTEM_LOGGER_RB_NAME, caller);
+ } else {
+ return manager.demandLogger(name, null, caller);
+ }
+ }
+
+ @Override
+ public Void run() {
+ LoggingProviderImpl.setLogManagerAccess(INSTANCE);
+ return null;
+ }
+
+ static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess();
+ }
+
+ static {
+ AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null,
+ controlPermission);
+ }
+
}
--- a/jdk/src/java.logging/share/classes/java/util/logging/LogRecord.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.logging/share/classes/java/util/logging/LogRecord.java Fri Nov 20 19:26:16 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2015, 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
@@ -33,6 +33,7 @@
import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;
+import static jdk.internal.logger.SimpleConsoleLogger.skipLoggingFrame;
/**
* LogRecord objects are used to pass logging requests between
@@ -637,6 +638,27 @@
}
// Private method to infer the caller's class and method names
+ //
+ // Note:
+ // For testing purposes - it is possible to customize the process
+ // by which LogRecord will infer the source class name and source method name
+ // when analyzing the call stack.
+ // <p>
+ // The system property {@code jdk.logger.packages} can define a comma separated
+ // list of strings corresponding to additional package name prefixes that
+ // should be ignored when trying to infer the source caller class name.
+ // Those stack frames whose {@linkplain StackTraceElement#getClassName()
+ // declaring class name} start with one such prefix will be ignored.
+ // <p>
+ // This is primarily useful when providing utility logging classes wrapping
+ // a logger instance, as it makes it possible to instruct LogRecord to skip
+ // those utility frames when inferring the caller source class name.
+ // <p>
+ // The {@code jdk.logger.packages} system property is consulted only once.
+ // <p>
+ // This property is not standard, implementation specific, and yet
+ // undocumented (and thus subject to changes without notice).
+ //
private void inferCaller() {
needToInferCaller = false;
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
@@ -658,8 +680,8 @@
}
} else {
if (!isLoggerImpl) {
- // skip reflection call
- if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
+ // skip logging/logger infrastructure and reflection calls
+ if (!skipLoggingFrame(cname)) {
// We've found the relevant frame.
setSourceClassName(cname);
setSourceMethodName(frame.getMethodName());
@@ -675,7 +697,6 @@
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."));
+ cname.startsWith("sun.util.logging.PlatformLogger"));
}
}
--- a/jdk/src/java.logging/share/classes/java/util/logging/Logger.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.logging/share/classes/java/util/logging/Logger.java Fri Nov 20 19:26:16 2015 +0100
@@ -447,8 +447,7 @@
private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
LogManager manager = LogManager.getLogManager();
- SecurityManager sm = System.getSecurityManager();
- if (sm != null && !SystemLoggerHelper.disableCallerCheck) {
+ if (!SystemLoggerHelper.disableCallerCheck) {
if (caller.getClassLoader() == null) {
return manager.demandSystemLogger(name, resourceBundleName, caller);
}
@@ -1254,14 +1253,14 @@
* with an optional list of message parameters.
* <p>
* If the logger is currently enabled for the given message
- * level then a corresponding LogRecord is created and forwarded
- * to all the registered output Handler objects.
+ * {@code level} then a corresponding {@code LogRecord} is created and
+ * forwarded to all the registered output {@code Handler} objects.
* <p>
* The {@code msg} string is localized using the given resource bundle.
* If the resource bundle is {@code null}, then the {@code msg} string is not
* localized.
*
- * @param level One of the message level identifiers, e.g., SEVERE
+ * @param level One of the message level identifiers, e.g., {@code SEVERE}
* @param sourceClass Name of the class that issued the logging request
* @param sourceMethod Name of the method that issued the logging request
* @param bundle Resource bundle to localize {@code msg},
@@ -1285,6 +1284,36 @@
}
/**
+ * Log a message, specifying source class, method, and resource bundle,
+ * with an optional list of message parameters.
+ * <p>
+ * If the logger is currently enabled for the given message
+ * {@code level} then a corresponding {@code LogRecord} is created
+ * and forwarded to all the registered output {@code Handler} objects.
+ * <p>
+ * The {@code msg} string is localized using the given resource bundle.
+ * If the resource bundle is {@code null}, then the {@code msg} string is not
+ * localized.
+ * <p>
+ * @param level One of the message level identifiers, e.g., {@code SEVERE}
+ * @param bundle Resource bundle to localize {@code msg};
+ * can be {@code null}.
+ * @param msg The string message (or a key in the message catalog)
+ * @param params Parameters to the message (optional, may be none).
+ * @since 1.9
+ */
+ public void logrb(Level level, ResourceBundle bundle, String msg, Object... params) {
+ if (!isLoggable(level)) {
+ return;
+ }
+ LogRecord lr = new LogRecord(level, msg);
+ if (params != null && params.length != 0) {
+ lr.setParameters(params);
+ }
+ doLog(lr, bundle);
+ }
+
+ /**
* Log a message, specifying source class, method, and resource bundle name,
* with associated Throwable information.
* <p>
@@ -1330,19 +1359,20 @@
* with associated Throwable information.
* <p>
* If the logger is currently enabled for the given message
- * level then the given arguments are stored in a LogRecord
+ * {@code level} then the given arguments are stored in a {@code LogRecord}
* which is forwarded to all registered output handlers.
* <p>
* The {@code msg} string is localized using the given resource bundle.
* If the resource bundle is {@code null}, then the {@code msg} string is not
* localized.
* <p>
- * Note that the thrown argument is stored in the LogRecord thrown
- * property, rather than the LogRecord parameters property. Thus it is
- * processed specially by output Formatters and is not treated
- * as a formatting parameter to the LogRecord message property.
+ * Note that the {@code thrown} argument is stored in the {@code LogRecord}
+ * {@code thrown} property, rather than the {@code LogRecord}
+ * {@code parameters} property. Thus it is
+ * processed specially by output {@code Formatter} objects and is not treated
+ * as a formatting parameter to the {@code LogRecord} {@code message} property.
*
- * @param level One of the message level identifiers, e.g., SEVERE
+ * @param level One of the message level identifiers, e.g., {@code SEVERE}
* @param sourceClass Name of the class that issued the logging request
* @param sourceMethod Name of the method that issued the logging request
* @param bundle Resource bundle to localize {@code msg},
@@ -1363,6 +1393,42 @@
doLog(lr, bundle);
}
+ /**
+ * Log a message, specifying source class, method, and resource bundle,
+ * with associated Throwable information.
+ * <p>
+ * If the logger is currently enabled for the given message
+ * {@code level} then the given arguments are stored in a {@code LogRecord}
+ * which is forwarded to all registered output handlers.
+ * <p>
+ * The {@code msg} string is localized using the given resource bundle.
+ * If the resource bundle is {@code null}, then the {@code msg} string is not
+ * localized.
+ * <p>
+ * Note that the {@code thrown} argument is stored in the {@code LogRecord}
+ * {@code thrown} property, rather than the {@code LogRecord}
+ * {@code parameters} property. Thus it is
+ * processed specially by output {@code Formatter} objects and is not treated
+ * as a formatting parameter to the {@code LogRecord} {@code message}
+ * property.
+ * <p>
+ * @param level One of the message level identifiers, e.g., {@code SEVERE}
+ * @param bundle Resource bundle to localize {@code msg};
+ * can be {@code null}.
+ * @param msg The string message (or a key in the message catalog)
+ * @param thrown Throwable associated with the log message.
+ * @since 1.9
+ */
+ public void logrb(Level level, ResourceBundle bundle, String msg,
+ Throwable thrown) {
+ if (!isLoggable(level)) {
+ return;
+ }
+ LogRecord lr = new LogRecord(level, msg);
+ lr.setThrown(thrown);
+ doLog(lr, bundle);
+ }
+
//======================================================================
// Start of convenience methods for logging method entries and returns.
//======================================================================
--- a/jdk/src/java.logging/share/classes/java/util/logging/LoggingProxyImpl.java Fri Nov 20 15:34:12 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-/*
- * Copyright (c) 2009, 2013, 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 sun.util.logging.LoggingProxy;
-
-/**
- * Implementation of LoggingProxy when java.util.logging classes exist.
- */
-class LoggingProxyImpl implements LoggingProxy {
- static final LoggingProxy INSTANCE = new LoggingProxyImpl();
-
- private LoggingProxyImpl() { }
-
- @Override
- public Object getLogger(String name) {
- // always create a platform logger with the resource bundle name
- return Logger.getPlatformLogger(name);
- }
-
- @Override
- public Object getLevel(Object logger) {
- return ((Logger) logger).getLevel();
- }
-
- @Override
- public void setLevel(Object logger, Object newLevel) {
- ((Logger) logger).setLevel((Level) newLevel);
- }
-
- @Override
- public boolean isLoggable(Object logger, Object level) {
- return ((Logger) logger).isLoggable((Level) level);
- }
-
- @Override
- public void log(Object logger, Object level, String msg) {
- ((Logger) logger).log((Level) level, msg);
- }
-
- @Override
- public void log(Object logger, Object level, String msg, Throwable t) {
- ((Logger) logger).log((Level) level, msg, t);
- }
-
- @Override
- public void log(Object logger, Object level, String msg, Object... params) {
- ((Logger) logger).log((Level) level, msg, params);
- }
-
- @Override
- public java.util.List<String> getLoggerNames() {
- return LogManager.getLoggingMXBean().getLoggerNames();
- }
-
- @Override
- public String getLoggerLevel(String loggerName) {
- return LogManager.getLoggingMXBean().getLoggerLevel(loggerName);
- }
-
- @Override
- public void setLoggerLevel(String loggerName, String levelName) {
- LogManager.getLoggingMXBean().setLoggerLevel(loggerName, levelName);
- }
-
- @Override
- public String getParentLoggerName(String loggerName) {
- return LogManager.getLoggingMXBean().getParentLoggerName(loggerName);
- }
-
- @Override
- public Object parseLevel(String levelName) {
- Level level = Level.findLevel(levelName);
- if (level == null) {
- throw new IllegalArgumentException("Unknown level \"" + levelName + "\"");
- }
- return level;
- }
-
- @Override
- public String getLevelName(Object level) {
- return ((Level) level).getLevelName();
- }
-
- @Override
- public int getLevelValue(Object level) {
- return ((Level) level).intValue();
- }
-
- @Override
- public String getProperty(String key) {
- return LogManager.getLogManager().getProperty(key);
- }
-}
--- a/jdk/src/java.logging/share/classes/java/util/logging/SimpleFormatter.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.logging/share/classes/java/util/logging/SimpleFormatter.java Fri Nov 20 19:26:16 2015 +0100
@@ -27,10 +27,9 @@
package java.util.logging;
import java.io.*;
-import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
-import sun.util.logging.LoggingSupport;
+import jdk.internal.logger.SimpleConsoleLogger;
/**
* Print a brief summary of the {@code LogRecord} in a human readable
@@ -60,8 +59,12 @@
public class SimpleFormatter extends Formatter {
// format string for printing the log record
- private final String format = LoggingSupport.getSimpleFormat();
- private final ZoneId zoneId = ZoneId.systemDefault();
+ static String getLoggingProperty(String name) {
+ return LogManager.getLogManager().getProperty(name);
+ }
+
+ private final String format =
+ SimpleConsoleLogger.getSimpleFormat(SimpleFormatter::getLoggingProperty);
/**
* Format the given LogRecord.
@@ -152,7 +155,7 @@
@Override
public synchronized String format(LogRecord record) {
ZonedDateTime zdt = ZonedDateTime.ofInstant(
- record.getInstant(), zoneId);
+ record.getInstant(), ZoneId.systemDefault());
String source;
if (record.getSourceClassName() != null) {
source = record.getSourceClassName();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.logging/share/classes/sun/util/logging/internal/LoggingProviderImpl.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2015, 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 sun.util.logging.internal;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.util.Objects;
+import java.util.logging.LogManager;
+import jdk.internal.logger.DefaultLoggerFinder;
+import java.util.logging.LoggingPermission;
+import sun.util.logging.PlatformLogger;
+import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration;
+
+/**
+ * This {@code LoggingProviderImpl} is the JDK internal implementation of the
+ * {@link jdk.internal.logger.DefaultLoggerFinder} which is used by
+ * the default implementation of the {@link Logger}
+ * when no {@link LoggerFinder} is found
+ * and {@code java.util.logging} is present.
+ * When {@code java.util.logging} is present, the {@code LoggingProviderImpl}
+ * is {@linkplain java.util.ServiceLoader#loadInstalled(Class) installed} as
+ * an internal service provider, making it possible to use {@code java.util.logging}
+ * as the backend for loggers returned by the default LoggerFinder implementation.
+ * <p>
+ * This implementation of {@link DefaultLoggerFinder} returns instances of
+ * {@link java.lang.System.Logger} which
+ * delegate to a wrapped instance of {@link java.util.logging.Logger
+ * java.util.logging.Logger}.
+ * <br>
+ * Loggers returned by this class can therefore be configured by accessing
+ * their wrapped implementation through the regular {@code java.util.logging}
+ * APIs - such as {@link java.util.logging.LogManager java.util.logging.LogManager}
+ * and {@link java.util.logging.Logger java.util.logging.Logger}.
+ *
+ * @apiNote Programmers are not expected to call this class directly.
+ * Instead they should rely on the static methods defined by
+ * {@link java.lang.System java.lang.System}.
+ * <p>
+ * To replace this default
+ * {@code java.util.logging} backend, an application is expected to install
+ * its own {@link java.lang.System.LoggerFinder}.
+ *
+ * @see java.lang.System.Logger
+ * @see java.lang.System.LoggerFinder
+ * @see sun.util.logging.PlatformLogger.Bridge
+ * @see java.lang.System
+ * @see jdk.internal.logger
+ * @see jdk.internal.logger
+ *
+ */
+public final class LoggingProviderImpl extends DefaultLoggerFinder {
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ private static final LoggingPermission LOGGING_CONTROL_PERMISSION =
+ new LoggingPermission("control", null);
+
+ /**
+ * Creates a new instance of LoggingProviderImpl.
+ * @throws SecurityException if the calling code does not have the
+ * {@code RuntimePermission("loggerFinder")}.
+ */
+ public LoggingProviderImpl() {
+ }
+
+ /**
+ * A logger that delegates to a java.util.logging.Logger delegate.
+ */
+ static final class JULWrapper extends LoggerConfiguration
+ implements System.Logger, PlatformLogger.Bridge,
+ PlatformLogger.ConfigurableBridge {
+
+
+ private static final java.util.logging.Level[] spi2JulLevelMapping = {
+ java.util.logging.Level.ALL, // mapped from ALL
+ java.util.logging.Level.FINER, // mapped from TRACE
+ java.util.logging.Level.FINE, // mapped from DEBUG
+ java.util.logging.Level.INFO, // mapped from INFO
+ java.util.logging.Level.WARNING, // mapped from WARNING
+ java.util.logging.Level.SEVERE, // mapped from ERROR
+ java.util.logging.Level.OFF // mapped from OFF
+ };
+
+ private static final java.util.logging.Level[] platform2JulLevelMapping = {
+ java.util.logging.Level.ALL, // mapped from ALL
+ java.util.logging.Level.FINEST, // mapped from FINEST
+ java.util.logging.Level.FINER, // mapped from FINER
+ java.util.logging.Level.FINE, // mapped from FINE
+ java.util.logging.Level.CONFIG, // mapped from CONFIG
+ java.util.logging.Level.INFO, // mapped from INFO
+ java.util.logging.Level.WARNING, // mapped from WARNING
+ java.util.logging.Level.SEVERE, // mapped from SEVERE
+ java.util.logging.Level.OFF // mapped from OFF
+ };
+
+ private final java.util.logging.Logger julLogger;
+
+
+ private JULWrapper(java.util.logging.Logger logger) {
+ this.julLogger = logger;
+ }
+
+ @Override
+ public String getName() {
+ return julLogger.getName();
+ }
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable throwable) {
+ julLogger.log(toJUL(level), msg, throwable);
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String format, Object... params) {
+ julLogger.log(toJUL(level), format, params);
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+ julLogger.log(toJUL(level), msg);
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, Supplier<String> msgSuppier) {
+ julLogger.log(toJUL(level), msgSuppier);
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown, Supplier<String> msgSuppier) {
+ julLogger.log(toJUL(level), thrown, msgSuppier);
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Throwable throwable) {
+ julLogger.logrb(toJUL(level), bundle, key, throwable);
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Object... params) {
+ julLogger.logrb(toJUL(level), bundle, key, params);
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg) {
+ julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg);
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ Supplier<String> msgSupplier) {
+ julLogger.logp(toJUL(level), sourceClass, sourceMethod, msgSupplier);
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ String msg, Object... params) {
+ julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, params);
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ String msg, Throwable thrown) {
+ julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, thrown);
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ Throwable thrown, Supplier<String> msgSupplier) {
+ julLogger.logp(toJUL(level), sourceClass, sourceMethod,
+ thrown, msgSupplier);
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ ResourceBundle bundle, String key, Object... params) {
+ julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
+ bundle, key, params);
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+ ResourceBundle bundle, String key, Throwable thrown) {
+ julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
+ bundle, key, thrown);
+ }
+
+ @Override
+ public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
+ return julLogger.isLoggable(toJUL(level));
+ }
+
+ // -----------------------------------------------------------------
+ // Generic methods taking a Level as parameter
+ // -----------------------------------------------------------------
+
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return julLogger.isLoggable(toJUL(level));
+ }
+
+ @Override
+ public void log(Level level, String msg) {
+ julLogger.log(toJUL(level), msg);
+ }
+
+ @Override
+ public void log(Level level,
+ Supplier<String> msgSupplier) {
+ // We need to check for null here to satisfy the contract
+ // of System.Logger - because the underlying implementation
+ // of julLogger will check for it only if the level is
+ // loggable
+ Objects.requireNonNull(msgSupplier);
+ julLogger.log(toJUL(level), msgSupplier);
+ }
+
+ @Override
+ public void log(Level level, Object obj) {
+ // We need to check for null here to satisfy the contract
+ // of System.Logger - because the underlying implementation
+ // of julLogger will check for it only if the level is
+ // loggable
+ Objects.requireNonNull(obj);
+ julLogger.log(toJUL(level), () -> obj.toString());
+ }
+
+ @Override
+ public void log(Level level,
+ String msg, Throwable thrown) {
+ julLogger.log(toJUL(level), msg, thrown);
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier,
+ Throwable thrown) {
+ // We need to check for null here to satisfy the contract
+ // of System.Logger - because the underlying implementation
+ // of julLogger will check for it only if the level is
+ // loggable
+ Objects.requireNonNull(msgSupplier);
+ julLogger.log(toJUL(level), thrown, msgSupplier);
+ }
+
+ @Override
+ public void log(Level level,
+ String format, Object... params) {
+ julLogger.log(toJUL(level), format, params);
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ julLogger.logrb(toJUL(level), bundle, key, thrown);
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle,
+ String format, Object... params) {
+ julLogger.logrb(toJUL(level), bundle, format, params);
+ }
+
+ static java.util.logging.Level toJUL(Level level) {
+ if (level == null) return null;
+ assert level.ordinal() < spi2JulLevelMapping.length;
+ return spi2JulLevelMapping[level.ordinal()];
+ }
+
+ // ---------------------------------------------------------
+ // Methods from PlatformLogger.Bridge
+ // ---------------------------------------------------------
+
+ @Override
+ public boolean isEnabled() {
+ return julLogger.getLevel() != java.util.logging.Level.OFF;
+ }
+
+ @Override
+ public PlatformLogger.Level getPlatformLevel() {
+ final java.util.logging.Level javaLevel = julLogger.getLevel();
+ if (javaLevel == null) return null;
+ try {
+ return PlatformLogger.Level.valueOf(javaLevel.getName());
+ } catch (IllegalArgumentException e) {
+ return PlatformLogger.Level.valueOf(javaLevel.intValue());
+ }
+ }
+
+ @Override
+ public void setPlatformLevel(PlatformLogger.Level level) {
+ // null is allowed here
+ julLogger.setLevel(toJUL(level));
+ }
+
+ @Override
+ public LoggerConfiguration getLoggerConfiguration() {
+ return this;
+ }
+
+ static java.util.logging.Level toJUL(PlatformLogger.Level level) {
+ // The caller will throw if null is invalid in its context.
+ // There's at least one case where a null level is valid.
+ if (level == null) return null;
+ assert level.ordinal() < platform2JulLevelMapping.length;
+ return platform2JulLevelMapping[level.ordinal()];
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof JULWrapper)
+ && obj.getClass() == this.getClass()
+ && ((JULWrapper)obj).julLogger == this.julLogger;
+ }
+
+ @Override
+ public int hashCode() {
+ return julLogger.hashCode();
+ }
+
+ // A JULWrapper is just a stateless thin shell over a JUL logger - so
+ // for a given JUL logger, we could always return the same wrapper.
+ //
+ // This is an optimization which may - or may not - be worth the
+ // trouble: if many classes use the same logger, and if each class
+ // keeps a reference to that logger, then caching the wrapper will
+ // be worthwhile. Otherwise, if each logger is only referred once,
+ // then the cache will eat up more memory than would be necessary...
+ //
+ // Here is an example of how we could implement JULWrapper.of(...)
+ // if we wanted to create at most one wrapper instance for each logger
+ // instance:
+ //
+ // static final WeakHashMap<JULWrapper, WeakReference<JULWrapper>>
+ // wrappers = new WeakHashMap<>();
+ //
+ // static JULWrapper of(java.util.logging.Logger logger) {
+ //
+ // // First access without synchronizing
+ // final JULWrapper candidate = new JULWrapper(logger);
+ // WeakReference<JULWrapper> ref = wrappers.get(candidate);
+ // JULWrapper found = ref.get();
+ //
+ // // OK - we found it - lets return it.
+ // if (found != null) return found;
+ //
+ // // Not found. Need to synchronize.
+ // synchronized (wrappers) {
+ // ref = wrappers.get(candidate);
+ // found = ref.get();
+ // if (found == null) {
+ // wrappers.put(candidate, new WeakReference<>(candidate));
+ // found = candidate;
+ // }
+ // }
+ // assert found != null;
+ // return found;
+ // }
+ //
+ // But given that it may end up eating more memory in the nominal case
+ // (where each class that does logging has its own logger with the
+ // class name as logger name and stashes that logger away in a static
+ // field, thus making the cache redundant - as only one wrapper will
+ // ever be created anyway) - then we will simply return a new wrapper
+ // for each invocation of JULWrapper.of(...) - which may
+ // still prove more efficient in terms of memory consumption...
+ //
+ static JULWrapper of(java.util.logging.Logger logger) {
+ return new JULWrapper(logger);
+ }
+
+
+ }
+
+ /**
+ * Creates a java.util.logging.Logger for the given caller.
+ * @param name the logger name.
+ * @param caller the caller for which the logger should be created.
+ * @return a Logger suitable for use in the given caller.
+ */
+ private static java.util.logging.Logger demandJULLoggerFor(final String name,
+ /* Module */
+ final Class<?> caller) {
+ final LogManager manager = LogManager.getLogManager();
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm == null) {
+ return logManagerAccess.demandLoggerFor(manager, name, caller);
+ } else {
+ final PrivilegedAction<java.util.logging.Logger> pa =
+ () -> logManagerAccess.demandLoggerFor(manager, name, caller);
+ return AccessController.doPrivileged(pa, null, LOGGING_CONTROL_PERMISSION);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @apiNote The logger returned by this method can be configured through
+ * its {@linkplain java.util.logging.LogManager#getLogger(String)
+ * corresponding java.util.logging.Logger backend}.
+ *
+ * @return {@inheritDoc}
+ * @throws SecurityException if the calling code doesn't have the
+ * {@code RuntimePermission("loggerFinder")}.
+ */
+ @Override
+ protected Logger demandLoggerFor(String name, /* Module */ Class<?> caller) {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ return JULWrapper.of(demandJULLoggerFor(name,caller));
+ }
+
+ public static interface LogManagerAccess {
+ java.util.logging.Logger demandLoggerFor(LogManager manager,
+ String name, /* Module */ Class<?> caller);
+ }
+
+ // Hook for tests
+ public static LogManagerAccess getLogManagerAccess() {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGING_CONTROL_PERMISSION);
+ }
+ // Triggers initialization of accessJulLogger if not set.
+ if (logManagerAccess == null) LogManager.getLogManager();
+ return logManagerAccess;
+ }
+
+
+ private static volatile LogManagerAccess logManagerAccess;
+ public static void setLogManagerAccess(LogManagerAccess accesLoggers) {
+ final SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGING_CONTROL_PERMISSION);
+ }
+ logManagerAccess = accesLoggers;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.logging/share/classes/sun/util/logging/internal/package-info.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+/**
+ * <p>
+ * <b>[JDK INTERNAL]</b>
+ * The {@code sun.util.logging.internal} package defines an internal
+ * implementation of the {@link jdk.internal.logger.DefaultLoggerFinder} which
+ * provides an extension of the {@link java.lang.System.Logger System.Logger}
+ * interface making it easy to bridge from {@link java.util.logging};
+ * the JDK default implementation of the LoggerFinder will return loggers
+ * implementing this extension when {@code java.util.logging} is present.
+ * </p>
+ * <p>
+ * When {@code java.util.logging} is present, Logger instances returned by
+ * the JDK default implementation of the LoggerFinder
+ * wrap an instance of {@link java.util.logging.Logger java.util.logging.Logger}
+ * and implement the {@link
+ * sun.util.logging.PlatformLogger.Bridge PlatformLogger.Bridge}
+ * extension, overriding all the methods defined in
+ * that extension in order to call the corresponding methods on their wrapped
+ * {@linkplain java.util.logging.Logger backend Logger} instance.
+ * <p>
+ * <br>
+ * @see java.lang.System.LoggerFinder
+ * @see java.lang.System.Logger
+ * @see sun.util.logging.PlatformLogger
+ * @see sun.util.logging.PlatformLogger.Bridge
+ * @see jdk.internal.logger
+ *
+ * @since 1.9
+ */
+package sun.util.logging.internal;
--- a/jdk/src/java.management/share/classes/java/lang/management/DefaultPlatformMBeanProvider.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.management/share/classes/java/lang/management/DefaultPlatformMBeanProvider.java Fri Nov 20 19:26:16 2015 +0100
@@ -355,36 +355,38 @@
}
});
- /**
- * Logging facility.
- */
- initMBeanList.add(new PlatformComponent<PlatformLoggingMXBean>() {
- private final Set<String> platformLoggingMXBeanInterfaceNames
+ if (ManagementFactoryHelper.isPlatformLoggingMXBeanAvailable()) {
+ /**
+ * Logging facility.
+ */
+ initMBeanList.add(new PlatformComponent<PlatformLoggingMXBean>() {
+ private final Set<String> platformLoggingMXBeanInterfaceNames
= Collections.unmodifiableSet(Collections.singleton(
"java.lang.management.PlatformLoggingMXBean"));
- @Override
- public Set<Class<? extends PlatformLoggingMXBean>> mbeanInterfaces() {
- return Collections.singleton(PlatformLoggingMXBean.class);
- }
+ @Override
+ public Set<Class<? extends PlatformLoggingMXBean>> mbeanInterfaces() {
+ return Collections.singleton(PlatformLoggingMXBean.class);
+ }
- @Override
- public Set<String> mbeanInterfaceNames() {
- return platformLoggingMXBeanInterfaceNames;
- }
+ @Override
+ public Set<String> mbeanInterfaceNames() {
+ return platformLoggingMXBeanInterfaceNames;
+ }
- @Override
- public String getObjectNamePattern() {
- return "java.util.logging:type=Logging";
- }
+ @Override
+ public String getObjectNamePattern() {
+ return "java.util.logging:type=Logging";
+ }
- @Override
- public Map<String, PlatformLoggingMXBean> nameToMBeanMap() {
- return Collections.singletonMap(
+ @Override
+ public Map<String, PlatformLoggingMXBean> nameToMBeanMap() {
+ return Collections.singletonMap(
"java.util.logging:type=Logging",
ManagementFactoryHelper.getPlatformLoggingMXBean());
- }
- });
+ }
+ });
+ }
/**
* Buffer pools.
--- a/jdk/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java Fri Nov 20 19:26:16 2015 +0100
@@ -39,10 +39,14 @@
import jdk.internal.misc.JavaNioAccess;
import jdk.internal.misc.SharedSecrets;
-import sun.util.logging.LoggingSupport;
+
import java.util.ArrayList;
import java.util.List;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.security.PrivilegedAction;
+
/**
* ManagementFactoryHelper provides static factory methods to create
* instances of the management interface.
@@ -141,13 +145,17 @@
}
public static PlatformLoggingMXBean getPlatformLoggingMXBean() {
- if (LoggingSupport.isAvailable()) {
+ if (LoggingMXBeanSupport.isAvailable()) {
return PlatformLoggingImpl.instance;
} else {
return null;
}
}
+ public static boolean isPlatformLoggingMXBeanAvailable() {
+ return LoggingMXBeanSupport.isAvailable();
+ }
+
/**
* The logging MXBean object is an instance of
* PlatformLoggingMXBean and java.util.logging.LoggingMXBean
@@ -165,8 +173,44 @@
extends PlatformLoggingMXBean, java.util.logging.LoggingMXBean {
}
+ // This is a trick: if java.util.logging is not present then
+ // attempting to access something that implements
+ // java.util.logging.LoggingMXBean will trigger a CNFE.
+ // So we cannot directly call any static method or access any static field
+ // on PlatformLoggingImpl, as we would risk raising a CNFE.
+ // Instead we use this intermediate LoggingMXBeanSupport class to determine
+ // whether java.util.logging is present, and load the actual LoggingMXBean
+ // implementation.
+ //
+ static final class LoggingMXBeanSupport {
+ final static Object loggingImpl =
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ try {
+ // create a LoggingProxyImpl instance when
+ // java.util.logging classes exist
+ Class<?> c = Class.forName("java.util.logging.Logging", true, null);
+ Constructor<?> cons = c.getDeclaredConstructor();
+ cons.setAccessible(true);
+ return cons.newInstance();
+ } catch (ClassNotFoundException cnf) {
+ return null;
+ } catch (NoSuchMethodException | InstantiationException
+ | IllegalAccessException | InvocationTargetException e) {
+ throw new AssertionError(e);
+ }
+ }});
+
+ static boolean isAvailable() {
+ return loggingImpl != null;
+ }
+ }
+
static class PlatformLoggingImpl implements LoggingMXBean
{
+ final static java.util.logging.LoggingMXBean impl =
+ (java.util.logging.LoggingMXBean) LoggingMXBeanSupport.loggingImpl;
final static PlatformLoggingMXBean instance = new PlatformLoggingImpl();
final static String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
@@ -188,22 +232,22 @@
@Override
public java.util.List<String> getLoggerNames() {
- return LoggingSupport.getLoggerNames();
+ return impl.getLoggerNames();
}
@Override
public String getLoggerLevel(String loggerName) {
- return LoggingSupport.getLoggerLevel(loggerName);
+ return impl.getLoggerLevel(loggerName);
}
@Override
public void setLoggerLevel(String loggerName, String levelName) {
- LoggingSupport.setLoggerLevel(loggerName, levelName);
+ impl.setLoggerLevel(loggerName, levelName);
}
@Override
public String getParentLoggerName(String loggerName) {
- return LoggingSupport.getParentLoggerName(loggerName);
+ return impl.getParentLoggerName(loggerName);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/Level/LoggerLevelTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.System.Logger.Level;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests System.Logger.Level names and severity.
+ * @author danielfuchs
+ */
+public class LoggerLevelTest {
+ public static void main(String[] args) {
+ Set<Level> untested = EnumSet.allOf(Level.class);
+ testLevel(untested, Level.ALL, java.util.logging.Level.ALL);
+ testLevel(untested, Level.TRACE, java.util.logging.Level.FINER);
+ testLevel(untested, Level.DEBUG, java.util.logging.Level.FINE);
+ testLevel(untested, Level.INFO, java.util.logging.Level.INFO);
+ testLevel(untested, Level.WARNING, java.util.logging.Level.WARNING);
+ testLevel(untested, Level.ERROR, java.util.logging.Level.SEVERE);
+ testLevel(untested, Level.OFF, java.util.logging.Level.OFF);
+ if (!untested.isEmpty()) {
+ throw new RuntimeException("Some level values were not tested: " + untested);
+ }
+ }
+
+ private static void testLevel(Set<Level> untested, Level systemLevel, java.util.logging.Level julLevel) {
+ untested.remove(systemLevel);
+ assertEquals(systemLevel.getName(), systemLevel.name(),
+ "System.Logger.Level." + systemLevel.name() + ".getName()");
+ assertEquals(systemLevel.getSeverity(), julLevel.intValue(),
+ "System.Logger.Level." + systemLevel.name() + ".getSeverity");
+ }
+
+ private static void assertEquals(Object actual, Object expected, String what) {
+ if (!Objects.equals(actual, expected)) {
+ throw new RuntimeException("Bad value for " + what
+ + "\n\t expected: " + expected
+ + "\n\t actual: " + actual);
+ } else {
+ System.out.println("Got expected value for " + what + ": " + actual);
+ }
+ }
+
+ private static void assertEquals(int actual, int expected, String what) {
+ if (!Objects.equals(actual, expected)) {
+ throw new RuntimeException("Bad value for " + what
+ + "\n\t expected: " + toString(expected)
+ + "\n\t actual: " + toString(actual));
+ } else {
+ System.out.println("Got expected value for " + what + ": " + toString(actual));
+ }
+ }
+
+ private static String toString(int value) {
+ switch (value) {
+ case Integer.MAX_VALUE: return "Integer.MAX_VALUE";
+ case Integer.MIN_VALUE: return "Integer.MIN_VALUE";
+ default:
+ return Integer.toString(value);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/custom/AccessSystemLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+ public AccessSystemLogger() {
+ this(check());
+ }
+
+ private AccessSystemLogger(Void unused) {
+ }
+
+ private static Void check() {
+ if (AccessSystemLogger.class.getClassLoader() != null) {
+ throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+ }
+ return null;
+ }
+
+ public Logger getLogger(String name) {
+ Logger logger = System.getLogger(name);
+ System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+ return logger;
+ }
+
+ public Logger getLogger(String name, ResourceBundle bundle) {
+ Logger logger = System.getLogger(name, bundle);
+ System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+ return logger;
+ }
+
+ // copy AccessSystemLogger.class to ./boot
+ public static void main(String[] args) throws IOException {
+ Path testDir = Paths.get(System.getProperty("user.dir", "."));
+ Path bootDir = Paths.get(testDir.toString(), "boot");
+ Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+ Path thisClass = Paths.get(classes.toString(),
+ AccessSystemLogger.class.getSimpleName()+".class");
+ if (Files.notExists(bootDir)) {
+ Files.createDirectory(bootDir);
+ }
+ Path dest = Paths.get(bootDir.toString(),
+ AccessSystemLogger.class.getSimpleName()+".class");
+ Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/custom/CustomLoggerTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,728 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests loggers returned by System.getLogger with a naive implementation
+ * of LoggerFinder, and in particular the default body of
+ * System.Logger methods.
+ * @build CustomLoggerTest AccessSystemLogger
+ * @run driver AccessSystemLogger
+ * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class CustomLoggerTest {
+
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+
+ public static class BaseLoggerFinder extends LoggerFinder {
+ final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+ final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+ public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ // changing this to true requires changing the logic in the
+ // test in order to load this class with a protection domain
+ // that has the CONTROL_PERMISSION (e.g. by using a custom
+ // system class loader.
+ final boolean doChecks = false;
+
+ public static final class LogEvent {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ Supplier<String> supplier;
+ String msg;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ isLoggable,
+ loggerName,
+ level,
+ bundle,
+ thrown,
+ args,
+ supplier,
+ msg,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = null;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = null;
+ evt.msg = key;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Object... params) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = null;
+ evt.supplier = null;
+ evt.msg = key;
+ return evt;
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Supplier<String> supplier,
+ Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = supplier;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ return evt;
+ }
+
+ }
+
+ public class LoggerImpl implements Logger {
+ private final String name;
+ private Level level = Level.INFO;
+
+ public LoggerImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+ log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
+ }
+
+ void log(LogEvent event) {
+ eventQueue.add(event);
+ }
+ }
+
+ @Override
+ public Logger getLogger(String name, Class<?> caller) {
+ // We should check the permission to obey the API contract, but
+ // what happens if we don't?
+ // This is the main difference compared with what we test in
+ // java/lang/System/LoggerFinder/BaseLoggerFinderTest
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null && doChecks) {
+ sm.checkPermission(SimplePolicy.LOGGERFINDER_PERMISSION);
+ }
+
+ PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+ ClassLoader callerLoader = AccessController.doPrivileged(pa);
+ if (callerLoader == null) {
+ return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ } else {
+ return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ }
+ }
+ }
+
+ static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ "NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+ // 1. Obtain destination loggers directly from the LoggerFinder
+ // - LoggerFinder.getLogger("foo", type)
+ BaseLoggerFinder provider =
+ BaseLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ BaseLoggerFinder.LoggerImpl appSink =
+ BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", CustomLoggerTest.class));
+ BaseLoggerFinder.LoggerImpl sysSink =
+ BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ test(provider, true, appSink, sysSink);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ test(provider, false, appSink, sysSink);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ test(provider, true, appSink, sysSink);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(BaseLoggerFinder provider, boolean hasRequiredPermissions,
+ BaseLoggerFinder.LoggerImpl appSink, BaseLoggerFinder.LoggerImpl sysSink) {
+
+ ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+
+ // 1. Test loggers returned by:
+ // - System.getLogger("foo")
+ // - and AccessSystemLogger.getLogger("foo")
+ Logger appLogger1 = System.getLogger("foo");
+ loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");
+
+ Logger sysLogger1 = null;
+ try {
+ sysLogger1 = accessSystemLogger.getLogger("foo");
+ loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ throw new RuntimeException("unexpected exception: " + acx, acx);
+ }
+
+ if (appLogger1 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ if (provider.system.contains(appLogger1)) {
+ throw new RuntimeException("app logger in system map");
+ }
+ if (provider.user.contains(sysLogger1)) {
+ throw new RuntimeException("sys logger in appplication map");
+ }
+ if (provider.system.contains(sysLogger1)) {
+ // sysLogger should be a a LazyLoggerWrapper
+ throw new RuntimeException("sys logger is in system map (should be wrapped)");
+ }
+
+
+ // 2. Test loggers returned by:
+ // - System.getLogger(\"foo\", loggerBundle)
+ // - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)
+ Logger appLogger2 =
+ System.getLogger("foo", loggerBundle);
+ loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
+
+ Logger sysLogger2 = null;
+ try {
+ sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle);
+ loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ throw new RuntimeException("unexpected exception: " + acx, acx);
+ }
+ if (appLogger2 == sysLogger2) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (appLogger2 == appSink) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (sysLogger2 == sysSink) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ if (provider.system.contains(appLogger2)) {
+ throw new RuntimeException("localized app logger in system map");
+ }
+ if (provider.user.contains(appLogger2)) {
+ throw new RuntimeException("localized app logger in appplication map");
+ }
+ if (provider.user.contains(sysLogger2)) {
+ throw new RuntimeException("localized sys logger in appplication map");
+ }
+ if (provider.system.contains(sysLogger2)) {
+ throw new RuntimeException("localized sys logger not in system map");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ // Calls the 8 methods defined on Logger and verify the
+ // parameters received by the underlying BaseLoggerFinder.LoggerImpl
+ // logger.
+ private static void testLogger(BaseLoggerFinder provider,
+ Map<Logger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ Logger logger,
+ BaseLoggerFinder.LoggerImpl sink) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger));
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, foo): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ BaseLoggerFinder.LogEvent expected =
+ BaseLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, messageLevel, (ResourceBundle)null,
+ fooMsg, null, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, foo);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (provider.eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ String msg = "blah";
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ BaseLoggerFinder.LogEvent expected =
+ BaseLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, loggerBundle,
+ msg, null, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, msg);
+ BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+
+ Supplier<String> fooSupplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ BaseLoggerFinder.LogEvent expected =
+ BaseLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, messageLevel, (ResourceBundle)null,
+ fooSupplier.get(), null,
+ (Throwable)null, (Object[])null);
+ logger.log(messageLevel, fooSupplier);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (provider.eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = msg;
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ BaseLoggerFinder.LogEvent expected =
+ BaseLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, loggerBundle,
+ format, null, (Throwable)null, new Object[] {arg1, arg2});
+ logger.log(messageLevel, format, arg1, arg2);
+ BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ BaseLoggerFinder.LogEvent expected =
+ BaseLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, loggerBundle,
+ msg, null, thrown, (Object[]) null);
+ logger.log(messageLevel, msg, thrown);
+ BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+
+
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ BaseLoggerFinder.LogEvent expected =
+ BaseLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, messageLevel, (ResourceBundle)null,
+ fooSupplier.get(), null,
+ (Throwable)thrown, (Object[])null);
+ logger.log(messageLevel, fooSupplier, thrown);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (provider.eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ BaseLoggerFinder.LogEvent expected =
+ BaseLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, bundle,
+ format, null, (Throwable)null, new Object[] {foo, msg});
+ logger.log(messageLevel, bundle, format, foo, msg);
+ BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ BaseLoggerFinder.LogEvent expected =
+ BaseLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, bundle,
+ msg, null, thrown, (Object[]) null);
+ logger.log(messageLevel, bundle, msg, thrown);
+ BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final Permissions permissions;
+ final Permissions allPermissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl) {
+ this.allowControl = allowControl;
+ permissions = new Permissions();
+
+ // these are used for configuring the test itself...
+ allPermissions = new Permissions();
+ allPermissions.add(LOGGERFINDER_PERMISSION);
+
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (allowControl.get().get()) return allPermissions.implies(permission);
+ return permissions.implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(allowControl.get().get()
+ ? allPermissions : permissions).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(allowControl.get().get()
+ ? allPermissions : permissions).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/custom/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1 @@
+CustomLoggerTest$BaseLoggerFinder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/default/AccessSystemLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+import java.util.logging.LogManager;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+ public AccessSystemLogger() {
+ this(check());
+ }
+
+ private AccessSystemLogger(Void unused) {
+ }
+
+ private static Void check() {
+ if (AccessSystemLogger.class.getClassLoader() != null) {
+ throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+ }
+ return null;
+ }
+
+ public Logger getLogger(String name) {
+ Logger logger = System.getLogger(name);
+ System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+ return logger;
+ }
+
+ public Logger getLogger(String name, ResourceBundle bundle) {
+ Logger logger = System.getLogger(name, bundle);
+ System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+ return logger;
+ }
+
+ public java.util.logging.Logger demandSystemLogger(String name) {
+ return java.util.logging.Logger.getLogger(name);
+ }
+
+ // copy AccessSystemLogger.class to ./boot
+ public static void main(String[] args) throws IOException {
+ Path testDir = Paths.get(System.getProperty("user.dir", "."));
+ Path bootDir = Paths.get(testDir.toString(), "boot");
+ Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+ Path thisClass = Paths.get(classes.toString(),
+ AccessSystemLogger.class.getSimpleName()+".class");
+ if (Files.notExists(bootDir)) {
+ Files.createDirectory(bootDir);
+ }
+ Path dest = Paths.get(bootDir.toString(),
+ AccessSystemLogger.class.getSimpleName()+".class");
+ Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/default/DefaultLoggerTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests default loggers returned by System.getLogger, and in
+ * particular the implementation of the the System.Logger method
+ * performed by the default binding.
+ *
+ * @build DefaultLoggerTest AccessSystemLogger
+ * @run driver AccessSystemLogger
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class DefaultLoggerTest {
+
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ java.util.logging.Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ String msg;
+ String className;
+ String methodName;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ isLoggable,
+ loggerName,
+ level,
+ bundle,
+ thrown,
+ args,
+ msg,
+ className,
+ methodName,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ java.util.logging.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ return LogEvent.of(sequenceNumber, isLoggable, name,
+ DefaultLoggerTest.class.getName(),
+ "testLogger", level, bundle, key,
+ thrown, params);
+ }
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ String className, String methodName,
+ java.util.logging.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ evt.className = className;
+ evt.methodName = methodName;
+ return evt;
+ }
+
+ }
+
+ static java.util.logging.Level mapToJul(Level level) {
+ switch (level) {
+ case ALL: return java.util.logging.Level.ALL;
+ case TRACE: return java.util.logging.Level.FINER;
+ case DEBUG: return java.util.logging.Level.FINE;
+ case INFO: return java.util.logging.Level.INFO;
+ case WARNING: return java.util.logging.Level.WARNING;
+ case ERROR: return java.util.logging.Level.SEVERE;
+ case OFF: return java.util.logging.Level.OFF;
+ }
+ throw new InternalError("No such level: " + level);
+ }
+
+ static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
+ boolean before = allowAll.get().get();
+ try {
+ allowAll.get().set(true);
+ sink.setLevel(loggerLevel);
+ } finally {
+ allowAll.get().set(before);
+ }
+ }
+
+ public static class MyHandler extends Handler {
+
+ @Override
+ public java.util.logging.Level getLevel() {
+ return java.util.logging.Level.ALL;
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+ true, record.getLoggerName(),
+ record.getSourceClassName(),
+ record.getSourceMethodName(),
+ record.getLevel(),
+ record.getResourceBundle(), record.getMessage(),
+ record.getThrown(), record.getParameters()));
+ }
+ @Override
+ public void flush() {
+ }
+ @Override
+ public void close() throws SecurityException {
+ }
+
+ }
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAll));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ "NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+ // 1. Obtain destination loggers directly from the LoggerFinder
+ // - LoggerFinder.getLogger("foo", type)
+
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ test(true);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ test(false);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ test(true);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(boolean hasRequiredPermissions) {
+
+ ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+
+ // 1. Test loggers returned by:
+ // - System.getLogger("foo")
+ // - and AccessSystemLogger.getLogger("foo")
+ Logger sysLogger1 = null;
+ try {
+ sysLogger1 = accessSystemLogger.getLogger("foo");
+ loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ throw new RuntimeException("unexpected exception: " + acx, acx);
+ }
+
+ Logger appLogger1 = System.getLogger("foo");
+ loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");
+
+ if (appLogger1 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ // 2. Test loggers returned by:
+ // - System.getLogger(\"foo\", loggerBundle)
+ // - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)
+ Logger appLogger2 =
+ System.getLogger("foo", loggerBundle);
+ loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
+
+ Logger sysLogger2 = null;
+ try {
+ sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle);
+ loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ throw new RuntimeException("unexpected exception: " + acx, acx);
+ }
+ if (appLogger2 == sysLogger2) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ final java.util.logging.Logger appSink;
+ final java.util.logging.Logger sysSink;
+ final java.util.logging.Handler appHandler;
+ final java.util.logging.Handler sysHandler;
+ final LoggerFinder provider;
+ allowAll.get().set(true);
+ try {
+ appSink = java.util.logging.Logger.getLogger("foo");
+ sysSink = accessSystemLogger.demandSystemLogger("foo");
+ appSink.addHandler(appHandler = new MyHandler());
+ sysSink.addHandler(sysHandler = new MyHandler());
+ appSink.setUseParentHandlers(false);
+ sysSink.setUseParentHandlers(false);
+ provider = LoggerFinder.getLoggerFinder();
+ } finally {
+ allowAll.get().set(false);
+ }
+ try {
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
+ testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
+ } finally {
+ allowAll.get().set(true);
+ try {
+ appSink.removeHandler(appHandler);
+ sysSink.removeHandler(sysHandler);
+ sysSink.setLevel(null);
+ appSink.setLevel(null);
+ } finally {
+ allowAll.get().set(false);
+ }
+ }
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ // Calls the 8 methods defined on Logger and verify the
+ // parameters received by the underlying BaseLoggerFinder.LoggerImpl
+ // logger.
+ private static void testLogger(LoggerFinder provider,
+ Map<Logger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ Logger logger,
+ java.util.logging.Logger sink) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger));
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ for (Level loggerLevel : Level.values()) {
+ setLevel(sink, mapToJul(loggerLevel));
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, foo): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, mapToJul(messageLevel), (ResourceBundle)null,
+ fooMsg, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, foo);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ String msg = "blah";
+ for (Level loggerLevel : Level.values()) {
+ setLevel(sink, mapToJul(loggerLevel));
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, mapToJul(messageLevel), loggerBundle,
+ msg, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, msg);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ Supplier<String> fooSupplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+
+ for (Level loggerLevel : Level.values()) {
+ setLevel(sink, mapToJul(loggerLevel));
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, mapToJul(messageLevel), (ResourceBundle)null,
+ fooSupplier.get(),
+ (Throwable)null, (Object[])null);
+ logger.log(messageLevel, fooSupplier);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = msg;
+ for (Level loggerLevel : Level.values()) {
+ setLevel(sink, mapToJul(loggerLevel));
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, mapToJul(messageLevel), loggerBundle,
+ format, (Throwable)null, new Object[] {arg1, arg2});
+ logger.log(messageLevel, format, arg1, arg2);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ for (Level loggerLevel : Level.values()) {
+ setLevel(sink, mapToJul(loggerLevel));
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, mapToJul(messageLevel), loggerBundle,
+ msg, thrown, (Object[]) null);
+ logger.log(messageLevel, msg, thrown);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+
+ for (Level loggerLevel : Level.values()) {
+ setLevel(sink, mapToJul(loggerLevel));
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, mapToJul(messageLevel), (ResourceBundle)null,
+ fooSupplier.get(),
+ (Throwable)thrown, (Object[])null);
+ logger.log(messageLevel, fooSupplier, thrown);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ for (Level loggerLevel : Level.values()) {
+ setLevel(sink, mapToJul(loggerLevel));
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, mapToJul(messageLevel), bundle,
+ format, (Throwable)null, new Object[] {foo, msg});
+ logger.log(messageLevel, bundle, format, foo, msg);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ for (Level loggerLevel : Level.values()) {
+ setLevel(sink, mapToJul(loggerLevel));
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, mapToJul(messageLevel), bundle,
+ msg, thrown, (Object[]) null);
+ logger.log(messageLevel, bundle, msg, thrown);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final Permissions permissions;
+ final Permissions allPermissions;
+ final Permissions controlPermissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAll;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAll) {
+ this.allowControl = allowControl;
+ this.allowAll = allowAll;
+ permissions = new Permissions();
+
+ // these are used for configuring the test itself...
+ controlPermissions = new Permissions();
+ controlPermissions.add(LOGGERFINDER_PERMISSION);
+ allPermissions = new Permissions();
+ allPermissions.add(new java.security.AllPermission());
+
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (allowAll.get().get()) return allPermissions.implies(permission);
+ if (allowControl.get().get()) return controlPermissions.implies(permission);
+ return permissions.implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(allowAll.get().get()
+ ? allPermissions : allowControl.get().get()
+ ? controlPermissions : permissions).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(allowAll.get().get()
+ ? allPermissions : allowControl.get().get()
+ ? controlPermissions : permissions).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/interface/LoggerInterfaceTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.ResourceBundle;
+import java.util.function.Consumer;
+import java.lang.System.Logger.Level;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.function.Supplier;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests the default body of the System.Logger interface.
+ * @author danielfuchs
+ */
+public class LoggerInterfaceTest {
+
+ public static class LoggerImpl implements System.Logger {
+
+ public static class LogEvent implements Cloneable {
+ Level level;
+ ResourceBundle bundle;
+ String msg;
+ Throwable thrown;
+ Object[] params;
+ StackTraceElement[] callStack;
+
+ @Override
+ protected LogEvent clone() {
+ try {
+ return (LogEvent)super.clone();
+ } catch (CloneNotSupportedException x) {
+ throw new RuntimeException(x);
+ }
+ }
+
+
+ }
+
+ public static class LogEventBuilder {
+ private LogEvent event = new LogEvent();
+ public LogEventBuilder level(Level level) {
+ event.level = level;
+ return this;
+ }
+ public LogEventBuilder stack(StackTraceElement... stack) {
+ event.callStack = stack;
+ return this;
+ }
+ public LogEventBuilder bundle(ResourceBundle bundle) {
+ event.bundle = bundle;
+ return this;
+ }
+ public LogEventBuilder msg(String msg) {
+ event.msg = msg;
+ return this;
+ }
+ public LogEventBuilder thrown(Throwable thrown) {
+ event.thrown = thrown;
+ return this;
+ }
+ public LogEventBuilder params(Object... params) {
+ event.params = params;
+ return this;
+ }
+ public LogEvent build() {
+ return event.clone();
+ }
+
+ public LogEventBuilder clear() {
+ event = new LogEvent();
+ return this;
+ }
+
+ }
+
+ Level level = Level.WARNING;
+ Consumer<LogEvent> consumer;
+ final LogEventBuilder builder = new LogEventBuilder();
+
+ @Override
+ public String getName() {
+ return "noname";
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return level.getSeverity() >= this.level.getSeverity();
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
+ builder.clear().level(level).bundle(bundle).msg(msg).thrown(thrown)
+ .stack(new Exception().getStackTrace());
+ consumer.accept(builder.build());
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+ builder.clear().level(level).bundle(bundle).msg(format).params(params)
+ .stack(new Exception().getStackTrace());
+ consumer.accept(builder.build());
+ }
+
+ }
+
+ static class Throwing {
+ @Override
+ public String toString() {
+ throw new RuntimeException("should not have been called");
+ }
+ }
+ static class NotTrowing {
+ private final String toString;
+ private int count = 0;
+ public NotTrowing(String toString) {
+ this.toString = toString;
+ }
+
+ @Override
+ public String toString() {
+ return toString + "[" + (++count) + "]";
+ }
+ }
+
+ public static void main(String[] args) {
+ final LoggerImpl loggerImpl = new LoggerImpl();
+ final System.Logger logger = loggerImpl;
+ final Queue<LoggerImpl.LogEvent> events = new LinkedList<>();
+ loggerImpl.consumer = (x) -> events.add(x);
+
+ System.out.println("\nlogger.isLoggable(Level)");
+ assertTrue(logger.isLoggable(Level.WARNING), "logger.isLoggable(Level.WARNING)"," ");
+ assertFalse(logger.isLoggable(Level.INFO), "logger.isLoggable(Level.INFO)", " ");
+
+
+ System.out.println("\nlogger.log(Level, Object)");
+ for (Level l : Level.values()) {
+ boolean logged = l.compareTo(Level.WARNING) >= 0;
+ Object[][] cases = new Object[][] {
+ {null}, {"baz"}
+ };
+ for (Object[] p : cases) {
+ String msg = (String)p[0];
+ final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
+ String par1 = msg == null ? "(Object)null"
+ : logged ? "new NotTrowing(\""+ msg+"\")" : "new Throwing()";
+ System.out.println(" logger.log(" + l + ", " + par1 + ")");
+ try {
+ logger.log(l, obj);
+ if (obj == null) {
+ throw new RuntimeException("Expected NullPointerException not thrown for"
+ + " logger.log(" + l + ", " + par1 + ")");
+ }
+ } catch (NullPointerException x) {
+ if (obj == null) {
+ System.out.println(" Got expected exception: " + x);
+ continue;
+ } else {
+ throw x;
+ }
+ }
+ LoggerImpl.LogEvent e = events.poll();
+ if (logged) {
+ assertNonNull(e, "e", " ");
+ assertEquals(l, e.level, "e.level", " ");
+ assertToString(e.msg, msg, 1, "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.params, null, "e.params", " ");
+ assertEquals(e.thrown, null, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+ } else {
+ assertEquals(e, null, "e", " ");
+ }
+ }
+ }
+ System.out.println(" logger.log(" + null + ", " +
+ "new NotThrowing(\"foobar\")" + ")");
+ try {
+ logger.log(null, new NotTrowing("foobar"));
+ throw new RuntimeException("Expected NullPointerException not thrown for"
+ + " logger.log(" + null + ", "
+ + "new NotThrowing(\"foobar\")" + ")");
+ } catch (NullPointerException x) {
+ System.out.println(" Got expected exception: " + x);
+ }
+
+
+ System.out.println("\nlogger.log(Level, String)");
+ for (Level l : Level.values()) {
+ boolean logged = l.compareTo(Level.WARNING) >= 0;
+ String par = "bar";
+ System.out.println(" logger.log(" + l + ", \"" + par +"\");");
+ logger.log(l, par);
+ LoggerImpl.LogEvent e = events.poll();
+ assertNonNull(e, "e", " ");
+ assertEquals(e.level, l, "e.level", " ");
+ assertEquals(e.msg, "bar", "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.params, null, "e.params", " ");
+ assertEquals(e.thrown, null, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+
+ System.out.println(" logger.log(" + l + ", (String)null);");
+ logger.log(l, (String)null);
+ e = events.poll();
+ assertNonNull(e, "e", " ");
+ assertEquals(e.level, l, "e.level", " ");
+ assertEquals(e.msg, null, "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.params, null, "e.params", " ");
+ assertEquals(e.thrown, null, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+ }
+
+ System.out.println("\nlogger.log(Level, Supplier<String>)");
+ for (Level l : Level.values()) {
+ boolean logged = l.compareTo(Level.WARNING) >= 0;
+ Object[][] cases = new Object[][] {
+ {null}, {"baz"}
+ };
+ for (Object[] p : cases) {
+ String msg = (String)p[0];
+ final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
+ final Supplier<String> s = msg == null ? null : () -> obj.toString();
+ String par1 = msg == null ? "(Supplier<String>)null"
+ : logged ? "() -> new NotTrowing(\""+ msg+"\").toString()" : "new Throwing()";
+ System.out.println(" logger.log(" + l + ", " + par1 + ")");
+ try {
+ logger.log(l, s);
+ if (s == null) {
+ throw new RuntimeException("Expected NullPointerException not thrown for"
+ + " logger.log(" + l + ", " + par1 + ")");
+ }
+ } catch (NullPointerException x) {
+ if (s == null) {
+ System.out.println(" Got expected exception: " + x);
+ continue;
+ } else {
+ throw x;
+ }
+ }
+ LoggerImpl.LogEvent e = events.poll();
+ if (logged) {
+ assertNonNull(e, "e", " ");
+ assertEquals(l, e.level, "e.level", " ");
+ assertToString(e.msg, msg, 1, "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.params, null, "e.params", " ");
+ assertEquals(e.thrown, null, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+ } else {
+ assertEquals(e, null, "e", " ");
+ }
+ }
+ }
+ System.out.println(" logger.log(" + null + ", " + "() -> \"biz\"" + ")");
+ try {
+ logger.log(null, () -> "biz");
+ throw new RuntimeException("Expected NullPointerException not thrown for"
+ + " logger.log(" + null + ", "
+ + "() -> \"biz\"" + ")");
+ } catch (NullPointerException x) {
+ System.out.println(" Got expected exception: " + x);
+ }
+
+ System.out.println("\nlogger.log(Level, String, Object...)");
+ for (Level l : Level.values()) {
+ boolean logged = l.compareTo(Level.WARNING) >= 0;
+ String par = "bam";
+ Object[] params = null;
+ System.out.println(" logger.log(" + l + ", \"" + par +"\", null);");
+ logger.log(l, par, params);
+ LoggerImpl.LogEvent e = events.poll();
+ assertNonNull(e, "e", " ");
+ assertEquals(l, e.level, "e.level", " ");
+ assertEquals(e.msg, "bam", "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.params, null, "e.params", " ");
+ assertEquals(e.thrown, null, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+
+ params = new Object[] {new NotTrowing("one")};
+ par = "bam {0}";
+ System.out.println(" logger.log(" + l + ", \"" + par
+ + "\", new NotTrowing(\"one\"));");
+ logger.log(l, par, params[0]);
+ e = events.poll();
+ assertNonNull(e, "e", " ");
+ assertEquals(l, e.level, "e.level", " ");
+ assertEquals(e.msg, par, "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertArrayEquals(e.params, params, "e.params", " ");
+ assertEquals(e.thrown, null, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+
+ params = new Object[] {new NotTrowing("fisrt"), new NotTrowing("second")};
+ par = "bam {0} {1}";
+ System.out.println(" logger.log(" + l + ", \"" + par
+ + "\", new NotTrowing(\"fisrt\"),"
+ + " new NotTrowing(\"second\"));");
+ logger.log(l, par, params[0], params[1]);
+ e = events.poll();
+ assertNonNull(e, "e", " ");
+ assertEquals(l, e.level, "e.level", " ");
+ assertEquals(e.msg, par, "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertArrayEquals(e.params, params, "e.params", " ");
+ assertEquals(e.thrown, null, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+
+ params = new Object[] {new NotTrowing("third"), new NotTrowing("fourth")};
+ par = "bam {2}";
+ System.out.println(" logger.log(" + l + ", \"" + par
+ + "\", new Object[] {new NotTrowing(\"third\"),"
+ + " new NotTrowing(\"fourth\")});");
+ logger.log(l, par, params);
+ e = events.poll();
+ assertNonNull(e, "e", " ");
+ assertEquals(l, e.level, "e.level", " ");
+ assertEquals(e.msg, par, "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertArrayEquals(e.params, params, "e.params", " ");
+ assertEquals(e.thrown, null, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(), logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+ }
+
+ System.out.println("\nlogger.log(Level, String, Throwable)");
+ for (Level l : Level.values()) {
+ boolean logged = l.compareTo(Level.WARNING) >= 0;
+ Object[][] cases = new Object[][] {
+ {null, null}, {null, new Throwable()}, {"biz", null}, {"boz", new Throwable()}
+ };
+ for (Object[] p : cases) {
+ String msg = (String)p[0];
+ Throwable thrown = (Throwable)p[1];
+ String par1 = msg == null ? "(String)null" : "\"" + msg + "\"";
+ String par2 = thrown == null ? "(Throwable)null" : "new Throwable()";
+ System.out.println(" logger.log(" + l + ", " + par1 +", " + par2 + ")");
+ logger.log(l, msg, thrown);
+ LoggerImpl.LogEvent e = events.poll();
+ assertNonNull(e, "e", " ");
+ assertEquals(e.level, l, "e.level", " ");
+ assertEquals(e.msg, msg, "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.params, null, "e.params", " ");
+ assertEquals(e.thrown, thrown, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(),
+ "log", "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+ }
+ }
+
+ System.out.println("\nlogger.log(Level, Supplier<String>, Throwable)");
+ for (Level l : Level.values()) {
+ boolean logged = l.compareTo(Level.WARNING) >= 0;
+ Object[][] cases = new Object[][] {
+ {null, null}, {null, new Throwable()}, {"biz", null}, {"boz", new Throwable()}
+ };
+ for (Object[] p : cases) {
+ String msg = (String)p[0];
+ Throwable thrown = (Throwable)p[1];
+ final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
+ final Supplier<String> s = msg == null ? null : () -> obj.toString();
+ String par1 = msg == null ? "(Supplier<String>)null"
+ : logged ? "() -> new NotTrowing(\""+ msg+"\").toString()" : "new Throwing()";
+ String par2 = thrown == null ? "(Throwable)null" : "new Throwable()";
+ System.out.println(" logger.log(" + l + ", " + par1 +", " + par2 + ")");
+ try {
+ logger.log(l, s, thrown);
+ if (s== null) {
+ throw new RuntimeException("Expected NullPointerException not thrown for"
+ + " logger.log(" + l + ", " + par1 +", " + par2 + ")");
+ }
+ } catch (NullPointerException x) {
+ if (s == null) {
+ System.out.println(" Got expected exception: " + x);
+ continue;
+ } else {
+ throw x;
+ }
+ }
+ LoggerImpl.LogEvent e = events.poll();
+ if (logged) {
+ assertNonNull(e, "e", " ");
+ assertEquals(l, e.level, "e.level", " ");
+ assertToString(e.msg, msg, 1, "e.msg", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.params, null, "e.params", " ");
+ assertEquals(e.thrown, thrown, "e.thrown", " ");
+ assertEquals(e.bundle, null, "e.bundle", " ");
+ assertEquals(e.callStack[0].getMethodName(), "log",
+ "e.callStack[0].getMethodName()", " ");
+ assertEquals(e.callStack[0].getClassName(),
+ logger.getClass().getName(),
+ "e.callStack[0].getClassName() ", " ");
+ assertEquals(e.callStack[1].getMethodName(), "log",
+ "e.callStack[1].getMethodName()", " ");
+ assertEquals(e.callStack[1].getClassName(),
+ System.Logger.class.getName(),
+ "e.callStack[1].getClassName() ", " ");
+ assertEquals(e.callStack[2].getMethodName(), "main",
+ "e.callStack[2].getMethodName()", " ");
+ } else {
+ assertEquals(e, null, "e", " ");
+ }
+ }
+ }
+ System.out.println(" logger.log(" + null + ", " + "() -> \"biz\""
+ + ", " + "new Throwable()" + ")");
+ try {
+ logger.log(null, () -> "biz", new Throwable());
+ throw new RuntimeException("Expected NullPointerException not thrown for"
+ + " logger.log(" + null + ", "
+ + "() -> \"biz\"" + ", "
+ + "new Throwable()" + ")");
+ } catch (NullPointerException x) {
+ System.out.println(" Got expected exception: " + x);
+ }
+
+ System.out.println("Checking that we have no spurious events in the queue");
+ assertEquals(events.poll(), null, "events.poll()", " ");
+ }
+
+ static void assertTrue(boolean test, String what, String prefix) {
+ if (!test) {
+ throw new RuntimeException("Expected true for " + what);
+ }
+ System.out.println(prefix + "Got expected " + what + ": " + test);
+ }
+ static void assertFalse(boolean test, String what, String prefix) {
+ if (test) {
+ throw new RuntimeException("Expected false for " + what);
+ }
+ System.out.println(prefix + "Got expected " + what + ": " + test);
+ }
+ static void assertToString(String actual, String expected, int count, String what, String prefix) {
+ assertEquals(actual, expected + "["+count+"]", what, prefix);
+ }
+ static void assertEquals(Object actual, Object expected, String what, String prefix) {
+ if (!Objects.equals(actual, expected)) {
+ throw new RuntimeException("Bad " + what + ":"
+ + "\n\t expected: " + expected
+ + "\n\t actual: " + actual);
+ }
+ System.out.println(prefix + "Got expected " + what + ": " + actual);
+ }
+ static void assertArrayEquals(Object[] actual, Object[] expected, String what, String prefix) {
+ if (!Objects.deepEquals(actual, expected)) {
+ throw new RuntimeException("Bad " + what + ":"
+ + "\n\t expected: " + expected == null ? "null" : Arrays.deepToString(expected)
+ + "\n\t actual: " + actual == null ? "null" : Arrays.deepToString(actual));
+ }
+ System.out.println(prefix + "Got expected " + what + ": " + Arrays.deepToString(actual));
+ }
+ static void assertNonNull(Object actual, String what, String prefix) {
+ if (Objects.equals(actual, null)) {
+ throw new RuntimeException("Bad " + what + ":"
+ + "\n\t expected: non null"
+ + "\n\t actual: " + actual);
+ }
+ System.out.println(prefix + "Got expected " + what + ": " + "non null");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/AccessSystemLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+ public AccessSystemLogger() {
+ this(check());
+ }
+
+ private AccessSystemLogger(Void unused) {
+ }
+
+ private static Void check() {
+ if (AccessSystemLogger.class.getClassLoader() != null) {
+ throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+ }
+ return null;
+ }
+
+ public Logger getLogger(String name) {
+ Logger logger = System.getLogger(name);
+ System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+ return logger;
+ }
+
+ public Logger getLogger(String name, ResourceBundle bundle) {
+ Logger logger = System.getLogger(name, bundle);
+ System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+ return logger;
+ }
+
+ // copy AccessSystemLogger.class to ./boot
+ public static void main(String[] args) throws IOException {
+ Path testDir = Paths.get(System.getProperty("user.dir", "."));
+ Path bootDir = Paths.get(testDir.toString(), "boot");
+ Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+ Path thisClass = Paths.get(classes.toString(),
+ AccessSystemLogger.class.getSimpleName()+".class");
+ if (Files.notExists(bootDir)) {
+ Files.createDirectory(bootDir);
+ }
+ Path dest = Paths.get(bootDir.toString(),
+ AccessSystemLogger.class.getSimpleName()+".class");
+ Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinder.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+
+public class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ @Override
+ public Logger getLogger(String name, Class<?> caller) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+ ClassLoader callerLoader = AccessController.doPrivileged(pa);
+ if (callerLoader == null) {
+ return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ } else {
+ return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinderTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.stream.Stream;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests a naive implementation of LoggerFinder, and in particular
+ * the default body of System.Logger methods.
+ * @build AccessSystemLogger BaseLoggerFinderTest CustomSystemClassLoader BaseLoggerFinder TestLoggerFinder
+ * @run driver AccessSystemLogger
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class BaseLoggerFinderTest {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+ static final Class<?> providerClass;
+ static {
+ try {
+ providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder");
+ } catch (ClassNotFoundException ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ //"NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+ System.out.println("Using provider class: " + providerClass + "[" + providerClass.getClassLoader() + "]");
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ TestLoggerFinder provider;
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ test(provider, true);
+ System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ try {
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ throw new RuntimeException("Expected exception not raised");
+ } catch (AccessControlException x) {
+ if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+ test(provider, false);
+ System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ test(provider, true);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
+ }
+
+ public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
+
+ ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+
+ // 1. Test loggers returned by LoggerFinder, both for system callers
+ // and not system callers.
+ TestLoggerFinder.LoggerImpl appLogger1 = null;
+ try {
+ appLogger1 =
+ TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerFinderTest.class));
+ loggerDescMap.put(appLogger1, "provider.getLogger(\"foo\", BaseLoggerFinderTest.class)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for logger: " + acx);
+ final boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ appLogger1 =
+ TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerFinderTest.class));
+ loggerDescMap.put(appLogger1, "provider.getLogger(\"foo\", BaseLoggerFinderTest.class)");
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+
+ TestLoggerFinder.LoggerImpl sysLogger1 = null;
+ try {
+ sysLogger1 = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+ loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for system logger: " + acx);
+ final boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ sysLogger1 = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+ loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class)");
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+ if (appLogger1 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ if (provider.system.contains(appLogger1)) {
+ throw new RuntimeException("app logger in system map");
+ }
+ if (!provider.user.contains(appLogger1)) {
+ throw new RuntimeException("app logger not in appplication map");
+ }
+ if (provider.user.contains(sysLogger1)) {
+ throw new RuntimeException("sys logger in appplication map");
+ }
+ if (!provider.system.contains(sysLogger1)) {
+ throw new RuntimeException("sys logger not in system map");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", null, appLogger1, appLogger1);
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysLogger1);
+
+ // 2. Test localized loggers returned LoggerFinder, both for system
+ // callers and non system callers
+ Logger appLogger2 = null;
+ try {
+ appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, BaseLoggerFinderTest.class);
+ loggerDescMap.put(appLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, BaseLoggerFinderTest.class)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for logger: " + acx);
+ final boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, BaseLoggerFinderTest.class);
+ loggerDescMap.put(appLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, BaseLoggerFinderTest.class)");
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+
+ Logger sysLogger2 = null;
+ try {
+ sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+ loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for localized system logger: " + acx);
+ final boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+ loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class))");
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+ if (appLogger2 == sysLogger2) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (appLogger2 == appLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (sysLogger2 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ if (provider.system.contains(appLogger2)) {
+ throw new RuntimeException("localized app logger in system map");
+ }
+ if (provider.user.contains(appLogger2)) {
+ throw new RuntimeException("localized app logger in appplication map");
+ }
+ if (provider.user.contains(sysLogger2)) {
+ throw new RuntimeException("localized sys logger in appplication map");
+ }
+ if (provider.system.contains(sysLogger2)) {
+ throw new RuntimeException("localized sys logger not in system map");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appLogger1);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysLogger1);
+
+ // 3 Test loggers returned by:
+ // 3.1: System.getLogger("foo")
+ Logger appLogger3 = System.getLogger("foo");
+ loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")");
+ testLogger(provider, loggerDescMap, "foo", null, appLogger3, appLogger1);
+
+ // 3.2: System.getLogger("foo")
+ // Emulate what System.getLogger() does when the caller is a
+ // platform classes
+ Logger sysLogger3 = accessSystemLogger.getLogger("foo");
+ loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")");
+
+ if (appLogger3 == sysLogger3) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysLogger1);
+
+ // 4. Test loggers returned by:
+ // 4.1 System.getLogger("foo", loggerBundle)
+ Logger appLogger4 =
+ System.getLogger("foo", loggerBundle);
+ loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)");
+ if (appLogger4 == appLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appLogger1);
+
+ // 4.2: System.getLogger("foo", loggerBundle)
+ // Emulate what System.getLogger() does when the caller is a
+ // platform classes
+ Logger sysLogger4 = accessSystemLogger.getLogger("foo", loggerBundle);
+ loggerDescMap.put(sysLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
+ if (appLogger4 == sysLogger4) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysLogger1);
+
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ // Calls the 8 methods defined on Logger and verify the
+ // parameters received by the underlying TestProvider.LoggerImpl
+ // logger.
+ private static void testLogger(TestLoggerFinder provider,
+ Map<Logger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ Logger logger,
+ TestLoggerFinder.LoggerImpl sink) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
+ AtomicLong sequencer = TestLoggerFinder.sequencer;
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, foo): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, messageLevel, (ResourceBundle)null,
+ fooMsg, null, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, foo);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (provider.eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ String msg = "blah";
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, loggerBundle,
+ msg, null, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, msg);
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+
+ Supplier<String> fooSupplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, messageLevel, (ResourceBundle)null,
+ fooSupplier.get(), null,
+ (Throwable)null, (Object[])null);
+ logger.log(messageLevel, fooSupplier);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (provider.eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = msg;
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, loggerBundle,
+ format, null, (Throwable)null, new Object[] {foo, msg});
+ logger.log(messageLevel, format, foo, msg);
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, loggerBundle,
+ msg, null, thrown, (Object[]) null);
+ logger.log(messageLevel, msg, thrown);
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+
+
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0,
+ name, messageLevel, (ResourceBundle)null,
+ fooSupplier.get(), null,
+ (Throwable)thrown, (Object[])null);
+ logger.log(messageLevel, fooSupplier, thrown);
+ if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (provider.eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, bundle,
+ format, null, (Throwable)null, new Object[] {foo, msg});
+ logger.log(messageLevel, bundle, format, foo, msg);
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+ name, messageLevel, bundle,
+ msg, null, thrown, (Object[]) null);
+ logger.log(messageLevel, bundle, msg, thrown);
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+ final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+
+ final Permissions permissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAccess;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
+ this.allowControl = allowControl;
+ this.allowAccess = allowAccess;
+ permissions = new Permissions();
+ }
+
+ Permissions getPermissions() {
+ if (allowControl.get().get() || allowAccess.get().get()) {
+ PermissionsBuilder builder = new PermissionsBuilder()
+ .addAll(permissions);
+ if (allowControl.get().get()) {
+ builder.add(CONTROL);
+ }
+ if (allowAccess.get().get()) {
+ builder.add(ACCESS);
+ }
+ return builder.toPermissions();
+ }
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions().implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/CustomSystemClassLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+ Class<?> finderClass = null;
+
+ public CustomSystemClassLoader() {
+ super();
+ }
+ public CustomSystemClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> defineFinderClass(String name)
+ throws ClassNotFoundException {
+ final Object obj = getClassLoadingLock(name);
+ synchronized(obj) {
+ if (finderClass != null) return finderClass;
+
+ URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+ File file = new File(url.getPath(), name+".class");
+ if (file.canRead()) {
+ try {
+ byte[] b = Files.readAllBytes(file.toPath());
+ Permissions perms = new Permissions();
+ perms.add(new AllPermission());
+ finderClass = defineClass(
+ name, b, 0, b.length, new ProtectionDomain(
+ this.getClass().getProtectionDomain().getCodeSource(),
+ perms));
+ System.out.println("Loaded " + name);
+ return finderClass;
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ throw new ClassNotFoundException(name, ex);
+ }
+ } else {
+ throw new ClassNotFoundException(name,
+ new IOException(file.toPath() + ": can't read"));
+ }
+ }
+ }
+
+ @Override
+ public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (name.equals("BaseLoggerFinder")) {
+ Class<?> c = defineFinderClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("BaseLoggerFinder")) {
+ return defineFinderClass(name);
+ }
+ return super.findClass(name);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1 @@
+BaseLoggerFinder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/TestLoggerFinder.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.Logger;
+
+/**
+ * What our test provider needs to implement.
+ * @author danielfuchs
+ */
+public interface TestLoggerFinder {
+ public final static AtomicLong sequencer = new AtomicLong();
+ public final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+ public final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+ public final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ Logger.Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ Supplier<String> supplier;
+ String msg;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ isLoggable,
+ loggerName,
+ level,
+ bundle,
+ thrown,
+ args,
+ supplier,
+ msg,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Logger.Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = null;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = null;
+ evt.msg = key;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Logger.Level level, ResourceBundle bundle,
+ String key, Object... params) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = null;
+ evt.supplier = null;
+ evt.msg = key;
+ return evt;
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ Logger.Level level, ResourceBundle bundle,
+ String key, Supplier<String> supplier,
+ Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = supplier;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ return evt;
+ }
+
+ }
+
+ public class LoggerImpl implements Logger {
+ final String name;
+ Logger.Level level = Logger.Level.INFO;
+
+ public LoggerImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isLoggable(Logger.Level level) {
+ return this.level != Logger.Level.OFF && this.level.getSeverity() <= level.getSeverity();
+ }
+
+ @Override
+ public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
+ }
+
+ @Override
+ public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) {
+ log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
+ }
+
+ void log(LogEvent event) {
+ eventQueue.add(event);
+ }
+ }
+
+ public Logger getLogger(String name, Class<?> caller);
+ public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/AccessSystemLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+ public AccessSystemLogger() {
+ this(check());
+ }
+
+ private AccessSystemLogger(Void unused) {
+ }
+
+ private static Void check() {
+ if (AccessSystemLogger.class.getClassLoader() != null) {
+ throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+ }
+ return null;
+ }
+
+ public Logger getLogger(String name) {
+ Logger logger = System.getLogger(name);
+ System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+ return logger;
+ }
+
+ public Logger getLogger(String name, ResourceBundle bundle) {
+ Logger logger = System.getLogger(name, bundle);
+ System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+ return logger;
+ }
+
+ public java.util.logging.Logger demandSystemLogger(String name) {
+ return java.util.logging.Logger.getLogger(name);
+ }
+
+ // copy AccessSystemLogger.class to ./boot
+ public static void main(String[] args) throws IOException {
+ Path testDir = Paths.get(System.getProperty("user.dir", "."));
+ Path bootDir = Paths.get(testDir.toString(), "boot");
+ Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+ Path thisClass = Paths.get(classes.toString(),
+ AccessSystemLogger.class.getSimpleName()+".class");
+ if (Files.notExists(bootDir)) {
+ Files.createDirectory(bootDir);
+ }
+ Path dest = Paths.get(bootDir.toString(),
+ AccessSystemLogger.class.getSimpleName()+".class");
+ Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/DefaultLoggerFinderTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,887 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests the default implementation of System.Logger, when
+ * JUL is the default backend.
+ * @build AccessSystemLogger DefaultLoggerFinderTest
+ * @run driver AccessSystemLogger
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class DefaultLoggerFinderTest {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+
+ public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ java.util.logging.Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ String msg;
+ String className;
+ String methodName;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ isLoggable,
+ loggerName,
+ level,
+ bundle,
+ thrown,
+ args,
+ msg,
+ className,
+ methodName,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ java.util.logging.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ return LogEvent.of(sequenceNumber, isLoggable, name,
+ DefaultLoggerFinderTest.class.getName(),
+ "testLogger", level, bundle, key,
+ thrown, params);
+ }
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ String className, String methodName,
+ java.util.logging.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ evt.className = className;
+ evt.methodName = methodName;
+ return evt;
+ }
+
+ }
+
+ static java.util.logging.Level mapToJul(Level level) {
+ switch (level) {
+ case ALL: return java.util.logging.Level.ALL;
+ case TRACE: return java.util.logging.Level.FINER;
+ case DEBUG: return java.util.logging.Level.FINE;
+ case INFO: return java.util.logging.Level.INFO;
+ case WARNING: return java.util.logging.Level.WARNING;
+ case ERROR: return java.util.logging.Level.SEVERE;
+ case OFF: return java.util.logging.Level.OFF;
+ }
+ throw new InternalError("No such level: " + level);
+ }
+
+ static final java.util.logging.Level[] julLevels = {
+ java.util.logging.Level.ALL,
+ new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
+ java.util.logging.Level.FINEST,
+ new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
+ java.util.logging.Level.FINER,
+ new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
+ java.util.logging.Level.FINE,
+ new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
+ java.util.logging.Level.CONFIG,
+ new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
+ java.util.logging.Level.INFO,
+ new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
+ java.util.logging.Level.WARNING,
+ new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
+ java.util.logging.Level.SEVERE,
+ new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
+ java.util.logging.Level.OFF,
+ };
+
+ static final Level[] mappedLevels = {
+ Level.ALL, // ALL
+ Level.DEBUG, // FINER_THAN_FINEST
+ Level.DEBUG, // FINEST
+ Level.DEBUG, // FINER_THAN_FINER
+ Level.TRACE, // FINER
+ Level.TRACE, // FINER_THAN_FINE
+ Level.DEBUG, // FINE
+ Level.DEBUG, // FINER_THAN_CONFIG
+ Level.DEBUG, // CONFIG
+ Level.DEBUG, // FINER_THAN_INFO
+ Level.INFO, // INFO
+ Level.INFO, // FINER_THAN_WARNING
+ Level.WARNING, // WARNING
+ Level.WARNING, // FINER_THAN_SEVERE
+ Level.ERROR, // SEVERE
+ Level.ERROR, // FATAL
+ Level.OFF, // OFF
+ };
+
+ final static Map<java.util.logging.Level, Level> julToSpiMap;
+ static {
+ Map<java.util.logging.Level, Level> map = new HashMap<>();
+ if (mappedLevels.length != julLevels.length) {
+ throw new ExceptionInInitializerError("Array lengths differ"
+ + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+ + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
+ }
+ for (int i=0; i<julLevels.length; i++) {
+ map.put(julLevels[i], mappedLevels[i]);
+ }
+ julToSpiMap = Collections.unmodifiableMap(map);
+ }
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+
+ public static class MyHandler extends Handler {
+
+ @Override
+ public java.util.logging.Level getLevel() {
+ return java.util.logging.Level.ALL;
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+ true, record.getLoggerName(),
+ record.getSourceClassName(),
+ record.getSourceMethodName(),
+ record.getLevel(),
+ record.getResourceBundle(), record.getMessage(),
+ record.getThrown(), record.getParameters()));
+ }
+ @Override
+ public void flush() {
+ }
+ @Override
+ public void close() throws SecurityException {
+ }
+
+ }
+
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowAll, allowControl));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ "NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+ final java.util.logging.Logger appSink = java.util.logging.Logger.getLogger("foo");
+ final java.util.logging.Logger sysSink = accessSystemLogger.demandSystemLogger("foo");
+ appSink.addHandler(new MyHandler());
+ sysSink.addHandler(new MyHandler());
+ appSink.setUseParentHandlers(VERBOSE);
+ sysSink.setUseParentHandlers(VERBOSE);
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ LoggerFinder provider;
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ provider = LoggerFinder.getLoggerFinder();
+ test(provider, true, appSink, sysSink);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ try {
+ provider = LoggerFinder.getLoggerFinder();
+ throw new RuntimeException("Expected exception not raised");
+ } catch (AccessControlException x) {
+ if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = LoggerFinder.getLoggerFinder();
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+ test(provider, false, appSink, sysSink);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = LoggerFinder.getLoggerFinder();
+ test(provider, true, appSink, sysSink);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(LoggerFinder provider,
+ boolean hasRequiredPermissions,
+ java.util.logging.Logger appSink,
+ java.util.logging.Logger sysSink) {
+
+ ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+
+ Logger appLogger1 = null;
+ try {
+ appLogger1 = provider.getLogger("foo", DefaultLoggerFinderTest.class);
+ loggerDescMap.put(appLogger1, "provider.getApplicationLogger(\"foo\")");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for logger: " + acx);
+ boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ appLogger1 =provider.getLogger("foo", DefaultLoggerFinderTest.class);
+ loggerDescMap.put(appLogger1, "provider.getApplicationLogger(\"foo\")");
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+
+ Logger sysLogger1 = null;
+ try {
+ sysLogger1 = provider.getLogger("foo", Thread.class);
+ loggerDescMap.put(sysLogger1, "provider.getSystemLogger(\"foo\")");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for system logger: " + acx);
+ boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ sysLogger1 = provider.getLogger("foo", Thread.class);
+ loggerDescMap.put(sysLogger1, "provider.getSystemLogger(\"foo\")");
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+ if (appLogger1 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ Logger appLogger2 = null;
+ try {
+ appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class);
+ loggerDescMap.put(appLogger2, "provider.getLocalizedApplicationLogger(\"foo\", loggerBundle)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for logger: " + acx);
+ boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class);
+ loggerDescMap.put(appLogger2, "provider.getLocalizedApplicationLogger(\"foo\", loggerBundle)");
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+
+ Logger sysLogger2 = null;
+ try {
+ sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+ loggerDescMap.put(sysLogger2, "provider.getLocalizedSystemLogger(\"foo\", loggerBundle)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for localized system logger: " + acx);
+ boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+ loggerDescMap.put(sysLogger2, "provider.getLocalizedSystemLogger(\"foo\", loggerBundle)");
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+ if (appLogger2 == sysLogger2) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (appLogger2 == appLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (sysLogger2 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+
+ testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
+
+
+ Logger appLogger3 = System.getLogger("foo");
+ loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")");
+
+ testLogger(provider, loggerDescMap, "foo", null, appLogger3, appSink);
+
+ Logger appLogger4 =
+ System.getLogger("foo", loggerBundle);
+ loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)");
+
+ if (appLogger4 == appLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appSink);
+
+ Logger sysLogger3 = accessSystemLogger.getLogger("foo");
+ loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")");
+
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysSink);
+
+ Logger sysLogger4 =
+ accessSystemLogger.getLogger("foo", loggerBundle);
+ loggerDescMap.put(appLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
+
+ if (sysLogger4 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysSink);
+
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
+ boolean before = allowAll.get().get();
+ try {
+ allowAll.get().set(true);
+ sink.setLevel(loggerLevel);
+ } finally {
+ allowAll.get().set(before);
+ }
+ }
+
+
+ // Calls the 8 methods defined on Logger and verify the
+ // parameters received by the underlying Logger Impl
+ // logger.
+ private static void testLogger(LoggerFinder provider,
+ Map<Logger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ Logger logger,
+ java.util.logging.Logger sink) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]");
+ final java.util.logging.Level OFF = java.util.logging.Level.OFF;
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (Level messageLevel : Level.values()) {
+ java.util.logging.Level julLevel = mapToJul(messageLevel);
+ String desc = "logger.log(messageLevel, foo): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ julLevel.intValue() >= loggerLevel.intValue(),
+ name, julLevel, (ResourceBundle)null,
+ fooMsg, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, foo);
+ if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ String msg = "blah";
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (Level messageLevel : Level.values()) {
+ java.util.logging.Level julLevel = mapToJul(messageLevel);
+ String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ julLevel.intValue() >= loggerLevel.intValue(),
+ name, julLevel, loggerBundle,
+ msg, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, msg);
+ if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ Supplier<String> fooSupplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (Level messageLevel : Level.values()) {
+ java.util.logging.Level julLevel = mapToJul(messageLevel);
+ String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ julLevel.intValue() >= loggerLevel.intValue(),
+ name, julLevel, (ResourceBundle)null,
+ fooSupplier.get(),
+ (Throwable)null, (Object[])null);
+ logger.log(messageLevel, fooSupplier);
+ if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = msg;
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (Level messageLevel : Level.values()) {
+ java.util.logging.Level julLevel = mapToJul(messageLevel);
+ String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ julLevel.intValue() >= loggerLevel.intValue(),
+ name, julLevel, loggerBundle,
+ format, (Throwable)null, new Object[] {arg1, arg2});
+ logger.log(messageLevel, format, arg1, arg2);
+ if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (Level messageLevel : Level.values()) {
+ java.util.logging.Level julLevel = mapToJul(messageLevel);
+ String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ julLevel.intValue() >= loggerLevel.intValue(),
+ name, julLevel, loggerBundle,
+ msg, thrown, (Object[]) null);
+ logger.log(messageLevel, msg, thrown);
+ if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (Level messageLevel : Level.values()) {
+ java.util.logging.Level julLevel = mapToJul(messageLevel);
+ String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ julLevel.intValue() >= loggerLevel.intValue(),
+ name, julLevel, (ResourceBundle)null,
+ fooSupplier.get(),
+ (Throwable)thrown, (Object[])null);
+ logger.log(messageLevel, fooSupplier, thrown);
+ if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (Level messageLevel : Level.values()) {
+ java.util.logging.Level julLevel = mapToJul(messageLevel);
+ String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ julLevel.intValue() >= loggerLevel.intValue(),
+ name, julLevel, bundle,
+ format, (Throwable)null, new Object[] {foo, msg});
+ logger.log(messageLevel, bundle, format, foo, msg);
+ if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (Level messageLevel : Level.values()) {
+ java.util.logging.Level julLevel = mapToJul(messageLevel);
+ String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ julLevel.intValue() >= loggerLevel.intValue(),
+ name, julLevel, bundle,
+ msg, thrown, (Object[]) null);
+ logger.log(messageLevel, bundle, msg, thrown);
+ if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+ if (eventQueue.poll() != null) {
+ throw new RuntimeException("unexpected event in queue for " + desc);
+ }
+ } else {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+ }
+ }
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+
+ final Permissions permissions;
+ final Permissions withControlPermissions;
+ final Permissions allPermissions;
+ final ThreadLocal<AtomicBoolean> allowAll;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowAll,
+ ThreadLocal<AtomicBoolean> allowControl) {
+ this.allowAll = allowAll;
+ this.allowControl = allowControl;
+ permissions = new Permissions();
+
+ withControlPermissions = new Permissions();
+ withControlPermissions.add(LOGGERFINDER_PERMISSION);
+
+ // these are used for configuring the test itself...
+ allPermissions = new Permissions();
+ allPermissions.add(new java.security.AllPermission());
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (allowAll.get().get()) return allPermissions.implies(permission);
+ if (allowControl.get().get()) return withControlPermissions.implies(permission);
+ return permissions.implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(
+ allowAll.get().get() ? allPermissions :
+ allowControl.get().get()
+ ? withControlPermissions : permissions).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(
+ allowAll.get().get() ? allPermissions :
+ allowControl.get().get()
+ ? withControlPermissions : permissions).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/AccessSystemLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+ public AccessSystemLogger() {
+ this(check());
+ }
+
+ private AccessSystemLogger(Void unused) {
+ }
+
+ private static Void check() {
+ if (AccessSystemLogger.class.getClassLoader() != null) {
+ throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+ }
+ return null;
+ }
+
+ public Logger getLogger(String name) {
+ Logger logger = System.getLogger(name);
+ System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+ return logger;
+ }
+
+ public Logger getLogger(String name, ResourceBundle bundle) {
+ Logger logger = System.getLogger(name, bundle);
+ System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+ return logger;
+ }
+
+ static final Class<?>[] toCopy = { AccessSystemLogger.class, CustomSystemClassLoader.class };
+
+ // copy AccessSystemLogger.class to ./boot
+ public static void main(String[] args) throws IOException {
+ Path testDir = Paths.get(System.getProperty("user.dir", "."));
+ Path bootDir = Paths.get(testDir.toString(), "boot");
+ Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+ if (Files.notExists(bootDir)) {
+ Files.createDirectory(bootDir);
+ }
+ for (Class<?> c : toCopy) {
+ Path thisClass = Paths.get(classes.toString(),
+ c.getSimpleName()+".class");
+ Path dest = Paths.get(bootDir.toString(),
+ c.getSimpleName()+".class");
+ Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/BaseDefaultLoggerFinderTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.stream.Stream;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicReference;
+import jdk.internal.logger.DefaultLoggerFinder;
+import jdk.internal.logger.SimpleConsoleLogger;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary JDK implementation specific unit test for the base DefaultLoggerFinder.
+ * Tests the behavior of DefaultLoggerFinder and SimpleConsoleLogger
+ * implementation.
+ * @modules java.base/sun.util.logging
+ * java.base/jdk.internal.logger
+ * @build AccessSystemLogger BaseDefaultLoggerFinderTest CustomSystemClassLoader
+ * @run driver AccessSystemLogger
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class BaseDefaultLoggerFinderTest {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+ static final Class<?>[] providerClass;
+ static {
+ try {
+ providerClass = new Class<?>[] {
+ ClassLoader.getSystemClassLoader().loadClass("BaseDefaultLoggerFinderTest$BaseLoggerFinder"),
+ };
+ } catch (ClassNotFoundException ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ /**
+ * What our test provider needs to implement.
+ */
+ public static interface TestLoggerFinder {
+ public final static AtomicBoolean fails = new AtomicBoolean();
+ public final static AtomicReference<String> conf = new AtomicReference<>("");
+ public final static AtomicLong sequencer = new AtomicLong();
+
+
+ public Logger getLogger(String name, Class<?> caller);
+ public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
+ void setLevel(Logger logger, Level level, Class<?> caller);
+ void setLevel(Logger logger, PlatformLogger.Level level, Class<?> caller);
+ PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger);
+ }
+
+ public static class BaseLoggerFinder extends DefaultLoggerFinder implements TestLoggerFinder {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ public BaseLoggerFinder() {
+ if (fails.get()) {
+ throw new RuntimeException("Simulate exception while loading provider");
+ }
+ }
+
+ @Override
+ public void setLevel(Logger logger, Level level, Class<?> caller) {
+ PrivilegedAction<Void> pa = () -> {
+ setLevel(logger, PlatformLogger.toPlatformLevel(level), caller);
+ return null;
+ };
+ AccessController.doPrivileged(pa);
+ }
+
+ @Override
+ public void setLevel(Logger logger, PlatformLogger.Level level, Class<?> caller) {
+ PrivilegedAction<Logger> pa = () -> demandLoggerFor(logger.getName(), caller);
+ Logger impl = AccessController.doPrivileged(pa);
+ SimpleConsoleLogger.class.cast(impl)
+ .getLoggerConfiguration()
+ .setPlatformLevel(level);
+ }
+
+ @Override
+ public PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger) {
+ PrivilegedAction<PlatformLogger.Bridge> pa = () ->
+ PlatformLogger.Bridge.convert(logger);
+ return AccessController.doPrivileged(pa);
+ }
+
+ }
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ static TestLoggerFinder getLoggerFinder(Class<?> expectedClass) {
+ LoggerFinder provider = null;
+ try {
+ TestLoggerFinder.sequencer.incrementAndGet();
+ provider = LoggerFinder.getLoggerFinder();
+ } catch(AccessControlException a) {
+ throw a;
+ }
+ ErrorStream.errorStream.store();
+ System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
+ expectedClass.cast(provider);
+ return TestLoggerFinder.class.cast(provider);
+ }
+
+
+ static class ErrorStream extends PrintStream {
+
+ static AtomicBoolean forward = new AtomicBoolean();
+ ByteArrayOutputStream out;
+ String saved = "";
+ public ErrorStream(ByteArrayOutputStream out) {
+ super(out);
+ this.out = out;
+ }
+
+ @Override
+ public void write(int b) {
+ super.write(b);
+ if (forward.get()) err.write(b);
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ super.write(b);
+ if (forward.get()) err.write(b);
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) {
+ super.write(buf, off, len);
+ if (forward.get()) err.write(buf, off, len);
+ }
+
+ public String peek() {
+ flush();
+ return out.toString();
+ }
+
+ public String drain() {
+ flush();
+ String res = out.toString();
+ out.reset();
+ return res;
+ }
+
+ public void store() {
+ flush();
+ saved = out.toString();
+ out.reset();
+ }
+
+ public void restore() {
+ out.reset();
+ try {
+ out.write(saved.getBytes());
+ } catch(IOException io) {
+ throw new UncheckedIOException(io);
+ }
+ }
+
+ static final PrintStream err = System.err;
+ static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
+ }
+
+ private static StringBuilder appendProperty(StringBuilder b, String name) {
+ String value = System.getProperty(name);
+ if (value == null) return b;
+ return b.append(name).append("=").append(value).append('\n');
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ args = new String[] {
+ //"NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+ }
+ Locale.setDefault(Locale.ENGLISH);
+ System.setErr(ErrorStream.errorStream);
+ //System.setProperty("jdk.logger.finder.error", "ERROR");
+ //System.setProperty("jdk.logger.finder.singleton", "true");
+ //System.setProperty("test.fails", "true");
+ TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
+ StringBuilder c = new StringBuilder();
+ appendProperty(c, "jdk.logger.packages");
+ appendProperty(c, "jdk.logger.finder.error");
+ appendProperty(c, "jdk.logger.finder.singleton");
+ appendProperty(c, "test.fails");
+ TestLoggerFinder.conf.set(c.toString());
+ try {
+ test(args);
+ } finally {
+ try {
+ System.setErr(ErrorStream.err);
+ } catch (Error | RuntimeException x) {
+ x.printStackTrace(ErrorStream.err);
+ }
+ }
+ }
+
+
+ public static void test(String[] args) {
+
+ final Class<?> expectedClass = jdk.internal.logger.DefaultLoggerFinder.class;
+
+ System.out.println("Declared provider class: " + providerClass[0]
+ + "[" + providerClass[0].getClassLoader() + "]");
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ TestLoggerFinder provider;
+ ErrorStream.errorStream.restore();
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ System.out.println(TestLoggerFinder.conf.get());
+ provider = getLoggerFinder(expectedClass);
+ if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
+ throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
+ }
+ test(provider, true);
+ System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ System.out.println(TestLoggerFinder.conf.get());
+ setSecurityManager();
+ try {
+ provider = getLoggerFinder(expectedClass);
+ throw new RuntimeException("Expected exception not raised");
+ } catch (AccessControlException x) {
+ if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = getLoggerFinder(expectedClass);
+ if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
+ throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
+ }
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+ test(provider, false);
+ System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ System.out.println(TestLoggerFinder.conf.get());
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = getLoggerFinder(expectedClass);
+ if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
+ throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
+ }
+ test(provider, true);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
+ }
+
+ public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
+
+ ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+ System.Logger sysLogger = accessSystemLogger.getLogger("foo");
+ loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
+ System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle);
+ loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
+ System.Logger appLogger = System.getLogger("bar");
+ loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
+ System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle);
+ loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
+
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger, accessSystemLogger.getClass());
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger, accessSystemLogger.getClass());
+ testLogger(provider, loggerDescMap, "foo", null, appLogger, BaseDefaultLoggerFinderTest.class);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger, BaseDefaultLoggerFinderTest.class);
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ // Calls the 8 methods defined on Logger and verify the
+ // parameters received by the underlying TestProvider.LoggerImpl
+ // logger.
+ private static void testLogger(TestLoggerFinder provider,
+ Map<Logger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ Logger logger,
+ Class<?> caller) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
+ AtomicLong sequencer = TestLoggerFinder.sequencer;
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ for (Level loggerLevel : Level.values()) {
+ provider.setLevel(logger, loggerLevel, caller);
+ for (Level messageLevel : Level.values()) {
+ ErrorStream.errorStream.drain();
+ String desc = "logger.log(messageLevel, foo): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, foo);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ + messageLevel.getName() + " " + fooMsg
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ String msg = "blah";
+ for (Level loggerLevel : Level.values()) {
+ provider.setLevel(logger, loggerLevel, caller);
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, msg);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
+ if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + msgText)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ + messageLevel.getName() + " " + msgText
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ Supplier<String> fooSupplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+
+ for (Level loggerLevel : Level.values()) {
+ provider.setLevel(logger, loggerLevel, caller);
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, fooSupplier);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ + messageLevel.getName() + " " + fooSupplier.get()
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = msg;
+ for (Level loggerLevel : Level.values()) {
+ provider.setLevel(logger, loggerLevel, caller);
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, format, foo, msg);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
+ String text = java.text.MessageFormat.format(msgFormat, foo, msg);
+ if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ + messageLevel.getName() + " " + text
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ for (Level loggerLevel : Level.values()) {
+ provider.setLevel(logger, loggerLevel, caller);
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, msg, thrown);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ thrown.printStackTrace(new PrintStream(baos));
+ String text = baos.toString();
+ String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
+ if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + msgText)
+ || !logged.contains(text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ + messageLevel.getName() + " " + msgText +"\n"
+ + text
+ + ">>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+
+ for (Level loggerLevel : Level.values()) {
+ provider.setLevel(logger, loggerLevel, caller);
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, fooSupplier, thrown);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ thrown.printStackTrace(new PrintStream(baos));
+ String text = baos.toString();
+ if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
+ || !logged.contains(text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ + messageLevel.getName() + " " + fooSupplier.get() +"\n"
+ + text
+ + ">>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ for (Level loggerLevel : Level.values()) {
+ provider.setLevel(logger, loggerLevel, caller);
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, bundle, format, foo, msg);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
+ if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ + messageLevel.getName() + " " + text
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ for (Level loggerLevel : Level.values()) {
+ provider.setLevel(logger, loggerLevel, caller);
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, bundle, msg, thrown);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ String textMsg = bundle.getString(msg);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ thrown.printStackTrace(new PrintStream(baos));
+ String text = baos.toString();
+ if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + textMsg)
+ || !logged.contains(text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ + messageLevel.getName() + " " + textMsg +"\n"
+ + text
+ + ">>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+ final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+
+ final Permissions permissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAccess;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
+ this.allowControl = allowControl;
+ this.allowAccess = allowAccess;
+ permissions = new Permissions();
+ permissions.add(new RuntimePermission("setIO"));
+ }
+
+ Permissions getPermissions() {
+ if (allowControl.get().get() || allowAccess.get().get()) {
+ PermissionsBuilder builder = new PermissionsBuilder()
+ .addAll(permissions);
+ if (allowControl.get().get()) {
+ builder.add(CONTROL);
+ }
+ if (allowAccess.get().get()) {
+ builder.add(ACCESS);
+ }
+ return builder.toPermissions();
+ }
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions().implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/CustomSystemClassLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions. The CustomSystemClassLoader class must be
+ * in the BCL, otherwise when system classes - such as
+ * ZoneDateTime try to load their resource bundle a MissingResourceBundle
+ * caused by a SecurityException may be thrown, as the CustomSystemClassLoader
+ * code base will be found in the stack called by doPrivileged.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+ final List<String> finderClassNames =
+ Arrays.asList("BaseDefaultLoggerFinderTest$BaseLoggerFinder");
+ final Map<String, Class<?>> finderClasses = new HashMap<>();
+ Class<?> testLoggerFinderClass;
+
+ public CustomSystemClassLoader() {
+ super();
+ }
+ public CustomSystemClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> defineFinderClass(String name)
+ throws ClassNotFoundException {
+ final Object obj = getClassLoadingLock(name);
+ synchronized(obj) {
+ if (finderClasses.get(name) != null) return finderClasses.get(name);
+ if (testLoggerFinderClass == null) {
+ // Hack: we load testLoggerFinderClass to get its code source.
+ // we can't use this.getClass() since we are in the boot.
+ testLoggerFinderClass = super.loadClass("BaseDefaultLoggerFinderTest$TestLoggerFinder");
+ }
+ URL url = testLoggerFinderClass.getProtectionDomain().getCodeSource().getLocation();
+ File file = new File(url.getPath(), name+".class");
+ if (file.canRead()) {
+ try {
+ byte[] b = Files.readAllBytes(file.toPath());
+ Permissions perms = new Permissions();
+ perms.add(new AllPermission());
+ Class<?> finderClass = defineClass(
+ name, b, 0, b.length, new ProtectionDomain(
+ this.getClass().getProtectionDomain().getCodeSource(),
+ perms));
+ System.out.println("Loaded " + name);
+ finderClasses.put(name, finderClass);
+ return finderClass;
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ throw new ClassNotFoundException(name, ex);
+ }
+ } else {
+ throw new ClassNotFoundException(name,
+ new IOException(file.toPath() + ": can't read"));
+ }
+ }
+ }
+
+ @Override
+ public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (finderClassNames.contains(name)) {
+ Class<?> c = defineFinderClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (finderClassNames.contains(name)) {
+ return defineFinderClass(name);
+ }
+ return super.findClass(name);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1 @@
+BaseDefaultLoggerFinderTest$BaseLoggerFinder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/BaseLoggerBridgeTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1058 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import sun.util.logging.PlatformLogger;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ * Tests a naive implementation of System.Logger, and in particular
+ * the default mapping provided by PlatformLogger.Bridge.
+ * @modules java.base/sun.util.logging java.base/jdk.internal.logger
+ * @build CustomSystemClassLoader BaseLoggerBridgeTest
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOSECURITY
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOPERMISSIONS
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class BaseLoggerBridgeTest {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ // whether the implementation of Logger try to do a best
+ // effort for logp... Our base logger finder stub doesn't
+ // support logp, and thus the logp() implementation comes from
+ // LoggerWrapper - which does a best effort.
+ static final boolean BEST_EFFORT_FOR_LOGP = true;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ static final Class<?> providerClass;
+ static {
+ try {
+ providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerBridgeTest$BaseLoggerFinder");
+ } catch (ClassNotFoundException ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ static final sun.util.logging.PlatformLogger.Level[] julLevels = {
+ sun.util.logging.PlatformLogger.Level.ALL,
+ sun.util.logging.PlatformLogger.Level.FINEST,
+ sun.util.logging.PlatformLogger.Level.FINER,
+ sun.util.logging.PlatformLogger.Level.FINE,
+ sun.util.logging.PlatformLogger.Level.CONFIG,
+ sun.util.logging.PlatformLogger.Level.INFO,
+ sun.util.logging.PlatformLogger.Level.WARNING,
+ sun.util.logging.PlatformLogger.Level.SEVERE,
+ sun.util.logging.PlatformLogger.Level.OFF,
+ };
+
+ static final Level[] mappedLevels = {
+ Level.ALL, // ALL
+ Level.TRACE, // FINEST
+ Level.TRACE, // FINER
+ Level.DEBUG, // FINE
+ Level.DEBUG, // CONFIG
+ Level.INFO, // INFO
+ Level.WARNING, // WARNING
+ Level.ERROR, // SEVERE
+ Level.OFF, // OFF
+ };
+
+ final static Map<sun.util.logging.PlatformLogger.Level, Level> julToSpiMap;
+ static {
+ Map<sun.util.logging.PlatformLogger.Level, Level> map = new HashMap<>();
+ if (mappedLevels.length != julLevels.length) {
+ throw new ExceptionInInitializerError("Array lengths differ"
+ + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+ + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
+ }
+ for (int i=0; i<julLevels.length; i++) {
+ map.put(julLevels[i], mappedLevels[i]);
+ }
+ julToSpiMap = Collections.unmodifiableMap(map);
+ }
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ public static interface TestLoggerFinder {
+ final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+ final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+ public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent implements Cloneable {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ boolean callSupplier = false;
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ Supplier<String> supplier;
+ String msg;
+
+ Object[] toArray(boolean callSupplier) {
+ return new Object[] {
+ sequenceNumber,
+ isLoggable,
+ loggerName,
+ level,
+ bundle,
+ thrown,
+ args,
+ callSupplier && supplier != null ? supplier.get() : supplier,
+ msg,
+ };
+ }
+
+ boolean callSupplier(Object obj) {
+ return callSupplier || ((LogEvent)obj).callSupplier;
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray(false));
+ }
+
+
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(toArray(callSupplier(obj)), ((LogEvent)obj).toArray(callSupplier(obj)));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray(true));
+ }
+
+ public LogEvent cloneWith(long sequenceNumber)
+ throws CloneNotSupportedException {
+ LogEvent cloned = (LogEvent)super.clone();
+ cloned.sequenceNumber = sequenceNumber;
+ return cloned;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = null;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = null;
+ evt.msg = key;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Level level, Throwable thrown, Supplier<String> supplier) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = null;
+ evt.bundle = null;
+ evt.thrown = thrown;
+ evt.supplier = supplier;
+ evt.msg = null;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Object... params) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = null;
+ evt.supplier = null;
+ evt.msg = key;
+ return evt;
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Supplier<String> supplier,
+ Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = supplier;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ return evt;
+ }
+
+ public static LogEvent ofp(boolean callSupplier, LogEvent evt) {
+ evt.callSupplier = callSupplier;
+ return evt;
+ }
+ }
+
+ public class LoggerImpl implements Logger {
+ private final String name;
+ private Level level = Level.INFO;
+
+ public LoggerImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+ log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
+ }
+
+ void log(LogEvent event) {
+ eventQueue.add(event);
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
+ }
+
+
+
+ }
+
+ public Logger getLogger(String name, Class<?> caller);
+ public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
+ }
+
+ public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ @Override
+ public Logger getLogger(String name, Class<?> caller) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+ ClassLoader callerLoader = AccessController.doPrivileged(pa);
+ if (callerLoader == null) {
+ return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ } else {
+ return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ }
+ }
+ }
+
+ static PlatformLogger.Bridge convert(Logger logger) {
+ boolean old = allowAll.get().get();
+ allowAccess.get().set(true);
+ try {
+ return PlatformLogger.Bridge.convert(logger);
+ } finally {
+ allowAccess.get().set(old);
+ }
+ }
+
+ static Logger getLogger(String name, Class<?> caller) {
+ boolean old = allowAll.get().get();
+ allowAccess.get().set(true);
+ try {
+ return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
+ } finally {
+ allowAccess.get().set(old);
+ }
+ }
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ // Ugly test hack: preload the resources needed by String.format
+ // We need to do that before setting the security manager
+ // because our implementation of CustomSystemClassLoader
+ // doesn't have the required permission.
+ System.out.println(String.format("debug: %s", "Setting security manager"));
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ "NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ TestLoggerFinder provider;
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ test(provider, true);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ try {
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ throw new RuntimeException("Expected exception not raised");
+ } catch (AccessControlException x) {
+ if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+ test(provider, false);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ test(provider, true);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
+
+ ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Object, String> loggerDescMap = new HashMap<>();
+
+
+ TestLoggerFinder.LoggerImpl appSink = null;
+ try {
+ appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class));
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for logger: " + acx);
+ boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class));
+ } finally {
+ allowControl.get().set(old);
+ }
+ }
+
+
+ TestLoggerFinder.LoggerImpl sysSink = null;
+ try {
+ sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for system logger: " + acx);
+ }
+ if (hasRequiredPermissions && appSink == sysSink) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ if (provider.system.contains(appSink)) {
+ throw new RuntimeException("app logger in system map");
+ }
+ if (!provider.user.contains(appSink)) {
+ throw new RuntimeException("app logger not in appplication map");
+ }
+ if (hasRequiredPermissions && provider.user.contains(sysSink)) {
+ throw new RuntimeException("sys logger in appplication map");
+ }
+ if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
+ throw new RuntimeException("sys logger not in system map");
+ }
+
+ Logger appLogger1 = System.getLogger("foo");
+ loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
+ PlatformLogger.Bridge bridge = convert(appLogger1);
+ loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))");
+ testLogger(provider, loggerDescMap, "foo", null, bridge, appSink);
+
+ Logger sysLogger1 = null;
+ try {
+ sysLogger1 = getLogger("foo", Thread.class);
+ loggerDescMap.put(sysLogger1,
+ "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class)");
+
+ if (!hasRequiredPermissions) {
+ // check that the provider would have thrown an exception
+ provider.getLogger("foo", Thread.class);
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for system logger: " + acx);
+ }
+
+ if (hasRequiredPermissions) {
+ // if we don't have permissions sysSink will be null.
+ testLogger(provider, loggerDescMap, "foo", null,
+ PlatformLogger.Bridge.convert(sysLogger1), sysSink);
+ }
+
+ Logger appLogger2 =
+ System.getLogger("foo", loggerBundle);
+ loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
+
+ if (appLogger2 == appLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ if (provider.system.contains(appLogger2)) {
+ throw new RuntimeException("localized app logger in system map");
+ }
+ if (provider.user.contains(appLogger2)) {
+ throw new RuntimeException("localized app logger in appplication map");
+ }
+
+ testLogger(provider, loggerDescMap, "foo", loggerBundle,
+ PlatformLogger.Bridge.convert(appLogger2), appSink);
+
+ Logger sysLogger2 = null;
+ try {
+ sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+ loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for localized system logger: " + acx);
+ }
+ if (hasRequiredPermissions && appLogger2 == sysLogger2) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (hasRequiredPermissions && provider.user.contains(sysLogger2)) {
+ throw new RuntimeException("localized sys logger in appplication map");
+ }
+ if (hasRequiredPermissions && provider.system.contains(sysLogger2)) {
+ throw new RuntimeException("localized sys logger not in system map");
+ }
+
+ if (hasRequiredPermissions) {
+ // if we don't have permissions sysSink will be null.
+ testLogger(provider, loggerDescMap, "foo", loggerBundle,
+ PlatformLogger.Bridge.convert(sysLogger2), sysSink);
+ }
+
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ static void checkLogEvent(TestLoggerFinder provider, String desc,
+ TestLoggerFinder.LogEvent expected) {
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!Objects.equals(expected, actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void checkLogEvent(TestLoggerFinder provider, String desc,
+ TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (actual == null && !expectNotNull) return;
+ if (actual != null && !expectNotNull) {
+ throw new RuntimeException("Unexpected log event found for " + desc
+ + "\n\tgot: " + actual);
+ }
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static Supplier<String> logpMessage(ResourceBundle bundle,
+ String className, String methodName, Supplier<String> msg) {
+ if (BEST_EFFORT_FOR_LOGP && bundle == null
+ && (className != null || methodName != null)) {
+ final String cName = className == null ? "" : className;
+ final String mName = methodName == null ? "" : methodName;
+ return () -> String.format("[%s %s] %s", cName, mName, msg.get());
+ } else {
+ return msg;
+ }
+ }
+
+ static String logpMessage(ResourceBundle bundle,
+ String className, String methodName, String msg) {
+ if (BEST_EFFORT_FOR_LOGP && bundle == null
+ && (className != null || methodName != null)) {
+ final String cName = className == null ? "" : className;
+ final String mName = methodName == null ? "" : methodName;
+ return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg);
+ } else {
+ return msg;
+ }
+ }
+
+ // Calls the methods defined on LogProducer and verify the
+ // parameters received by the underlying TestLoggerFinder.LoggerImpl
+ // logger.
+ private static void testLogger(TestLoggerFinder provider,
+ Map<Object, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ PlatformLogger.Bridge logger,
+ TestLoggerFinder.LoggerImpl sink) {
+
+ if (loggerDescMap.get(logger) == null) {
+ throw new RuntimeException("Test bug: Missing description");
+ }
+ System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]");
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ System.out.println("\tlogger.log(messageLevel, fooMsg)");
+ System.out.println("\tlogger.<level>(fooMsg)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, loggerBundle,
+ fooMsg, null, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, fooMsg);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ Supplier<String> supplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+ System.out.println("\tlogger.log(messageLevel, supplier)");
+ System.out.println("\tlogger.<level>(supplier)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, (ResourceBundle) null,
+ null, supplier, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, supplier);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = fooMsg;
+ System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, loggerBundle,
+ format, null, (Throwable)null, arg1, arg2);
+ logger.log(messageLevel, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, loggerBundle,
+ fooMsg, null, thrown, (Object[])null);
+ logger.log(messageLevel, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, (ResourceBundle)null,
+ null, supplier, thrown, (Object[])null);
+ logger.log(messageLevel, thrown, supplier);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ String sourceClass = "blah.Blah";
+ String sourceMethod = "blih";
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+ TestLoggerFinder.LogEvent expected =
+ isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ isLoggable,
+ name, expectedMessageLevel, loggerBundle,
+ logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
+ null, (Throwable)null, (Object[]) null) : null;
+ logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+ TestLoggerFinder.LogEvent expected = isLoggable ?
+ TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ isLoggable,
+ name, expectedMessageLevel, null, null,
+ logpMessage(null, sourceClass, sourceMethod, supplier),
+ (Throwable)null, (Object[]) null)) : null;
+ logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+ TestLoggerFinder.LogEvent expected =
+ isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, loggerBundle,
+ logpMessage(loggerBundle, sourceClass, sourceMethod, format),
+ null, (Throwable)null, arg1, arg2) : null;
+ logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+ TestLoggerFinder.LogEvent expected =
+ isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ?
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, loggerBundle,
+ logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
+ null, thrown, (Object[])null) : null;
+ logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+ TestLoggerFinder.LogEvent expected = isLoggable ?
+ TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, null, null,
+ logpMessage(null, sourceClass, sourceMethod, supplier),
+ thrown, (Object[])null)) : null;
+ logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, bundle,
+ format, null, (Throwable)null, arg1, arg2);
+ logger.logrb(messageLevel, bundle, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, bundle,
+ fooMsg, null, thrown, (Object[])null);
+ logger.logrb(messageLevel, bundle, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, bundle,
+ format, null, (Throwable)null, arg1, arg2);
+ logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, bundle,
+ fooMsg, null, thrown, (Object[])null);
+ logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+ final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+ final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+ final Permissions permissions;
+ final Permissions allPermissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAccess;
+ final ThreadLocal<AtomicBoolean> allowAll;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+ ThreadLocal<AtomicBoolean> allowAccess,
+ ThreadLocal<AtomicBoolean> allowAll) {
+ this.allowControl = allowControl;
+ this.allowAccess = allowAccess;
+ this.allowAll = allowAll;
+ permissions = new Permissions();
+ allPermissions = new PermissionsBuilder()
+ .add(new java.security.AllPermission())
+ .toPermissions();
+ }
+
+ Permissions getPermissions() {
+ if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+ PermissionsBuilder builder = new PermissionsBuilder()
+ .addAll(permissions);
+ if (allowControl.get().get()) {
+ builder.add(CONTROL);
+ }
+ if (allowAccess.get().get()) {
+ builder.add(ACCESS_LOGGER);
+ builder.add(ACCESS_LOGGING);
+ }
+ if (allowAll.get().get()) {
+ builder.addAll(allPermissions);
+ }
+ return builder.toPermissions();
+ }
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions().implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/CustomSystemClassLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+ Class<?> finderClass = null;
+
+ public CustomSystemClassLoader() {
+ super();
+ }
+ public CustomSystemClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> defineFinderClass(String name)
+ throws ClassNotFoundException {
+ final Object obj = getClassLoadingLock(name);
+ synchronized(obj) {
+ if (finderClass != null) return finderClass;
+
+ URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+ File file = new File(url.getPath(), name+".class");
+ if (file.canRead()) {
+ try {
+ byte[] b = Files.readAllBytes(file.toPath());
+ Permissions perms = new Permissions();
+ perms.add(new AllPermission());
+ finderClass = defineClass(
+ name, b, 0, b.length, new ProtectionDomain(
+ this.getClass().getProtectionDomain().getCodeSource(),
+ perms));
+ System.out.println("Loaded " + name);
+ return finderClass;
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ throw new ClassNotFoundException(name, ex);
+ }
+ } else {
+ throw new ClassNotFoundException(name,
+ new IOException(file.toPath() + ": can't read"));
+ }
+ }
+ }
+
+ @Override
+ public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (name.endsWith("$BaseLoggerFinder")) {
+ Class<?> c = defineFinderClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.endsWith("$BaseLoggerFinder")) {
+ return defineFinderClass(name);
+ }
+ return super.findClass(name);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1 @@
+BaseLoggerBridgeTest$BaseLoggerFinder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/BasePlatformLoggerTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,732 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.security.AccessControlException;
+import java.util.stream.Stream;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary JDK implementation specific unit test for JDK internal API.
+ * Tests a naive implementation of System.Logger, and in particular
+ * the default mapping provided by PlatformLogger.
+ * @modules java.base/sun.util.logging
+ * @build CustomSystemClassLoader BasePlatformLoggerTest
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest NOSECURITY
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest NOPERMISSIONS
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class BasePlatformLoggerTest {
+
+ public static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ static final Class<?> providerClass;
+ static {
+ try {
+ providerClass = ClassLoader.getSystemClassLoader().loadClass("BasePlatformLoggerTest$BaseLoggerFinder");
+ } catch (ClassNotFoundException ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ static final PlatformLogger.Level[] julLevels = {
+ PlatformLogger.Level.ALL,
+ PlatformLogger.Level.FINEST,
+ PlatformLogger.Level.FINER,
+ PlatformLogger.Level.FINE,
+ PlatformLogger.Level.CONFIG,
+ PlatformLogger.Level.INFO,
+ PlatformLogger.Level.WARNING,
+ PlatformLogger.Level.SEVERE,
+ PlatformLogger.Level.OFF,
+ };
+
+ static final Level[] mappedLevels = {
+ Level.ALL, // ALL
+ Level.TRACE, // FINEST
+ Level.TRACE, // FINER
+ Level.DEBUG, // FINE
+ Level.DEBUG, // CONFIG
+ Level.INFO, // INFO
+ Level.WARNING, // WARNING
+ Level.ERROR, // SEVERE
+ Level.OFF, // OFF
+ };
+
+ final static Map<PlatformLogger.Level, Level> julToSpiMap;
+ static {
+ Map<PlatformLogger.Level, Level> map = new HashMap<>();
+ if (mappedLevels.length != julLevels.length) {
+ throw new ExceptionInInitializerError("Array lengths differ"
+ + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+ + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
+ }
+ for (int i=0; i<julLevels.length; i++) {
+ map.put(julLevels[i], mappedLevels[i]);
+ }
+ julToSpiMap = Collections.unmodifiableMap(map);
+ }
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+
+ public static interface TestLoggerFinder {
+ final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+ final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+ public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent implements Cloneable {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ Supplier<String> supplier;
+ String msg;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ isLoggable,
+ loggerName,
+ level,
+ bundle,
+ thrown,
+ args,
+ supplier,
+ msg,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+
+ public LogEvent cloneWith(long sequenceNumber)
+ throws CloneNotSupportedException {
+ LogEvent cloned = (LogEvent)super.clone();
+ cloned.sequenceNumber = sequenceNumber;
+ return cloned;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = null;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = null;
+ evt.msg = key;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Level level, Throwable thrown, Supplier<String> supplier) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = null;
+ evt.bundle = null;
+ evt.thrown = thrown;
+ evt.supplier = supplier;
+ evt.msg = null;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Object... params) {
+ LogEvent evt = new LogEvent();
+ evt.isLoggable = isLoggable;
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = null;
+ evt.supplier = null;
+ evt.msg = key;
+ return evt;
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ Level level, ResourceBundle bundle,
+ String key, Supplier<String> supplier,
+ Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = supplier;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ return evt;
+ }
+
+ }
+
+ public class LoggerImpl implements Logger {
+ private final String name;
+ private Level level = Level.INFO;
+
+ public LoggerImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+ log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
+ }
+
+ void log(LogEvent event) {
+ eventQueue.add(event);
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
+ }
+ }
+
+ public Logger getLogger(String name, Class<?> caller);
+ }
+
+ public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
+ @Override
+ public Logger getLogger(String name, Class<?> caller) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+ ClassLoader callerLoader = AccessController.doPrivileged(pa);
+ if (callerLoader == null) {
+ return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ } else {
+ return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ }
+ }
+ }
+
+ static PlatformLogger getPlatformLogger(String name) {
+ boolean old = allowAccess.get().get();
+ allowAccess.get().set(true);
+ try {
+ return PlatformLogger.getLogger(name);
+ } finally {
+ allowAccess.get().set(old);
+ }
+ }
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ "NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ TestLoggerFinder provider;
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ test(provider, true);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ try {
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ throw new RuntimeException("Expected exception not raised");
+ } catch (AccessControlException x) {
+ if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+ test(provider, false);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+ test(provider, true);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
+
+ final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
+
+ TestLoggerFinder.LoggerImpl appSink;
+ boolean before = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ appSink = TestLoggerFinder.LoggerImpl.class.cast(
+ provider.getLogger("foo", BasePlatformLoggerTest.class));
+ } finally {
+ allowControl.get().set(before);
+ }
+
+ TestLoggerFinder.LoggerImpl sysSink = null;
+ before = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+ } finally {
+ allowControl.get().set(before);
+ }
+
+ if (hasRequiredPermissions && appSink == sysSink) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ if (provider.system.contains(appSink)) {
+ throw new RuntimeException("app logger in system map");
+ }
+ if (!provider.user.contains(appSink)) {
+ throw new RuntimeException("app logger not in appplication map");
+ }
+ if (hasRequiredPermissions && provider.user.contains(sysSink)) {
+ throw new RuntimeException("sys logger in appplication map");
+ }
+ if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
+ throw new RuntimeException("sys logger not in system map");
+ }
+
+ PlatformLogger platform = getPlatformLogger("foo");
+ loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")");
+
+ testLogger(provider, loggerDescMap, "foo", null, platform, sysSink);
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ static void checkLogEvent(TestLoggerFinder provider, String desc,
+ TestLoggerFinder.LogEvent expected) {
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void checkLogEvent(TestLoggerFinder provider, String desc,
+ TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
+ TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
+ if (actual == null && !expectNotNull) return;
+ if (actual != null && !expectNotNull) {
+ throw new RuntimeException("Unexpected log event found for " + desc
+ + "\n\tgot: " + actual);
+ }
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ // Calls the methods defined on LogProducer and verify the
+ // parameters received by the underlying TestLoggerFinder.LoggerImpl
+ // logger.
+ private static void testLogger(TestLoggerFinder provider,
+ Map<PlatformLogger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ PlatformLogger logger,
+ TestLoggerFinder.LoggerImpl sink) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger));
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ System.out.println("\tlogger.<level>(fooMsg)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (PlatformLogger.Level messageLevel :julLevels) {
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, loggerBundle,
+ fooMsg, null, (Throwable)null, (Object[])null);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == PlatformLogger.Level.FINEST) {
+ logger.finest(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.FINER) {
+ logger.finer(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.FINE) {
+ logger.fine(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.CONFIG) {
+ logger.config(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.INFO) {
+ logger.info(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.WARNING) {
+ logger.warning(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.SEVERE) {
+ logger.severe(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ System.out.println("\tlogger.<level>(msg, thrown)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (PlatformLogger.Level messageLevel :julLevels) {
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, (ResourceBundle) null,
+ fooMsg, null, (Throwable)thrown, (Object[])null);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(msg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == PlatformLogger.Level.FINEST) {
+ logger.finest(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.FINER) {
+ logger.finer(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.FINE) {
+ logger.fine(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.CONFIG) {
+ logger.config(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.INFO) {
+ logger.info(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.WARNING) {
+ logger.warning(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.SEVERE) {
+ logger.severe(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ }
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = fooMsg;
+ System.out.println("\tlogger.<level>(format, arg1, arg2)");
+ for (Level loggerLevel : Level.values()) {
+ sink.level = loggerLevel;
+ for (PlatformLogger.Level messageLevel :julLevels) {
+ Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+ TestLoggerFinder.LogEvent expected =
+ TestLoggerFinder.LogEvent.of(
+ sequencer.get(),
+ loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+ name, expectedMessageLevel, (ResourceBundle) null,
+ format, null, (Throwable)null, foo, fooMsg);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(format, foo, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == PlatformLogger.Level.FINEST) {
+ logger.finest(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.FINER) {
+ logger.finer(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.FINE) {
+ logger.fine(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.CONFIG) {
+ logger.config(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.INFO) {
+ logger.info(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.WARNING) {
+ logger.warning(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == PlatformLogger.Level.SEVERE) {
+ logger.severe(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ }
+ }
+ }
+
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+ final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+ final Permissions permissions;
+ final Permissions allPermissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAccess;
+ final ThreadLocal<AtomicBoolean> allowAll;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+ ThreadLocal<AtomicBoolean> allowAccess,
+ ThreadLocal<AtomicBoolean> allowAll) {
+ this.allowControl = allowControl;
+ this.allowAccess = allowAccess;
+ this.allowAll = allowAll;
+ permissions = new Permissions();
+ allPermissions = new PermissionsBuilder()
+ .add(new java.security.AllPermission())
+ .toPermissions();
+ }
+
+ Permissions getPermissions() {
+ if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+ PermissionsBuilder builder = new PermissionsBuilder()
+ .addAll(permissions);
+ if (allowControl.get().get()) {
+ builder.add(CONTROL);
+ }
+ if (allowAccess.get().get()) {
+ builder.add(ACCESS_LOGGING);
+ }
+ if (allowAll.get().get()) {
+ builder.addAll(allPermissions);
+ }
+ return builder.toPermissions();
+ }
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions().implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/CustomSystemClassLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+ Class<?> finderClass = null;
+
+ public CustomSystemClassLoader() {
+ super();
+ }
+ public CustomSystemClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> defineFinderClass(String name)
+ throws ClassNotFoundException {
+ final Object obj = getClassLoadingLock(name);
+ synchronized(obj) {
+ if (finderClass != null) return finderClass;
+
+ URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+ File file = new File(url.getPath(), name+".class");
+ if (file.canRead()) {
+ try {
+ byte[] b = Files.readAllBytes(file.toPath());
+ Permissions perms = new Permissions();
+ perms.add(new AllPermission());
+ finderClass = defineClass(
+ name, b, 0, b.length, new ProtectionDomain(
+ this.getClass().getProtectionDomain().getCodeSource(),
+ perms));
+ System.out.println("Loaded " + name);
+ return finderClass;
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ throw new ClassNotFoundException(name, ex);
+ }
+ } else {
+ throw new ClassNotFoundException(name,
+ new IOException(file.toPath() + ": can't read"));
+ }
+ }
+ }
+
+ @Override
+ public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (name.endsWith("$BaseLoggerFinder")) {
+ Class<?> c = defineFinderClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.endsWith("$BaseLoggerFinder")) {
+ return defineFinderClass(name);
+ }
+ return super.findClass(name);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1 @@
+BasePlatformLoggerTest$BaseLoggerFinder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BooleanSupplier;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import jdk.internal.logger.BootstrapLogger;
+import jdk.internal.logger.LazyLoggers;
+
+/*
+ * @test
+ * @bug 8140364
+ * @author danielfuchs
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ Tests the behavior of bootstrap loggers (and SimpleConsoleLoggers
+ * too).
+ * @modules java.base/jdk.internal.logger
+ * @run main/othervm BootstrapLoggerTest NO_SECURITY
+ * @run main/othervm BootstrapLoggerTest SECURE
+ * @run main/othervm/timeout=120 BootstrapLoggerTest SECURE_AND_WAIT
+ */
+public class BootstrapLoggerTest {
+
+ static final Method awaitPending;
+ static final Method isAlive;
+ static final Field isBooted;
+ static final Field logManagerInitialized;
+ static {
+ try {
+ isBooted = BootstrapLogger.class.getDeclaredField("isBooted");
+ isBooted.setAccessible(true);
+ // private reflection hook that allows us to test wait until all
+ // the tasks pending in the BootstrapExecutor are finished.
+ awaitPending = BootstrapLogger.class
+ .getDeclaredMethod("awaitPendingTasks");
+ awaitPending.setAccessible(true);
+ // private reflection hook that allows us to test whether
+ // the BootstrapExecutor is alive.
+ isAlive = BootstrapLogger.class
+ .getDeclaredMethod("isAlive");
+ isAlive.setAccessible(true);
+ // private reflection hook that allows us to test whether the LogManager
+ // has initialized and registered with the BootstrapLogger class
+ logManagerInitialized = BootstrapLogger.class
+ .getDeclaredField("logManagerConfigured");
+ logManagerInitialized.setAccessible(true);
+ } catch (Exception ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ static void awaitPending() {
+ try {
+ awaitPending.invoke(null);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+ ex.printStackTrace(LogStream.err);
+ }
+ }
+
+ /**
+ * We use an instance of this class to check what the logging system has
+ * printed on System.err.
+ */
+ public static class LogStream extends OutputStream {
+
+ final static PrintStream err = System.err;
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ public LogStream() {
+ super();
+ }
+
+ @Override
+ public synchronized void write(int b) {
+ baos.write(b);
+ err.write(b);
+ }
+
+ public String drain() {
+ awaitPending();
+ synchronized(this) {
+ String txt = baos.toString();
+ baos.reset();
+ return txt;
+ }
+ }
+ }
+
+ static enum TestCase {
+ NO_SECURITY, SECURE, SECURE_AND_WAIT
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args == null || args.length == 0) {
+ args = new String[] { TestCase.SECURE_AND_WAIT.name() };
+ }
+ if (args.length > 1) throw new RuntimeException("Only one argument allowed");
+ TestCase test = TestCase.valueOf(args[0]);
+ System.err.println("Testing: " + test);
+
+
+ // private reflection hook that allows us to simulate a non booted VM
+ final AtomicBoolean vmBooted = new AtomicBoolean(false);
+ isBooted.set(null,(BooleanSupplier) () -> vmBooted.get());
+
+ // We replace System.err to check the messages that have been logged
+ // by the JUL ConsoleHandler and default SimpleConsoleLogger
+ // implementaion
+ final LogStream err = new LogStream();
+ System.setErr(new PrintStream(err));
+
+ if (BootstrapLogger.isBooted()) {
+ throw new RuntimeException("VM should not be booted!");
+ }
+ Logger logger = LazyLoggers.getLogger("foo.bar", Thread.class);
+
+ if (test != TestCase.NO_SECURITY) {
+ LogStream.err.println("Setting security manager");
+ Policy.setPolicy(new SimplePolicy());
+ System.setSecurityManager(new SecurityManager());
+ }
+
+ Level[] levels = {Level.INFO, Level.WARNING, Level.INFO};
+ int index = 0;
+ logger.log(levels[index], "Early message #" + (index+1)); index++;
+ logger.log(levels[index], "Early message #" + (index+1)); index++;
+ LogStream.err.println("VM Booted: " + vmBooted.get());
+ LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
+ logger.log(levels[index], "Early message #" + (index+1)); index++;
+ if (err.drain().contains("Early message")) {
+ // We're expecting that logger will be a LazyLogger wrapping a
+ // BootstrapLogger. The Bootstrap logger will stack the log messages
+ // it receives until the VM is booted.
+ // Since our private hook pretend that the VM is not booted yet,
+ // the logged messages shouldn't have reached System.err yet.
+ throw new RuntimeException("Early message logged while VM is not booted!");
+ }
+
+ // Now pretend that the VM is booted. Nothing should happen yet, until
+ // we try to log a new message.
+ vmBooted.getAndSet(true);
+ LogStream.err.println("VM Booted: " + vmBooted.get());
+ LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
+ if (!BootstrapLogger.isBooted()) {
+ throw new RuntimeException("VM should now be booted!");
+ }
+ if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+ throw new RuntimeException("LogManager shouldn't be initialized yet!");
+ }
+
+ // Logging a message should cause the BootstrapLogger to replace itself
+ // by a 'real' logger in the LazyLogger. But since the LogManager isn't
+ // initialized yet, this should be a SimpleConsoleLogger...
+ logger.log(Level.INFO, "LOG#4: VM now booted: {0}", vmBooted.get());
+ logger.log(Level.DEBUG, "LOG#5: hi!");
+ SimplePolicy.allowAll.set(Boolean.TRUE);
+ WeakReference<Thread> threadRef = null;
+ ReferenceQueue<Thread> queue = new ReferenceQueue<>();
+ try {
+ Set<Thread> set = Thread.getAllStackTraces().keySet().stream()
+ .filter((t) -> t.getName().startsWith("BootstrapMessageLoggerTask-"))
+ .collect(Collectors.toSet());
+ set.stream().forEach(t -> LogStream.err.println("Found: " + t));
+ if (set.size() > 1) {
+ throw new RuntimeException("Too many bootsrap threads found");
+ }
+ Optional<Thread> t = set.stream().findFirst();
+ if (t.isPresent()) {
+ threadRef = new WeakReference<>(t.get(), queue);
+ }
+ } finally{
+ SimplePolicy.allowAll.set(Boolean.FALSE);
+ }
+ if (!BootstrapLogger.isBooted()) {
+ throw new RuntimeException("VM should still be booted!");
+ }
+ if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+ throw new RuntimeException("LogManager shouldn't be initialized yet!");
+ }
+
+ // Now check that the early messages we had printed before the VM was
+ // booted have appeared on System.err...
+ String afterBoot = err.drain();
+ for (int i=0; i<levels.length; i++) {
+ String m = levels[i].getName()+": Early message #"+(i+1);
+ if (!afterBoot.contains(m)) {
+ throw new RuntimeException("System.err does not contain: "+m);
+ }
+ }
+ // check that the message logged *after* the VM was booted also printed.
+ if (!afterBoot.contains("INFO: LOG#4")) {
+ throw new RuntimeException("System.err does not contain: "
+ + "INFO: LOG#4");
+ }
+ // check that the debug message was not printed.
+ if (afterBoot.contains("LOG#5")) {
+ throw new RuntimeException("System.err contain: " + "LOG#5");
+ }
+ LogStream.err.println("VM Booted: " + vmBooted.get());
+ LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
+ if (!BootstrapLogger.isBooted()) {
+ throw new RuntimeException("VM should still be booted!");
+ }
+ if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+ throw new RuntimeException("LogManager shouldn't be initialized yet!");
+ }
+
+ // Now we're going to use reflection to access JUL, and change
+ // the level of the "foo" logger.
+ // We're using reflection so that the test can also run in
+ // configurations where java.util.logging is not present.
+ boolean hasJUL = false;
+ SimplePolicy.allowAll.set(Boolean.TRUE);
+ try {
+ Class<?> loggerClass = Class.forName("java.util.logging.Logger");
+ Class<?> levelClass = Class.forName("java.util.logging.Level");
+ Class<?> handlerClass = Class.forName("java.util.logging.Handler");
+
+ // java.util.logging.Logger.getLogger("foo")
+ // .setLevel(java.util.logging.Level.FINEST);
+ Object fooLogger = loggerClass.getMethod("getLogger", String.class)
+ .invoke(null, "foo");
+ loggerClass.getMethod("setLevel", levelClass)
+ .invoke(fooLogger, levelClass.getField("FINEST").get(null));
+
+ // java.util.logging.Logger.getLogger("").getHandlers()[0]
+ // .setLevel(java.util.logging.Level.ALL);
+ Object rootLogger = loggerClass.getMethod("getLogger", String.class)
+ .invoke(null, "");
+ Object handlers = loggerClass.getMethod("getHandlers").
+ invoke(rootLogger);
+ handlerClass.getMethod("setLevel", levelClass)
+ .invoke(Array.get(handlers, 0), levelClass.getField("ALL")
+ .get(null));
+
+ hasJUL = true;
+ } catch (ClassNotFoundException x) {
+ LogStream.err.println("JUL is not present: class " + x.getMessage()
+ + " not found");
+ hasJUL = false;
+ } finally {
+ SimplePolicy.allowAll.set(Boolean.FALSE);
+ }
+
+ logger.log(Level.DEBUG, "hi now!");
+ String debug = err.drain();
+ if (hasJUL) {
+ if (!((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+ throw new RuntimeException("LogManager should be initialized now!");
+ }
+ if (!debug.contains("FINE: hi now!")) {
+ throw new RuntimeException("System.err does not contain: "
+ + "FINE: hi now!");
+ }
+ } else {
+ if (debug.contains("hi now!")) {
+ throw new RuntimeException("System.err contains: " + "hi now!");
+ }
+ if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+ throw new RuntimeException("LogManager shouldn't be initialized yet!");
+ }
+ Logger baz = System.getLogger("foo.bar.baz");
+ if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+ throw new RuntimeException("LogManager shouldn't be initialized yet!");
+ }
+ }
+ Logger bazbaz = null;
+ SimplePolicy.allowAll.set(Boolean.TRUE);
+ try {
+ bazbaz = java.lang.System.LoggerFinder
+ .getLoggerFinder().getLogger("foo.bar.baz.baz", BootstrapLoggerTest.class);
+ } finally {
+ SimplePolicy.allowAll.set(Boolean.FALSE);
+ }
+ if (!((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+ throw new RuntimeException("LogManager should be initialized now!");
+ }
+ Logger bazbaz2 = System.getLogger("foo.bar.baz.baz");
+ if (bazbaz2.getClass() != bazbaz.getClass()) {
+ throw new RuntimeException("bazbaz2.class != bazbaz.class ["
+ + bazbaz2.getClass() + " != "
+ + bazbaz.getClass() + "]");
+ }
+ if (hasJUL != bazbaz2.getClass().getName()
+ .equals("sun.util.logging.internal.LoggingProviderImpl$JULWrapper")) {
+ throw new RuntimeException("Unexpected class for bazbaz: "
+ + bazbaz.getClass().getName()
+ + "\n\t expected: "
+ + "sun.util.logging.internal.LoggingProviderImpl$JULWrapper");
+ }
+
+ // Now we're going to check that the thread of the BootstrapLogger
+ // executor terminates, and that the Executor is GC'ed after that.
+ // This will involve a bit of waiting, hence the timeout=120 in
+ // the @run line.
+ // If this test fails in timeout - we could envisage skipping this part,
+ // or adding some System property to configure the keep alive delay
+ // of the executor.
+ SimplePolicy.allowAll.set(Boolean.TRUE);
+ try {
+ Stream<Thread> stream = Thread.getAllStackTraces().keySet().stream();
+ stream.filter((t) -> t.getName().startsWith("BootstrapMessageLoggerTask-"))
+ .forEach(t -> LogStream.err.println(t));
+ stream = null;
+ if (threadRef != null && test == TestCase.SECURE_AND_WAIT) {
+ Thread t = threadRef.get();
+ if (t != null) {
+ if (!(Boolean)isAlive.invoke(null)) {
+ throw new RuntimeException("Executor already terminated");
+ } else {
+ LogStream.err.println("Executor still alive as expected.");
+ }
+ LogStream.err.println("Waiting for " + t.getName() + " to terminate (join)");
+ t.join(60_000);
+ t = null;
+ }
+ LogStream.err.println("Calling System.gc()");
+ System.gc();
+ LogStream.err.println("Waiting for BootstrapMessageLoggerTask to be gc'ed");
+ while (queue.remove(1000) == null) {
+ LogStream.err.println("Calling System.gc()");
+ System.gc();
+ }
+
+ // Call the reference here to make sure threadRef will not be
+ // eagerly garbage collected before the thread it references.
+ // otherwise, it might not be enqueued, resulting in the
+ // queue.remove() call above to always return null....
+ if (threadRef.get() != null) {
+ throw new RuntimeException("Reference should have been cleared");
+ }
+
+ LogStream.err.println("BootstrapMessageLoggerTask has been gc'ed");
+ // Wait for the executor to be gc'ed...
+ for (int i=0; i<10; i++) {
+ LogStream.err.println("Calling System.gc()");
+ System.gc();
+ if (!(Boolean)isAlive.invoke(null)) break;
+ // It would be unexpected that we reach here...
+ Thread.sleep(1000);
+ }
+
+ if ((Boolean)isAlive.invoke(null)) {
+ throw new RuntimeException("Executor still alive");
+ } else {
+ LogStream.err.println("Executor terminated as expected.");
+ }
+ } else {
+ LogStream.err.println("Not checking executor termination for " + test);
+ }
+ } finally {
+ SimplePolicy.allowAll.set(Boolean.FALSE);
+ }
+ LogStream.err.println(test.name() + ": PASSED");
+ }
+
+ final static class SimplePolicy extends Policy {
+ static final ThreadLocal<Boolean> allowAll = new ThreadLocal<Boolean>() {
+ @Override
+ protected Boolean initialValue() {
+ return Boolean.FALSE;
+ }
+ };
+
+ Permissions getPermissions() {
+ Permissions perms = new Permissions();
+ if (allowAll.get()) {
+ perms.add(new AllPermission());
+ }
+ return perms;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions(domain).implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return getPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return getPermissions();
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/CustomSystemClassLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+ Class<?> loggerFinderClass = null;
+// Class<?> loggerImplClass = null;
+
+ public CustomSystemClassLoader() {
+ super();
+ }
+ public CustomSystemClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> defineFinderClass(String name)
+ throws ClassNotFoundException {
+ final Object obj = getClassLoadingLock(name);
+ synchronized(obj) {
+ if (loggerFinderClass != null) return loggerFinderClass;
+
+ URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+ File file = new File(url.getPath(), name+".class");
+ if (file.canRead()) {
+ try {
+ byte[] b = Files.readAllBytes(file.toPath());
+ Permissions perms = new Permissions();
+ perms.add(new AllPermission());
+ loggerFinderClass = defineClass(
+ name, b, 0, b.length, new ProtectionDomain(
+ this.getClass().getProtectionDomain().getCodeSource(),
+ perms));
+ System.out.println("Loaded " + name);
+ return loggerFinderClass;
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ throw new ClassNotFoundException(name, ex);
+ }
+ } else {
+ throw new ClassNotFoundException(name,
+ new IOException(file.toPath() + ": can't read"));
+ }
+ }
+ }
+// private Class<?> defineLoggerImplClass(String name)
+// throws ClassNotFoundException {
+// final Object obj = getClassLoadingLock(name);
+// synchronized(obj) {
+// if (loggerImplClass != null) return loggerImplClass;
+//
+// URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+// File file = new File(url.getPath(), name+".class");
+// if (file.canRead()) {
+// try {
+// byte[] b = Files.readAllBytes(file.toPath());
+// Permissions perms = new Permissions();
+// perms.add(new AllPermission());
+// loggerImplClass = defineClass(
+// name, b, 0, b.length, new ProtectionDomain(
+// this.getClass().getProtectionDomain().getCodeSource(),
+// perms));
+// System.out.println("Loaded " + name);
+// return loggerImplClass;
+// } catch (Throwable ex) {
+// ex.printStackTrace();
+// throw new ClassNotFoundException(name, ex);
+// }
+// } else {
+// throw new ClassNotFoundException(name,
+// new IOException(file.toPath() + ": can't read"));
+// }
+// }
+// }
+//
+ @Override
+ public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (name.endsWith("$LogProducerFinder")) {
+ Class<?> c = defineFinderClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+// if (name.endsWith("$LogProducerFinder$LoggerImpl")) {
+// Class<?> c = defineLoggerImplClass(name);
+// if (resolve) {
+// resolveClass(c);
+// }
+// return c;
+// }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+// if (name.endsWith("$LogProducerFinder$LoggerImpl")) {
+// return defineLoggerImplClass(name);
+// }
+ if (name.endsWith("$$LogProducerFinder")) {
+ return defineFinderClass(name);
+ }
+ return super.findClass(name);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/LoggerBridgeTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1087 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ * Tests all bridge methods with the a custom backend whose
+ * loggers implement PlatformLogger.Bridge.
+ * @modules java.base/sun.util.logging java.base/jdk.internal.logger
+ * @build CustomSystemClassLoader LoggerBridgeTest
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest NOSECURITY
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest NOPERMISSIONS
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class LoggerBridgeTest {
+
+ public static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent implements Cloneable {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ sun.util.logging.PlatformLogger.Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ String msg;
+ Supplier<String> supplier;
+ String className;
+ String methodName;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ loggerName,
+ level,
+ isLoggable,
+ bundle,
+ msg,
+ supplier,
+ thrown,
+ args,
+ className,
+ methodName,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+
+ public LogEvent cloneWith(long sequenceNumber)
+ throws CloneNotSupportedException {
+ LogEvent cloned = (LogEvent)super.clone();
+ cloned.sequenceNumber = sequenceNumber;
+ return cloned;
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ return LogEvent.of(sequenceNumber, isLoggable, name,
+ null, null, level, bundle, key,
+ thrown, params);
+ }
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ Supplier<String> supplier, Throwable thrown, Object... params) {
+ return LogEvent.of(sequenceNumber, isLoggable, name,
+ null, null, level, bundle, supplier,
+ thrown, params);
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ String className, String methodName,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ evt.className = className;
+ evt.methodName = methodName;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ String className, String methodName,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
+ className, methodName, level, bundle, key, thrown, params);
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ String className, String methodName,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ Supplier<String> supplier, Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = supplier;
+ evt.isLoggable = isLoggable;
+ evt.className = className;
+ evt.methodName = methodName;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ String className, String methodName,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ Supplier<String> supplier, Throwable thrown, Object... params) {
+ return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
+ className, methodName, level, bundle, supplier, thrown, params);
+ }
+
+ }
+ static final Class<?> providerClass;
+ static {
+ try {
+ // Preload classes before the security manager is on.
+ providerClass = ClassLoader.getSystemClassLoader().loadClass("LoggerBridgeTest$LogProducerFinder");
+ ((LoggerFinder)providerClass.newInstance()).getLogger("foo", providerClass);
+ } catch (Exception ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static class LogProducerFinder extends LoggerFinder {
+ final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+ final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+
+ public class LoggerImpl implements Logger, PlatformLogger.Bridge {
+ private final String name;
+ private sun.util.logging.PlatformLogger.Level level = sun.util.logging.PlatformLogger.Level.INFO;
+ private sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
+ private sun.util.logging.PlatformLogger.Level FINE = sun.util.logging.PlatformLogger.Level.FINE;
+ private sun.util.logging.PlatformLogger.Level FINER = sun.util.logging.PlatformLogger.Level.FINER;
+ private sun.util.logging.PlatformLogger.Level FINEST = sun.util.logging.PlatformLogger.Level.FINEST;
+ private sun.util.logging.PlatformLogger.Level CONFIG = sun.util.logging.PlatformLogger.Level.CONFIG;
+ private sun.util.logging.PlatformLogger.Level INFO = sun.util.logging.PlatformLogger.Level.INFO;
+ private sun.util.logging.PlatformLogger.Level WARNING = sun.util.logging.PlatformLogger.Level.WARNING;
+ private sun.util.logging.PlatformLogger.Level SEVERE = sun.util.logging.PlatformLogger.Level.SEVERE;
+
+ public LoggerImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return this.level != OFF && this.level.intValue() <= level.getSeverity();
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle,
+ String format, Object... params) {
+ throw new UnsupportedOperationException();
+ }
+
+ void log(LogEvent event) {
+ eventQueue.add(event);
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier,
+ Throwable thrown) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msg, null, (Object[])null));
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level,
+ Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msgSupplier, null, (Object[])null));
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String msg,
+ Object... params) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msg, null, params));
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String msg,
+ Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msg, thrown, (Object[])null));
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msgSupplier, thrown, (Object[])null));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msg, null, (Object[])null));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msgSupplier, null, (Object[])null));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Object... params) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msg, null, params));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msg, thrown, (Object[])null));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msgSupplier, thrown, (Object[])null));
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String msg,
+ Object... params) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, bundle, msg, null, params));
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String msg, Object... params) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, bundle, msg, null, params));
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String msg,
+ Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, bundle, msg, thrown, (Object[])null));
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String msg, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, bundle, msg, thrown, (Object[])null));
+ }
+
+ @Override
+ public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
+ return this.level != OFF && level.intValue()
+ >= this.level.intValue();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return this.level != OFF;
+ }
+
+ }
+
+ @Override
+ public Logger getLogger(String name, Class<?> caller) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+ ClassLoader callerLoader = AccessController.doPrivileged(pa);
+ if (callerLoader == null) {
+ return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ } else {
+ return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ }
+ }
+ }
+
+ static final sun.util.logging.PlatformLogger.Level[] julLevels = {
+ sun.util.logging.PlatformLogger.Level.ALL,
+ sun.util.logging.PlatformLogger.Level.FINEST,
+ sun.util.logging.PlatformLogger.Level.FINER,
+ sun.util.logging.PlatformLogger.Level.FINE,
+ sun.util.logging.PlatformLogger.Level.CONFIG,
+ sun.util.logging.PlatformLogger.Level.INFO,
+ sun.util.logging.PlatformLogger.Level.WARNING,
+ sun.util.logging.PlatformLogger.Level.SEVERE,
+ sun.util.logging.PlatformLogger.Level.OFF,
+ };
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+
+ public static class MyHandler extends Handler {
+
+ @Override
+ public java.util.logging.Level getLevel() {
+ return java.util.logging.Level.ALL;
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+ true, record.getLoggerName(),
+ record.getSourceClassName(),
+ record.getSourceMethodName(),
+ PlatformLogger.Level.valueOf(record.getLevel().getName()),
+ record.getResourceBundle(), record.getMessage(),
+ record.getThrown(), record.getParameters()));
+ }
+ @Override
+ public void flush() {
+ }
+ @Override
+ public void close() throws SecurityException {
+ }
+
+ }
+
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ final static Method lazyGetLogger;
+ static {
+ // jdk.internal.logging.LoggerBridge.getLogger(name, caller)
+ try {
+ Class<?> bridgeClass = Class.forName("jdk.internal.logger.LazyLoggers");
+ lazyGetLogger = bridgeClass.getDeclaredMethod("getLogger",
+ String.class, Class.class);
+ lazyGetLogger.setAccessible(true);
+ } catch (Throwable ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ static Logger getLogger(LoggerFinder provider, String name, Class<?> caller) {
+ Logger logger;
+ try {
+ logger = Logger.class.cast(lazyGetLogger.invoke(null, name, caller));
+ } catch (Throwable x) {
+ Throwable t = (x instanceof InvocationTargetException) ?
+ ((InvocationTargetException)x).getTargetException() : x;
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException)t;
+ } else if (t instanceof Exception) {
+ throw new RuntimeException(t);
+ } else {
+ throw (Error)t;
+ }
+ }
+ // The method above does not throw exception...
+ // call the provider here to verify that an exception would have
+ // been thrown by the provider.
+ if (logger != null && caller == Thread.class) {
+ Logger log = provider.getLogger(name, caller);
+ }
+ return logger;
+ }
+
+ static Logger getLogger(LoggerFinder provider, String name, ResourceBundle bundle, Class<?> caller) {
+ if (caller.getClassLoader() != null) {
+ return System.getLogger(name,bundle);
+ } else {
+ return provider.getLocalizedLogger(name, bundle, caller);
+ }
+ }
+
+ static PlatformLogger.Bridge convert(Logger logger) {
+ return PlatformLogger.Bridge.convert(logger);
+ }
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ //"NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ LoggerFinder provider;
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ provider = LoggerFinder.getLoggerFinder();
+ test(provider, true);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ try {
+ provider = LoggerFinder.getLoggerFinder();
+ throw new RuntimeException("Expected exception not raised");
+ } catch (AccessControlException x) {
+ if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = LoggerFinder.getLoggerFinder();
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+ test(provider, false);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = LoggerFinder.getLoggerFinder();
+ test(provider, true);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(LoggerFinder provider, boolean hasRequiredPermissions) {
+
+ ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Object, String> loggerDescMap = new HashMap<>();
+
+
+ Logger appLogger1 = System.getLogger("foo");
+ loggerDescMap.put(appLogger1, "LogProducer.getApplicationLogger(\"foo\")");
+
+ Logger sysLogger1 = null;
+ try {
+ sysLogger1 = getLogger(provider, "foo", Thread.class);
+ loggerDescMap.put(sysLogger1, "LogProducer.getSystemLogger(\"foo\")");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for system logger: " + acx);
+ }
+
+
+ Logger appLogger2 =
+ System.getLogger("foo", loggerBundle);
+ loggerDescMap.put(appLogger2, "LogProducer.getApplicationLogger(\"foo\", loggerBundle)");
+
+ Logger sysLogger2 = null;
+ try {
+ sysLogger2 = getLogger(provider, "foo", loggerBundle, Thread.class);
+ loggerDescMap.put(sysLogger2, "provider.getSystemLogger(\"foo\", loggerBundle)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for localized system logger: " + acx);
+ }
+ if (hasRequiredPermissions && appLogger2 == sysLogger2) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (appLogger2 == appLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+
+ final LogProducerFinder.LoggerImpl appSink;
+ final LogProducerFinder.LoggerImpl sysSink;
+ boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ appSink = LogProducerFinder.LoggerImpl.class.cast(
+ provider.getLogger("foo", LoggerBridgeTest.class));
+ sysSink = LogProducerFinder.LoggerImpl.class.cast(
+ provider.getLogger("foo", Thread.class));
+ } finally {
+ allowControl.get().set(old);
+ }
+
+ testLogger(provider, loggerDescMap, "foo", null, convert(appLogger1), appSink);
+ if (hasRequiredPermissions) {
+ testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1), sysSink);
+ }
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(appLogger2), appSink);
+ if (hasRequiredPermissions) {
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(sysLogger2), sysSink);
+ }
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ static void checkLogEvent(LoggerFinder provider, String desc,
+ LogEvent expected) {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void checkLogEvent(LoggerFinder provider, String desc,
+ LogEvent expected, boolean expectNotNull) {
+ LogEvent actual = eventQueue.poll();
+ if (actual == null && !expectNotNull) return;
+ if (actual != null && !expectNotNull) {
+ throw new RuntimeException("Unexpected log event found for " + desc
+ + "\n\tgot: " + actual);
+ }
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void setLevel( LogProducerFinder.LoggerImpl sink,
+ sun.util.logging.PlatformLogger.Level loggerLevel) {
+ sink.level = loggerLevel;
+ }
+
+ // Calls the methods defined on LogProducer and verify the
+ // parameters received by the underlying LogProducerFinder.LoggerImpl
+ // logger.
+ private static void testLogger(LoggerFinder provider,
+ Map<Object, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ PlatformLogger.Bridge logger,
+ LogProducerFinder.LoggerImpl sink) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger) + "[" + logger + "]");
+ final sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ System.out.println("\tlogger.log(messageLevel, fooMsg)");
+ System.out.println("\tlogger.<level>(fooMsg)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ fooMsg, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, fooMsg);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ Supplier<String> supplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+ System.out.println("\tlogger.log(messageLevel, supplier)");
+ System.out.println("\tlogger.<level>(supplier)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, null,
+ supplier, (Throwable)null, (Object[])null);
+ logger.log(messageLevel, supplier);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = fooMsg;
+ System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ format, (Throwable)null, arg1, arg2);
+ logger.log(messageLevel, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ fooMsg, thrown, (Object[])null);
+ logger.log(messageLevel, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, null,
+ supplier, thrown, (Object[])null);
+ logger.log(messageLevel, thrown, supplier);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ String sourceClass = "blah.Blah";
+ String sourceMethod = "blih";
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+ fooMsg, (Throwable)null, (Object[])null);
+ logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, null,
+ supplier, (Throwable)null, (Object[])null);
+ logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+ format, (Throwable)null, arg1, arg2);
+ logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+ fooMsg, thrown, (Object[])null);
+ logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, null,
+ supplier, thrown, (Object[])null);
+ logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, bundle,
+ format, (Throwable)null, arg1, arg2);
+ logger.logrb(messageLevel, bundle, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, bundle,
+ fooMsg, thrown, (Object[])null);
+ logger.logrb(messageLevel, bundle, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, bundle,
+ format, (Throwable)null, arg1, arg2);
+ logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, bundle,
+ fooMsg, thrown, (Object[])null);
+ logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected);
+ }
+ }
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+ final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+ final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+ final Permissions permissions;
+ final Permissions allPermissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAccess;
+ final ThreadLocal<AtomicBoolean> allowAll;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+ ThreadLocal<AtomicBoolean> allowAccess,
+ ThreadLocal<AtomicBoolean> allowAll) {
+ this.allowControl = allowControl;
+ this.allowAccess = allowAccess;
+ this.allowAll = allowAll;
+ permissions = new Permissions();
+ allPermissions = new PermissionsBuilder()
+ .add(new java.security.AllPermission())
+ .toPermissions();
+ }
+
+ Permissions getPermissions() {
+ if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+ PermissionsBuilder builder = new PermissionsBuilder()
+ .addAll(permissions);
+ if (allowControl.get().get()) {
+ builder.add(CONTROL);
+ }
+ if (allowAccess.get().get()) {
+ builder.add(ACCESS_LOGGER);
+ builder.add(ACCESS_LOGGING);
+ }
+ if (allowAll.get().get()) {
+ builder.addAll(allPermissions);
+ }
+ return builder.toPermissions();
+ }
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions().implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1 @@
+LoggerBridgeTest$LogProducerFinder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/AccessSystemLogger.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+ public AccessSystemLogger() {
+ this(check());
+ }
+
+ private AccessSystemLogger(Void unused) {
+ }
+
+ private static Void check() {
+ if (AccessSystemLogger.class.getClassLoader() != null) {
+ throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+ }
+ return null;
+ }
+
+ public Logger getLogger(String name) {
+ Logger logger = System.getLogger(name);
+ System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+ return logger;
+ }
+
+ public Logger getLogger(String name, ResourceBundle bundle) {
+ Logger logger = System.getLogger(name, bundle);
+ System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+ return logger;
+ }
+
+ static final Class<?>[] toCopy = { AccessSystemLogger.class, CustomSystemClassLoader.class };
+
+ // copy AccessSystemLogger.class to ./boot
+ public static void main(String[] args) throws IOException {
+ Path testDir = Paths.get(System.getProperty("user.dir", "."));
+ Path bootDir = Paths.get(testDir.toString(), "boot");
+ Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+ if (Files.notExists(bootDir)) {
+ Files.createDirectory(bootDir);
+ }
+ for (Class<?> c : toCopy) {
+ Path thisClass = Paths.get(classes.toString(),
+ c.getSimpleName()+".class");
+ Path dest = Paths.get(bootDir.toString(),
+ c.getSimpleName()+".class");
+ Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/CustomSystemClassLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions. The CustomSystemClassLoader class must be
+ * in the BCL, otherwise when system classes - such as
+ * ZoneDateTime try to load their resource bundle a MissingResourceBundle
+ * caused by a SecurityException may be thrown, as the CustomSystemClassLoader
+ * code base will be found in the stack called by doPrivileged.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+ final List<String> finderClassNames =
+ Arrays.asList("LoggerFinderLoaderTest$BaseLoggerFinder",
+ "LoggerFinderLoaderTest$BaseLoggerFinder2");
+ final Map<String, Class<?>> finderClasses = new HashMap<>();
+ Class<?> testLoggerFinderClass;
+
+ public CustomSystemClassLoader() {
+ super();
+ }
+ public CustomSystemClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> defineFinderClass(String name)
+ throws ClassNotFoundException {
+ final Object obj = getClassLoadingLock(name);
+ synchronized(obj) {
+ if (finderClasses.get(name) != null) return finderClasses.get(name);
+ if (testLoggerFinderClass == null) {
+ // Hack: we load testLoggerFinderClass to get its code source.
+ // we can't use this.getClass() since we are in the boot.
+ testLoggerFinderClass = super.loadClass("LoggerFinderLoaderTest$TestLoggerFinder");
+ }
+ URL url = testLoggerFinderClass.getProtectionDomain().getCodeSource().getLocation();
+ File file = new File(url.getPath(), name+".class");
+ if (file.canRead()) {
+ try {
+ byte[] b = Files.readAllBytes(file.toPath());
+ Permissions perms = new Permissions();
+ perms.add(new AllPermission());
+ Class<?> finderClass = defineClass(
+ name, b, 0, b.length, new ProtectionDomain(
+ this.getClass().getProtectionDomain().getCodeSource(),
+ perms));
+ System.out.println("Loaded " + name);
+ finderClasses.put(name, finderClass);
+ return finderClass;
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ throw new ClassNotFoundException(name, ex);
+ }
+ } else {
+ throw new ClassNotFoundException(name,
+ new IOException(file.toPath() + ": can't read"));
+ }
+ }
+ }
+
+ @Override
+ public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (finderClassNames.contains(name)) {
+ Class<?> c = defineFinderClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (finderClassNames.contains(name)) {
+ return defineFinderClass(name);
+ }
+ return super.findClass(name);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/LoggerFinderLoaderTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,882 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.stream.Stream;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.concurrent.atomic.AtomicReference;
+import jdk.internal.logger.SimpleConsoleLogger;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary JDK implementation specific unit test for LoggerFinderLoader.
+ * Tests the behavior of LoggerFinderLoader with respect to the
+ * value of the internal diagnosability switches. Also test the
+ * DefaultLoggerFinder and SimpleConsoleLogger implementation.
+ * @modules java.base/sun.util.logging
+ * java.base/jdk.internal.logger
+ * @build AccessSystemLogger LoggerFinderLoaderTest CustomSystemClassLoader
+ * @run driver AccessSystemLogger
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class LoggerFinderLoaderTest {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+ static final Class<?>[] providerClass;
+ static {
+ try {
+ providerClass = new Class<?>[] {
+ ClassLoader.getSystemClassLoader().loadClass("LoggerFinderLoaderTest$BaseLoggerFinder"),
+ ClassLoader.getSystemClassLoader().loadClass("LoggerFinderLoaderTest$BaseLoggerFinder2")
+ };
+ } catch (ClassNotFoundException ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ /**
+ * What our test provider needs to implement.
+ */
+ public static interface TestLoggerFinder {
+ public final static AtomicBoolean fails = new AtomicBoolean();
+ public final static AtomicReference<String> conf = new AtomicReference<>("");
+ public final static AtomicLong sequencer = new AtomicLong();
+ public final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+ public final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+
+ public class LoggerImpl implements System.Logger {
+ final String name;
+ final Logger logger;
+
+ public LoggerImpl(String name, Logger logger) {
+ this.name = name;
+ this.logger = logger;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isLoggable(Logger.Level level) {
+ return logger.isLoggable(level);
+ }
+
+ @Override
+ public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ logger.log(level, bundle, key, thrown);
+ }
+
+ @Override
+ public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) {
+ logger.log(level, bundle, format, params);
+ }
+
+ }
+
+ public Logger getLogger(String name, Class<?> caller);
+ public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
+ }
+
+ public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ public BaseLoggerFinder() {
+ if (fails.get()) {
+ throw new RuntimeException("Simulate exception while loading provider");
+ }
+ }
+
+ System.Logger createSimpleLogger(String name) {
+ PrivilegedAction<System.Logger> pa = () -> SimpleConsoleLogger.makeSimpleLogger(name, false);
+ return AccessController.doPrivileged(pa);
+ }
+
+
+ @Override
+ public Logger getLogger(String name, Class<?> caller) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+ ClassLoader callerLoader = AccessController.doPrivileged(pa);
+ if (callerLoader == null) {
+ return system.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name)));
+ } else {
+ return user.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name)));
+ }
+ }
+ }
+
+ public static class BaseLoggerFinder2 extends LoggerFinder implements TestLoggerFinder {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ public BaseLoggerFinder2() {
+ throw new ServiceConfigurationError("Should not come here");
+ }
+ @Override
+ public Logger getLogger(String name, Class<?> caller) {
+ throw new ServiceConfigurationError("Should not come here");
+ }
+ }
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ static LoggerFinder getLoggerFinder(Class<?> expectedClass,
+ String errorPolicy, boolean singleton) {
+ LoggerFinder provider = null;
+ try {
+ TestLoggerFinder.sequencer.incrementAndGet();
+ provider = LoggerFinder.getLoggerFinder();
+ if (TestLoggerFinder.fails.get() || singleton) {
+ if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+ throw new RuntimeException("Expected exception not thrown");
+ } else if ("WARNING".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+ String warning = ErrorStream.errorStream.peek();
+ if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) {
+ throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+ }
+ } else if ("DEBUG".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+ String warning = ErrorStream.errorStream.peek();
+ if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) {
+ throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+ }
+ if (!warning.contains("WARNING: Exception raised trying to instantiate LoggerFinder")) {
+ throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+ }
+ if (TestLoggerFinder.fails.get()) {
+ if (!warning.contains("java.util.ServiceConfigurationError: java.lang.System$LoggerFinder: Provider LoggerFinderLoaderTest$BaseLoggerFinder could not be instantiated")) {
+ throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+ }
+ } else if (singleton) {
+ if (!warning.contains("java.util.ServiceConfigurationError: More than on LoggerFinder implementation")) {
+ throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+ }
+ }
+ } else if ("QUIET".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("Unexpected error message found: "
+ + ErrorStream.errorStream.peek());
+ }
+ }
+ }
+ } catch(AccessControlException a) {
+ throw a;
+ } catch(Throwable t) {
+ if (TestLoggerFinder.fails.get() || singleton) {
+ // must check System.err
+ if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+ provider = LoggerFinder.getLoggerFinder();
+ } else {
+ Throwable orig = t.getCause();
+ while (orig != null && orig.getCause() != null) orig = orig.getCause();
+ if (orig != null) orig.printStackTrace(ErrorStream.err);
+ throw new RuntimeException("Unexpected exception: " + t, t);
+ }
+ } else {
+ throw new RuntimeException("Unexpected exception: " + t, t);
+ }
+ }
+ expectedClass.cast(provider);
+ ErrorStream.errorStream.store();
+ System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
+ return provider;
+ }
+
+
+ static class ErrorStream extends PrintStream {
+
+ static AtomicBoolean forward = new AtomicBoolean();
+ ByteArrayOutputStream out;
+ String saved = "";
+ public ErrorStream(ByteArrayOutputStream out) {
+ super(out);
+ this.out = out;
+ }
+
+ @Override
+ public void write(int b) {
+ super.write(b);
+ if (forward.get()) err.write(b);
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ super.write(b);
+ if (forward.get()) err.write(b);
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) {
+ super.write(buf, off, len);
+ if (forward.get()) err.write(buf, off, len);
+ }
+
+ public String peek() {
+ flush();
+ return out.toString();
+ }
+
+ public String drain() {
+ flush();
+ String res = out.toString();
+ out.reset();
+ return res;
+ }
+
+ public void store() {
+ flush();
+ saved = out.toString();
+ out.reset();
+ }
+
+ public void restore() {
+ out.reset();
+ try {
+ out.write(saved.getBytes());
+ } catch(IOException io) {
+ throw new UncheckedIOException(io);
+ }
+ }
+
+ static final PrintStream err = System.err;
+ static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
+ }
+
+ private static StringBuilder appendProperty(StringBuilder b, String name) {
+ String value = System.getProperty(name);
+ if (value == null) return b;
+ return b.append(name).append("=").append(value).append('\n');
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ args = new String[] {
+ "NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+ }
+ Locale.setDefault(Locale.ENGLISH);
+ System.setErr(ErrorStream.errorStream);
+ System.setProperty("jdk.logger.packages", TestLoggerFinder.LoggerImpl.class.getName());
+ //System.setProperty("jdk.logger.finder.error", "ERROR");
+ //System.setProperty("jdk.logger.finder.singleton", "true");
+ //System.setProperty("test.fails", "true");
+ TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
+ StringBuilder c = new StringBuilder();
+ appendProperty(c, "jdk.logger.packages");
+ appendProperty(c, "jdk.logger.finder.error");
+ appendProperty(c, "jdk.logger.finder.singleton");
+ appendProperty(c, "test.fails");
+ TestLoggerFinder.conf.set(c.toString());
+ try {
+ test(args);
+ } finally {
+ try {
+ System.setErr(ErrorStream.err);
+ } catch (Error | RuntimeException x) {
+ x.printStackTrace(ErrorStream.err);
+ }
+ }
+ }
+
+
+ public static void test(String[] args) {
+
+ final String errorPolicy = System.getProperty("jdk.logger.finder.error", "WARNING");
+ final Boolean ensureSingleton = Boolean.getBoolean("jdk.logger.finder.singleton");
+
+ final Class<?> expectedClass =
+ TestLoggerFinder.fails.get() || ensureSingleton
+ ? jdk.internal.logger.DefaultLoggerFinder.class
+ : TestLoggerFinder.class;
+
+ System.out.println("Declared provider class: " + providerClass[0]
+ + "[" + providerClass[0].getClassLoader() + "]");
+
+ if (!TestLoggerFinder.fails.get()) {
+ ServiceLoader<LoggerFinder> serviceLoader =
+ ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader());
+ Iterator<LoggerFinder> iterator = serviceLoader.iterator();
+ Object firstProvider = iterator.next();
+ if (!firstProvider.getClass().getName().equals("LoggerFinderLoaderTest$BaseLoggerFinder")) {
+ throw new RuntimeException("Unexpected provider: " + firstProvider.getClass().getName());
+ }
+ if (!iterator.hasNext()) {
+ throw new RuntimeException("Expected two providers");
+ }
+ }
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ LoggerFinder provider;
+ ErrorStream.errorStream.restore();
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ System.out.println(TestLoggerFinder.conf.get());
+ provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
+ test(provider, true);
+ System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ System.out.println(TestLoggerFinder.conf.get());
+ setSecurityManager();
+ try {
+ provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
+ throw new RuntimeException("Expected exception not raised");
+ } catch (AccessControlException x) {
+ if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+ test(provider, false);
+ System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ System.out.println(TestLoggerFinder.conf.get());
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
+ test(provider, true);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
+ }
+
+ public static void test(LoggerFinder provider, boolean hasRequiredPermissions) {
+
+ ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+ System.Logger sysLogger = accessSystemLogger.getLogger("foo");
+ loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
+ System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle);
+ loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
+ System.Logger appLogger = System.getLogger("bar");
+ loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
+ System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle);
+ loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
+
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger);
+ testLogger(provider, loggerDescMap, "foo", null, appLogger);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger);
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ // Calls the 8 methods defined on Logger and verify the
+ // parameters received by the underlying TestProvider.LoggerImpl
+ // logger.
+ private static void testLogger(LoggerFinder provider,
+ Map<Logger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ Logger logger) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
+ AtomicLong sequencer = TestLoggerFinder.sequencer;
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+ for (Level messageLevel : Level.values()) {
+ ErrorStream.errorStream.drain();
+ String desc = "logger.log(messageLevel, foo): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, foo);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ if (!logged.contains("LoggerFinderLoaderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] LoggerFinderLoaderTest testLogger\n"
+ + messageLevel.getName() + " " + fooMsg
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ String msg = "blah";
+ for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, msg);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
+ if (!logged.contains("LoggerFinderLoaderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + msgText)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] LoggerFinderLoaderTest testLogger\n"
+ + messageLevel.getName() + " " + msgText
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ Supplier<String> fooSupplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+
+ for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, fooSupplier);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ if (!logged.contains("LoggerFinderLoaderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] LoggerFinderLoaderTest testLogger\n"
+ + messageLevel.getName() + " " + fooSupplier.get()
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = msg;
+ for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, format, foo, msg);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
+ String text = java.text.MessageFormat.format(msgFormat, foo, msg);
+ if (!logged.contains("LoggerFinderLoaderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] LoggerFinderLoaderTest testLogger\n"
+ + messageLevel.getName() + " " + text
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, msg, thrown);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ thrown.printStackTrace(new PrintStream(baos));
+ String text = baos.toString();
+ String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
+ if (!logged.contains("LoggerFinderLoaderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + msgText)
+ || !logged.contains(text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] LoggerFinderLoaderTest testLogger\n"
+ + messageLevel.getName() + " " + msgText +"\n"
+ + text
+ + ">>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+
+ for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, fooSupplier, thrown);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ thrown.printStackTrace(new PrintStream(baos));
+ String text = baos.toString();
+ if (!logged.contains("LoggerFinderLoaderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
+ || !logged.contains(text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] LoggerFinderLoaderTest testLogger\n"
+ + messageLevel.getName() + " " + fooSupplier.get() +"\n"
+ + text
+ + ">>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, bundle, format, foo, msg);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
+ if (!logged.contains("LoggerFinderLoaderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] LoggerFinderLoaderTest testLogger\n"
+ + messageLevel.getName() + " " + text
+ + "\n>>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+ for (Level messageLevel : Level.values()) {
+ String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ sequencer.incrementAndGet();
+ logger.log(messageLevel, bundle, msg, thrown);
+ if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+ if (!ErrorStream.errorStream.peek().isEmpty()) {
+ throw new RuntimeException("unexpected event in queue for "
+ + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+ }
+ } else {
+ String logged = ErrorStream.errorStream.drain();
+ String textMsg = bundle.getString(msg);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ thrown.printStackTrace(new PrintStream(baos));
+ String text = baos.toString();
+ if (!logged.contains("LoggerFinderLoaderTest testLogger")
+ || !logged.contains(messageLevel.getName() + ": " + textMsg)
+ || !logged.contains(text)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected:" + "\n<<<<\n"
+ + "[date] LoggerFinderLoaderTest testLogger\n"
+ + messageLevel.getName() + " " + textMsg +"\n"
+ + text
+ + ">>>>"
+ + "\n\t actual:"
+ + "\n<<<<\n" + logged + ">>>>\n");
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n<<<<\n" + logged + ">>>>\n");
+ }
+ }
+ }
+ }
+
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+ final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+
+ final Permissions permissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAccess;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
+ this.allowControl = allowControl;
+ this.allowAccess = allowAccess;
+ permissions = new Permissions();
+ permissions.add(new RuntimePermission("setIO"));
+ }
+
+ Permissions getPermissions() {
+ if (allowControl.get().get() || allowAccess.get().get()) {
+ PermissionsBuilder builder = new PermissionsBuilder()
+ .addAll(permissions);
+ if (allowControl.get().get()) {
+ builder.add(CONTROL);
+ }
+ if (allowAccess.get().get()) {
+ builder.add(ACCESS);
+ }
+ return builder.toPermissions();
+ }
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions().implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,3 @@
+LoggerFinderLoaderTest$BaseLoggerFinder
+LoggerFinderLoaderTest$BaseLoggerFinder2
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/CustomSystemClassLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+ Class<?> loggerFinderClass = null;
+
+ public CustomSystemClassLoader() {
+ super();
+ }
+ public CustomSystemClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> defineFinderClass(String name)
+ throws ClassNotFoundException {
+ final Object obj = getClassLoadingLock(name);
+ synchronized(obj) {
+ if (loggerFinderClass != null) return loggerFinderClass;
+
+ URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+ File file = new File(url.getPath(), name+".class");
+ if (file.canRead()) {
+ try {
+ byte[] b = Files.readAllBytes(file.toPath());
+ Permissions perms = new Permissions();
+ perms.add(new AllPermission());
+ loggerFinderClass = defineClass(
+ name, b, 0, b.length, new ProtectionDomain(
+ this.getClass().getProtectionDomain().getCodeSource(),
+ perms));
+ System.out.println("Loaded " + name);
+ return loggerFinderClass;
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ throw new ClassNotFoundException(name, ex);
+ }
+ } else {
+ throw new ClassNotFoundException(name,
+ new IOException(file.toPath() + ": can't read"));
+ }
+ }
+ }
+
+ @Override
+ public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (name.endsWith("$LogProducerFinder")) {
+ Class<?> c = defineFinderClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.endsWith("$$LogProducerFinder")) {
+ return defineFinderClass(name);
+ }
+ return super.findClass(name);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,1 @@
+PlatformLoggerBridgeTest$LogProducerFinder
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/PlatformLoggerBridgeTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,876 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ * Tests all bridge methods from PlatformLogger with the a custom
+ * backend whose loggers implement PlatformLogger.Bridge.
+ * @modules java.base/sun.util.logging
+ * @build CustomSystemClassLoader PlatformLoggerBridgeTest
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest NOSECURITY
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest NOPERMISSIONS
+ * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class PlatformLoggerBridgeTest {
+
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ static final Class<?> providerClass;
+ static {
+ try {
+ // Preload classes before the security manager is on.
+ providerClass = ClassLoader.getSystemClassLoader().loadClass("PlatformLoggerBridgeTest$LogProducerFinder");
+ ((LoggerFinder)providerClass.newInstance()).getLogger("foo", providerClass);
+ } catch (Exception ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent implements Cloneable {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ sun.util.logging.PlatformLogger.Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ String msg;
+ Supplier<String> supplier;
+ String className;
+ String methodName;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ loggerName,
+ level,
+ isLoggable,
+ bundle,
+ msg,
+ supplier,
+ thrown,
+ args,
+ className,
+ methodName,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+
+ public LogEvent cloneWith(long sequenceNumber)
+ throws CloneNotSupportedException {
+ LogEvent cloned = (LogEvent)super.clone();
+ cloned.sequenceNumber = sequenceNumber;
+ return cloned;
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ return LogEvent.of(sequenceNumber, isLoggable, name,
+ null, null, level, bundle, key,
+ thrown, params);
+ }
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ Supplier<String> supplier, Throwable thrown, Object... params) {
+ return LogEvent.of(sequenceNumber, isLoggable, name,
+ null, null, level, bundle, supplier,
+ thrown, params);
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ String className, String methodName,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ evt.className = className;
+ evt.methodName = methodName;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ String className, String methodName,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
+ className, methodName, level, bundle, key, thrown, params);
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ String className, String methodName,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ Supplier<String> supplier, Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.supplier = supplier;
+ evt.isLoggable = isLoggable;
+ evt.className = className;
+ evt.methodName = methodName;
+ return evt;
+ }
+
+ public static LogEvent of(boolean isLoggable, String name,
+ String className, String methodName,
+ sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ Supplier<String> supplier, Throwable thrown, Object... params) {
+ return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
+ className, methodName, level, bundle, supplier, thrown, params);
+ }
+
+ }
+
+ public static class LogProducerFinder extends LoggerFinder {
+ static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+ final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+
+ public class LoggerImpl implements Logger, PlatformLogger.Bridge {
+ private final String name;
+ private sun.util.logging.PlatformLogger.Level level = sun.util.logging.PlatformLogger.Level.INFO;
+ private sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
+ private sun.util.logging.PlatformLogger.Level FINE = sun.util.logging.PlatformLogger.Level.FINE;
+ private sun.util.logging.PlatformLogger.Level FINER = sun.util.logging.PlatformLogger.Level.FINER;
+ private sun.util.logging.PlatformLogger.Level FINEST = sun.util.logging.PlatformLogger.Level.FINEST;
+ private sun.util.logging.PlatformLogger.Level CONFIG = sun.util.logging.PlatformLogger.Level.CONFIG;
+ private sun.util.logging.PlatformLogger.Level INFO = sun.util.logging.PlatformLogger.Level.INFO;
+ private sun.util.logging.PlatformLogger.Level WARNING = sun.util.logging.PlatformLogger.Level.WARNING;
+ private sun.util.logging.PlatformLogger.Level SEVERE = sun.util.logging.PlatformLogger.Level.SEVERE;
+
+ public LoggerImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return this.level != OFF && this.level.intValue() <= level.getSeverity();
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle,
+ String key, Throwable thrown) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle,
+ String format, Object... params) {
+ throw new UnsupportedOperationException();
+ }
+
+ void log(LogEvent event) {
+ eventQueue.add(event);
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier,
+ Throwable thrown) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msg, null, (Object[])null));
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level,
+ Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msgSupplier, null, (Object[])null));
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String msg,
+ Object... params) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msg, null, params));
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, String msg,
+ Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msg, thrown, (Object[])null));
+ }
+
+ @Override
+ public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, null, msgSupplier, thrown, (Object[])null));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msg, null, (Object[])null));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msgSupplier, null, (Object[])null));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Object... params) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msg, null, params));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msg, thrown, (Object[])null));
+ }
+
+ @Override
+ public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, null, msgSupplier, thrown, (Object[])null));
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String msg,
+ Object... params) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, bundle, msg, null, params));
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String msg, Object... params) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, bundle, msg, null, params));
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String msg,
+ Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name,
+ sourceClass, sourceMethod,
+ level, bundle, msg, thrown, (Object[])null));
+ }
+
+ @Override
+ public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+ String msg, Throwable thrown) {
+ log(LogEvent.of(isLoggable(level), name, null, null,
+ level, bundle, msg, thrown, (Object[])null));
+ }
+
+ @Override
+ public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
+ return this.level != OFF && level.intValue()
+ >= this.level.intValue();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return this.level != OFF;
+ }
+
+
+ }
+
+ @Override
+ public Logger getLogger(String name, Class<?> caller) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(LOGGERFINDER_PERMISSION);
+ }
+ PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+ ClassLoader callerLoader = AccessController.doPrivileged(pa);
+ if (callerLoader == null) {
+ return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ } else {
+ return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+ }
+ }
+ }
+
+ static final sun.util.logging.PlatformLogger.Level[] julLevels = {
+ sun.util.logging.PlatformLogger.Level.ALL,
+ sun.util.logging.PlatformLogger.Level.FINEST,
+ sun.util.logging.PlatformLogger.Level.FINER,
+ sun.util.logging.PlatformLogger.Level.FINE,
+ sun.util.logging.PlatformLogger.Level.CONFIG,
+ sun.util.logging.PlatformLogger.Level.INFO,
+ sun.util.logging.PlatformLogger.Level.WARNING,
+ sun.util.logging.PlatformLogger.Level.SEVERE,
+ sun.util.logging.PlatformLogger.Level.OFF,
+ };
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap map = new ConcurrentHashMap();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+
+ public static class MyHandler extends Handler {
+
+ @Override
+ public java.util.logging.Level getLevel() {
+ return java.util.logging.Level.ALL;
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+ true, record.getLoggerName(),
+ record.getSourceClassName(),
+ record.getSourceMethodName(),
+ PlatformLogger.Level.valueOf(record.getLevel().getName()),
+ record.getResourceBundle(), record.getMessage(),
+ record.getThrown(), record.getParameters()));
+ }
+ @Override
+ public void flush() {
+ }
+ @Override
+ public void close() throws SecurityException {
+ }
+
+ }
+
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ //"NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ LoggerFinder provider;
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ provider = LoggerFinder.getLoggerFinder();
+ test(provider, true);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ try {
+ provider = LoggerFinder.getLoggerFinder();
+ throw new RuntimeException("Expected exception not raised");
+ } catch (AccessControlException x) {
+ if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = LoggerFinder.getLoggerFinder();
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+ test(provider, false);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with access permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = LoggerFinder.getLoggerFinder();
+ } finally {
+ allowControl.get().set(control);
+ }
+ final boolean access = allowAccess.get().get();
+ try {
+ allowAccess.get().set(true);
+ test(provider, true);
+ } finally {
+ allowAccess.get().set(access);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(LoggerFinder provider, boolean hasRequiredPermissions) {
+
+ final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
+
+ PlatformLogger sysLogger1 = null;
+ try {
+ sysLogger1 = PlatformLogger.getLogger("foo");
+ loggerDescMap.put(sysLogger1, "PlatformLogger.getLogger(\"foo\")");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(SimplePolicy.ACCESS_LOGGING)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ final boolean old = allowAccess.get().get();
+ allowAccess.get().set(true);
+ try {
+ sysLogger1 = PlatformLogger.getLogger("foo");
+ loggerDescMap.put(sysLogger1, "PlatformLogger.getLogger(\"foo\")");
+ } finally {
+ allowAccess.get().set(old);
+ }
+ System.out.println("Got expected exception for system logger: " + acx);
+ }
+
+ final LogProducerFinder.LoggerImpl sysSink;
+ boolean old = allowControl.get().get();
+ allowControl.get().set(true);
+ try {
+ sysSink = LogProducerFinder.LoggerImpl.class.cast(
+ provider.getLogger("foo", Thread.class));
+ } finally {
+ allowControl.get().set(old);
+ }
+
+ testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ static void checkLogEvent(LoggerFinder provider, String desc,
+ LogEvent expected) {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void checkLogEvent(LoggerFinder provider, String desc,
+ LogEvent expected, boolean expectNotNull) {
+ LogEvent actual = eventQueue.poll();
+ if (actual == null && !expectNotNull) return;
+ if (actual != null && !expectNotNull) {
+ throw new RuntimeException("Unexpected log event found for " + desc
+ + "\n\tgot: " + actual);
+ }
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void setLevel( LogProducerFinder.LoggerImpl sink,
+ sun.util.logging.PlatformLogger.Level loggerLevel) {
+ sink.level = loggerLevel;
+ }
+
+ // Calls the methods defined on LogProducer and verify the
+ // parameters received by the underlying LogProducerFinder.LoggerImpl
+ // logger.
+ private static void testLogger(LoggerFinder provider,
+ Map<PlatformLogger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ PlatformLogger logger,
+ LogProducerFinder.LoggerImpl sink) {
+
+ System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
+ final sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
+ final sun.util.logging.PlatformLogger.Level ALL = sun.util.logging.PlatformLogger.Level.OFF;
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ System.out.println("\tlogger.log(messageLevel, fooMsg)");
+ System.out.println("\tlogger.<level>(fooMsg)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ if (messageLevel == ALL || messageLevel == OFF) continue;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ fooMsg, (Throwable)null, (Object[])null);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
+ logger.finest(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
+ logger.finer(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
+ logger.fine(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
+ logger.config(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
+ logger.info(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
+ logger.warning(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
+ logger.severe(fooMsg);
+ checkLogEvent(provider, desc2, expected);
+ }
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = fooMsg;
+ System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ if (messageLevel == ALL || messageLevel == OFF) continue;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ format, (Throwable)null, arg1, arg2);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(format, foo, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
+ logger.finest(format, arg1, arg2);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
+ logger.finer(format, arg1, arg2);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
+ logger.fine(format, arg1, arg2);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
+ logger.config(format, arg1, arg2);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
+ logger.info(format, arg1, arg2);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
+ logger.warning(format, arg1, arg2);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
+ logger.severe(format, arg1, arg2);
+ checkLogEvent(provider, desc2, expected);
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
+ for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+ if (messageLevel == ALL || messageLevel == OFF) continue;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ fooMsg, thrown, (Object[])null);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(fooMsg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
+ logger.finest(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
+ logger.finer(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
+ logger.fine(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
+ logger.config(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
+ logger.info(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
+ logger.warning(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
+ logger.severe(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected);
+ }
+ }
+ }
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+ final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+ final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+ final Permissions permissions;
+ final Permissions allPermissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAccess;
+ final ThreadLocal<AtomicBoolean> allowAll;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+ ThreadLocal<AtomicBoolean> allowAccess,
+ ThreadLocal<AtomicBoolean> allowAll) {
+ this.allowControl = allowControl;
+ this.allowAccess = allowAccess;
+ this.allowAll = allowAll;
+ permissions = new Permissions();
+ allPermissions = new PermissionsBuilder()
+ .add(new java.security.AllPermission())
+ .toPermissions();
+ }
+
+ Permissions getPermissions() {
+ if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+ PermissionsBuilder builder = new PermissionsBuilder()
+ .addAll(permissions);
+ if (allowControl.get().get()) {
+ builder.add(CONTROL);
+ }
+ if (allowAccess.get().get()) {
+ builder.add(ACCESS_LOGGER);
+ builder.add(ACCESS_LOGGING);
+ }
+ if (allowAll.get().get()) {
+ builder.addAll(allPermissions);
+ }
+ return builder.toPermissions();
+ }
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions().implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/api/LoggerFinderAPITest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8140364
+ * @author danielfuchs
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ * Tests the consistency of the LoggerFinder and JDK extensions.
+ * @modules java.base/sun.util.logging
+ * java.base/jdk.internal.logger
+ * @run main LoggerFinderAPITest
+ */
+
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import sun.util.logging.PlatformLogger;
+
+public class LoggerFinderAPITest {
+
+ static final Class<java.lang.System.Logger> spiLoggerClass
+ = java.lang.System.Logger.class;
+ static final Class<java.lang.System.Logger> jdkLoggerClass
+ = java.lang.System.Logger.class;
+ static final Class<sun.util.logging.PlatformLogger.Bridge> bridgeLoggerClass
+ = sun.util.logging.PlatformLogger.Bridge.class;
+ static final Class<java.util.logging.Logger> julLoggerClass
+ = java.util.logging.Logger.class;
+ static final Class<sun.util.logging.PlatformLogger.Bridge> julLogProducerClass
+ = PlatformLogger.Bridge.class;
+ static final Pattern julLogNames = Pattern.compile(
+ "^((log(p|rb)?)|severe|warning|info|config|fine|finer|finest|isLoggable)$");
+ static final Collection<Method> julLoggerIgnores;
+ static {
+ List<Method> ignores = new ArrayList<>();
+ try {
+ ignores.add(julLoggerClass.getDeclaredMethod("log", LogRecord.class));
+ } catch (NoSuchMethodException | SecurityException ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ julLoggerIgnores = Collections.unmodifiableList(ignores);
+ }
+
+
+
+ // Don't require LoggerBridge to have a body for those methods
+ interface LoggerBridgeMethodsWithNoBody extends
+ PlatformLogger.Bridge, java.lang.System.Logger {
+
+ @Override
+ public default String getName() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public default boolean isLoggable(PlatformLogger.Level level) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public default void log(sun.util.logging.PlatformLogger.Level level,
+ String msg, Throwable thrown) {
+ }
+ @Override
+ public default void log(sun.util.logging.PlatformLogger.Level level,
+ Throwable thrown, Supplier<String> msgSupplier) {
+ }
+ @Override
+ public default void log(sun.util.logging.PlatformLogger.Level level,
+ Supplier<String> msgSupplier) {
+ }
+ @Override
+ public default void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+ }
+ @Override
+ public default void log(sun.util.logging.PlatformLogger.Level level,
+ String format, Object... params) {
+ }
+ @Override
+ public default void logrb(sun.util.logging.PlatformLogger.Level level,
+ ResourceBundle bundle, String key, Throwable thrown) {
+ }
+ @Override
+ public default void logrb(sun.util.logging.PlatformLogger.Level level,
+ ResourceBundle bundle, String format, Object... params) {
+ }
+
+ @Override
+ public default void logrb(PlatformLogger.Level level,
+ String sourceClass, String sourceMethod,
+ ResourceBundle bundle, String msg, Throwable thrown) {
+ }
+
+ @Override
+ public default void logrb(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, ResourceBundle bundle, String msg,
+ Object... params) {
+ }
+
+ @Override
+ public default void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Supplier<String> msgSupplier) {
+ }
+
+ @Override
+ public default void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Object... params) {
+ }
+
+ @Override
+ public default void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg, Throwable thrown) {
+ }
+
+ @Override
+ public default void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, String msg) {
+ }
+
+ @Override
+ public default void logp(PlatformLogger.Level level, String sourceClass,
+ String sourceMethod, Throwable thrown,
+ Supplier<String> msgSupplier) {
+ }
+
+ static boolean requiresDefaultBodyFor(Method m) {
+ try {
+ Method m2 = LoggerBridgeMethodsWithNoBody.class
+ .getDeclaredMethod(m.getName(),
+ m.getParameterTypes());
+ return !m2.isDefault();
+ } catch (NoSuchMethodException x) {
+ return true;
+ }
+ }
+ }
+
+ final boolean warnDuplicateMappings;
+ public LoggerFinderAPITest(boolean verbose) {
+ this.warnDuplicateMappings = verbose;
+ for (Handler h : Logger.getLogger("").getHandlers()) {
+ if (h instanceof ConsoleHandler) {
+ Logger.getLogger("").removeHandler(h);
+ }
+ }
+ Logger.getLogger("").addHandler( new Handler() {
+ @Override
+ public void publish(LogRecord record) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("GOT LogRecord: ")
+ .append(record.getLevel().getLocalizedName())
+ .append(": [").append(record.getLoggerName())
+ .append("] ").append(record.getSourceClassName())
+ .append('.')
+ .append(record.getSourceMethodName()).append(" -> ")
+ .append(record.getMessage())
+ .append(' ')
+ .append(record.getParameters() == null ? ""
+ : Arrays.toString(record.getParameters()))
+ ;
+ System.out.println(builder);
+ if (record.getThrown() != null) {
+ record.getThrown().printStackTrace(System.out);
+ }
+ }
+ @Override public void flush() {}
+ @Override public void close() {}
+ });
+ }
+
+ public Stream<Method> getJulLogMethodStream(Class<?> loggerClass) {
+
+ return Stream.of(loggerClass.getMethods()).filter((x) -> {
+ final Matcher m = julLogNames.matcher(x.getName());
+ return m.matches() ? x.getAnnotation(Deprecated.class) == null : false;
+ });
+ }
+
+ /**
+ * Tells whether a method invocation of 'origin' can be transformed in a
+ * method invocation of 'target'.
+ * This method only look at the parameter signatures, it doesn't look at
+ * the name, nor does it look at the return types.
+ * <p>
+ * Example:
+ * <ul>
+ * <li>java.util.logging.Logger.log(Level, String, Object) can be invoked as<br>
+ java.util.logging.spi.Logger.log(Level, String, Object...) because the
+ last parameter in 'target' is a varargs.</li>
+ * <li>java.util.logging.Logger.log(Level, String) can also be invoked as<br>
+ java.util.logging.spi.Logger.log(Level, String, Object...) for the
+ same reason.</li>
+ * </ul>
+ * <p>
+ * The algorithm is tailored for our needs: when the last parameter in the
+ * target is a vararg, and when origin & target have the same number of
+ * parameters, then we consider that the types of the last parameter *must*
+ * match.
+ * <p>
+ * Similarly - we do not consider that o(X x, Y y, Y y) matches t(X x, Y... y)
+ * although strictly speaking, it should...
+ *
+ * @param origin The method in the original class
+ * @param target The correspondent candidate in the target class
+ * @return true if a method invocation of 'origin' can be transformed in a
+ * method invocation of 'target'.
+ */
+ public boolean canBeInvokedAs(Method origin, Method target,
+ Map<Class<?>,Class<?>> substitutes) {
+ final Class<?>[] xParams = target.getParameterTypes();
+ final Class<?>[] mParams = Stream.of(origin.getParameterTypes())
+ .map((x) -> substitutes.getOrDefault(x, x))
+ .collect(Collectors.toList()).toArray(new Class<?>[0]);
+ if (Arrays.deepEquals(xParams, mParams)) return true;
+ if (target.isVarArgs()) {
+ if (xParams.length == mParams.length) {
+ if (xParams[xParams.length-1].isArray()) {
+ return mParams[mParams.length -1].equals(
+ xParams[xParams.length -1].getComponentType());
+ }
+ } else if (xParams.length == mParams.length + 1) {
+ return Arrays.deepEquals(
+ Arrays.copyOfRange(xParams, 0, xParams.length-1), mParams);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Look whether {@code otherClass} has a public method similar to m
+ * @param m
+ * @param otherClass
+ * @return
+ */
+ public Stream<Method> findInvokable(Method m, Class<?> otherClass) {
+ final Map<Class<?>,Class<?>> substitues =
+ Collections.singletonMap(java.util.logging.Level.class,
+ sun.util.logging.PlatformLogger.Level.class);
+ return Stream.of(otherClass.getMethods())
+ .filter((x) -> m.getName().equals(x.getName()))
+ .filter((x) -> canBeInvokedAs(m, x, substitues));
+ }
+
+ /**
+ * Test that the concrete Logger implementation passed as parameter
+ * overrides all the methods defined by its interface.
+ * @param julLogger A concrete implementation of System.Logger
+ * whose backend is a JUL Logger.
+ */
+ StringBuilder testDefaultJULLogger(java.lang.System.Logger julLogger) {
+ final StringBuilder errors = new StringBuilder();
+ if (!bridgeLoggerClass.isInstance(julLogger)) {
+ final String errorMsg =
+ "Logger returned by LoggerFactory.getLogger(\"foo\") is not a "
+ + bridgeLoggerClass + "\n\t" + julLogger;
+ System.err.println(errorMsg);
+ errors.append(errorMsg).append('\n');
+ }
+ final Class<? extends java.lang.System.Logger> xClass = julLogger.getClass();
+ List<Method> notOverridden =
+ Stream.of(bridgeLoggerClass.getDeclaredMethods()).filter((m) -> {
+ try {
+ Method x = xClass.getDeclaredMethod(m.getName(), m.getParameterTypes());
+ return x == null;
+ } catch (NoSuchMethodException ex) {
+ return !Modifier.isStatic(m.getModifiers());
+ }
+ }).collect(Collectors.toList());
+ notOverridden.stream().filter((x) -> {
+ boolean shouldOverride = true;
+ try {
+ final Method m = xClass.getMethod(x.getName(), x.getParameterTypes());
+ Method m2 = null;
+ try {
+ m2 = jdkLoggerClass.getDeclaredMethod(x.getName(), x.getParameterTypes());
+ } catch (Exception e) {
+
+ }
+ shouldOverride = m.isDefault() || m2 == null;
+ } catch (Exception e) {
+ // should override.
+ }
+ return shouldOverride;
+ }).forEach(x -> {
+ final String errorMsg = xClass.getName() + " should override\n\t" + x.toString();
+ System.err.println(errorMsg);
+ errors.append(errorMsg).append('\n');
+ });
+ if (notOverridden.isEmpty()) {
+ System.out.println(xClass + " overrides all methods from " + bridgeLoggerClass);
+ }
+ return errors;
+ }
+
+ public static class ResourceBundeParam extends ResourceBundle {
+ Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
+ @Override
+ protected Object handleGetObject(String key) {
+ map.putIfAbsent(key, "${"+key+"}");
+ return map.get(key);
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
+ }
+
+ }
+
+ final ResourceBundle bundleParam =
+ ResourceBundle.getBundle(ResourceBundeParam.class.getName());
+
+ public static class ResourceBundeLocalized extends ResourceBundle {
+ Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
+ @Override
+ protected Object handleGetObject(String key) {
+ map.putIfAbsent(key, "Localized:${"+key+"}");
+ return map.get(key);
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
+ }
+
+ }
+
+ final static ResourceBundle bundleLocalized =
+ ResourceBundle.getBundle(ResourceBundeLocalized.class.getName());
+
+ final Map<Class<?>, Object> params = new HashMap<>();
+ {
+ params.put(String.class, "TestString");
+ params.put(sun.util.logging.PlatformLogger.Level.class, sun.util.logging.PlatformLogger.Level.WARNING);
+ params.put(java.lang.System.Logger.Level.class, java.lang.System.Logger.Level.WARNING);
+ params.put(ResourceBundle.class, bundleParam);
+ params.put(Throwable.class, new Throwable("TestThrowable (Please ignore it!)"));
+ params.put(Object[].class, new Object[] {"One", "Two"});
+ params.put(Object.class, new Object() {
+ @Override public String toString() { return "I am an object!"; }
+ });
+ }
+
+ public Object[] getParamsFor(Method m) {
+ final Object[] res = new Object[m.getParameterCount()];
+ final Class<?>[] sig = m.getParameterTypes();
+ if (res.length == 0) {
+ return res;
+ }
+ for (int i=0; i<res.length; i++) {
+ Object p = params.get(sig[i]);
+ if (p == null && sig[i].equals(Supplier.class)) {
+ final String msg = "SuppliedMsg["+i+"]";
+ p = (Supplier<String>) () -> msg;
+ }
+ if (p instanceof String) {
+ res[i] = String.valueOf(p)+"["+i+"]";
+ } else {
+ res[i] = p;
+ }
+ }
+ return res;
+ }
+
+ public void invokeOn(java.lang.System.Logger logger, Method m) {
+ Object[] p = getParamsFor(m);
+ try {
+ m.invoke(logger, p);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke "+m.toString(), e);
+ }
+ }
+
+ public void testAllJdkExtensionMethods(java.lang.System.Logger logger) {
+ Stream.of(jdkLoggerClass.getDeclaredMethods())
+ .filter(m -> !Modifier.isStatic(m.getModifiers()))
+ .forEach((m) -> invokeOn(logger, m));
+ }
+
+ public void testAllAPIMethods(java.lang.System.Logger logger) {
+ Stream.of(spiLoggerClass.getDeclaredMethods())
+ .filter(m -> !Modifier.isStatic(m.getModifiers()))
+ .forEach((m) -> invokeOn(logger, m));
+ }
+
+ public void testAllBridgeMethods(java.lang.System.Logger logger) {
+ Stream.of(bridgeLoggerClass.getDeclaredMethods())
+ .filter(m -> !Modifier.isStatic(m.getModifiers()))
+ .forEach((m) -> invokeOn(logger, m));
+ }
+
+ public void testAllLogProducerMethods(java.lang.System.Logger logger) {
+ Stream.of(julLogProducerClass.getDeclaredMethods())
+ .filter(m -> !Modifier.isStatic(m.getModifiers()))
+ .forEach((m) -> invokeOn(logger, m));
+ }
+
+ public StringBuilder testGetLoggerOverriddenOnSpi() {
+ final StringBuilder errors = new StringBuilder();
+ Stream.of(jdkLoggerClass.getDeclaredMethods())
+ .filter(m -> Modifier.isStatic(m.getModifiers()))
+ .filter(m -> Modifier.isPublic(m.getModifiers()))
+ .filter(m -> !m.getName().equals("getLoggerFinder"))
+ .filter(m -> {
+ try {
+ final Method x = bridgeLoggerClass.getDeclaredMethod(m.getName(), m.getParameterTypes());
+ return x == null;
+ } catch (NoSuchMethodException ex) {
+ return true;
+ }
+ }).forEach(m -> {
+ final String errorMsg = bridgeLoggerClass.getName() + " should override\n\t" + m.toString();
+ System.err.println(errorMsg);
+ errors.append(errorMsg).append('\n');
+ });
+ if (errors.length() == 0) {
+ System.out.println(bridgeLoggerClass + " overrides all static methods from " + jdkLoggerClass);
+ } else {
+ if (errors.length() > 0) throw new RuntimeException(errors.toString());
+ }
+ return errors;
+ }
+
+ public static void main(String argv[]) throws Exception {
+ final LoggerFinderAPITest test = new LoggerFinderAPITest(false);
+ final StringBuilder errors = new StringBuilder();
+ errors.append(test.testGetLoggerOverriddenOnSpi());
+ java.lang.System.Logger julLogger =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLogger("foo", LoggerFinderAPITest.class);
+ errors.append(test.testDefaultJULLogger(julLogger));
+ if (errors.length() > 0) throw new RuntimeException(errors.toString());
+ java.lang.System.Logger julSystemLogger =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLogger("bar", Thread.class);
+ errors.append(test.testDefaultJULLogger(julSystemLogger));
+ if (errors.length() > 0) throw new RuntimeException(errors.toString());
+ java.lang.System.Logger julLocalizedLogger =
+ (java.lang.System.Logger)
+ System.getLogger("baz", bundleLocalized);
+ java.lang.System.Logger julLocalizedSystemLogger =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLocalizedLogger("oof", bundleLocalized, Thread.class);
+ final String error = errors.toString();
+ if (!error.isEmpty()) throw new RuntimeException(error);
+ for (java.lang.System.Logger logger : new java.lang.System.Logger[] {
+ julLogger, julSystemLogger, julLocalizedLogger, julLocalizedSystemLogger
+ }) {
+ test.testAllJdkExtensionMethods(logger);
+ test.testAllAPIMethods(logger);
+ test.testAllBridgeMethods(logger);
+ test.testAllLogProducerMethods(logger);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/LoggerFinderBackendTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,2316 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8140364
+ * @author danielfuchs
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ * This test tests all the public API methods defined in the {@link
+ * java.lang.System.Logger} interface, as well as all the JDK
+ * internal methods defined in the
+ * {@link sun.util.logging.PlatformLogger.Bridge}
+ * interface, with loggers returned by {@link
+ * java.lang.System.LoggerFinder#getLogger(java.lang.String, java.lang.Class)}
+ * and {@link java.lang.System.LoggerFinder#getLocalizedLogger(java.lang.String,
+ * java.util.ResourceBundle, java.lang.Class)}
+ * (using both a null resource bundle and a non null resource bundle).
+ * It calls both the {@link java.lang.System} factory methods and
+ * {@link jdk.internal.logger.LazyLoggers} to obtains those loggers,
+ * and configure them with all possible known levels.
+ * @modules java.base/sun.util.logging
+ * java.base/jdk.internal.logger
+ * java.logging/sun.util.logging.internal
+ * @build LoggerFinderBackendTest SystemClassLoader
+ * @run main/othervm -Djava.system.class.loader=SystemClassLoader -Dtest.logger.hidesProvider=true LoggerFinderBackendTest
+ * @run main/othervm -Djava.system.class.loader=SystemClassLoader -Dtest.logger.hidesProvider=false LoggerFinderBackendTest
+ */
+
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import sun.util.logging.PlatformLogger.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import sun.util.logging.internal.LoggingProviderImpl;
+
+/**
+ * @author danielfuchs
+ */
+public class LoggerFinderBackendTest {
+
+ // whether the implementation of Logger try to do a best
+ // effort for logp... If the provider is not hidden, then
+ // the logp() implementation comes from LoggerWrapper - which does a
+ // best effort. Otherwise, it comes from the default provider
+ // which does support logp.
+ static final boolean BEST_EFFORT_FOR_LOGP =
+ !Boolean.getBoolean("test.logger.hidesProvider");
+ static final boolean VERBOSE = false;
+
+ static final Class<java.lang.System.Logger> spiLoggerClass =
+ java.lang.System.Logger.class;
+ static final Class<java.lang.System.Logger> jdkLoggerClass =
+ java.lang.System.Logger.class;
+ static final Class<sun.util.logging.PlatformLogger.Bridge> bridgeLoggerClass =
+ sun.util.logging.PlatformLogger.Bridge.class;
+
+ /** Use to retrieve the log records that were produced by the JUL backend */
+ static class LoggerTesterHandler extends Handler {
+ public final List<LogRecord> records =
+ Collections.synchronizedList(new ArrayList<>());
+
+ @Override
+ public void publish(LogRecord record) {
+ record.getSourceClassName(); record.getSourceMethodName();
+ records.add(record);
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() throws SecurityException {
+ records.clear();
+ }
+
+ public void reset() {
+ records.clear();
+ }
+ }
+
+ /** The {@link LoggerTesterHandler} handler is added to the root logger. */
+ static final LoggerTesterHandler handler = new LoggerTesterHandler();
+ static {
+ for (Handler h : Logger.getLogger("").getHandlers()) {
+ if (h instanceof ConsoleHandler) {
+ Logger.getLogger("").removeHandler(h);
+ }
+ }
+ Logger.getLogger("").addHandler(handler);
+ }
+
+ /**
+ * A resource handler parameter that will be used when calling out the
+ * logrb-like methods - as well as when calling the level-specific
+ * methods that take a ResourceBundle parameter.
+ */
+ public static class ResourceBundeParam extends ResourceBundle {
+ Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
+ @Override
+ protected Object handleGetObject(String key) {
+ map.putIfAbsent(key, "${"+key+"}");
+ return map.get(key);
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
+ }
+
+ }
+
+ final static ResourceBundle bundleParam =
+ ResourceBundle.getBundle(ResourceBundeParam.class.getName());
+
+ /**
+ * A resource handler parameter that will be used when creating localized
+ * loggers by calling {@link
+ * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)}.
+ */
+ public static class ResourceBundeLocalized extends ResourceBundle {
+ Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
+ @Override
+ protected Object handleGetObject(String key) {
+ map.putIfAbsent(key, "Localized:${"+key+"}");
+ return map.get(key);
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
+ }
+
+ }
+
+ /**
+ * The Levels enum is used to call all the level-specific methods on
+ * a logger instance. To minimize the amount of code it uses reflection
+ * to do so.
+ */
+ static Lookup lookup = MethodHandles.lookup();
+ public enum Levels {
+ /** Used to call all forms of Logger.log?(SEVERE, ...) */
+ SEVERE("severe", bridgeLoggerClass, Level.SEVERE, null, "error", false),
+ /** Used to call all forms of Logger.log?(WARNING,...) */
+ WARNING("warning", bridgeLoggerClass, Level.WARNING, "warning", "warning", false),
+ /** Used to call all forms of Logger.log?(INFO,...) */
+ INFO("info", bridgeLoggerClass, Level.INFO, "info", "info", false),
+ /** Used to call all forms of Logger.log?(CONFIG,...) */
+ CONFIG("config", bridgeLoggerClass, Level.CONFIG, null, "debug", false),
+ /** Used to call all forms of Logger.log?(FINE,...) */
+ FINE("fine", bridgeLoggerClass, Level.FINE, null, "debug", false),
+ /** Used to call all forms of Logger.log?(FINER,...) */
+ FINER("finer", bridgeLoggerClass, Level.FINER, null, "trace", false),
+ /** Used to call all forms of Logger.log?(FINEST,...) */
+ FINEST("finest", bridgeLoggerClass, Level.FINEST, null, "trace", false),
+ ;
+ public final String method; // The name of the level-specific method to call
+ public final Class<?> definingClass; // which interface j.u.logger.Logger or j.u.logging.spi.Logger defines it
+ public final Level platformLevel; // The platform Level it will be mapped to in Jul when Jul is the backend
+ public final String jdkExtensionToJUL; // The name of the method called on the JUL logger when JUL is the backend
+ public final String julToJdkExtension; // The name of the method called in the jdk extension by the default impl in jdk.internal.logging.Logger
+ public final String enableMethod; // The name of the isXxxxEnabled method
+ public final boolean hasSpecificIsEnabled;
+ Levels(String method, Class<?> definingClass, Level defaultMapping,
+ String jdkExtensionToJUL, String julToJdkExtension,
+ boolean hasSpecificIsEnabled) {
+ this.method = method;
+ this.definingClass = definingClass;
+ this.platformLevel = defaultMapping;
+ this.jdkExtensionToJUL = jdkExtensionToJUL;
+ this.julToJdkExtension = julToJdkExtension;
+ this.hasSpecificIsEnabled = hasSpecificIsEnabled;
+ if (hasSpecificIsEnabled) {
+ this.enableMethod = "is" + method.substring(0,1).toUpperCase()
+ + method.substring(1) + "Enabled";
+ } else {
+ this.enableMethod = "isLoggable";
+ }
+ }
+
+ /*
+ * calls this level specific method - e.g. if this==INFO: logger.info(msg);
+ */
+ public void level(Object logger, String msg) {
+ MethodType mt = MethodType.methodType(void.class, Level.class, String.class);
+ invoke("log", logger, mt, platformLevel, msg);
+ }
+
+ /*
+ * calls this level specific method - e.g. if this==INFO: logger.info(msgSupplier);
+ */
+ public void level(Object logger, Supplier<String> msgSupplier) {
+ MethodType mt = MethodType.methodType(void.class, Level.class, Supplier.class);
+ invoke("log", logger, mt, platformLevel, msgSupplier);
+ }
+
+ /*
+ * calls this level specific method - e.g. if this==INFO: logger.info(msg, params);
+ */
+ public void level(Object logger, String msg, Object... params) {
+ MethodType mt = MethodType.methodType(void.class, Level.class, String.class,
+ Object[].class);
+ invoke("log", logger, mt, platformLevel, msg, params);
+ }
+
+ /*
+ * calls this level specific method - e.g. if this==INFO: logger.info(msg, thrown);
+ */
+ public void level(Object logger, String msg, Throwable thrown) {
+ MethodType mt = MethodType.methodType(void.class, Level.class, String.class,
+ Throwable.class);
+ invoke("log", logger, mt, platformLevel, msg, thrown);
+ }
+
+ /*
+ * calls this level specific method - e.g. if this==INFO: logger.info(msgSupplier, thrown);
+ */
+ public void level(Object logger, Supplier<String> msgSupplier, Throwable thrown) {
+ MethodType mt = MethodType.methodType(void.class, Level.class,
+ Throwable.class, Supplier.class);
+ invoke("log", logger, mt, platformLevel, thrown, msgSupplier);
+ }
+
+ /*
+ * calls this level specific method - e.g. if this==INFO: logger.info(bundle, msg);
+ */
+ public void level(Object logger, String msg, ResourceBundle bundle) {
+ MethodType mt = MethodType.methodType(void.class, Level.class,
+ ResourceBundle.class, String.class, Object[].class);
+ invoke("logrb", logger, mt, platformLevel, bundle, msg, null);
+ }
+
+ public void level(Object logger, String msg, ResourceBundle bundle,
+ Object... params) {
+ MethodType mt = MethodType.methodType(void.class, Level.class,
+ ResourceBundle.class, String.class, Object[].class);
+ invoke("logrb", logger, mt, platformLevel, bundle, msg, params);
+ }
+
+ public void level(Object logger, String msg, ResourceBundle bundle,
+ Throwable thrown) {
+ MethodType mt = MethodType.methodType(void.class, Level.class,
+ ResourceBundle.class, String.class, Throwable.class);
+ invoke("logrb", logger, mt, platformLevel, bundle, msg, thrown);
+ }
+
+ public boolean isEnabled(Object logger) {
+ try {
+ if (hasSpecificIsEnabled) {
+ MethodType mt = MethodType.methodType(boolean.class);
+ final MethodHandle handle = lookup.findVirtual(definingClass,
+ enableMethod, mt).bindTo(logger);
+ return Boolean.class.cast(handle.invoke());
+ } else {
+ MethodType mt = MethodType.methodType(boolean.class,
+ Level.class);
+ final MethodHandle handle = lookup.findVirtual(definingClass,
+ enableMethod, mt).bindTo(logger);
+ return Boolean.class.cast(handle.invoke(platformLevel));
+ }
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private void invoke(String method, Object logger, MethodType mt, Object... args) {
+ try {
+ final int last = mt.parameterCount()-1;
+ boolean isVarargs = mt.parameterType(last).isArray();
+ final MethodHandle handle = lookup.findVirtual(definingClass,
+ method, mt).bindTo(logger);
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append(logger.getClass().getSimpleName()).append('.')
+ .append(method).append('(');
+ String sep = "";
+ int offset = 0;
+ Object[] params = args;
+ for (int i=0; (i-offset) < params.length; i++) {
+ if (isVarargs && i == last) {
+ offset = last;
+ params = (Object[])args[i];
+ if (params == null) break;
+ }
+ Object p = params[i - offset];
+ String quote = (p instanceof String) ? "\"" : "";
+ builder.append(sep).append(quote).append(p).append(quote);
+ sep = ", ";
+ }
+ builder.append(')');
+ if (verbose) {
+ System.out.println(builder);
+ }
+ handle.invokeWithArguments(args);
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ };
+
+ static interface Checker<LogResult, L> extends BiFunction<LogResult, L, Void> {}
+ static interface JdkLogTester
+ extends BiFunction<sun.util.logging.PlatformLogger.Bridge, Level, Void> {}
+ static interface SpiLogTester
+ extends BiFunction<java.lang.System.Logger, java.lang.System.Logger.Level, Void> {}
+
+ static interface MethodInvoker<LOGGER, LEVEL> {
+ public void logX(LOGGER logger, LEVEL level, Object... args);
+ }
+
+ public enum JdkLogMethodInvoker
+ implements MethodInvoker<sun.util.logging.PlatformLogger.Bridge, Level> {
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, String, Object...)};
+ **/
+ LOG_STRING_PARAMS("log", MethodType.methodType(void.class,
+ Level.class, String.class, Object[].class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, String, Throwable)};
+ **/
+ LOG_STRING_THROWN("log", MethodType.methodType(void.class,
+ Level.class, String.class, Throwable.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
+ **/
+ LOG_SUPPLIER("log", MethodType.methodType(void.class,
+ Level.class, Supplier.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, Throwable, Supplier<String>)};
+ **/
+ LOG_SUPPLIER_THROWN("log", MethodType.methodType(void.class,
+ Level.class, Throwable.class, Supplier.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logp(Level, String, String, String)};
+ **/
+ LOGP_STRING("logp", MethodType.methodType(void.class,
+ Level.class, String.class, String.class, String.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logp(Level, String, String, String, Object...)};
+ **/
+ LOGP_STRING_PARAMS("logp", MethodType.methodType(void.class,
+ Level.class, String.class, String.class, String.class, Object[].class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logp(Level, String, String, String, Throwable)};
+ **/
+ LOGP_STRING_THROWN("logp", MethodType.methodType(void.class,
+ Level.class, String.class, String.class, String.class, Throwable.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logp(Level, String, String, Supplier<String>)};
+ **/
+ LOGP_SUPPLIER("logp", MethodType.methodType(void.class,
+ Level.class, String.class, String.class, Supplier.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logp(Level, String, String, Throwable, Supplier<String>)};
+ **/
+ LOGP_SUPPLIER_THROWN("logp", MethodType.methodType(void.class,
+ Level.class, String.class, String.class,
+ Throwable.class, Supplier.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Object...)};
+ **/
+ LOGRB_STRING_PARAMS("logrb", MethodType.methodType(void.class,
+ Level.class, ResourceBundle.class, String.class, Object[].class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Throwable)};
+ **/
+ LOGRB_STRING_THROWN("logrb", MethodType.methodType(void.class,
+ Level.class, ResourceBundle.class, String.class, Throwable.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logrb(Level, String, String, ResourceBundle, String, Object...)};
+ **/
+ LOGRBP_STRING_PARAMS("logrb", MethodType.methodType(void.class,
+ Level.class, String.class, String.class, ResourceBundle.class,
+ String.class, Object[].class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logrb(Level, String, String, ResourceBundle, String, Throwable)};
+ **/
+ LOGRBP_STRING_THROWN("logrb", MethodType.methodType(void.class,
+ Level.class, String.class, String.class, ResourceBundle.class,
+ String.class, Throwable.class)),
+ ;
+ final MethodType mt;
+ final String method;
+ JdkLogMethodInvoker(String method, MethodType mt) {
+ this.mt = mt;
+ this.method = method;
+ }
+ Object[] makeArgs(Level level, Object... rest) {
+ List<Object> list = new ArrayList<>(rest == null ? 1 : rest.length + 1);
+ list.add(level);
+ if (rest != null) {
+ list.addAll(Arrays.asList(rest));
+ }
+ return list.toArray(new Object[list.size()]);
+ }
+
+ @Override
+ public void logX(sun.util.logging.PlatformLogger.Bridge logger, Level level, Object... args) {
+ try {
+ MethodHandle handle = lookup.findVirtual(bridgeLoggerClass,
+ method, mt).bindTo(logger);
+ final int last = mt.parameterCount()-1;
+ boolean isVarargs = mt.parameterType(last).isArray();
+
+ args = makeArgs(level, args);
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append(logger.getClass().getSimpleName()).append('.')
+ .append(this.method).append('(');
+ String sep = "";
+ int offset = 0;
+ Object[] params = args;
+ for (int i=0; (i-offset) < params.length; i++) {
+ if (isVarargs && i == last) {
+ offset = last;
+ params = (Object[])args[i];
+ if (params == null) break;
+ }
+ Object p = params[i - offset];
+ String quote = (p instanceof String) ? "\"" : "";
+ p = p instanceof Level ? "Level."+p : p;
+ builder.append(sep).append(quote).append(p).append(quote);
+ sep = ", ";
+ }
+ builder.append(')');
+ if (verbose) System.out.println(builder);
+ handle.invokeWithArguments(args);
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+
+ public enum SpiLogMethodInvoker implements MethodInvoker<java.lang.System.Logger,
+ java.lang.System.Logger.Level> {
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, String, Object...)};
+ **/
+ LOG_STRING_PARAMS("log", MethodType.methodType(void.class,
+ java.lang.System.Logger.Level.class, String.class, Object[].class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, String, Throwable)};
+ **/
+ LOG_STRING_THROWN("log", MethodType.methodType(void.class,
+ java.lang.System.Logger.Level.class, String.class, Throwable.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
+ **/
+ LOG_SUPPLIER("log", MethodType.methodType(void.class,
+ java.lang.System.Logger.Level.class, Supplier.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, Throwable, Supplier<String>)};
+ **/
+ LOG_SUPPLIER_THROWN("log", MethodType.methodType(void.class,
+ java.lang.System.Logger.Level.class, Supplier.class, Throwable.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
+ **/
+ LOG_OBJECT("log", MethodType.methodType(void.class,
+ java.lang.System.Logger.Level.class, Object.class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Object...)};
+ **/
+ LOGRB_STRING_PARAMS("log", MethodType.methodType(void.class,
+ java.lang.System.Logger.Level.class, ResourceBundle.class,
+ String.class, Object[].class)),
+ /**
+ * Tests {@link
+ * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Throwable)};
+ **/
+ LOGRB_STRING_THROWN("log", MethodType.methodType(void.class,
+ java.lang.System.Logger.Level.class, ResourceBundle.class,
+ String.class, Throwable.class)),
+ ;
+ final MethodType mt;
+ final String method;
+ SpiLogMethodInvoker(String method, MethodType mt) {
+ this.mt = mt;
+ this.method = method;
+ }
+ Object[] makeArgs(java.lang.System.Logger.Level level, Object... rest) {
+ List<Object> list = new ArrayList<>(rest == null ? 1 : rest.length + 1);
+ list.add(level);
+ if (rest != null) {
+ list.addAll(Arrays.asList(rest));
+ }
+ return list.toArray(new Object[list.size()]);
+ }
+
+ @Override
+ public void logX(java.lang.System.Logger logger,
+ java.lang.System.Logger.Level level, Object... args) {
+ try {
+ MethodHandle handle = lookup.findVirtual(spiLoggerClass,
+ method, mt).bindTo(logger);
+ final int last = mt.parameterCount()-1;
+ boolean isVarargs = mt.parameterType(last).isArray();
+
+ args = makeArgs(level, args);
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append(logger.getClass().getSimpleName()).append('.')
+ .append(this.method).append('(');
+ String sep = "";
+ int offset = 0;
+ Object[] params = args;
+ for (int i=0; (i-offset) < params.length; i++) {
+ if (isVarargs && i == last) {
+ offset = last;
+ params = (Object[])args[i];
+ if (params == null) break;
+ }
+ Object p = params[i - offset];
+ String quote = (p instanceof String) ? "\"" : "";
+ p = p instanceof Level ? "Level."+p : p;
+ builder.append(sep).append(quote).append(p).append(quote);
+ sep = ", ";
+ }
+ builder.append(')');
+ if (verbose) System.out.println(builder);
+ handle.invokeWithArguments(args);
+ } catch (Throwable ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+
+ public abstract static class BackendTester<BackendRecord> {
+ static final Level[] levelMap = {Level.ALL, Level.FINER, Level.FINE,
+ Level.INFO, Level.WARNING, Level.SEVERE, Level.OFF};
+
+ abstract class BackendAdaptor {
+ public abstract String getLoggerName(BackendRecord res);
+ public abstract Object getLevel(BackendRecord res);
+ public abstract String getMessage(BackendRecord res);
+ public abstract String getSourceClassName(BackendRecord res);
+ public abstract String getSourceMethodName(BackendRecord res);
+ public abstract Throwable getThrown(BackendRecord res);
+ public abstract ResourceBundle getResourceBundle(BackendRecord res);
+ public abstract void setLevel(java.lang.System.Logger logger,
+ Level level);
+ public abstract void setLevel(java.lang.System.Logger logger,
+ java.lang.System.Logger.Level level);
+ public abstract List<BackendRecord> getBackendRecords();
+ public abstract void resetBackendRecords();
+ public boolean shouldBeLoggable(Levels level, Level loggerLevel) {
+ final Level logLevel = level.platformLevel;
+ return shouldBeLoggable(logLevel, loggerLevel);
+ }
+ public boolean shouldBeLoggable(Level logLevel, Level loggerLevel) {
+ return loggerLevel.intValue() != Level.OFF.intValue()
+ && logLevel.intValue() >= loggerLevel.intValue();
+ }
+ public boolean shouldBeLoggable(java.lang.System.Logger.Level logLevel,
+ java.lang.System.Logger.Level loggerLevel) {
+ return loggerLevel != java.lang.System.Logger.Level.OFF
+ && logLevel.ordinal() >= loggerLevel.ordinal();
+ }
+ public boolean isLoggable(java.lang.System.Logger logger, Level l) {
+ return bridgeLoggerClass.cast(logger).isLoggable(l);
+ }
+ public String getCallerClassName(Levels level, String clazz) {
+ return clazz != null ? clazz : Levels.class.getName();
+ }
+ public String getCallerClassName(MethodInvoker<?,?> logMethod,
+ String clazz) {
+ return clazz != null ? clazz : logMethod.getClass().getName();
+ }
+ public String getCallerMethodName(Levels level, String method) {
+ return method != null ? method : "invoke";
+ }
+ public String getCallerMethodName(MethodInvoker<?,?> logMethod,
+ String method) {
+ return method != null ? method : "logX";
+ }
+ public Object getMappedLevel(Object level) {
+ return level;
+ }
+
+ public Level toJUL(java.lang.System.Logger.Level level) {
+ return levelMap[level.ordinal()];
+ }
+ }
+
+ public final boolean isSystem;
+ public final Class<? extends java.lang.System.Logger> restrictedTo;
+ public final ResourceBundle localized;
+ public BackendTester(boolean isSystem) {
+ this(isSystem,null,null);
+ }
+ public BackendTester(boolean isSystem, ResourceBundle localized) {
+ this(isSystem,null,localized);
+ }
+ public BackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo) {
+ this(isSystem, restrictedTo, null);
+ }
+ public BackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo,
+ ResourceBundle localized) {
+ this.isSystem = isSystem;
+ this.restrictedTo = restrictedTo;
+ this.localized = localized;
+ }
+
+ public java.lang.System.Logger convert(java.lang.System.Logger logger) {
+ return logger;
+ }
+
+ public static Level[] LEVELS = {
+ Level.OFF,
+ Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG,
+ Level.FINE, Level.FINER, Level.FINEST,
+ Level.ALL
+ };
+
+ abstract BackendAdaptor adaptor();
+
+ protected void checkRecord(Levels test, BackendRecord res, String loggerName,
+ Level level, String msg, String className, String methodName,
+ Throwable thrown, ResourceBundle bundle, Object... params) {
+ checkRecord(test, res, loggerName, level, ()->msg, className,
+ methodName, thrown, bundle, params);
+
+ }
+ protected void checkRecord(Levels test, BackendRecord res, String loggerName,
+ Level level, Supplier<String> msg, String className, String methodName,
+ Throwable thrown, ResourceBundle bundle, Object... params) {
+ checkRecord(test.method, res, loggerName, level, msg,
+ className, methodName, thrown, bundle, params);
+ }
+ protected <L> void checkRecord(String logMethod, BackendRecord res, String loggerName,
+ L level, Supplier<String> msg, String className, String methodName,
+ Throwable thrown, ResourceBundle bundle, Object... params) {
+ final BackendAdaptor analyzer = adaptor();
+ if (! Objects.equals(analyzer.getLoggerName(res), loggerName)) {
+ throw new RuntimeException(logMethod+": expected logger name "
+ + loggerName + " got " + analyzer.getLoggerName(res));
+ }
+ if (!Objects.equals(analyzer.getLevel(res), analyzer.getMappedLevel(level))) {
+ throw new RuntimeException(logMethod+": expected level "
+ + analyzer.getMappedLevel(level) + " got " + analyzer.getLevel(res));
+ }
+ if (!Objects.equals(analyzer.getMessage(res), msg.get())) {
+ throw new RuntimeException(logMethod+": expected message \""
+ + msg.get() + "\" got \"" + analyzer.getMessage(res) +"\"");
+ }
+ if (!Objects.equals(analyzer.getSourceClassName(res), className)) {
+ throw new RuntimeException(logMethod
+ + ": expected class name \"" + className
+ + "\" got \"" + analyzer.getSourceClassName(res) +"\"");
+ }
+ if (!Objects.equals(analyzer.getSourceMethodName(res), methodName)) {
+ throw new RuntimeException(logMethod
+ + ": expected method name \"" + methodName
+ + "\" got \"" + analyzer.getSourceMethodName(res) +"\"");
+ }
+ final Throwable thrownRes = analyzer.getThrown(res);
+ if (!Objects.equals(thrownRes, thrown)) {
+ throw new RuntimeException(logMethod
+ + ": expected throwable \"" + thrown
+ + "\" got \"" + thrownRes + "\"");
+ }
+ if (!Objects.equals(analyzer.getResourceBundle(res), bundle)) {
+ throw new RuntimeException(logMethod
+ + ": expected bundle \"" + bundle
+ + "\" got \"" + analyzer.getResourceBundle(res) +"\"");
+ }
+ }
+
+ public void testLevel(Levels level, java.lang.System.Logger logger,
+ String msg) {
+ Runnable test = () -> level.level(logger, msg);
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord(level, res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(level, Levels.class.getName()),
+ adaptor().getCallerMethodName(level, "invoke"),
+ null, localized);
+ return null;
+ };
+ test("msg", level, logger, test, check);
+ }
+
+ public void testLevel(Levels level, java.lang.System.Logger logger,
+ String msg, Object... params) {
+ Runnable test = () -> level.level(logger, msg, (Object[])params);
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord(level, res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(level, Levels.class.getName()),
+ adaptor().getCallerMethodName(level, "invoke"),
+ null, localized, (Object[])params);
+ return null;
+ };
+ test("msg, params", level, logger, test, check);
+ }
+
+ public void testLevel(Levels level, java.lang.System.Logger logger,
+ String msg, Throwable thrown) {
+ Runnable test = () -> level.level(logger, msg, thrown);
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord(level, res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(level, Levels.class.getName()),
+ adaptor().getCallerMethodName(level, "invoke"),
+ thrown, localized);
+ return null;
+ };
+ test("msg, thrown", level, logger, test, check);
+ }
+
+ public void testLevel(Levels level, java.lang.System.Logger logger,
+ Supplier<String> msg) {
+ Runnable test = () -> level.level(logger, msg);
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord(level, res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(level, Levels.class.getName()),
+ adaptor().getCallerMethodName(level, "invoke"),
+ null, null);
+ return null;
+ };
+ test("msgSupplier", level, logger, test, check);
+ }
+
+ public void testLevel(Levels level, java.lang.System.Logger logger,
+ Supplier<String> msg, Throwable thrown) {
+ Runnable test = () -> level.level(logger, msg, thrown);
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord(level, res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(level, Levels.class.getName()),
+ adaptor().getCallerMethodName(level, "invoke"),
+ thrown, null);
+ return null;
+ };
+ test("throw, msgSupplier", level, logger, test, check);
+ }
+
+ public void testLevel(Levels level, java.lang.System.Logger logger,
+ String msg, ResourceBundle bundle) {
+ Runnable test = () -> level.level(logger, msg, bundle);
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord(level, res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(level, Levels.class.getName()),
+ adaptor().getCallerMethodName(level, "invoke"),
+ null, bundle);
+ return null;
+ };
+ test("bundle, msg", level, logger, test, check);
+ }
+
+ public void testLevel(Levels level, java.lang.System.Logger logger,
+ String msg, ResourceBundle bundle, Object... params) {
+ Runnable test = () -> level.level(logger, msg, bundle, (Object[])params);
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord(level, res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(level, Levels.class.getName()),
+ adaptor().getCallerMethodName(level, "invoke"),
+ null, bundle, (Object[])params);
+ return null;
+ };
+ test("bundle, msg, params", level, logger, test, check);
+ }
+
+ public void testLevel(Levels level, java.lang.System.Logger logger,
+ String msg, ResourceBundle bundle, Throwable thrown) {
+ Runnable test = () -> level.level(logger, msg, bundle, thrown);
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord(level, res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(level, Levels.class.getName()),
+ adaptor().getCallerMethodName(level, "invoke"),
+ thrown, bundle);
+ return null;
+ };
+ test("bundle, msg, throwable", level, logger, test, check);
+ }
+
+ // System.Logger
+ public void testSpiLog(java.lang.System.Logger logger, String msg) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOG_STRING_PARAMS,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOG_STRING_PARAMS,
+ "logX"), null, localized);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, (Object[])null);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\")";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+ public void testSpiLog(java.lang.System.Logger logger,
+ ResourceBundle bundle, String msg) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
+ "logX"), null, bundle);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, (Object[])null);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) -> "log(Level." + l
+ + ", bundle, \"" + msg + "\")";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+ public void testSpiLog(java.lang.System.Logger logger, String msg, Object... params) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOG_STRING_PARAMS,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOG_STRING_PARAMS,
+ "logX"), null, localized, params);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, params);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\", params...)";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+ public void testSpiLog(java.lang.System.Logger logger,
+ ResourceBundle bundle, String msg, Object... params) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
+ "logX"), null, bundle, params);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, params);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) -> "log(Level." + l
+ + ", bundle, \"" + msg + "\", params...)";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+ public void testSpiLog(java.lang.System.Logger logger, String msg, Throwable thrown) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOG_STRING_THROWN,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOG_STRING_THROWN,
+ "logX"), thrown, localized);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOG_STRING_THROWN.logX(x, level, msg, thrown);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", \"" + msg + "\", thrown)";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+ public void testSpiLog(java.lang.System.Logger logger,
+ ResourceBundle bundle, String msg, Throwable thrown) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOGRB_STRING_THROWN,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOGRB_STRING_THROWN,
+ "logX"), thrown, bundle);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOGRB_STRING_THROWN.logX(x, level, bundle, msg, thrown);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", bundle, \"" + msg + "\", thrown)";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+ public void testSpiLog(java.lang.System.Logger logger, Supplier<String> msg) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOG_SUPPLIER,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOG_SUPPLIER,
+ "logX"), null, null);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOG_SUPPLIER.logX(x, level, msg);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", () -> \"" + msg.get() + "\")";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+ public void testSpiLog(java.lang.System.Logger logger, Object obj) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> obj.toString(),
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOG_OBJECT,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOG_OBJECT,
+ "logX"), null, null);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOG_OBJECT.logX(x, level, obj);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", new "+obj.getClass().getSimpleName()+"(\""
+ + obj.toString() + "\"))";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+ public void testSpiLog(java.lang.System.Logger logger, Throwable thrown, Supplier<String> msg) {
+ Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(
+ SpiLogMethodInvoker.LOG_SUPPLIER_THROWN,
+ SpiLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ SpiLogMethodInvoker.LOG_SUPPLIER_THROWN,
+ "logX"), thrown, null);
+ return null;
+ };
+ SpiLogTester tester = (x, level) -> {
+ SpiLogMethodInvoker.LOG_SUPPLIER_THROWN.logX(x, level, msg, thrown);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", () -> \"" + msg.get() + "\", thrown)";
+ testSpiLog(logger, tester, check, nameProducer);
+ }
+
+
+ // JDK
+
+ public void testLog(java.lang.System.Logger logger, String msg) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOG_STRING_PARAMS,
+ JdkLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ JdkLogMethodInvoker.LOG_STRING_PARAMS,
+ "logX"), null, localized);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, (Object[])null);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\")";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogrb(java.lang.System.Logger logger,
+ ResourceBundle bundle, String msg) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
+ JdkLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
+ "logX"), null, bundle);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, (Object[])null);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) -> "logrb(Level." + l
+ + ", bundle, \"" + msg + "\")";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLog(java.lang.System.Logger logger, String msg, Object... params) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOG_STRING_PARAMS,
+ JdkLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ JdkLogMethodInvoker.LOG_STRING_PARAMS,
+ "logX"), null, localized, params);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, params);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\", params...)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogrb(java.lang.System.Logger logger,
+ ResourceBundle bundle, String msg, Object... params) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
+ JdkLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
+ "logX"), null, bundle, params);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, params);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) -> "log(Level." + l
+ + ", bundle, \"" + msg + "\", params...)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLog(java.lang.System.Logger logger, String msg, Throwable thrown) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOG_STRING_THROWN,
+ JdkLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ JdkLogMethodInvoker.LOG_STRING_THROWN,
+ "logX"), thrown, localized);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOG_STRING_THROWN.logX(x, level, msg, thrown);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", \"" + msg + "\", thrown)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogrb(java.lang.System.Logger logger,
+ ResourceBundle bundle, String msg, Throwable thrown) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRB_STRING_THROWN,
+ JdkLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ JdkLogMethodInvoker.LOGRB_STRING_THROWN,
+ "logX"), thrown, bundle);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGRB_STRING_THROWN.logX(x, level, bundle, msg, thrown);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", bundle, \"" + msg + "\", thrown)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLog(java.lang.System.Logger logger, Supplier<String> msg) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOG_SUPPLIER,
+ JdkLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ JdkLogMethodInvoker.LOG_SUPPLIER,
+ "logX"), null, null);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOG_SUPPLIER.logX(x, level, msg);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", () -> \"" + msg.get() + "\")";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLog(java.lang.System.Logger logger, Throwable thrown, Supplier<String> msg) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOG_SUPPLIER_THROWN,
+ JdkLogMethodInvoker.class.getName()),
+ adaptor().getCallerMethodName(
+ JdkLogMethodInvoker.LOG_SUPPLIER_THROWN,
+ "logX"), thrown, null);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOG_SUPPLIER_THROWN.logX(x, level, thrown, msg);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", () -> \"" + msg.get() + "\", thrown)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ static Supplier<String> logpMessage(ResourceBundle bundle,
+ String className, String methodName, Supplier<String> msg) {
+ if (BEST_EFFORT_FOR_LOGP && bundle == null
+ && (className != null || methodName != null)) {
+ final String cName = className == null ? "" : className;
+ final String mName = methodName == null ? "" : methodName;
+ return () -> {
+ String m = msg.get();
+ return String.format("[%s %s] %s", cName, mName, m == null ? "" : m);
+ };
+ } else {
+ return msg;
+ }
+ }
+
+ public void testLogp(java.lang.System.Logger logger, String className,
+ String methodName, String msg) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("logp", res, logger.getName(), l,
+ logpMessage(localized, className, methodName, () -> msg),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_STRING, className),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_STRING, methodName),
+ null, localized);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGP_STRING.logX(x, level,
+ className, methodName, msg);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "logp(Level." + l + ", class, method, \"" + msg + "\")";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogrb(java.lang.System.Logger logger, String className,
+ String methodName, ResourceBundle bundle, String msg) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("logp", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, className),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, methodName),
+ null, bundle);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGRBP_STRING_PARAMS.logX(x, level,
+ className, methodName, bundle, msg, (Object[])null);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "logp(Level." + l + ", class, method, bundle, \"" + msg + "\")";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogp(java.lang.System.Logger logger, String className,
+ String methodName, String msg, Object... params) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("logp", res, logger.getName(), l,
+ logpMessage(localized, className, methodName, () -> msg),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_STRING_PARAMS, className),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_STRING_PARAMS, methodName),
+ null, localized, params);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGP_STRING_PARAMS.logX(x, level,
+ className, methodName, msg, params);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", class, method, \"" + msg + "\", params...)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogrb(java.lang.System.Logger logger, String className,
+ String methodName, ResourceBundle bundle, String msg,
+ Object... params) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("logp", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, className),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, methodName),
+ null, bundle, params);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGRBP_STRING_PARAMS.logX(x, level,
+ className, methodName, bundle, msg, params);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", class, method, bundle, \""
+ + msg + "\", params...)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogp(java.lang.System.Logger logger, String className,
+ String methodName, String msg, Throwable thrown) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l,
+ logpMessage(localized, className, methodName, () -> msg),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_STRING_THROWN, className),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_STRING_THROWN, methodName),
+ thrown, localized);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGP_STRING_THROWN.logX(x, level,
+ className, methodName, msg, thrown);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", class, method, \"" + msg + "\", thrown)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogrb(java.lang.System.Logger logger, String className,
+ String methodName, ResourceBundle bundle,
+ String msg, Throwable thrown) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l, () -> msg,
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRBP_STRING_THROWN, className),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGRBP_STRING_THROWN, methodName),
+ thrown, bundle);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGRBP_STRING_THROWN.logX(x, level,
+ className, methodName, bundle, msg, thrown);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", class, method, bundle, \"" + msg + "\", thrown)";
+ testJdkLog(logger, tester, check, nameProducer);
+
+ }
+
+ public void testLogp(java.lang.System.Logger logger, String className,
+ String methodName, Supplier<String> msg) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l,
+ logpMessage(null, className, methodName, msg),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_SUPPLIER, className),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_SUPPLIER, methodName),
+ null, null);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGP_SUPPLIER.logX(x, level,
+ className, methodName, msg);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", class, method, () -> \"" + msg.get() + "\")";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ public void testLogp(java.lang.System.Logger logger, String className,
+ String methodName, Throwable thrown, Supplier<String> msg) {
+ Checker<BackendRecord, Level> check = (res, l) -> {
+ checkRecord("log", res, logger.getName(), l,
+ logpMessage(null, className, methodName, msg),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN, className),
+ adaptor().getCallerClassName(
+ JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN, methodName),
+ thrown, null);
+ return null;
+ };
+ JdkLogTester tester = (x, level) -> {
+ JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN.logX(x, level,
+ className, methodName, thrown, msg);
+ return null;
+ };
+ Function<String, String> nameProducer = (l) ->
+ "log(Level." + l + ", class, method, () -> \"" + msg.get() + "\", thrown)";
+ testJdkLog(logger, tester, check, nameProducer);
+ }
+
+ private void testJdkLog(java.lang.System.Logger logger,
+ JdkLogTester log, Checker<BackendRecord,Level> check,
+ Function<String, String> nameProducer) {
+ if (restrictedTo != null) {
+ if (!bridgeLoggerClass.isAssignableFrom(restrictedTo)) {
+ if (VERBOSE) {
+ System.out.println("Skipping method from "
+ + bridgeLoggerClass);
+ }
+ return;
+ }
+ }
+ System.out.println("Testing Logger." + nameProducer.apply("*")
+ + " on " + logger);
+ final BackendAdaptor adaptor = adaptor();
+ for (Level loggerLevel : LEVELS) {
+ adaptor.setLevel(logger, loggerLevel);
+ for (Level l : LEVELS) {
+ check(logger, () -> log.apply(bridgeLoggerClass.cast(logger), l),
+ check, () -> adaptor.isLoggable(logger, l),
+ () -> adaptor.shouldBeLoggable(l, loggerLevel),
+ l, loggerLevel, nameProducer.apply(l.toString()));
+ }
+ }
+ }
+
+ private void testSpiLog(java.lang.System.Logger logger,
+ SpiLogTester log, Checker<BackendRecord, java.lang.System.Logger.Level> check,
+ Function<String, String> nameProducer) {
+ System.out.println("Testing System.Logger." + nameProducer.apply("*")
+ + " on " + logger);
+ final BackendAdaptor adaptor = adaptor();
+ for (java.lang.System.Logger.Level loggerLevel : java.lang.System.Logger.Level.values()) {
+
+ adaptor.setLevel(logger, loggerLevel);
+ for (java.lang.System.Logger.Level l : java.lang.System.Logger.Level.values()) {
+ check(logger, () -> log.apply(logger, l),
+ check, () -> logger.isLoggable(l),
+ () -> adaptor.shouldBeLoggable(l, loggerLevel),
+ l, loggerLevel, nameProducer.apply(l.toString()));
+ }
+ }
+ }
+
+ private void test(String args, Levels level, java.lang.System.Logger logger,
+ Runnable test, Checker<BackendRecord, Level> check) {
+ if (restrictedTo != null) {
+ if (!level.definingClass.isAssignableFrom(restrictedTo)) {
+ if (VERBOSE) {
+ System.out.println("Skipping method from "
+ + level.definingClass);
+ }
+ return;
+ }
+ }
+ String method = args.contains("bundle") ? "logrb" : "log";
+ System.out.println("Testing Logger."
+ + method + "(Level." + level.platformLevel
+ + ", "+ args + ")" + " on " + logger);
+ final BackendAdaptor adaptor = adaptor();
+ for (Level loggerLevel : LEVELS) {
+ adaptor.setLevel(logger, loggerLevel);
+ check(logger, test, check,
+ () -> level.isEnabled(logger),
+ () -> adaptor.shouldBeLoggable(level, loggerLevel),
+ level.platformLevel, loggerLevel, level.method);
+ }
+ }
+
+ private <L> void check(java.lang.System.Logger logger,
+ Runnable test, Checker<BackendRecord,L> check,
+ BooleanSupplier checkLevelEnabled,
+ BooleanSupplier shouldBeLoggable,
+ L logLevel, L loggerLevel, String logMethod) {
+ final BackendAdaptor adaptor = adaptor();
+ adaptor.resetBackendRecords();
+ test.run();
+ final List<BackendRecord> records = adaptor.getBackendRecords();
+ if (shouldBeLoggable.getAsBoolean()) {
+ if (!checkLevelEnabled.getAsBoolean()) {
+ throw new RuntimeException("Logger is not enabled for "
+ + logMethod
+ + " although logger level is " + loggerLevel);
+ }
+ if (records.size() != 1) {
+ throw new RuntimeException(loggerLevel + " [" +
+ logLevel + "] : Unexpected record sizes: "
+ + records.toString());
+ }
+ BackendRecord res = records.get(0);
+ check.apply(res, logLevel);
+ } else {
+ if (checkLevelEnabled.getAsBoolean()) {
+ throw new RuntimeException("Logger is enabled for "
+ + logMethod
+ + " although logger level is " + loggerLevel);
+ }
+ if (!records.isEmpty()) {
+ throw new RuntimeException(loggerLevel + " [" +
+ logLevel + "] : Unexpected record sizes: "
+ + records.toString());
+ }
+ }
+ }
+ }
+
+ public static class JULBackendTester extends BackendTester<LogRecord>{
+
+ public JULBackendTester(boolean isSystem) {
+ this(isSystem,null,null);
+ }
+ public JULBackendTester(boolean isSystem, ResourceBundle localized) {
+ this(isSystem,null,localized);
+ }
+ public JULBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo) {
+ this(isSystem, restrictedTo, null);
+ }
+ public JULBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo,
+ ResourceBundle localized) {
+ super(isSystem, restrictedTo, localized);
+ }
+
+ Logger getBackendLogger(String name) {
+ if (isSystem) {
+ return LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
+ LogManager.getLogManager(), name, Thread.class);
+ } else {
+ return Logger.getLogger(name);
+ }
+ }
+
+ class JULBackendAdaptor extends BackendAdaptor {
+ @Override
+ public String getLoggerName(LogRecord res) {
+ return res.getLoggerName();
+ }
+ @Override
+ public Level getLevel(LogRecord res) {
+ return Level.valueOf(res.getLevel().getName());
+ }
+ @Override
+ public String getMessage(LogRecord res) {
+ return res.getMessage();
+ }
+ @Override
+ public String getSourceClassName(LogRecord res) {
+ return res.getSourceClassName();
+ }
+ @Override
+ public String getSourceMethodName(LogRecord res) {
+ return res.getSourceMethodName();
+ }
+ @Override
+ public Throwable getThrown(LogRecord res) {
+ return res.getThrown();
+ }
+ @Override
+ public ResourceBundle getResourceBundle(LogRecord res) {
+ return res.getResourceBundle();
+ }
+ @Override
+ public void setLevel(java.lang.System.Logger logger, Level level) {
+ Logger backend = getBackendLogger(logger.getName());
+ backend.setLevel(java.util.logging.Level.parse(level.name()));
+ }
+ @Override
+ public void setLevel(java.lang.System.Logger logger, java.lang.System.Logger.Level level) {
+ setLevel(logger, toJUL(level));
+ }
+ @Override
+ public List<LogRecord> getBackendRecords() {
+ return handler.records;
+ }
+ @Override
+ public void resetBackendRecords() {
+ handler.reset();
+ }
+ @Override
+ public Level getMappedLevel(Object level) {
+ if (level instanceof java.lang.System.Logger.Level) {
+ return toJUL((java.lang.System.Logger.Level)level);
+ }
+ return (Level)level;
+ }
+ }
+
+ final JULBackendAdaptor julAdaptor = new JULBackendAdaptor();
+
+ @Override
+ BackendAdaptor adaptor() {
+ return julAdaptor;
+ }
+
+ }
+
+ public abstract static class BackendTesterFactory {
+ public abstract BackendTester createBackendTester(boolean isSystem);
+ public abstract BackendTester createBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo);
+ public abstract BackendTester createBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo,
+ ResourceBundle bundle);
+ public abstract BackendTester createBackendTester(boolean isSystem,
+ ResourceBundle bundle);
+ }
+
+ public static class JULBackendTesterFactory extends BackendTesterFactory {
+
+ @Override
+ public BackendTester createBackendTester(boolean isSystem) {
+ return new JULBackendTester(isSystem);
+ }
+
+ @Override
+ public BackendTester createBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo) {
+ return new JULBackendTester(isSystem, restrictedTo);
+ }
+
+ @Override
+ public BackendTester createBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo,
+ ResourceBundle bundle) {
+ return new JULBackendTester(isSystem, restrictedTo, bundle);
+ }
+
+ @Override
+ public BackendTester createBackendTester(boolean isSystem,
+ ResourceBundle bundle) {
+ return new JULBackendTester(isSystem, bundle);
+ }
+ }
+
+ public static class CustomLoggerFinder extends LoggerFinder {
+
+ static enum CustomLevel { OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL };
+ static CustomLevel[] customLevelMap = { CustomLevel.ALL,
+ CustomLevel.TRACE, CustomLevel.DEBUG, CustomLevel.INFO,
+ CustomLevel.WARN, CustomLevel.ERROR, CustomLevel.OFF
+ };
+ static class CustomLogRecord {
+ public final CustomLevel logLevel;
+ public final java.lang.System.Logger logger;
+ public final String msg;
+ public final Object[] params;
+ public final Throwable thrown;
+ public final ResourceBundle bundle;
+
+ CustomLogRecord(java.lang.System.Logger producer,
+ CustomLevel level, String msg) {
+ this(producer, level, msg, (ResourceBundle)null, (Throwable)null, (Object[])null);
+ }
+
+ CustomLogRecord(java.lang.System.Logger producer,
+ CustomLevel level, String msg, ResourceBundle bundle,
+ Throwable thrown, Object... params) {
+ this.logger = producer;
+ this.logLevel = level;
+ this.msg = msg;
+ this.params = params;
+ this.thrown = thrown;
+ this.bundle = bundle;
+ }
+ }
+
+ static final List<CustomLogRecord> records =
+ Collections.synchronizedList(new ArrayList<>());
+
+ static class CustomLogger implements java.lang.System.Logger {
+
+ final String name;
+ volatile CustomLevel level;
+ CustomLogger(String name) {
+ this.name = name;
+ this.level = CustomLevel.INFO;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public void setLevel(CustomLevel level) {
+ this.level = level;
+ }
+
+
+ @Override
+ public boolean isLoggable(java.lang.System.Logger.Level level) {
+
+ return this.level != CustomLevel.OFF && this.level.ordinal()
+ >= customLevelMap[level.ordinal()].ordinal();
+ }
+
+ @Override
+ public void log(java.lang.System.Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
+ if (isLoggable(level)) {
+ records.add(new CustomLogRecord(this, customLevelMap[level.ordinal()],
+ key, bundle, thrown));
+ }
+ }
+
+ @Override
+ public void log(java.lang.System.Logger.Level level, ResourceBundle bundle, String format, Object... params) {
+ if (isLoggable(level)) {
+ records.add(new CustomLogRecord(this, customLevelMap[level.ordinal()],
+ format, bundle, null, params));
+ }
+ }
+
+ }
+
+ final Map<String, java.lang.System.Logger> applicationLoggers =
+ Collections.synchronizedMap(new HashMap<>());
+ final Map<String, java.lang.System.Logger> systemLoggers =
+ Collections.synchronizedMap(new HashMap<>());
+
+ @Override
+ public java.lang.System.Logger getLogger(String name, Class<?> caller) {
+ ClassLoader callerLoader = caller.getClassLoader();
+ if (callerLoader == null) {
+ systemLoggers.putIfAbsent(name, new CustomLogger(name));
+ return systemLoggers.get(name);
+ } else {
+ applicationLoggers.putIfAbsent(name, new CustomLogger(name));
+ return applicationLoggers.get(name);
+ }
+ }
+
+ CustomLevel fromJul(Level level) {
+ if (level.intValue() == Level.OFF.intValue()) {
+ return CustomLevel.OFF;
+ } else if (level.intValue() > Level.SEVERE.intValue()) {
+ return CustomLevel.ERROR;
+ } else if (level.intValue() > Level.WARNING.intValue()) {
+ return CustomLevel.ERROR;
+ } else if (level.intValue() > Level.INFO.intValue()) {
+ return CustomLevel.WARN;
+ } else if (level.intValue() > Level.CONFIG.intValue()) {
+ return CustomLevel.INFO;
+ } else if (level.intValue() > Level.FINER.intValue()) {
+ return CustomLevel.DEBUG;
+ } else if (level.intValue() > Level.FINEST.intValue()) {
+ return CustomLevel.TRACE;
+ } else if (level.intValue() == Level.ALL.intValue()) {
+ return CustomLevel.ALL;
+ } else {
+ return CustomLevel.TRACE;
+ }
+ }
+
+ Level toJul(CustomLevel level) {
+ switch(level) {
+ case OFF: return Level.OFF;
+ case FATAL: return Level.SEVERE;
+ case ERROR: return Level.SEVERE;
+ case WARN: return Level.WARNING;
+ case INFO: return Level.INFO;
+ case DEBUG: return Level.FINE;
+ case TRACE: return Level.FINER;
+ case ALL: return Level.ALL;
+ default: throw new InternalError("No such level: "+level);
+ }
+ }
+
+ }
+
+ public static class CustomBackendTester extends
+ BackendTester<CustomLoggerFinder.CustomLogRecord> {
+
+ public final CustomLoggerFinder provider;
+
+ public CustomBackendTester(boolean isSystem) {
+ this(isSystem, null, null);
+ }
+
+ public CustomBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo) {
+ this(isSystem, restrictedTo, null);
+ }
+
+ public CustomBackendTester(boolean isSystem,
+ ResourceBundle localized) {
+ this(isSystem, null, localized);
+ }
+
+ public CustomBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo,
+ ResourceBundle localized) {
+ super(isSystem, restrictedTo, localized);
+ provider = (CustomLoggerFinder)java.lang.System.LoggerFinder.getLoggerFinder();
+ }
+
+ @Override
+ public java.lang.System.Logger convert(java.lang.System.Logger logger) {
+ if (restrictedTo != null && restrictedTo.isInstance(logger)) {
+ return logger;
+ } else if (restrictedTo == jdkLoggerClass) {
+ return logger;
+ } else {
+ return java.lang.System.Logger.class.cast(
+ sun.util.logging.PlatformLogger.Bridge.convert(logger));
+ }
+ }
+
+ class CustomBackendAdaptor extends BackendAdaptor {
+
+ @Override
+ public String getLoggerName(CustomLoggerFinder.CustomLogRecord res) {
+ return res.logger.getName();
+ }
+
+ @Override
+ public CustomLoggerFinder.CustomLevel getLevel(CustomLoggerFinder.CustomLogRecord res) {
+ return res.logLevel;
+ }
+
+ @Override
+ public String getMessage(CustomLoggerFinder.CustomLogRecord res) {
+ return res.msg;
+ }
+
+ @Override // we don't support source class name in our custom provider implementation
+ public String getSourceClassName(CustomLoggerFinder.CustomLogRecord res) {
+ return null;
+ }
+
+ @Override // we don't support source method name in our custom provider implementation
+ public String getSourceMethodName(CustomLoggerFinder.CustomLogRecord res) {
+ return null;
+ }
+
+ @Override
+ public Throwable getThrown(CustomLoggerFinder.CustomLogRecord res) {
+ return res.thrown;
+ }
+
+ @Override
+ public ResourceBundle getResourceBundle(CustomLoggerFinder.CustomLogRecord res) {
+ return res.bundle;
+ }
+
+ @Override
+ public void setLevel(java.lang.System.Logger logger, Level level) {
+ final CustomLoggerFinder.CustomLogger l =
+ (CustomLoggerFinder.CustomLogger)
+ (isSystem ? provider.getLogger(logger.getName(), Thread.class) :
+ provider.getLogger(logger.getName(), LoggerFinderBackendTest.class));
+ l.setLevel(provider.fromJul(level));
+ }
+ @Override
+ public void setLevel(java.lang.System.Logger logger,
+ java.lang.System.Logger.Level level) {
+ setLevel(logger, toJUL(level));
+ }
+
+ CustomLoggerFinder.CustomLevel getLevel(java.lang.System.Logger logger) {
+ final CustomLoggerFinder.CustomLogger l =
+ (CustomLoggerFinder.CustomLogger)
+ (isSystem ? provider.getLogger(logger.getName(), Thread.class) :
+ provider.getLogger(logger.getName(), LoggerFinderBackendTest.class));
+ return l.level;
+ }
+
+ @Override
+ public List<CustomLoggerFinder.CustomLogRecord> getBackendRecords() {
+ return CustomLoggerFinder.records;
+ }
+
+ @Override
+ public void resetBackendRecords() {
+ CustomLoggerFinder.records.clear();
+ }
+
+ @Override
+ public boolean shouldBeLoggable(Levels level, Level loggerLevel) {
+ return loggerLevel != Level.OFF &&
+ fromLevels(level).ordinal() <= provider.fromJul(loggerLevel).ordinal();
+ }
+
+ @Override
+ public boolean isLoggable(java.lang.System.Logger logger, Level l) {
+ return super.isLoggable(logger, l);
+ }
+
+ @Override
+ public boolean shouldBeLoggable(Level logLevel, Level loggerLevel) {
+ return loggerLevel != Level.OFF &&
+ provider.fromJul(logLevel).ordinal() <= provider.fromJul(loggerLevel).ordinal();
+ }
+
+ @Override // we don't support source class name in our custom provider implementation
+ public String getCallerClassName(Levels level, String clazz) {
+ return null;
+ }
+
+ @Override // we don't support source method name in our custom provider implementation
+ public String getCallerMethodName(Levels level, String method) {
+ return null;
+ }
+
+ @Override // we don't support source class name in our custom provider implementation
+ public String getCallerClassName(MethodInvoker<?,?> logMethod, String clazz) {
+ return null;
+ }
+
+ @Override // we don't support source method name in our custom provider implementation
+ public String getCallerMethodName(MethodInvoker<?,?> logMethod, String method) {
+ return null;
+ }
+
+ @Override
+ public CustomLoggerFinder.CustomLevel getMappedLevel(Object level) {
+ if (level instanceof java.lang.System.Logger.Level) {
+ final int index = ((java.lang.System.Logger.Level)level).ordinal();
+ return CustomLoggerFinder.customLevelMap[index];
+ } else if (level instanceof Level) {
+ return provider.fromJul((Level)level);
+ }
+ return (CustomLoggerFinder.CustomLevel) level;
+ }
+
+ CustomLoggerFinder.CustomLevel fromLevels(Levels level) {
+ switch(level) {
+ case SEVERE:
+ return CustomLoggerFinder.CustomLevel.ERROR;
+ case WARNING:
+ return CustomLoggerFinder.CustomLevel.WARN;
+ case INFO:
+ return CustomLoggerFinder.CustomLevel.INFO;
+ case CONFIG: case FINE:
+ return CustomLoggerFinder.CustomLevel.DEBUG;
+ case FINER: case FINEST:
+ return CustomLoggerFinder.CustomLevel.TRACE;
+ }
+ throw new InternalError("No such level "+level);
+ }
+
+ }
+
+ @Override
+ BackendAdaptor adaptor() {
+ return new CustomBackendAdaptor();
+ }
+
+ }
+
+ public static class CustomBackendTesterFactory extends BackendTesterFactory {
+
+ @Override
+ public BackendTester createBackendTester(boolean isSystem) {
+ return new CustomBackendTester(isSystem);
+ }
+
+ @Override
+ public BackendTester createBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo) {
+ return new CustomBackendTester(isSystem, restrictedTo);
+ }
+
+ @Override
+ public BackendTester createBackendTester(boolean isSystem,
+ Class<? extends java.lang.System.Logger> restrictedTo,
+ ResourceBundle bundle) {
+ return new CustomBackendTester(isSystem, restrictedTo, bundle);
+ }
+
+ @Override
+ public BackendTester createBackendTester(boolean isSystem,
+ ResourceBundle bundle) {
+ return new CustomBackendTester(isSystem, bundle);
+ }
+ }
+
+ static final Method getLazyLogger;
+ static final Method accessLoggerFinder;
+ static {
+ // jdk.internal.logger.LazyLoggers.getLazyLogger(name, caller);
+ try {
+ Class<?> lazyLoggers = jdk.internal.logger.LazyLoggers.class;
+ getLazyLogger = lazyLoggers.getMethod("getLazyLogger",
+ String.class, Class.class);
+ getLazyLogger.setAccessible(true);
+ Class<?> loggerFinderLoader =
+ Class.forName("java.lang.System$LoggerFinder");
+ accessLoggerFinder = loggerFinderLoader.getDeclaredMethod("accessProvider");
+ accessLoggerFinder.setAccessible(true);
+ } catch (Throwable ex) {
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ static java.lang.System.Logger getSystemLogger(String name, Class<?> caller) throws Exception {
+ try {
+ return java.lang.System.Logger.class.cast(getLazyLogger.invoke(null, name, caller));
+ } catch (InvocationTargetException x) {
+ Throwable t = x.getTargetException();
+ if (t instanceof Exception) {
+ throw (Exception)t;
+ } else {
+ throw (Error)t;
+ }
+ }
+ }
+ static java.lang.System.Logger getSystemLogger(String name,
+ ResourceBundle bundle, Class<?> caller) throws Exception {
+ try {
+ LoggerFinder provider = LoggerFinder.class.cast(accessLoggerFinder.invoke(null));
+ return provider.getLocalizedLogger(name, bundle, caller);
+ } catch (InvocationTargetException x) {
+ Throwable t = x.getTargetException();
+ if (t instanceof Exception) {
+ throw (Exception)t;
+ } else {
+ throw (Error)t;
+ }
+ }
+ }
+
+ // Change this to 'true' to get more traces...
+ public static boolean verbose = false;
+
+ public static void main(String[] argv) throws Exception {
+
+ final AtomicInteger nb = new AtomicInteger(0);
+ final boolean hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
+ System.out.println(ClassLoader.getSystemClassLoader());
+ final BackendTesterFactory factory;
+ if (java.lang.System.LoggerFinder.getLoggerFinder() instanceof CustomLoggerFinder) {
+ if (hidesProvider) {
+ System.err.println("Custom backend "
+ + java.lang.System.LoggerFinder.getLoggerFinder()
+ + " should have been hidden!");
+ throw new RuntimeException(
+ "Custom backend should have been hidden: "
+ + "check value of java.system.class.loader property");
+ }
+ System.out.println("Using custom backend");
+ factory = new CustomBackendTesterFactory();
+ } else {
+ if (!hidesProvider) {
+ System.err.println("Default JUL backend "
+ + java.lang.System.LoggerFinder.getLoggerFinder()
+ + " should have been hidden!");
+ throw new RuntimeException(
+ "Default JUL backend should have been hidden: "
+ + "check value of java.system.class.loader property");
+ }
+ System.out.println("Using JUL backend");
+ factory = new JULBackendTesterFactory();
+ }
+
+ testBackend(nb, factory);
+ }
+
+ public static void testBackend(AtomicInteger nb, BackendTesterFactory factory) throws Exception {
+
+ // Tests all level specifics methods with loggers configured with
+ // all possible levels and loggers obtained with all possible
+ // entry points from LoggerFactory and JdkLoggerFactory, with
+ // JUL as backend.
+
+ // Test a simple application logger with JUL backend
+ final BackendTester tester = factory.createBackendTester(false);
+ final java.lang.System.Logger logger =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLogger("foo", LoggerFinderBackendTest.class);
+
+ testLogger(tester, logger, nb);
+
+ // Test a simple system logger with JUL backend
+ final java.lang.System.Logger system =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLogger("bar", Thread.class);
+ final BackendTester systemTester = factory.createBackendTester(true);
+ testLogger(systemTester, system, nb);
+
+ // Test a localized application logger with null resource bundle and
+ // JUL backend
+ final java.lang.System.Logger noBundleLogger =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLocalizedLogger("baz", null, LoggerFinderBackendTest.class);
+ final BackendTester noBundleTester =
+ factory.createBackendTester(false, spiLoggerClass);
+ testLogger(noBundleTester, noBundleLogger, nb);
+
+ // Test a localized system logger with null resource bundle and JUL
+ // backend
+ final java.lang.System.Logger noBundleSysLogger =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLocalizedLogger("oof", null, Thread.class);
+ final BackendTester noBundleSysTester =
+ factory.createBackendTester(true, spiLoggerClass);
+ testLogger(noBundleSysTester, noBundleSysLogger, nb);
+
+ // Test a localized application logger with null resource bundle and
+ // JUL backend
+ try {
+ System.getLogger("baz", null);
+ throw new RuntimeException("Expected NullPointerException not thrown");
+ } catch (NullPointerException x) {
+ System.out.println("System.Loggers.getLogger(\"baz\", null): got expected " + x);
+ }
+ final java.lang.System.Logger noBundleExtensionLogger =
+ getSystemLogger("baz", null, LoggerFinderBackendTest.class);
+ final BackendTester noBundleExtensionTester =
+ factory.createBackendTester(false, jdkLoggerClass);
+ testLogger(noBundleExtensionTester, noBundleExtensionLogger, nb);
+
+ // Test a simple system logger with JUL backend
+ final java.lang.System.Logger sysExtensionLogger =
+ getSystemLogger("oof", Thread.class);
+ final BackendTester sysExtensionTester =
+ factory.createBackendTester(true, jdkLoggerClass);
+ testLogger(sysExtensionTester, sysExtensionLogger, nb);
+
+ // Test a localized system logger with null resource bundle and JUL
+ // backend
+ final java.lang.System.Logger noBundleSysExtensionLogger =
+ getSystemLogger("oof", null, Thread.class);
+ final BackendTester noBundleSysExtensionTester =
+ factory.createBackendTester(true, jdkLoggerClass);
+ testLogger(noBundleSysExtensionTester, noBundleSysExtensionLogger, nb);
+
+ // Test a localized application logger converted to JDK with null
+ // resource bundle and JUL backend
+ final java.lang.System.Logger noBundleConvertedLogger =
+ (java.lang.System.Logger)
+ sun.util.logging.PlatformLogger.Bridge.convert(noBundleLogger);
+ final BackendTester noBundleJdkTester = factory.createBackendTester(false);
+ testLogger(noBundleJdkTester, noBundleConvertedLogger, nb);
+
+ // Test a localized system logger converted to JDK with null resource
+ // bundle and JUL backend
+ final java.lang.System.Logger noBundleConvertedSysLogger =
+ (java.lang.System.Logger)
+ sun.util.logging.PlatformLogger.Bridge.convert(noBundleSysLogger);
+ final BackendTester noBundleJdkSysTester = factory.createBackendTester(true);
+ testLogger(noBundleJdkSysTester, noBundleConvertedSysLogger, nb);
+
+ // Test a localized application logger with resource bundle and JUL
+ // backend
+ final ResourceBundle bundle =
+ ResourceBundle.getBundle(ResourceBundeLocalized.class.getName());
+ final java.lang.System.Logger bundleLogger =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLocalizedLogger("toto", bundle, LoggerFinderBackendTest.class);
+ final BackendTester bundleTester =
+ factory.createBackendTester(false, spiLoggerClass, bundle);
+ testLogger(bundleTester, bundleLogger, nb);
+
+ // Test a localized system logger with resource bundle and JUL backend
+ final java.lang.System.Logger bundleSysLogger =
+ java.lang.System.LoggerFinder.getLoggerFinder()
+ .getLocalizedLogger("titi", bundle, Thread.class);
+ final BackendTester bundleSysTester =
+ factory.createBackendTester(true, spiLoggerClass, bundle);
+ testLogger(bundleSysTester, bundleSysLogger, nb);
+
+ // Test a localized Jdk application logger with resource bundle and JUL
+ // backend
+ final java.lang.System.Logger bundleExtensionLogger =
+ System.getLogger("tita", bundle);
+ final BackendTester bundleExtensionTester =
+ factory.createBackendTester(false, jdkLoggerClass, bundle);
+ testLogger(bundleExtensionTester, bundleExtensionLogger, nb);
+
+ // Test a localized Jdk system logger with resource bundle and JUL
+ // backend
+ final java.lang.System.Logger bundleExtensionSysLogger =
+ getSystemLogger("titu", bundle, Thread.class);
+ final BackendTester bundleExtensionSysTester =
+ factory.createBackendTester(true, jdkLoggerClass, bundle);
+ testLogger(bundleExtensionSysTester, bundleExtensionSysLogger, nb);
+
+ // Test a localized application logger converted to JDK with resource
+ // bundle and JUL backend
+ final BackendTester bundleJdkTester =
+ factory.createBackendTester(false, bundle);
+ final java.lang.System.Logger bundleConvertedLogger =
+ (java.lang.System.Logger)
+ sun.util.logging.PlatformLogger.Bridge.convert(bundleLogger);
+ testLogger(bundleJdkTester, bundleConvertedLogger, nb);
+
+ // Test a localized Jdk system logger converted to JDK with resource
+ // bundle and JUL backend
+ final BackendTester bundleJdkSysTester =
+ factory.createBackendTester(true, bundle);
+ final java.lang.System.Logger bundleConvertedSysLogger =
+ (java.lang.System.Logger)
+ sun.util.logging.PlatformLogger.Bridge.convert(bundleSysLogger);
+ testLogger(bundleJdkSysTester, bundleConvertedSysLogger, nb);
+
+ // Now need to add tests for all the log/logp/logrb methods...
+
+ }
+
+ private static class FooObj {
+ final String s;
+ FooObj(String s) {
+ this.s = s;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() +": "+s;
+ }
+
+ }
+
+ public static void testLogger(BackendTester tester,
+ java.lang.System.Logger spiLogger, AtomicInteger nb) {
+
+ // Test all level-specific method forms:
+ // fatal(...) error(...) severe(...) etc...
+ java.lang.System.Logger jdkLogger = tester.convert(spiLogger);
+ for (Levels l : Levels.values()) {
+ java.lang.System.Logger logger =
+ l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
+ tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet());
+ tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(),
+ bundleParam);
+ final int nbb = nb.incrementAndGet();
+ tester.testLevel(l, logger, () -> l.method + "[" + logger.getName()
+ + "]-" + nbb);
+ }
+ for (Levels l : Levels.values()) {
+ java.lang.System.Logger logger =
+ l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
+ tester.testLevel(l, logger,
+ l.method + "[" + logger.getName()+ "]({0},{1})-"
+ + nb.incrementAndGet(),
+ "One", "Two");
+ tester.testLevel(l, logger,
+ l.method + "[" + logger.getName()+ "]({0},{1})-"
+ + nb.incrementAndGet(),
+ bundleParam, "One", "Two");
+ }
+ final Throwable thrown = new RuntimeException("Test");
+ for (Levels l : Levels.values()) {
+ java.lang.System.Logger logger =
+ l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
+ tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(),
+ thrown);
+ tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(),
+ bundleParam, thrown);
+ final int nbb = nb.incrementAndGet();
+ tester.testLevel(l, logger, ()->l.method + "[" + logger.getName()+ "]-"
+ + nbb, thrown);
+ }
+
+ java.lang.System.Logger logger = jdkLogger;
+
+ // test System.Logger methods
+ tester.testSpiLog(logger, "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet());
+ tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet());
+ tester.testSpiLog(logger, "[" + logger.getName()+ "]-({0},{1})"
+ + nb.incrementAndGet(), "One", "Two");
+ tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-({0},{1})"
+ + nb.incrementAndGet(), "One", "Two");
+ tester.testSpiLog(logger, "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(), thrown);
+ tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(), thrown);
+ final int nbb01 = nb.incrementAndGet();
+ tester.testSpiLog(logger, () -> "[" + logger.getName()+ "]-" + nbb01);
+ final int nbb02 = nb.incrementAndGet();
+ tester.testSpiLog(logger, thrown, () -> "[" + logger.getName()+ "]-" + nbb02);
+ final int nbb03 = nb.incrementAndGet();
+ tester.testSpiLog(logger, new FooObj("[" + logger.getName()+ "]-" + nbb03));
+
+ // Test all log method forms:
+ // jdk.internal.logging.Logger.log(...)
+ tester.testLog(logger, "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet());
+ tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet());
+ tester.testLog(logger, "[" + logger.getName()+ "]-({0},{1})"
+ + nb.incrementAndGet(), "One", "Two");
+ tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-({0},{1})"
+ + nb.incrementAndGet(), "One", "Two");
+ tester.testLog(logger, "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(), thrown);
+ tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(), thrown);
+ final int nbb1 = nb.incrementAndGet();
+ tester.testLog(logger, () -> "[" + logger.getName()+ "]-" + nbb1);
+ final int nbb2 = nb.incrementAndGet();
+ tester.testLog(logger, thrown, () -> "[" + logger.getName()+ "]-" + nbb2);
+
+ // Test all logp method forms
+ // jdk.internal.logging.Logger.logp(...)
+ tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+ "method" + nb.incrementAndGet(),
+ "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet());
+ tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
+ "method" + nb.incrementAndGet(), bundleParam,
+ "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet());
+ tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+ "method" + nb.incrementAndGet(),
+ "[" + logger.getName()+ "]-({0},{1})"
+ + nb.incrementAndGet(), "One", "Two");
+ tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
+ "method" + nb.incrementAndGet(), bundleParam,
+ "[" + logger.getName()+ "]-({0},{1})"
+ + nb.incrementAndGet(), "One", "Two");
+ tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+ "method" + nb.incrementAndGet(),
+ "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(), thrown);
+ tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
+ "method" + nb.incrementAndGet(), bundleParam,
+ "[" + logger.getName()+ "]-"
+ + nb.incrementAndGet(), thrown);
+ final int nbb3 = nb.incrementAndGet();
+ tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+ "method" + nb.incrementAndGet(),
+ () -> "[" + logger.getName()+ "]-" + nbb3);
+ final int nbb4 = nb.incrementAndGet();
+ tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+ "method" + nb.incrementAndGet(),
+ thrown, () -> "[" + logger.getName()+ "]-" + nbb4);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/META-INF/services/java.lang.System$LoggerFinder Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,2 @@
+LoggerFinderBackendTest$CustomLoggerFinder
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/SystemClassLoader.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.lang.System.LoggerFinder;
+
+/**
+ * A custom class loader which can hide the registered LoggerProvider
+ * depending on the value of a test.logger.hidesProvider system property.
+ * @author danielfuchs
+ */
+public class SystemClassLoader extends ClassLoader {
+
+ final public boolean hidesProvider;
+
+ public SystemClassLoader() {
+ hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
+ }
+ public SystemClassLoader(ClassLoader parent) {
+ super(parent);
+ hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
+ }
+
+ boolean accept(String name) {
+ final boolean res = !name.endsWith(LoggerFinder.class.getName());
+ if (res == false) {
+ System.out.println("Hiding " + name);
+ }
+ return res;
+ }
+
+ @Override
+ public URL getResource(String name) {
+ if (hidesProvider && !accept(name)) {
+ return null;
+ } else {
+ return super.getResource(name);
+ }
+ }
+
+ class Enumerator implements Enumeration<URL> {
+ final Enumeration<URL> enumeration;
+ volatile URL next;
+ Enumerator(Enumeration<URL> enumeration) {
+ this.enumeration = enumeration;
+ }
+
+ @Override
+ public boolean hasMoreElements() {
+ if (next != null) return true;
+ if (!enumeration.hasMoreElements()) return false;
+ if (hidesProvider == false) return true;
+ next = enumeration.nextElement();
+ if (accept(next.getPath())) return true;
+ next = null;
+ return hasMoreElements();
+ }
+
+ @Override
+ public URL nextElement() {
+ final URL res = next == null ? enumeration.nextElement() : next;
+ next = null;
+ if (hidesProvider == false || accept(res.getPath())) return res;
+ return nextElement();
+ }
+ }
+
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ final Enumeration<URL> enumeration = super.getResources(name);
+ return hidesProvider ? new Enumerator(enumeration) : enumeration;
+ }
+
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultLoggerBridgeTest/DefaultLoggerBridgeTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,850 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+import sun.util.logging.PlatformLogger;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.util.stream.Stream;
+import sun.util.logging.internal.LoggingProviderImpl;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ * Tests all internal bridge methods with the default LoggerFinder
+ * JUL backend.
+ * @modules java.base/sun.util.logging
+ * java.base/jdk.internal.logger
+ * java.logging/sun.util.logging.internal
+ * @run main/othervm DefaultLoggerBridgeTest
+ * @author danielfuchs
+ */
+public class DefaultLoggerBridgeTest {
+
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent implements Cloneable {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ java.util.logging.Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ String msg;
+ String className;
+ String methodName;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ isLoggable,
+ loggerName,
+ level,
+ bundle,
+ thrown,
+ args,
+ msg,
+ className,
+ methodName,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+
+ public LogEvent cloneWith(long sequenceNumber)
+ throws CloneNotSupportedException {
+ LogEvent cloned = (LogEvent)super.clone();
+ cloned.sequenceNumber = sequenceNumber;
+ return cloned;
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ java.util.logging.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ return LogEvent.of(sequenceNumber, isLoggable, name,
+ DefaultLoggerBridgeTest.class.getName(),
+ "testLogger", level, bundle, key,
+ thrown, params);
+ }
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ String className, String methodName,
+ java.util.logging.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ evt.className = className;
+ evt.methodName = methodName;
+ return evt;
+ }
+
+ }
+
+ static final java.util.logging.Level[] julLevels = {
+ java.util.logging.Level.ALL,
+ java.util.logging.Level.FINEST,
+ java.util.logging.Level.FINER,
+ java.util.logging.Level.FINE,
+ java.util.logging.Level.CONFIG,
+ java.util.logging.Level.INFO,
+ java.util.logging.Level.WARNING,
+ java.util.logging.Level.SEVERE,
+ java.util.logging.Level.OFF,
+ };
+
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+
+ public static class MyHandler extends Handler {
+
+ @Override
+ public java.util.logging.Level getLevel() {
+ return java.util.logging.Level.ALL;
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+ true, record.getLoggerName(),
+ record.getSourceClassName(),
+ record.getSourceMethodName(),
+ record.getLevel(),
+ record.getResourceBundle(), record.getMessage(),
+ record.getThrown(), record.getParameters()));
+ }
+ @Override
+ public void flush() {
+ }
+ @Override
+ public void close() throws SecurityException {
+ }
+
+ }
+
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ static PlatformLogger.Bridge convert(Logger logger) {
+ boolean old = allowAccess.get().get();
+ allowAccess.get().set(true);
+ try {
+ return PlatformLogger.Bridge.convert(logger);
+ } finally {
+ allowAccess.get().set(old);
+ }
+ }
+
+ static Logger getLogger(String name, Class<?> caller) {
+ boolean old = allowAccess.get().get();
+ allowAccess.get().set(true);
+ try {
+ return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
+ } finally {
+ allowAccess.get().set(old);
+ }
+ }
+
+ static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+ static void setSecurityManager() {
+ if (System.getSecurityManager() == null) {
+ Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length == 0)
+ args = new String[] {
+ "NOSECURITY",
+ "NOPERMISSIONS",
+ "WITHPERMISSIONS"
+ };
+
+ Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+ LoggerFinder provider;
+ switch (testCase) {
+ case NOSECURITY:
+ System.out.println("\n*** Without Security Manager\n");
+ test(true);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case NOPERMISSIONS:
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ setSecurityManager();
+ test(false);
+ System.out.println("Tetscase count: " + sequencer.get());
+ break;
+ case WITHPERMISSIONS:
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ setSecurityManager();
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ test(true);
+ } finally {
+ allowControl.get().set(control);
+ }
+ break;
+ default:
+ throw new RuntimeException("Unknown test case: " + testCase);
+ }
+ });
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(boolean hasRequiredPermissions) {
+
+ ResourceBundle loggerBundle =
+ ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<Object, String> loggerDescMap = new HashMap<>();
+
+ Logger sysLogger1a = getLogger("foo", Thread.class);
+ loggerDescMap.put(sysLogger1a, "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class)");
+
+ Logger appLogger1 = System.getLogger("foo");
+ loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
+
+ LoggerFinder provider;
+ try {
+ provider = LoggerFinder.getLoggerFinder();
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Expected exception not raised");
+ }
+ } catch (AccessControlException x) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected permission check", x);
+ }
+ if (!SimplePolicy.LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+ throw new RuntimeException("Unexpected permission in exception: " + x, x);
+ }
+ final boolean control = allowControl.get().get();
+ try {
+ allowControl.get().set(true);
+ provider = LoggerFinder.getLoggerFinder();
+ } finally {
+ allowControl.get().set(control);
+ }
+ }
+
+ Logger sysLogger1b = null;
+ try {
+ sysLogger1b = provider.getLogger("foo", Thread.class);
+ if (sysLogger1b != sysLogger1a) {
+ loggerDescMap.put(sysLogger1b, "provider.getLogger(\"foo\", Thread.class)");
+ }
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for system logger: " + acx);
+ }
+
+ Logger appLogger2 = System.getLogger("foo", loggerBundle);
+ loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
+
+ if (appLogger2 == appLogger1) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ Logger sysLogger2 = null;
+ try {
+ sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+ loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)");
+ if (!hasRequiredPermissions) {
+ throw new RuntimeException("Managed to obtain a system logger without permission");
+ }
+ } catch (AccessControlException acx) {
+ if (hasRequiredPermissions) {
+ throw new RuntimeException("Unexpected security exception: ", acx);
+ }
+ if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+ throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+ }
+ System.out.println("Got expected exception for localized system logger: " + acx);
+ }
+ if (hasRequiredPermissions && appLogger2 == sysLogger2) {
+ throw new RuntimeException("identical loggers");
+ }
+ if (hasRequiredPermissions && sysLogger2 == sysLogger1a) {
+ throw new RuntimeException("identical loggers");
+ }
+
+ final java.util.logging.Logger appSink;
+ final java.util.logging.Logger sysSink;
+ final MyHandler appHandler;
+ final MyHandler sysHandler;
+ final boolean old = allowAll.get().get();
+ allowAll.get().set(true);
+ try {
+ sysSink = LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
+ LogManager.getLogManager(), "foo", Thread.class);
+ appSink = LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
+ LogManager.getLogManager(), "foo", DefaultLoggerBridgeTest.class);
+ if (appSink == sysSink) {
+ throw new RuntimeException("identical backend loggers");
+ }
+ appSink.addHandler(appHandler = new MyHandler());
+ sysSink.addHandler(sysHandler = new MyHandler());
+ appSink.setUseParentHandlers(VERBOSE);
+ sysSink.setUseParentHandlers(VERBOSE);
+ } finally {
+ allowAll.get().set(old);
+ }
+
+ try {
+ testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1a), sysSink);
+ testLogger(provider, loggerDescMap, "foo", null, convert(appLogger1), appSink);
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(appLogger2), appSink);
+ if (sysLogger1b != null && sysLogger1b != sysLogger1a) {
+ testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1b), sysSink);
+ }
+ if (sysLogger2 != null) {
+ testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(sysLogger2), sysSink);
+ }
+ } finally {
+ allowAll.get().set(true);
+ try {
+ appSink.removeHandler(appHandler);
+ sysSink.removeHandler(sysHandler);
+ } finally {
+ allowAll.get().set(old);
+ }
+ }
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ static void checkLogEvent(LoggerFinder provider, String desc,
+ LogEvent expected) {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void checkLogEvent(LoggerFinder provider, String desc,
+ LogEvent expected, boolean expectNotNull) {
+ LogEvent actual = eventQueue.poll();
+ if (actual == null && !expectNotNull) return;
+ if (actual != null && !expectNotNull) {
+ throw new RuntimeException("Unexpected log event found for " + desc
+ + "\n\tgot: " + actual);
+ }
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
+ boolean before = allowAll.get().get();
+ try {
+ allowAll.get().set(true);
+ sink.setLevel(loggerLevel);
+ } finally {
+ allowAll.get().set(before);
+ }
+ }
+
+ static sun.util.logging.PlatformLogger.Level toPlatformLevel(java.util.logging.Level level) {
+ boolean old = allowAccess.get().get();
+ allowAccess.get().set(true);
+ try {
+ return sun.util.logging.PlatformLogger.Level.valueOf(level.getName());
+ } finally {
+ allowAccess.get().set(old);
+ }
+ }
+
+ // Calls the methods defined on LogProducer and verify the
+ // parameters received by the underlying logger.
+ private static void testLogger(LoggerFinder provider,
+ Map<Object, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ PlatformLogger.Bridge logger,
+ java.util.logging.Logger sink) {
+
+ if (loggerDescMap.get(logger) == null) {
+ throw new RuntimeException("Missing description for " + logger);
+ }
+ System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]");
+ final java.util.logging.Level OFF = java.util.logging.Level.OFF;
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ System.out.println("\tlogger.log(messageLevel, fooMsg)");
+ System.out.println("\tlogger.<level>(fooMsg)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ fooMsg, (Throwable)null, (Object[])null);
+ logger.log(toPlatformLevel(messageLevel), fooMsg);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ Supplier<String> supplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return this.toString();
+ }
+ };
+ System.out.println("\tlogger.log(messageLevel, supplier)");
+ System.out.println("\tlogger.<level>(supplier)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, null,
+ supplier.get(), (Throwable)null, (Object[])null);
+ logger.log(toPlatformLevel(messageLevel), supplier);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = fooMsg;
+ System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ format, (Throwable)null, arg1, arg2);
+ logger.log(toPlatformLevel(messageLevel), format, arg1, arg2);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ fooMsg, thrown, (Object[])null);
+ logger.log(toPlatformLevel(messageLevel), fooMsg, thrown);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, null,
+ supplier.get(), thrown, (Object[])null);
+ logger.log(toPlatformLevel(messageLevel), thrown, supplier);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ String sourceClass = "blah.Blah";
+ String sourceMethod = "blih";
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+ fooMsg, (Throwable)null, (Object[])null);
+ logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, fooMsg);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, null,
+ supplier.get(), (Throwable)null, (Object[])null);
+ logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, supplier);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+ format, (Throwable)null, arg1, arg2);
+ logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+ fooMsg, thrown, (Object[])null);
+ logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, null,
+ supplier.get(), thrown, (Object[])null);
+ logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, thrown, supplier);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+ System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, bundle,
+ format, (Throwable)null, arg1, arg2);
+ logger.logrb(toPlatformLevel(messageLevel), bundle, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, bundle,
+ fooMsg, thrown, (Object[])null);
+ logger.logrb(toPlatformLevel(messageLevel), bundle, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, bundle,
+ format, (Throwable)null, arg1, arg2);
+ logger.logrb(toPlatformLevel(messageLevel), sourceClass, sourceMethod, bundle, format, arg1, arg2);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+
+ System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julLevels) {
+ String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, sourceClass, sourceMethod, messageLevel, bundle,
+ fooMsg, thrown, (Object[])null);
+ logger.logrb(toPlatformLevel(messageLevel), sourceClass, sourceMethod, bundle, fooMsg, thrown);
+ checkLogEvent(provider, desc, expected, expected.isLoggable);
+ }
+ }
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ public static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+ final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+ final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+ final Permissions permissions;
+ final Permissions allPermissions;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ final ThreadLocal<AtomicBoolean> allowAccess;
+ final ThreadLocal<AtomicBoolean> allowAll;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+ ThreadLocal<AtomicBoolean> allowAccess,
+ ThreadLocal<AtomicBoolean> allowAll) {
+ this.allowControl = allowControl;
+ this.allowAccess = allowAccess;
+ this.allowAll = allowAll;
+ permissions = new Permissions();
+ allPermissions = new PermissionsBuilder()
+ .add(new java.security.AllPermission())
+ .toPermissions();
+ }
+
+ Permissions getPermissions() {
+ if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+ PermissionsBuilder builder = new PermissionsBuilder()
+ .addAll(permissions);
+ if (allowControl.get().get()) {
+ builder.add(LOGGERFINDER_PERMISSION);
+ }
+ if (allowAccess.get().get()) {
+ builder.add(ACCESS_LOGGER);
+ builder.add(ACCESS_LOGGING);
+ }
+ if (allowAll.get().get()) {
+ builder.addAll(allPermissions);
+ }
+ return builder.toPermissions();
+ }
+ return permissions;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return getPermissions().implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultPlatformLoggerTest/DefaultPlatformLoggerTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import sun.util.logging.PlatformLogger;
+import sun.util.logging.internal.LoggingProviderImpl;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests all PlatformLogger methods with the default LoggerFinder JUL backend.
+ * @modules java.base/sun.util.logging java.logging/sun.util.logging.internal
+ * @run main/othervm DefaultPlatformLoggerTest
+ * @author danielfuchs
+ */
+public class DefaultPlatformLoggerTest {
+
+ final static AtomicLong sequencer = new AtomicLong();
+ final static boolean VERBOSE = false;
+ static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+ static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+ @Override
+ protected AtomicBoolean initialValue() {
+ return new AtomicBoolean(false);
+ }
+ };
+
+ public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+ public static final class LogEvent implements Cloneable {
+
+ public LogEvent() {
+ this(sequencer.getAndIncrement());
+ }
+
+ LogEvent(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ long sequenceNumber;
+ boolean isLoggable;
+ String loggerName;
+ java.util.logging.Level level;
+ ResourceBundle bundle;
+ Throwable thrown;
+ Object[] args;
+ String msg;
+ String className;
+ String methodName;
+
+ Object[] toArray() {
+ return new Object[] {
+ sequenceNumber,
+ isLoggable,
+ loggerName,
+ level,
+ bundle,
+ thrown,
+ args,
+ msg,
+ className,
+ methodName,
+ };
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.deepToString(toArray());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof LogEvent
+ && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toArray());
+ }
+
+ public LogEvent cloneWith(long sequenceNumber)
+ throws CloneNotSupportedException {
+ LogEvent cloned = (LogEvent)super.clone();
+ cloned.sequenceNumber = sequenceNumber;
+ return cloned;
+ }
+
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ java.util.logging.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ return LogEvent.of(sequenceNumber, isLoggable, name,
+ DefaultPlatformLoggerTest.class.getName(),
+ "testLogger", level, bundle, key,
+ thrown, params);
+ }
+ public static LogEvent of(long sequenceNumber,
+ boolean isLoggable, String name,
+ String className, String methodName,
+ java.util.logging.Level level, ResourceBundle bundle,
+ String key, Throwable thrown, Object... params) {
+ LogEvent evt = new LogEvent(sequenceNumber);
+ evt.loggerName = name;
+ evt.level = level;
+ evt.args = params;
+ evt.bundle = bundle;
+ evt.thrown = thrown;
+ evt.msg = key;
+ evt.isLoggable = isLoggable;
+ evt.className = className;
+ evt.methodName = methodName;
+ return evt;
+ }
+
+ }
+
+ static final java.util.logging.Level[] julLevels = {
+ java.util.logging.Level.ALL,
+ new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
+ java.util.logging.Level.FINEST,
+ new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
+ java.util.logging.Level.FINER,
+ new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
+ java.util.logging.Level.FINE,
+ new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
+ java.util.logging.Level.CONFIG,
+ new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
+ java.util.logging.Level.INFO,
+ new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
+ java.util.logging.Level.WARNING,
+ new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
+ java.util.logging.Level.SEVERE,
+ new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
+ java.util.logging.Level.OFF,
+ };
+
+ static final java.util.logging.Level[] julPlatformLevels = {
+ java.util.logging.Level.FINEST,
+ java.util.logging.Level.FINER,
+ java.util.logging.Level.FINE,
+ java.util.logging.Level.CONFIG,
+ java.util.logging.Level.INFO,
+ java.util.logging.Level.WARNING,
+ java.util.logging.Level.SEVERE,
+ };
+
+
+ public static class MyBundle extends ResourceBundle {
+
+ final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
+
+ @Override
+ protected Object handleGetObject(String key) {
+ if (key.contains(" (translated)")) {
+ throw new RuntimeException("Unexpected key: " + key);
+ }
+ return map.computeIfAbsent(key, k -> k + " (translated)");
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ }
+
+ public static class MyHandler extends Handler {
+
+ @Override
+ public java.util.logging.Level getLevel() {
+ return java.util.logging.Level.ALL;
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+ true, record.getLoggerName(),
+ record.getSourceClassName(),
+ record.getSourceMethodName(),
+ record.getLevel(),
+ record.getResourceBundle(), record.getMessage(),
+ record.getThrown(), record.getParameters()));
+ }
+ @Override
+ public void flush() {
+ }
+ @Override
+ public void close() throws SecurityException {
+ }
+
+ }
+
+ public static class MyLoggerBundle extends MyBundle {
+
+ }
+
+ public static void main(String[] args) throws Exception {
+ LoggerFinder provider = LoggerFinder.getLoggerFinder();
+ java.util.logging.Logger appSink = LoggingProviderImpl.getLogManagerAccess()
+ .demandLoggerFor(LogManager.getLogManager(), "foo",
+ DefaultPlatformLoggerTest.class);
+ java.util.logging.Logger sysSink = LoggingProviderImpl.getLogManagerAccess()
+ .demandLoggerFor(LogManager.getLogManager(),"foo", Thread.class);
+ appSink.addHandler(new MyHandler());
+ sysSink.addHandler(new MyHandler());
+ appSink.setUseParentHandlers(VERBOSE);
+ sysSink.setUseParentHandlers(VERBOSE);
+
+ System.out.println("\n*** Without Security Manager\n");
+ test(provider, true, appSink, sysSink);
+ System.out.println("Tetscase count: " + sequencer.get());
+
+ Policy.setPolicy(new SimplePolicy(allowAll, allowControl));
+ System.setSecurityManager(new SecurityManager());
+
+ System.out.println("\n*** With Security Manager, without permissions\n");
+ test(provider, false, appSink, sysSink);
+ System.out.println("Tetscase count: " + sequencer.get());
+
+ System.out.println("\n*** With Security Manager, with control permission\n");
+ allowControl.get().set(true);
+ test(provider, true, appSink, sysSink);
+
+ System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+ }
+
+ public static void test(LoggerFinder provider, boolean hasRequiredPermissions,
+ java.util.logging.Logger appSink, java.util.logging.Logger sysSink) throws Exception {
+
+ // No way to giva a resource bundle to a platform logger.
+ // ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+ final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
+
+ PlatformLogger platform = PlatformLogger.getLogger("foo");
+ loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")");
+
+ testLogger(provider, loggerDescMap, "foo", null, platform, sysSink);
+ }
+
+ public static class Foo {
+
+ }
+
+ static void verbose(String msg) {
+ if (VERBOSE) {
+ System.out.println(msg);
+ }
+ }
+
+ static void checkLogEvent(LoggerFinder provider, String desc,
+ LogEvent expected) {
+ LogEvent actual = eventQueue.poll();
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void checkLogEvent(LoggerFinder provider, String desc,
+ LogEvent expected, boolean expectNotNull) {
+ LogEvent actual = eventQueue.poll();
+ if (actual == null && !expectNotNull) return;
+ if (actual != null && !expectNotNull) {
+ throw new RuntimeException("Unexpected log event found for " + desc
+ + "\n\tgot: " + actual);
+ }
+ if (!expected.equals(actual)) {
+ throw new RuntimeException("mismatch for " + desc
+ + "\n\texpected=" + expected
+ + "\n\t actual=" + actual);
+ } else {
+ verbose("Got expected results for "
+ + desc + "\n\t" + expected);
+ }
+ }
+
+ static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
+ boolean before = allowAll.get().get();
+ try {
+ allowAll.get().set(true);
+ sink.setLevel(loggerLevel);
+ } finally {
+ allowAll.get().set(before);
+ }
+ }
+
+ // Calls the methods defined on LogProducer and verify the
+ // parameters received by the underlying logger.
+ private static void testLogger(LoggerFinder provider,
+ Map<PlatformLogger, String> loggerDescMap,
+ String name,
+ ResourceBundle loggerBundle,
+ PlatformLogger logger,
+ java.util.logging.Logger sink) throws Exception {
+
+ System.out.println("Testing " + loggerDescMap.get(logger));
+ final java.util.logging.Level OFF = java.util.logging.Level.OFF;
+
+ Foo foo = new Foo();
+ String fooMsg = foo.toString();
+ System.out.println("\tlogger.<level>(fooMsg)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel : julPlatformLevels) {
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ fooMsg, (Throwable)null, (Object[])null);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == java.util.logging.Level.FINEST) {
+ logger.finest(fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.FINER) {
+ logger.finer(fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.FINE) {
+ logger.fine(fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.CONFIG) {
+ logger.config(fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.INFO) {
+ logger.info(fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.WARNING) {
+ logger.warning(fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.SEVERE) {
+ logger.severe(fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ }
+ }
+ }
+
+ Throwable thrown = new Exception("OK: log me!");
+ System.out.println("\tlogger.<level>(msg, thrown)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel :julPlatformLevels) {
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ fooMsg, thrown, (Object[])null);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(msg, thrown): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == java.util.logging.Level.FINEST) {
+ logger.finest(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.FINER) {
+ logger.finer(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.FINE) {
+ logger.fine(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.CONFIG) {
+ logger.config(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.INFO) {
+ logger.info(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.WARNING) {
+ logger.warning(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.SEVERE) {
+ logger.severe(fooMsg, thrown);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ }
+ }
+ }
+
+ String format = "two params [{1} {2}]";
+ Object arg1 = foo;
+ Object arg2 = fooMsg;
+ System.out.println("\tlogger.<level>(format, arg1, arg2)");
+ for (java.util.logging.Level loggerLevel : julLevels) {
+ setLevel(sink, loggerLevel);
+ for (java.util.logging.Level messageLevel : julPlatformLevels) {
+ LogEvent expected =
+ LogEvent.of(
+ sequencer.get(),
+ loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+ name, messageLevel, loggerBundle,
+ format, (Throwable)null, foo, fooMsg);
+ String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ + "(format, foo, fooMsg): loggerLevel="
+ + loggerLevel+", messageLevel="+messageLevel;
+ if (messageLevel == java.util.logging.Level.FINEST) {
+ logger.finest(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.FINER) {
+ logger.finer(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.FINE) {
+ logger.fine(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.CONFIG) {
+ logger.config(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.INFO) {
+ logger.info(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.WARNING) {
+ logger.warning(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ } else if (messageLevel == java.util.logging.Level.SEVERE) {
+ logger.severe(format, foo, fooMsg);
+ checkLogEvent(provider, desc2, expected, expected.isLoggable);
+ }
+ }
+ }
+
+ }
+
+ final static class PermissionsBuilder {
+ final Permissions perms;
+ public PermissionsBuilder() {
+ this(new Permissions());
+ }
+ public PermissionsBuilder(Permissions perms) {
+ this.perms = perms;
+ }
+ public PermissionsBuilder add(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ public PermissionsBuilder addAll(PermissionCollection col) {
+ if (col != null) {
+ for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+ perms.add(e.nextElement());
+ }
+ }
+ return this;
+ }
+ public Permissions toPermissions() {
+ final PermissionsBuilder builder = new PermissionsBuilder();
+ builder.addAll(perms);
+ return builder.perms;
+ }
+ }
+
+ public static class SimplePolicy extends Policy {
+ public static final RuntimePermission LOGGERFINDER_PERMISSION =
+ new RuntimePermission("loggerFinder");
+
+ final Permissions permissions;
+ final Permissions withControlPermissions;
+ final Permissions allPermissions;
+ final ThreadLocal<AtomicBoolean> allowAll;
+ final ThreadLocal<AtomicBoolean> allowControl;
+ public SimplePolicy(ThreadLocal<AtomicBoolean> allowAll,
+ ThreadLocal<AtomicBoolean> allowControl) {
+ this.allowAll = allowAll;
+ this.allowControl = allowControl;
+ permissions = new Permissions();
+
+ withControlPermissions = new Permissions();
+ withControlPermissions.add(LOGGERFINDER_PERMISSION);
+
+ // these are used for configuring the test itself...
+ allPermissions = new Permissions();
+ allPermissions.add(new java.security.AllPermission());
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (allowAll.get().get()) return allPermissions.implies(permission);
+ if (allowControl.get().get()) return withControlPermissions.implies(permission);
+ return permissions.implies(permission);
+ }
+
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return new PermissionsBuilder().addAll(
+ allowAll.get().get() ? allPermissions :
+ allowControl.get().get()
+ ? withControlPermissions : permissions).toPermissions();
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return new PermissionsBuilder().addAll(
+ allowAll.get().get() ? allPermissions :
+ allowControl.get().get()
+ ? withControlPermissions : permissions).toPermissions();
+ }
+ }
+}
--- a/jdk/test/java/util/logging/LoggerSubclass.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/test/java/util/logging/LoggerSubclass.java Fri Nov 20 19:26:16 2015 +0100
@@ -92,7 +92,7 @@
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {new LoggerSubclass().instanceMain(args);}
- catch (Throwable e) {throw e.getCause();}}
+ catch (Throwable e) {throw e.getCause() == null ? e : e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
--- a/jdk/test/sun/util/logging/PlatformLoggerTest.java Fri Nov 20 15:34:12 2015 +0100
+++ b/jdk/test/sun/util/logging/PlatformLoggerTest.java Fri Nov 20 19:26:16 2015 +0100
@@ -38,7 +38,6 @@
import java.lang.reflect.Modifier;
import java.util.logging.*;
import sun.util.logging.PlatformLogger;
-import sun.util.logging.LoggingSupport;
import static sun.util.logging.PlatformLogger.Level.*;
public class PlatformLoggerTest {
@@ -195,7 +194,9 @@
System.out.println("Testing Java Level with: " + level.getName());
// create a brand new java logger
- Logger javaLogger = (Logger) LoggingSupport.getLogger(logger.getName()+"."+level.getName());
+ Logger javaLogger = sun.util.logging.internal.LoggingProviderImpl.getLogManagerAccess()
+ .demandLoggerFor(LogManager.getLogManager(),
+ logger.getName()+"."+level.getName(), Thread.class);
// Set a non standard java.util.logging.Level on the java logger
// (except for OFF & ALL - which will remain unchanged)