1 /*
2  * Copyright (c) 2015, 2018, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 import java.security.AccessControlException;
24 import java.security.AccessController;
25 import java.security.CodeSource;
26 import java.security.Permission;
27 import java.security.PermissionCollection;
28 import java.security.Permissions;
29 import java.security.Policy;
30 import java.security.PrivilegedAction;
31 import java.security.ProtectionDomain;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Enumeration;
35 import java.util.HashMap;
36 import java.util.Map;
37 import java.util.Objects;
38 import java.util.Queue;
39 import java.util.ResourceBundle;
40 import java.util.concurrent.ArrayBlockingQueue;
41 import java.util.concurrent.ConcurrentHashMap;
42 import java.util.concurrent.atomic.AtomicBoolean;
43 import java.util.concurrent.atomic.AtomicLong;
44 import java.util.function.Supplier;
45 import sun.util.logging.PlatformLogger;
46 import java.lang.System.LoggerFinder;
47 import java.lang.System.Logger;
48 import java.lang.System.Logger.Level;
49 import java.util.stream.Stream;
50 
51 /**
52  * @test
53  * @bug     8140364
54  * @summary JDK implementation specific unit test for JDK internal artifacts.
55  *   Tests a naive implementation of System.Logger, and in particular
56  *   the default mapping provided by PlatformLogger.Bridge.
57  * @modules java.base/sun.util.logging java.base/jdk.internal.logger
58  * @build CustomSystemClassLoader BaseLoggerFinder BaseLoggerBridgeTest
59  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOSECURITY
60  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOPERMISSIONS
61  * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest WITHPERMISSIONS
62  * @author danielfuchs
63  */
64 public class BaseLoggerBridgeTest {
65 
66     static final RuntimePermission LOGGERFINDER_PERMISSION =
67                 new RuntimePermission("loggerFinder");
68     final static AtomicLong sequencer = new AtomicLong();
69     final static boolean VERBOSE = false;
70     // whether the implementation of Logger try to do a best
71     // effort for logp... Our base logger finder stub doesn't
72     // support logp, and thus the logp() implementation comes from
73     // LoggerWrapper - which does a best effort.
74     static final boolean BEST_EFFORT_FOR_LOGP = true;
75     static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
76         @Override
77         protected AtomicBoolean initialValue() {
78             return  new AtomicBoolean(false);
79         }
80     };
81     static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
82         @Override
83         protected AtomicBoolean initialValue() {
84             return  new AtomicBoolean(false);
85         }
86     };
87     static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
88         @Override
89         protected AtomicBoolean initialValue() {
90             return  new AtomicBoolean(false);
91         }
92     };
93 
94     static final Class<?> providerClass;
95     static {
96         try {
97             providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder");
98         } catch (ClassNotFoundException ex) {
99             throw new ExceptionInInitializerError(ex);
100         }
101     }
102 
103     static final sun.util.logging.PlatformLogger.Level[] julLevels = {
104         sun.util.logging.PlatformLogger.Level.ALL,
105         sun.util.logging.PlatformLogger.Level.FINEST,
106         sun.util.logging.PlatformLogger.Level.FINER,
107         sun.util.logging.PlatformLogger.Level.FINE,
108         sun.util.logging.PlatformLogger.Level.CONFIG,
109         sun.util.logging.PlatformLogger.Level.INFO,
110         sun.util.logging.PlatformLogger.Level.WARNING,
111         sun.util.logging.PlatformLogger.Level.SEVERE,
112         sun.util.logging.PlatformLogger.Level.OFF,
113     };
114 
115     static final Level[] mappedLevels = {
116         Level.ALL,     // ALL
117         Level.TRACE,   // FINEST
118         Level.TRACE,   // FINER
119         Level.DEBUG,   // FINE
120         Level.DEBUG,   // CONFIG
121         Level.INFO,    // INFO
122         Level.WARNING, // WARNING
123         Level.ERROR,   // SEVERE
124         Level.OFF,     // OFF
125     };
126 
127     final static Map<sun.util.logging.PlatformLogger.Level, Level> julToSpiMap;
128     static {
129         Map<sun.util.logging.PlatformLogger.Level, Level> map = new HashMap<>();
130         if (mappedLevels.length != julLevels.length) {
131             throw new ExceptionInInitializerError("Array lengths differ"
132                 + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
133                 + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
134         }
135         for (int i=0; i<julLevels.length; i++) {
map.put(julLevels[i], mappedLevels[i])136             map.put(julLevels[i], mappedLevels[i]);
137         }
138         julToSpiMap = Collections.unmodifiableMap(map);
139     }
140 
141     public static class MyBundle extends ResourceBundle {
142 
143         final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
144 
145         @Override
handleGetObject(String key)146         protected Object handleGetObject(String key) {
147             if (key.contains(" (translated)")) {
148                 throw new RuntimeException("Unexpected key: " + key);
149             }
150             return map.computeIfAbsent(key, k -> k + " (translated)");
151         }
152 
153         @Override
getKeys()154         public Enumeration<String> getKeys() {
155             return Collections.enumeration(map.keySet());
156         }
157 
158     }
159     public static class MyLoggerBundle extends MyBundle {
160 
161     }
162 
163     public static interface TestLoggerFinder {
164         final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
165         final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
166         public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
167 
168         public static final class LogEvent implements Cloneable {
169 
LogEvent()170             public LogEvent() {
171                 this(sequencer.getAndIncrement());
172             }
173 
LogEvent(long sequenceNumber)174             LogEvent(long sequenceNumber) {
175                 this.sequenceNumber = sequenceNumber;
176             }
177 
178             boolean callSupplier = false;
179             long sequenceNumber;
180             boolean isLoggable;
181             String loggerName;
182             Level level;
183             ResourceBundle bundle;
184             Throwable thrown;
185             Object[] args;
186             Supplier<String> supplier;
187             String msg;
188 
toArray(boolean callSupplier)189             Object[] toArray(boolean callSupplier) {
190                 return new Object[] {
191                     sequenceNumber,
192                     isLoggable,
193                     loggerName,
194                     level,
195                     bundle,
196                     thrown,
197                     args,
198                     callSupplier && supplier != null ? supplier.get() : supplier,
199                     msg,
200                 };
201             }
202 
callSupplier(Object obj)203             boolean callSupplier(Object obj) {
204                 return callSupplier || ((LogEvent)obj).callSupplier;
205             }
206 
207             @Override
toString()208             public String toString() {
209                 return Arrays.deepToString(toArray(false));
210             }
211 
212             @Override
equals(Object obj)213             public boolean equals(Object obj) {
214                 return obj instanceof LogEvent
215                         && Objects.deepEquals(toArray(callSupplier(obj)), ((LogEvent)obj).toArray(callSupplier(obj)));
216             }
217 
218             @Override
hashCode()219             public int hashCode() {
220                 return Objects.hash(toArray(true));
221             }
222 
cloneWith(long sequenceNumber)223             public LogEvent cloneWith(long sequenceNumber)
224                     throws CloneNotSupportedException {
225                 LogEvent cloned = (LogEvent)super.clone();
226                 cloned.sequenceNumber = sequenceNumber;
227                 return cloned;
228             }
229 
of(boolean isLoggable, String name, Level level, ResourceBundle bundle, String key, Throwable thrown)230             public static LogEvent of(boolean isLoggable, String name,
231                     Level level, ResourceBundle bundle,
232                     String key, Throwable thrown) {
233                 LogEvent evt = new LogEvent();
234                 evt.isLoggable = isLoggable;
235                 evt.loggerName = name;
236                 evt.level = level;
237                 evt.args = null;
238                 evt.bundle = bundle;
239                 evt.thrown = thrown;
240                 evt.supplier = null;
241                 evt.msg = key;
242                 return evt;
243             }
244 
of(boolean isLoggable, String name, Level level, Throwable thrown, Supplier<String> supplier)245             public static LogEvent of(boolean isLoggable, String name,
246                     Level level, Throwable thrown, Supplier<String> supplier) {
247                 LogEvent evt = new LogEvent();
248                 evt.isLoggable = isLoggable;
249                 evt.loggerName = name;
250                 evt.level = level;
251                 evt.args = null;
252                 evt.bundle = null;
253                 evt.thrown = thrown;
254                 evt.supplier = supplier;
255                 evt.msg = null;
256                 return evt;
257             }
258 
of(boolean isLoggable, String name, Level level, ResourceBundle bundle, String key, Object... params)259             public static LogEvent of(boolean isLoggable, String name,
260                     Level level, ResourceBundle bundle,
261                     String key, Object... params) {
262                 LogEvent evt = new LogEvent();
263                 evt.isLoggable = isLoggable;
264                 evt.loggerName = name;
265                 evt.level = level;
266                 evt.args = params;
267                 evt.bundle = bundle;
268                 evt.thrown = null;
269                 evt.supplier = null;
270                 evt.msg = key;
271                 return evt;
272             }
273 
of(long sequenceNumber, boolean isLoggable, String name, Level level, ResourceBundle bundle, String key, Supplier<String> supplier, Throwable thrown, Object... params)274             public static LogEvent of(long sequenceNumber,
275                     boolean isLoggable, String name,
276                     Level level, ResourceBundle bundle,
277                     String key, Supplier<String> supplier,
278                     Throwable thrown, Object... params) {
279                 LogEvent evt = new LogEvent(sequenceNumber);
280                 evt.loggerName = name;
281                 evt.level = level;
282                 evt.args = params;
283                 evt.bundle = bundle;
284                 evt.thrown = thrown;
285                 evt.supplier = supplier;
286                 evt.msg = key;
287                 evt.isLoggable = isLoggable;
288                 return evt;
289             }
290 
ofp(boolean callSupplier, LogEvent evt)291             public static LogEvent ofp(boolean callSupplier, LogEvent evt) {
292                 evt.callSupplier = callSupplier;
293                 return evt;
294             }
295         }
296 
297         public class LoggerImpl implements Logger {
298             private final String name;
299             private Level level = Level.INFO;
300 
LoggerImpl(String name)301             public LoggerImpl(String name) {
302                 this.name = name;
303             }
304 
305             @Override
getName()306             public String getName() {
307                 return name;
308             }
309 
310             @Override
isLoggable(Level level)311             public boolean isLoggable(Level level) {
312                 return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
313             }
314 
315             @Override
log(Level level, ResourceBundle bundle, String key, Throwable thrown)316             public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
317                 log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
318             }
319 
320             @Override
log(Level level, ResourceBundle bundle, String format, Object... params)321             public void log(Level level, ResourceBundle bundle, String format, Object... params) {
322                 log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
323             }
324 
log(LogEvent event)325             void log(LogEvent event) {
326                 eventQueue.add(event);
327             }
328 
329             @Override
log(Level level, Supplier<String> msgSupplier)330             public void log(Level level, Supplier<String> msgSupplier) {
331                 log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
332             }
333 
334             @Override
log(Level level, Supplier<String> msgSupplier, Throwable thrown)335             public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
336                 log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
337             }
338 
339         }
340 
getLogger(String name, Module caller)341         public Logger getLogger(String name, Module caller);
getLocalizedLogger(String name, ResourceBundle bundle, Module caller)342         public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
343     }
344 
convert(Logger logger)345     static PlatformLogger.Bridge convert(Logger logger) {
346         boolean old = allowAll.get().get();
347         allowAccess.get().set(true);
348         try {
349             return PlatformLogger.Bridge.convert(logger);
350         } finally {
351             allowAccess.get().set(old);
352         }
353     }
354 
getLogger(String name, Module caller)355     static Logger getLogger(String name, Module caller) {
356         boolean old = allowAll.get().get();
357         allowAccess.get().set(true);
358         try {
359             return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
360         } finally {
361             allowAccess.get().set(old);
362         }
363     }
364 
365     static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
366 
setSecurityManager()367     static void setSecurityManager() {
368         if (System.getSecurityManager() == null) {
369             // Ugly test hack: preload the resources needed by String.format
370             //   We need to do that before setting the security manager
371             //   because our implementation of CustomSystemClassLoader
372             //   doesn't have the required permission.
373             System.out.println(String.format("debug: %s", "Setting security manager"));
374             Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
375             System.setSecurityManager(new SecurityManager());
376         }
377     }
378 
main(String[] args)379     public static void main(String[] args) {
380         if (args.length == 0)
381             args = new String[] {
382                 "NOSECURITY",
383                 "NOPERMISSIONS",
384                 "WITHPERMISSIONS"
385             };
386 
387 
388         Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
389             TestLoggerFinder provider;
390             switch (testCase) {
391                 case NOSECURITY:
392                     System.out.println("\n*** Without Security Manager\n");
393                     provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
394                     test(provider, true);
395                     System.out.println("Tetscase count: " + sequencer.get());
396                     break;
397                 case NOPERMISSIONS:
398                     System.out.println("\n*** With Security Manager, without permissions\n");
399                     setSecurityManager();
400                     try {
401                         provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
402                         throw new RuntimeException("Expected exception not raised");
403                     } catch (AccessControlException x) {
404                         if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
405                             throw new RuntimeException("Unexpected permission check", x);
406                         }
407                         final boolean control = allowControl.get().get();
408                         try {
409                             allowControl.get().set(true);
410                             provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
411                         } finally {
412                             allowControl.get().set(control);
413                         }
414                     }
415                     test(provider, false);
416                     System.out.println("Tetscase count: " + sequencer.get());
417                     break;
418                 case WITHPERMISSIONS:
419                     System.out.println("\n*** With Security Manager, with control permission\n");
420                     setSecurityManager();
421                     final boolean control = allowControl.get().get();
422                     try {
423                         allowControl.get().set(true);
424                         provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
425                         test(provider, true);
426                     } finally {
427                         allowControl.get().set(control);
428                     }
429                     break;
430                 default:
431                     throw new RuntimeException("Unknown test case: " + testCase);
432             }
433         });
434         System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
435     }
436 
test(TestLoggerFinder provider, boolean hasRequiredPermissions)437     public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
438 
439         ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
440         final Map<Object, String> loggerDescMap = new HashMap<>();
441 
442 
443         TestLoggerFinder.LoggerImpl appSink = null;
444         try {
445             appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
446             if (!hasRequiredPermissions) {
447                 throw new RuntimeException("Managed to obtain a system logger without permission");
448             }
449         } catch (AccessControlException acx) {
450             if (hasRequiredPermissions) {
451                 throw new RuntimeException("Unexpected security exception: ", acx);
452             }
453             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
454                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
455             }
456             System.out.println("Got expected exception for logger: " + acx);
457             boolean old = allowControl.get().get();
458             allowControl.get().set(true);
459             try {
460                 appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
461             } finally {
462                 allowControl.get().set(old);
463             }
464         }
465 
466 
467         TestLoggerFinder.LoggerImpl sysSink = null;
468         try {
469             sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class.getModule()));
470             if (!hasRequiredPermissions) {
471                 throw new RuntimeException("Managed to obtain a system logger without permission");
472             }
473         } catch (AccessControlException acx) {
474             if (hasRequiredPermissions) {
475                 throw new RuntimeException("Unexpected security exception: ", acx);
476             }
477             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
478                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
479             }
480             System.out.println("Got expected exception for system logger: " + acx);
481         }
482         if (hasRequiredPermissions && appSink == sysSink) {
483             throw new RuntimeException("identical loggers");
484         }
485 
486         if (provider.system.contains(appSink)) {
487             throw new RuntimeException("app logger in system map");
488         }
489         if (!provider.user.contains(appSink)) {
490             throw new RuntimeException("app logger not in appplication map");
491         }
492         if (hasRequiredPermissions && provider.user.contains(sysSink)) {
493             throw new RuntimeException("sys logger in appplication map");
494         }
495         if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
496             throw new RuntimeException("sys logger not in system map");
497         }
498 
499         Logger appLogger1 = System.getLogger("foo");
500         loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
501         PlatformLogger.Bridge bridge = convert(appLogger1);
502         loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))");
503         testLogger(provider, loggerDescMap, "foo", null, bridge, appSink);
504 
505         Logger sysLogger1 = null;
506         try {
507             sysLogger1 = getLogger("foo", Thread.class.getModule());
508             loggerDescMap.put(sysLogger1,
509                     "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class.getModule())");
510 
511             if (!hasRequiredPermissions) {
512                 // check that the provider would have thrown an exception
513                 provider.getLogger("foo", Thread.class.getModule());
514                 throw new RuntimeException("Managed to obtain a system logger without permission");
515             }
516         } catch (AccessControlException acx) {
517             if (hasRequiredPermissions) {
518                 throw new RuntimeException("Unexpected security exception: ", acx);
519             }
520             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
521                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
522             }
523             System.out.println("Got expected exception for system logger: " + acx);
524         }
525 
526         if (hasRequiredPermissions) {
527             // if we don't have permissions sysSink will be null.
528             testLogger(provider, loggerDescMap, "foo", null,
529                 PlatformLogger.Bridge.convert(sysLogger1), sysSink);
530         }
531 
532         Logger appLogger2 =
533                 System.getLogger("foo", loggerBundle);
534         loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
535 
536         if (appLogger2 == appLogger1) {
537             throw new RuntimeException("identical loggers");
538         }
539 
540         if (provider.system.contains(appLogger2)) {
541             throw new RuntimeException("localized app logger in system map");
542         }
543         if (provider.user.contains(appLogger2)) {
544             throw new RuntimeException("localized app logger  in appplication map");
545         }
546 
547         testLogger(provider, loggerDescMap, "foo", loggerBundle,
548                 PlatformLogger.Bridge.convert(appLogger2), appSink);
549 
550         Logger sysLogger2 = null;
551         try {
552             sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class.getModule());
553             loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class.getModule())");
554             if (!hasRequiredPermissions) {
555                 throw new RuntimeException("Managed to obtain a system logger without permission");
556             }
557         } catch (AccessControlException acx) {
558             if (hasRequiredPermissions) {
559                 throw new RuntimeException("Unexpected security exception: ", acx);
560             }
561             if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
562                 throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
563             }
564             System.out.println("Got expected exception for localized system logger: " + acx);
565         }
566         if (hasRequiredPermissions && appLogger2 == sysLogger2) {
567             throw new RuntimeException("identical loggers");
568         }
569         if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
570             throw new RuntimeException("identical loggers");
571         }
572         if (hasRequiredPermissions && provider.user.contains(sysLogger2)) {
573             throw new RuntimeException("localized sys logger in appplication map");
574         }
575         if (hasRequiredPermissions && provider.system.contains(sysLogger2)) {
576             throw new RuntimeException("localized sys logger not in system map");
577         }
578 
579         if (hasRequiredPermissions) {
580             // if we don't have permissions sysSink will be null.
581             testLogger(provider, loggerDescMap, "foo", loggerBundle,
582                 PlatformLogger.Bridge.convert(sysLogger2), sysSink);
583         }
584 
585     }
586 
587     public static class Foo {
588 
589     }
590 
verbose(String msg)591     static void verbose(String msg) {
592        if (VERBOSE) {
593            System.out.println(msg);
594        }
595     }
596 
checkLogEvent(TestLoggerFinder provider, String desc, TestLoggerFinder.LogEvent expected)597     static void checkLogEvent(TestLoggerFinder provider, String desc,
598             TestLoggerFinder.LogEvent expected) {
599         TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
600         if (!Objects.equals(expected, actual)) {
601             throw new RuntimeException("mismatch for " + desc
602                     + "\n\texpected=" + expected
603                     + "\n\t  actual=" + actual);
604         } else {
605             verbose("Got expected results for "
606                     + desc + "\n\t" + expected);
607         }
608     }
609 
checkLogEvent(TestLoggerFinder provider, String desc, TestLoggerFinder.LogEvent expected, boolean expectNotNull)610     static void checkLogEvent(TestLoggerFinder provider, String desc,
611             TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
612         TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
613         if (actual == null && !expectNotNull) return;
614         if (actual != null && !expectNotNull) {
615             throw new RuntimeException("Unexpected log event found for " + desc
616                 + "\n\tgot: " + actual);
617         }
618         if (!expected.equals(actual)) {
619             throw new RuntimeException("mismatch for " + desc
620                     + "\n\texpected=" + expected
621                     + "\n\t  actual=" + actual);
622         } else {
623             verbose("Got expected results for "
624                     + desc + "\n\t" + expected);
625         }
626     }
627 
logpMessage(ResourceBundle bundle, String className, String methodName, Supplier<String> msg)628         static Supplier<String> logpMessage(ResourceBundle bundle,
629                 String className, String methodName, Supplier<String> msg) {
630             if (BEST_EFFORT_FOR_LOGP && bundle == null
631                     && (className != null || methodName != null)) {
632                 final String cName = className == null ? "" :  className;
633                 final String mName = methodName == null ? "" : methodName;
634                 return () -> String.format("[%s %s] %s", cName, mName, msg.get());
635             } else {
636                 return msg;
637             }
638         }
639 
logpMessage(ResourceBundle bundle, String className, String methodName, String msg)640         static String logpMessage(ResourceBundle bundle,
641                 String className, String methodName, String msg) {
642             if (BEST_EFFORT_FOR_LOGP && bundle == null
643                     && (className != null || methodName != null)) {
644                 final String cName = className == null ? "" :  className;
645                 final String mName = methodName == null ? "" : methodName;
646                 return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg);
647             } else {
648                 return msg;
649             }
650         }
651 
652     // Calls the methods defined on LogProducer and verify the
653     // parameters received by the underlying TestLoggerFinder.LoggerImpl
654     // logger.
testLogger(TestLoggerFinder provider, Map<Object, String> loggerDescMap, String name, ResourceBundle loggerBundle, PlatformLogger.Bridge logger, TestLoggerFinder.LoggerImpl sink)655     private static void testLogger(TestLoggerFinder provider,
656             Map<Object, String> loggerDescMap,
657             String name,
658             ResourceBundle loggerBundle,
659             PlatformLogger.Bridge logger,
660             TestLoggerFinder.LoggerImpl sink) {
661 
662         if (loggerDescMap.get(logger) == null) {
663             throw new RuntimeException("Test bug: Missing description");
664         }
665         System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]");
666 
667         Foo foo = new Foo();
668         String fooMsg = foo.toString();
669         System.out.println("\tlogger.log(messageLevel, fooMsg)");
670         System.out.println("\tlogger.<level>(fooMsg)");
671         for (Level loggerLevel : Level.values()) {
672             sink.level = loggerLevel;
673             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
674                 String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
675                         + loggerLevel+", messageLevel="+messageLevel;
676                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
677                 TestLoggerFinder.LogEvent expected =
678                         TestLoggerFinder.LogEvent.of(
679                             sequencer.get(),
680                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
681                             name, expectedMessageLevel, loggerBundle,
682                             fooMsg, null, (Throwable)null, (Object[])null);
683                 logger.log(messageLevel, fooMsg);
684                 checkLogEvent(provider, desc, expected);
685             }
686         }
687 
688         Supplier<String> supplier = new Supplier<String>() {
689             @Override
690             public String get() {
691                 return this.toString();
692             }
693         };
694         System.out.println("\tlogger.log(messageLevel, supplier)");
695         System.out.println("\tlogger.<level>(supplier)");
696         for (Level loggerLevel : Level.values()) {
697             sink.level = loggerLevel;
698             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
699                 String desc = "logger.log(messageLevel, supplier): loggerLevel="
700                         + loggerLevel+", messageLevel="+messageLevel;
701                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
702                 TestLoggerFinder.LogEvent expected =
703                         TestLoggerFinder.LogEvent.of(
704                             sequencer.get(),
705                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
706                             name, expectedMessageLevel, (ResourceBundle) null,
707                             null, supplier, (Throwable)null, (Object[])null);
708                 logger.log(messageLevel, supplier);
709                 checkLogEvent(provider, desc, expected);
710             }
711         }
712 
713         String format = "two params [{1} {2}]";
714         Object arg1 = foo;
715         Object arg2 = fooMsg;
716         System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
717         for (Level loggerLevel : Level.values()) {
718             sink.level = loggerLevel;
719             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
720                 String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
721                         + loggerLevel+", messageLevel="+messageLevel;
722                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
723                 TestLoggerFinder.LogEvent expected =
724                         TestLoggerFinder.LogEvent.of(
725                             sequencer.get(),
726                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
727                             name, expectedMessageLevel, loggerBundle,
728                             format, null, (Throwable)null, arg1, arg2);
729                 logger.log(messageLevel, format, arg1, arg2);
730                 checkLogEvent(provider, desc, expected);
731             }
732         }
733 
734         Throwable thrown = new Exception("OK: log me!");
735         System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
736         for (Level loggerLevel : Level.values()) {
737             sink.level = loggerLevel;
738             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
739                 String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
740                         + loggerLevel+", messageLevel="+messageLevel;
741                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
742                 TestLoggerFinder.LogEvent expected =
743                         TestLoggerFinder.LogEvent.of(
744                             sequencer.get(),
745                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
746                             name, expectedMessageLevel, loggerBundle,
747                             fooMsg, null, thrown, (Object[])null);
748                 logger.log(messageLevel, fooMsg, thrown);
749                 checkLogEvent(provider, desc, expected);
750             }
751         }
752 
753         System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
754         for (Level loggerLevel : Level.values()) {
755             sink.level = loggerLevel;
756             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
757                 String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
758                         + loggerLevel+", messageLevel="+messageLevel;
759                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
760                 TestLoggerFinder.LogEvent expected =
761                         TestLoggerFinder.LogEvent.of(
762                             sequencer.get(),
763                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
764                             name, expectedMessageLevel, (ResourceBundle)null,
765                             null, supplier, thrown, (Object[])null);
766                 logger.log(messageLevel, thrown, supplier);
767                 checkLogEvent(provider, desc, expected);
768             }
769         }
770 
771         String sourceClass = "blah.Blah";
772         String sourceMethod = "blih";
773         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
774         for (Level loggerLevel : Level.values()) {
775             sink.level = loggerLevel;
776             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
777                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
778                         + loggerLevel+", messageLevel="+messageLevel;
779                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
780                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
781                 TestLoggerFinder.LogEvent expected =
782                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
783                         TestLoggerFinder.LogEvent.of(
784                             sequencer.get(),
785                             isLoggable,
786                             name, expectedMessageLevel, loggerBundle,
787                             logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
788                             null, (Throwable)null, (Object[]) null) : null;
789                 logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
790                 checkLogEvent(provider, desc, expected);
791             }
792         }
793 
794         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
795         for (Level loggerLevel : Level.values()) {
796             sink.level = loggerLevel;
797             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
798                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
799                         + loggerLevel+", messageLevel="+messageLevel;
800                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
801                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
802                 TestLoggerFinder.LogEvent expected = isLoggable ?
803                     TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
804                         TestLoggerFinder.LogEvent.of(
805                             sequencer.get(),
806                             isLoggable,
807                             name, expectedMessageLevel, null, null,
808                             logpMessage(null, sourceClass, sourceMethod, supplier),
809                             (Throwable)null, (Object[]) null)) : null;
810                 logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
811                 checkLogEvent(provider, desc, expected);
812             }
813         }
814 
815         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
816         for (Level loggerLevel : Level.values()) {
817             sink.level = loggerLevel;
818             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
819                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
820                         + loggerLevel+", messageLevel="+messageLevel;
821                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
822                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
823                 TestLoggerFinder.LogEvent expected =
824                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
825                         TestLoggerFinder.LogEvent.of(
826                             sequencer.get(),
827                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
828                             name, expectedMessageLevel, loggerBundle,
829                             logpMessage(loggerBundle, sourceClass, sourceMethod, format),
830                             null, (Throwable)null, arg1, arg2) : null;
831                 logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
832                 checkLogEvent(provider, desc, expected);
833             }
834         }
835 
836         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
837         for (Level loggerLevel : Level.values()) {
838             sink.level = loggerLevel;
839             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
840                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
841                         + loggerLevel+", messageLevel="+messageLevel;
842                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
843                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
844                 TestLoggerFinder.LogEvent expected =
845                     isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ?
846                         TestLoggerFinder.LogEvent.of(
847                             sequencer.get(),
848                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
849                             name, expectedMessageLevel, loggerBundle,
850                             logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
851                             null, thrown, (Object[])null) : null;
852                 logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
853                 checkLogEvent(provider, desc, expected);
854             }
855         }
856 
857         System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
858         for (Level loggerLevel : Level.values()) {
859             sink.level = loggerLevel;
860             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
861                 String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
862                         + loggerLevel+", messageLevel="+messageLevel;
863                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
864                 boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
865                 TestLoggerFinder.LogEvent expected = isLoggable ?
866                     TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
867                         TestLoggerFinder.LogEvent.of(
868                             sequencer.get(),
869                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
870                             name, expectedMessageLevel, null, null,
871                             logpMessage(null, sourceClass, sourceMethod, supplier),
872                             thrown, (Object[])null)) : null;
873                 logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
874                 checkLogEvent(provider, desc, expected);
875             }
876         }
877 
878         ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
879         System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
880         for (Level loggerLevel : Level.values()) {
881             sink.level = loggerLevel;
882             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
883                 String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
884                         + loggerLevel+", messageLevel="+messageLevel;
885                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
886                 TestLoggerFinder.LogEvent expected =
887                         TestLoggerFinder.LogEvent.of(
888                             sequencer.get(),
889                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
890                             name, expectedMessageLevel, bundle,
891                             format, null, (Throwable)null, arg1, arg2);
892                 logger.logrb(messageLevel, bundle, format, arg1, arg2);
893                 checkLogEvent(provider, desc, expected);
894             }
895         }
896 
897         System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
898         for (Level loggerLevel : Level.values()) {
899             sink.level = loggerLevel;
900             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
901                 String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
902                         + loggerLevel+", messageLevel="+messageLevel;
903                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
904                 TestLoggerFinder.LogEvent expected =
905                         TestLoggerFinder.LogEvent.of(
906                             sequencer.get(),
907                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
908                             name, expectedMessageLevel, bundle,
909                             fooMsg, null, thrown, (Object[])null);
910                 logger.logrb(messageLevel, bundle, fooMsg, thrown);
911                 checkLogEvent(provider, desc, expected);
912             }
913         }
914 
915         System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
916         for (Level loggerLevel : Level.values()) {
917             sink.level = loggerLevel;
918             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
919                 String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
920                         + loggerLevel+", messageLevel="+messageLevel;
921                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
922                 TestLoggerFinder.LogEvent expected =
923                         TestLoggerFinder.LogEvent.of(
924                             sequencer.get(),
925                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
926                             name, expectedMessageLevel, bundle,
927                             format, null, (Throwable)null, arg1, arg2);
928                 logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
929                 checkLogEvent(provider, desc, expected);
930             }
931         }
932 
933         System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
934         for (Level loggerLevel : Level.values()) {
935             sink.level = loggerLevel;
936             for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
937                 String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
938                         + loggerLevel+", messageLevel="+messageLevel;
939                 Level expectedMessageLevel = julToSpiMap.get(messageLevel);
940                 TestLoggerFinder.LogEvent expected =
941                         TestLoggerFinder.LogEvent.of(
942                             sequencer.get(),
943                             loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
944                             name, expectedMessageLevel, bundle,
945                             fooMsg, null, thrown, (Object[])null);
946                 logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
947                 checkLogEvent(provider, desc, expected);
948             }
949         }
950     }
951 
952     final static class PermissionsBuilder {
953         final Permissions perms;
PermissionsBuilder()954         public PermissionsBuilder() {
955             this(new Permissions());
956         }
PermissionsBuilder(Permissions perms)957         public PermissionsBuilder(Permissions perms) {
958             this.perms = perms;
959         }
add(Permission p)960         public PermissionsBuilder add(Permission p) {
961             perms.add(p);
962             return this;
963         }
addAll(PermissionCollection col)964         public PermissionsBuilder addAll(PermissionCollection col) {
965             if (col != null) {
966                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
967                     perms.add(e.nextElement());
968                 }
969             }
970             return this;
971         }
toPermissions()972         public Permissions toPermissions() {
973             final PermissionsBuilder builder = new PermissionsBuilder();
974             builder.addAll(perms);
975             return builder.perms;
976         }
977     }
978 
979     public static class SimplePolicy extends Policy {
980         final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
981         final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
982         final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
983 
984         final Permissions permissions;
985         final Permissions allPermissions;
986         final ThreadLocal<AtomicBoolean> allowControl;
987         final ThreadLocal<AtomicBoolean> allowAccess;
988         final ThreadLocal<AtomicBoolean> allowAll;
SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess, ThreadLocal<AtomicBoolean> allowAll)989         public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
990                 ThreadLocal<AtomicBoolean> allowAccess,
991                 ThreadLocal<AtomicBoolean> allowAll) {
992             this.allowControl = allowControl;
993             this.allowAccess = allowAccess;
994             this.allowAll = allowAll;
995             permissions = new Permissions();
996             allPermissions = new PermissionsBuilder()
997                     .add(new java.security.AllPermission())
998                     .toPermissions();
999         }
1000 
getPermissions()1001         Permissions getPermissions() {
1002             if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
1003                 PermissionsBuilder builder =  new PermissionsBuilder()
1004                         .addAll(permissions);
1005                 if (allowControl.get().get()) {
1006                     builder.add(CONTROL);
1007                 }
1008                 if (allowAccess.get().get()) {
1009                     builder.add(ACCESS_LOGGER);
1010                     builder.add(ACCESS_LOGGING);
1011                 }
1012                 if (allowAll.get().get()) {
1013                     builder.addAll(allPermissions);
1014                 }
1015                 return builder.toPermissions();
1016             }
1017             return permissions;
1018         }
1019 
1020         @Override
implies(ProtectionDomain domain, Permission permission)1021         public boolean implies(ProtectionDomain domain, Permission permission) {
1022             return getPermissions().implies(permission);
1023         }
1024 
1025         @Override
getPermissions(CodeSource codesource)1026         public PermissionCollection getPermissions(CodeSource codesource) {
1027             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1028         }
1029 
1030         @Override
getPermissions(ProtectionDomain domain)1031         public PermissionCollection getPermissions(ProtectionDomain domain) {
1032             return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1033         }
1034     }
1035 }
1036