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 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. 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. 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 82 private static ErrorPolicy configurationErrorPolicy() { 83 String errorPolicy = 84 GetPropertyAction.getProperty("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. 97 private static boolean ensureSingletonProvider() { 98 return Boolean.parseBoolean( 99 GetPropertyAction.getProperty("jdk.logger.finder.singleton")); 100 } 101 102 private static Iterator<System.LoggerFinder> findLoggerFinderProviders() { 103 final Iterator<System.LoggerFinder> iterator; 104 if (System.getSecurityManager() == null) { 105 iterator = ServiceLoader.load(System.LoggerFinder.class, 106 ClassLoader.getSystemClassLoader()).iterator(); 107 } else { 108 final PrivilegedAction<Iterator<System.LoggerFinder>> pa = 109 () -> ServiceLoader.load(System.LoggerFinder.class, 110 ClassLoader.getSystemClassLoader()).iterator(); 111 iterator = AccessController.doPrivileged(pa, null, 112 LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION, 113 READ_PERMISSION); 114 } 115 return iterator; 116 } 117 118 // Loads the LoggerFinder using ServiceLoader. If no LoggerFinder 119 // is found returns the default (possibly JUL based) implementation 120 private static System.LoggerFinder loadLoggerFinder() { 121 System.LoggerFinder result; 122 try { 123 // Iterator iterates with the access control context stored 124 // at ServiceLoader creation time. 125 final Iterator<System.LoggerFinder> iterator = 126 findLoggerFinderProviders(); 127 if (iterator.hasNext()) { 128 result = iterator.next(); 129 if (iterator.hasNext() && ensureSingletonProvider()) { 130 throw new ServiceConfigurationError( 131 "More than on LoggerFinder implementation"); 132 } 133 } else { 134 result = loadDefaultImplementation(); 135 } 136 } catch (Error | RuntimeException x) { 137 // next caller will get the plain default impl (not linked 138 // to java.util.logging) 139 service = result = new DefaultLoggerFinder(); 140 ErrorPolicy errorPolicy = configurationErrorPolicy(); 141 if (errorPolicy == ErrorPolicy.ERROR) { 142 // rethrow any exception as a ServiceConfigurationError. 143 if (x instanceof Error) { 144 throw x; 145 } else { 146 throw new ServiceConfigurationError( 147 "Failed to instantiate LoggerFinder provider; Using default.", x); 148 } 149 } else if (errorPolicy != ErrorPolicy.QUIET) { 150 // if QUIET just silently use the plain default impl 151 // otherwise, log a warning, possibly adding the exception 152 // stack trace (if DEBUG is specified). 153 SimpleConsoleLogger logger = 154 new SimpleConsoleLogger("jdk.internal.logger", false); 155 logger.log(System.Logger.Level.WARNING, 156 "Failed to instantiate LoggerFinder provider; Using default."); 157 if (errorPolicy == ErrorPolicy.DEBUG) { 158 logger.log(System.Logger.Level.WARNING, 159 "Exception raised trying to instantiate LoggerFinder", x); 160 } 161 } 162 } 163 return result; 164 } 165 166 private static System.LoggerFinder loadDefaultImplementation() { 167 final SecurityManager sm = System.getSecurityManager(); 168 final Iterator<DefaultLoggerFinder> iterator; 169 if (sm == null) { 170 iterator = ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator(); 171 } else { 172 // We use limited do privileged here - the minimum set of 173 // permissions required to 'see' the META-INF/services resources 174 // seems to be CLASSLOADER_PERMISSION and READ_PERMISSION. 175 // Note that do privileged is required because 176 // otherwise the SecurityManager will prevent the ServiceLoader 177 // from seeing the installed provider. 178 PrivilegedAction<Iterator<DefaultLoggerFinder>> pa = () -> 179 ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator(); 180 iterator = AccessController.doPrivileged(pa, null, 181 LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION, 182 READ_PERMISSION); 183 } 184 DefaultLoggerFinder result = null; 185 try { 186 // Iterator iterates with the access control context stored 187 // at ServiceLoader creation time. 188 if (iterator.hasNext()) { 189 result = iterator.next(); 190 } 191 } catch (RuntimeException x) { 192 throw new ServiceConfigurationError( 193 "Failed to instantiate default LoggerFinder", x); 194 } 195 if (result == null) { 196 result = new DefaultLoggerFinder(); 197 } 198 return result; 199 } 200 201 public static System.LoggerFinder getLoggerFinder() { 202 final SecurityManager sm = System.getSecurityManager(); 203 if (sm != null) { 204 sm.checkPermission(LOGGERFINDER_PERMISSION); 205 } 206 return service(); 207 } 208 209 }