1 /* 2 * Copyright (c) 2015, 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 37 /** 38 * Helper class used to load the {@link java.lang.System.LoggerFinder}. 39 */ 40 public final class LoggerFinderLoader { 41 private static volatile System.LoggerFinder service; 42 private static final Object lock = new int[0]; 43 static final Permission CLASSLOADER_PERMISSION = 44 SecurityConstants.GET_CLASSLOADER_PERMISSION; 45 static final Permission READ_PERMISSION = 46 new FilePermission("<<ALL FILES>>", 47 SecurityConstants.FILE_READ_ACTION); 48 public static final RuntimePermission LOGGERFINDER_PERMISSION = 49 new RuntimePermission("loggerFinder"); 50 51 // This is used to control how the LoggerFinderLoader handles 52 // errors when instantiating the LoggerFinder provider. 53 // ERROR => throws ServiceConfigurationError 54 // WARNING => Do not fail, use plain default (simple logger) implementation, 55 // prints warning on console. (this is the default) 56 // DEBUG => Do not fail, use plain default (simple logger) implementation, 57 // prints warning and exception stack trace on console. 58 // QUIET => Do not fail and stay silent. 59 private static enum ErrorPolicy { ERROR, WARNING, DEBUG, QUIET }; 60 61 // This class is static and cannot be instantiated. 62 private LoggerFinderLoader() { 63 throw new InternalError("LoggerFinderLoader cannot be instantiated"); 64 } 65 66 67 // Return the loaded LoggerFinder, or load it if not already loaded. 68 private static System.LoggerFinder service() { 69 if (service != null) return service; 70 synchronized(lock) { 71 if (service != null) return service; 72 service = loadLoggerFinder(); 73 } 74 // Since the LoggerFinder is already loaded - we can stop using 75 // temporary loggers. 76 BootstrapLogger.redirectTemporaryLoggers(); 77 return service; 78 } 79 80 // Get configuration error policy 81 private static ErrorPolicy configurationErrorPolicy() { 82 final PrivilegedAction<String> getConfigurationErrorPolicy = 83 () -> System.getProperty("jdk.logger.finder.error"); 84 String errorPolicy = AccessController.doPrivileged(getConfigurationErrorPolicy); 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. 97 private static boolean ensureSingletonProvider() { 98 final PrivilegedAction<Boolean> ensureSingletonProvider = 99 () -> Boolean.getBoolean("jdk.logger.finder.singleton"); 100 return AccessController.doPrivileged(ensureSingletonProvider); 101 } 102 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 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 private static System.LoggerFinder loadDefaultImplementation() { 168 final SecurityManager sm = System.getSecurityManager(); 169 final Iterator<DefaultLoggerFinder> iterator; 170 if (sm == null) { 171 iterator = ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator(); 172 } else { 173 // We use limited do privileged here - the minimum set of 174 // permissions required to 'see' the META-INF/services resources 175 // seems to be CLASSLOADER_PERMISSION and READ_PERMISSION. 176 // Note that do privileged is required because 177 // otherwise the SecurityManager will prevent the ServiceLoader 178 // from seeing the installed provider. 179 PrivilegedAction<Iterator<DefaultLoggerFinder>> pa = () -> 180 ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator(); 181 iterator = AccessController.doPrivileged(pa, null, 182 LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION, 183 READ_PERMISSION); 184 } 185 DefaultLoggerFinder result = null; 186 try { 187 // Iterator iterates with the access control context stored 188 // at ServiceLoader creation time. 189 if (iterator.hasNext()) { 190 result = iterator.next(); 191 } 192 } catch (RuntimeException x) { 193 throw new ServiceConfigurationError( 194 "Failed to instantiate default LoggerFinder", x); 195 } 196 if (result == null) { 197 result = new DefaultLoggerFinder(); 198 } 199 return result; 200 } 201 202 public static System.LoggerFinder getLoggerFinder() { 203 final SecurityManager sm = System.getSecurityManager(); 204 if (sm != null) { 205 sm.checkPermission(LOGGERFINDER_PERMISSION); 206 } 207 return service(); 208 } 209 210 }