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