/*
 * 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;
import sun.security.action.GetPropertyAction;

Helper class used to load the LoggerFinder.
/** * 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() { String errorPolicy = GetPropertyAction.privilegedGetProperty("jdk.logger.finder.error"); 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() { return Boolean.parseBoolean( GetPropertyAction.privilegedGetProperty("jdk.logger.finder.singleton")); } 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(); } }