1 /* 2 * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.internal.logger; 26 27 import java.io.FilePermission; 28 import java.security.AccessController; 29 import java.security.Permission; 30 import java.security.PrivilegedAction; 31 import java.util.Iterator; 32 import java.util.Locale; 33 import java.util.ServiceConfigurationError; 34 import java.util.ServiceLoader; 35 import sun.security.util.SecurityConstants; 36 import sun.security.action.GetPropertyAction; 37 38 /** 39 * Helper class used to load the {@link java.lang.System.LoggerFinder}. 40 */ 41 public final class LoggerFinderLoader { 42 private static volatile System.LoggerFinder service; 43 private static final Object lock = new int[0]; 44 static final Permission CLASSLOADER_PERMISSION = 45 SecurityConstants.GET_CLASSLOADER_PERMISSION; 46 static final Permission READ_PERMISSION = 47 new FilePermission("<<ALL FILES>>", 48 SecurityConstants.FILE_READ_ACTION); 49 public static final RuntimePermission LOGGERFINDER_PERMISSION = 50 new RuntimePermission("loggerFinder"); 51 52 // This is used to control how the LoggerFinderLoader handles 53 // errors when instantiating the LoggerFinder provider. 54 // ERROR => throws ServiceConfigurationError 55 // WARNING => Do not fail, use plain default (simple logger) implementation, 56 // prints warning on console. (this is the default) 57 // DEBUG => Do not fail, use plain default (simple logger) implementation, 58 // prints warning and exception stack trace on console. 59 // QUIET => Do not fail and stay silent. 60 private static enum ErrorPolicy { ERROR, WARNING, DEBUG, QUIET }; 61 62 // This class is static and cannot be instantiated. LoggerFinderLoader()63 private LoggerFinderLoader() { 64 throw new InternalError("LoggerFinderLoader cannot be instantiated"); 65 } 66 67 68 // Return the loaded LoggerFinder, or load it if not already loaded. service()69 private static System.LoggerFinder service() { 70 if (service != null) return service; 71 synchronized(lock) { 72 if (service != null) return service; 73 service = loadLoggerFinder(); 74 } 75 // Since the LoggerFinder is already loaded - we can stop using 76 // temporary loggers. 77 BootstrapLogger.redirectTemporaryLoggers(); 78 return service; 79 } 80 81 // Get configuration error policy configurationErrorPolicy()82 private static ErrorPolicy configurationErrorPolicy() { 83 String errorPolicy = 84 GetPropertyAction.privilegedGetProperty("jdk.logger.finder.error"); 85 if (errorPolicy == null || errorPolicy.isEmpty()) { 86 return ErrorPolicy.WARNING; 87 } 88 try { 89 return ErrorPolicy.valueOf(errorPolicy.toUpperCase(Locale.ROOT)); 90 } catch (IllegalArgumentException x) { 91 return ErrorPolicy.WARNING; 92 } 93 } 94 95 // Whether multiple provider should be considered as an error. 96 // This is further submitted to the configuration error policy. ensureSingletonProvider()97 private static boolean ensureSingletonProvider() { 98 return Boolean.parseBoolean( 99 GetPropertyAction.privilegedGetProperty("jdk.logger.finder.singleton")); 100 } 101 102 @SuppressWarnings("removal") findLoggerFinderProviders()103 private static Iterator<System.LoggerFinder> findLoggerFinderProviders() { 104 final Iterator<System.LoggerFinder> iterator; 105 if (System.getSecurityManager() == null) { 106 iterator = ServiceLoader.load(System.LoggerFinder.class, 107 ClassLoader.getSystemClassLoader()).iterator(); 108 } else { 109 final PrivilegedAction<Iterator<System.LoggerFinder>> pa = 110 () -> ServiceLoader.load(System.LoggerFinder.class, 111 ClassLoader.getSystemClassLoader()).iterator(); 112 iterator = AccessController.doPrivileged(pa, null, 113 LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION, 114 READ_PERMISSION); 115 } 116 return iterator; 117 } 118 119 // Loads the LoggerFinder using ServiceLoader. If no LoggerFinder 120 // is found returns the default (possibly JUL based) implementation loadLoggerFinder()121 private static System.LoggerFinder loadLoggerFinder() { 122 System.LoggerFinder result; 123 try { 124 // Iterator iterates with the access control context stored 125 // at ServiceLoader creation time. 126 final Iterator<System.LoggerFinder> iterator = 127 findLoggerFinderProviders(); 128 if (iterator.hasNext()) { 129 result = iterator.next(); 130 if (iterator.hasNext() && ensureSingletonProvider()) { 131 throw new ServiceConfigurationError( 132 "More than on LoggerFinder implementation"); 133 } 134 } else { 135 result = loadDefaultImplementation(); 136 } 137 } catch (Error | RuntimeException x) { 138 // next caller will get the plain default impl (not linked 139 // to java.util.logging) 140 service = result = new DefaultLoggerFinder(); 141 ErrorPolicy errorPolicy = configurationErrorPolicy(); 142 if (errorPolicy == ErrorPolicy.ERROR) { 143 // rethrow any exception as a ServiceConfigurationError. 144 if (x instanceof Error) { 145 throw x; 146 } else { 147 throw new ServiceConfigurationError( 148 "Failed to instantiate LoggerFinder provider; Using default.", x); 149 } 150 } else if (errorPolicy != ErrorPolicy.QUIET) { 151 // if QUIET just silently use the plain default impl 152 // otherwise, log a warning, possibly adding the exception 153 // stack trace (if DEBUG is specified). 154 SimpleConsoleLogger logger = 155 new SimpleConsoleLogger("jdk.internal.logger", false); 156 logger.log(System.Logger.Level.WARNING, 157 "Failed to instantiate LoggerFinder provider; Using default."); 158 if (errorPolicy == ErrorPolicy.DEBUG) { 159 logger.log(System.Logger.Level.WARNING, 160 "Exception raised trying to instantiate LoggerFinder", x); 161 } 162 } 163 } 164 return result; 165 } 166 167 @SuppressWarnings("removal") loadDefaultImplementation()168 private static System.LoggerFinder loadDefaultImplementation() { 169 final SecurityManager sm = System.getSecurityManager(); 170 final Iterator<DefaultLoggerFinder> iterator; 171 if (sm == null) { 172 iterator = ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator(); 173 } else { 174 // We use limited do privileged here - the minimum set of 175 // permissions required to 'see' the META-INF/services resources 176 // seems to be CLASSLOADER_PERMISSION and READ_PERMISSION. 177 // Note that do privileged is required because 178 // otherwise the SecurityManager will prevent the ServiceLoader 179 // from seeing the installed provider. 180 PrivilegedAction<Iterator<DefaultLoggerFinder>> pa = () -> 181 ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator(); 182 iterator = AccessController.doPrivileged(pa, null, 183 LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION, 184 READ_PERMISSION); 185 } 186 DefaultLoggerFinder result = null; 187 try { 188 // Iterator iterates with the access control context stored 189 // at ServiceLoader creation time. 190 if (iterator.hasNext()) { 191 result = iterator.next(); 192 } 193 } catch (RuntimeException x) { 194 throw new ServiceConfigurationError( 195 "Failed to instantiate default LoggerFinder", x); 196 } 197 if (result == null) { 198 result = new DefaultLoggerFinder(); 199 } 200 return result; 201 } 202 getLoggerFinder()203 public static System.LoggerFinder getLoggerFinder() { 204 @SuppressWarnings("removal") 205 final SecurityManager sm = System.getSecurityManager(); 206 if (sm != null) { 207 sm.checkPermission(LOGGERFINDER_PERMISSION); 208 } 209 return service(); 210 } 211 212 } 213