/* * 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 {@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(); } }