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 26 package jdk.internal.logger; 27 28 import jdk.internal.misc.VM; 29 30 import java.lang.ref.Reference; 31 import java.lang.ref.WeakReference; 32 import java.util.HashMap; 33 import java.util.Map; 34 import java.util.function.Function; 35 import java.util.Objects; 36 import java.lang.System.LoggerFinder; 37 import java.lang.System.Logger; 38 import java.lang.ref.ReferenceQueue; 39 import java.security.AccessController; 40 import java.security.PrivilegedAction; 41 import java.util.Collection; 42 import java.util.ResourceBundle; 43 44 /** 45 * Internal Service Provider Interface (SPI) that makes it possible to use 46 * {@code java.util.logging} as backend when the {@link 47 * sun.util.logging.internal.LoggingProviderImpl 48 * sun.util.logging.internal.LoggingProviderImpl} is present. 49 * <p> 50 * The JDK default implementation of the {@link LoggerFinder} will 51 * attempt to locate and load an {@linkplain 52 * java.util.ServiceLoader#loadInstalled(java.lang.Class) installed} 53 * implementation of the {@code DefaultLoggerFinder}. If {@code java.util.logging} 54 * is present, this will usually resolve to an instance of {@link 55 * sun.util.logging.internal.LoggingProviderImpl sun.util.logging.internal.LoggingProviderImpl}. 56 * Otherwise, if no concrete service provider is declared for 57 * {@code DefaultLoggerFinder}, the default implementation provided by this class 58 * will be used. 59 * <p> 60 * When the {@link sun.util.logging.internal.LoggingProviderImpl 61 * sun.util.logging.internal.LoggingProviderImpl} is not present then the 62 * default implementation provided by this class is to use a simple logger 63 * that will log messages whose level is INFO and above to the console. 64 * These simple loggers are not configurable. 65 * <p> 66 * When configuration is needed, an application should either link with 67 * {@code java.util.logging} - and use the {@code java.util.logging} for 68 * configuration, or link with {@link LoggerFinder another implementation} 69 * of the {@link LoggerFinder} 70 * that provides the necessary configuration. 71 * 72 * @apiNote Programmers are not expected to call this class directly. 73 * Instead they should rely on the static methods defined by {@link 74 * java.lang.System java.lang.System} or {@link sun.util.logging.PlatformLogger 75 * sun.util.logging.PlatformLogger}. 76 * 77 * @see java.lang.System.LoggerFinder 78 * @see jdk.internal.logger 79 * @see sun.util.logging.internal 80 * 81 */ 82 public class DefaultLoggerFinder extends LoggerFinder { 83 84 static final RuntimePermission LOGGERFINDER_PERMISSION = 85 new RuntimePermission("loggerFinder"); 86 87 /** 88 * Creates a new instance of DefaultLoggerFinder. 89 * @throws SecurityException if the calling code does not have the 90 * {@code RuntimePermission("loggerFinder")} 91 */ DefaultLoggerFinder()92 protected DefaultLoggerFinder() { 93 this(checkPermission()); 94 } 95 DefaultLoggerFinder(Void unused)96 private DefaultLoggerFinder(Void unused) { 97 // nothing to do. 98 } 99 checkPermission()100 private static Void checkPermission() { 101 @SuppressWarnings("removal") 102 final SecurityManager sm = System.getSecurityManager(); 103 if (sm != null) { 104 sm.checkPermission(LOGGERFINDER_PERMISSION); 105 } 106 return null; 107 } 108 109 // SharedLoggers is a default cache of loggers used when JUL is not 110 // present - in that case we use instances of SimpleConsoleLogger which 111 // cannot be directly configure through public APIs. 112 // 113 // We can therefore afford to simply maintain two domains - one for the 114 // system, and one for the application. 115 // 116 static final class SharedLoggers { 117 private final Map<String, Reference<Logger>> loggers = 118 new HashMap<>(); 119 private final ReferenceQueue<Logger> queue = new ReferenceQueue<>(); 120 get(Function<String, Logger> loggerSupplier, final String name)121 synchronized Logger get(Function<String, Logger> loggerSupplier, final String name) { 122 Reference<? extends Logger> ref = loggers.get(name); 123 Logger w = ref == null ? null : ref.get(); 124 if (w == null) { 125 w = loggerSupplier.apply(name); 126 loggers.put(name, new WeakReference<>(w, queue)); 127 } 128 129 // Remove stale mapping... 130 Collection<Reference<Logger>> values = null; 131 while ((ref = queue.poll()) != null) { 132 if (values == null) values = loggers.values(); 133 values.remove(ref); 134 } 135 return w; 136 } 137 138 static final SharedLoggers system = new SharedLoggers(); 139 static final SharedLoggers application = new SharedLoggers(); 140 } 141 142 @SuppressWarnings("removal") isSystem(Module m)143 public static boolean isSystem(Module m) { 144 return AccessController.doPrivileged(new PrivilegedAction<>() { 145 @Override 146 public Boolean run() { 147 // returns true if moduleCL is the platform class loader 148 // or one of its ancestors. 149 return VM.isSystemDomainLoader(m.getClassLoader()); 150 } 151 }); 152 } 153 154 @Override 155 public final Logger getLogger(String name, Module module) { 156 Objects.requireNonNull(name, "name"); 157 Objects.requireNonNull(module, "module"); 158 checkPermission(); 159 return demandLoggerFor(name, module); 160 } 161 162 @Override 163 public final Logger getLocalizedLogger(String name, ResourceBundle bundle, 164 Module module) { 165 return super.getLocalizedLogger(name, bundle, module); 166 } 167 168 /** 169 * Returns a {@link Logger logger} suitable for use within the 170 * given {@code module}. 171 * 172 * @implSpec The default implementation for this method is to return a 173 * simple logger that will print all messages of INFO level and above 174 * to the console. That simple logger is not configurable. 175 * 176 * @param name The name of the logger. 177 * @param module The module on behalf of which the logger is created. 178 * @return A {@link Logger logger} suitable for the application usage. 179 * @throws SecurityException if the calling code does not have the 180 * {@code RuntimePermission("loggerFinder")}. 181 */ 182 protected Logger demandLoggerFor(String name, Module module) { 183 checkPermission(); 184 if (isSystem(module)) { 185 return SharedLoggers.system.get(SimpleConsoleLogger::makeSimpleLogger, name); 186 } else { 187 return SharedLoggers.application.get(SimpleConsoleLogger::makeSimpleLogger, name); 188 } 189 } 190 191 } 192