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.
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 */
23import java.security.AccessControlException;
24import java.security.AccessController;
25import java.security.CodeSource;
26import java.security.Permission;
27import java.security.PermissionCollection;
28import java.security.Permissions;
29import java.security.Policy;
30import java.security.PrivilegedAction;
31import java.security.ProtectionDomain;
32import java.util.Arrays;
33import java.util.Collections;
34import java.util.Enumeration;
35import java.util.HashMap;
36import java.util.Map;
37import java.util.Objects;
38import java.util.Queue;
39import java.util.ResourceBundle;
40import java.util.concurrent.ArrayBlockingQueue;
41import java.util.concurrent.ConcurrentHashMap;
42import java.util.concurrent.atomic.AtomicBoolean;
43import java.util.concurrent.atomic.AtomicLong;
44import java.util.function.Supplier;
45import sun.util.logging.PlatformLogger;
46import java.lang.System.LoggerFinder;
47import java.lang.System.Logger;
48import java.lang.System.Logger.Level;
49import 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 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 */
64public 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("BaseLoggerBridgeTest$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++) {
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
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
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
170            public LogEvent() {
171                this(sequencer.getAndIncrement());
172            }
173
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
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
203            boolean callSupplier(Object obj) {
204                return callSupplier || ((LogEvent)obj).callSupplier;
205            }
206
207            @Override
208            public String toString() {
209                return Arrays.deepToString(toArray(false));
210            }
211
212            @Override
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
219            public int hashCode() {
220                return Objects.hash(toArray(true));
221            }
222
223            public LogEvent cloneWith(long sequenceNumber)
224                    throws CloneNotSupportedException {
225                LogEvent cloned = (LogEvent)super.clone();
226                cloned.sequenceNumber = sequenceNumber;
227                return cloned;
228            }
229
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
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
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
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
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
301            public LoggerImpl(String name) {
302                this.name = name;
303            }
304
305            @Override
306            public String getName() {
307                return name;
308            }
309
310            @Override
311            public boolean isLoggable(Level level) {
312                return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
313            }
314
315            @Override
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
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
325            void log(LogEvent event) {
326                eventQueue.add(event);
327            }
328
329            @Override
330            public void log(Level level, Supplier<String> msgSupplier) {
331                log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
332            }
333
334            @Override
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
341        }
342
343        public Logger getLogger(String name, Module caller);
344        public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
345    }
346
347    public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
348        static final RuntimePermission LOGGERFINDER_PERMISSION =
349                new RuntimePermission("loggerFinder");
350        @Override
351        public Logger getLogger(String name, Module caller) {
352            SecurityManager sm = System.getSecurityManager();
353            if (sm != null) {
354                sm.checkPermission(LOGGERFINDER_PERMISSION);
355            }
356            PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
357            ClassLoader callerLoader = AccessController.doPrivileged(pa);
358            if (callerLoader == null) {
359                return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
360            } else {
361                return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
362            }
363        }
364    }
365
366    static PlatformLogger.Bridge convert(Logger logger) {
367        boolean old = allowAll.get().get();
368        allowAccess.get().set(true);
369        try {
370            return PlatformLogger.Bridge.convert(logger);
371        } finally {
372            allowAccess.get().set(old);
373        }
374    }
375
376    static Logger getLogger(String name, Module caller) {
377        boolean old = allowAll.get().get();
378        allowAccess.get().set(true);
379        try {
380            return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
381        } finally {
382            allowAccess.get().set(old);
383        }
384    }
385
386    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
387
388    static void setSecurityManager() {
389        if (System.getSecurityManager() == null) {
390            // Ugly test hack: preload the resources needed by String.format
391            //   We need to do that before setting the security manager
392            //   because our implementation of CustomSystemClassLoader
393            //   doesn't have the required permission.
394            System.out.println(String.format("debug: %s", "Setting security manager"));
395            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
396            System.setSecurityManager(new SecurityManager());
397        }
398    }
399
400    public static void main(String[] args) {
401        if (args.length == 0)
402            args = new String[] {
403                "NOSECURITY",
404                "NOPERMISSIONS",
405                "WITHPERMISSIONS"
406            };
407
408
409        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
410            TestLoggerFinder provider;
411            switch (testCase) {
412                case NOSECURITY:
413                    System.out.println("\n*** Without Security Manager\n");
414                    provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
415                    test(provider, true);
416                    System.out.println("Tetscase count: " + sequencer.get());
417                    break;
418                case NOPERMISSIONS:
419                    System.out.println("\n*** With Security Manager, without permissions\n");
420                    setSecurityManager();
421                    try {
422                        provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
423                        throw new RuntimeException("Expected exception not raised");
424                    } catch (AccessControlException x) {
425                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
426                            throw new RuntimeException("Unexpected permission check", x);
427                        }
428                        final boolean control = allowControl.get().get();
429                        try {
430                            allowControl.get().set(true);
431                            provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
432                        } finally {
433                            allowControl.get().set(control);
434                        }
435                    }
436                    test(provider, false);
437                    System.out.println("Tetscase count: " + sequencer.get());
438                    break;
439                case WITHPERMISSIONS:
440                    System.out.println("\n*** With Security Manager, with control permission\n");
441                    setSecurityManager();
442                    final boolean control = allowControl.get().get();
443                    try {
444                        allowControl.get().set(true);
445                        provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
446                        test(provider, true);
447                    } finally {
448                        allowControl.get().set(control);
449                    }
450                    break;
451                default:
452                    throw new RuntimeException("Unknown test case: " + testCase);
453            }
454        });
455        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
456    }
457
458    public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
459
460        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
461        final Map<Object, String> loggerDescMap = new HashMap<>();
462
463
464        TestLoggerFinder.LoggerImpl appSink = null;
465        try {
466            appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
467            if (!hasRequiredPermissions) {
468                throw new RuntimeException("Managed to obtain a system logger without permission");
469            }
470        } catch (AccessControlException acx) {
471            if (hasRequiredPermissions) {
472                throw new RuntimeException("Unexpected security exception: ", acx);
473            }
474            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
475                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
476            }
477            System.out.println("Got expected exception for logger: " + acx);
478            boolean old = allowControl.get().get();
479            allowControl.get().set(true);
480            try {
481                appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
482            } finally {
483                allowControl.get().set(old);
484            }
485        }
486
487
488        TestLoggerFinder.LoggerImpl sysSink = null;
489        try {
490            sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class.getModule()));
491            if (!hasRequiredPermissions) {
492                throw new RuntimeException("Managed to obtain a system logger without permission");
493            }
494        } catch (AccessControlException acx) {
495            if (hasRequiredPermissions) {
496                throw new RuntimeException("Unexpected security exception: ", acx);
497            }
498            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
499                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
500            }
501            System.out.println("Got expected exception for system logger: " + acx);
502        }
503        if (hasRequiredPermissions && appSink == sysSink) {
504            throw new RuntimeException("identical loggers");
505        }
506
507        if (provider.system.contains(appSink)) {
508            throw new RuntimeException("app logger in system map");
509        }
510        if (!provider.user.contains(appSink)) {
511            throw new RuntimeException("app logger not in appplication map");
512        }
513        if (hasRequiredPermissions && provider.user.contains(sysSink)) {
514            throw new RuntimeException("sys logger in appplication map");
515        }
516        if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
517            throw new RuntimeException("sys logger not in system map");
518        }
519
520        Logger appLogger1 = System.getLogger("foo");
521        loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
522        PlatformLogger.Bridge bridge = convert(appLogger1);
523        loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))");
524        testLogger(provider, loggerDescMap, "foo", null, bridge, appSink);
525
526        Logger sysLogger1 = null;
527        try {
528            sysLogger1 = getLogger("foo", Thread.class.getModule());
529            loggerDescMap.put(sysLogger1,
530                    "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class.getModule())");
531
532            if (!hasRequiredPermissions) {
533                // check that the provider would have thrown an exception
534                provider.getLogger("foo", Thread.class.getModule());
535                throw new RuntimeException("Managed to obtain a system logger without permission");
536            }
537        } catch (AccessControlException acx) {
538            if (hasRequiredPermissions) {
539                throw new RuntimeException("Unexpected security exception: ", acx);
540            }
541            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
542                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
543            }
544            System.out.println("Got expected exception for system logger: " + acx);
545        }
546
547        if (hasRequiredPermissions) {
548            // if we don't have permissions sysSink will be null.
549            testLogger(provider, loggerDescMap, "foo", null,
550                PlatformLogger.Bridge.convert(sysLogger1), sysSink);
551        }
552
553        Logger appLogger2 =
554                System.getLogger("foo", loggerBundle);
555        loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
556
557        if (appLogger2 == appLogger1) {
558            throw new RuntimeException("identical loggers");
559        }
560
561        if (provider.system.contains(appLogger2)) {
562            throw new RuntimeException("localized app logger in system map");
563        }
564        if (provider.user.contains(appLogger2)) {
565            throw new RuntimeException("localized app logger  in appplication map");
566        }
567
568        testLogger(provider, loggerDescMap, "foo", loggerBundle,
569                PlatformLogger.Bridge.convert(appLogger2), appSink);
570
571        Logger sysLogger2 = null;
572        try {
573            sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class.getModule());
574            loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class.getModule())");
575            if (!hasRequiredPermissions) {
576                throw new RuntimeException("Managed to obtain a system logger without permission");
577            }
578        } catch (AccessControlException acx) {
579            if (hasRequiredPermissions) {
580                throw new RuntimeException("Unexpected security exception: ", acx);
581            }
582            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
583                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
584            }
585            System.out.println("Got expected exception for localized system logger: " + acx);
586        }
587        if (hasRequiredPermissions && appLogger2 == sysLogger2) {
588            throw new RuntimeException("identical loggers");
589        }
590        if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
591            throw new RuntimeException("identical loggers");
592        }
593        if (hasRequiredPermissions && provider.user.contains(sysLogger2)) {
594            throw new RuntimeException("localized sys logger in appplication map");
595        }
596        if (hasRequiredPermissions && provider.system.contains(sysLogger2)) {
597            throw new RuntimeException("localized sys logger not in system map");
598        }
599
600        if (hasRequiredPermissions) {
601            // if we don't have permissions sysSink will be null.
602            testLogger(provider, loggerDescMap, "foo", loggerBundle,
603                PlatformLogger.Bridge.convert(sysLogger2), sysSink);
604        }
605
606    }
607
608    public static class Foo {
609
610    }
611
612    static void verbose(String msg) {
613       if (VERBOSE) {
614           System.out.println(msg);
615       }
616    }
617
618    static void checkLogEvent(TestLoggerFinder provider, String desc,
619            TestLoggerFinder.LogEvent expected) {
620        TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
621        if (!Objects.equals(expected, actual)) {
622            throw new RuntimeException("mismatch for " + desc
623                    + "\n\texpected=" + expected
624                    + "\n\t  actual=" + actual);
625        } else {
626            verbose("Got expected results for "
627                    + desc + "\n\t" + expected);
628        }
629    }
630
631    static void checkLogEvent(TestLoggerFinder provider, String desc,
632            TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
633        TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
634        if (actual == null && !expectNotNull) return;
635        if (actual != null && !expectNotNull) {
636            throw new RuntimeException("Unexpected log event found for " + desc
637                + "\n\tgot: " + actual);
638        }
639        if (!expected.equals(actual)) {
640            throw new RuntimeException("mismatch for " + desc
641                    + "\n\texpected=" + expected
642                    + "\n\t  actual=" + actual);
643        } else {
644            verbose("Got expected results for "
645                    + desc + "\n\t" + expected);
646        }
647    }
648
649        static Supplier<String> logpMessage(ResourceBundle bundle,
650                String className, String methodName, Supplier<String> msg) {
651            if (BEST_EFFORT_FOR_LOGP && bundle == null
652                    && (className != null || methodName != null)) {
653                final String cName = className == null ? "" :  className;
654                final String mName = methodName == null ? "" : methodName;
655                return () -> String.format("[%s %s] %s", cName, mName, msg.get());
656            } else {
657                return msg;
658            }
659        }
660
661        static String logpMessage(ResourceBundle bundle,
662                String className, String methodName, String msg) {
663            if (BEST_EFFORT_FOR_LOGP && bundle == null
664                    && (className != null || methodName != null)) {
665                final String cName = className == null ? "" :  className;
666                final String mName = methodName == null ? "" : methodName;
667                return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg);
668            } else {
669                return msg;
670            }
671        }
672
673    // Calls the methods defined on LogProducer and verify the
674    // parameters received by the underlying TestLoggerFinder.LoggerImpl
675    // logger.
676    private static void testLogger(TestLoggerFinder provider,
677            Map<Object, String> loggerDescMap,
678            String name,
679            ResourceBundle loggerBundle,
680            PlatformLogger.Bridge logger,
681            TestLoggerFinder.LoggerImpl sink) {
682
683        if (loggerDescMap.get(logger) == null) {
684            throw new RuntimeException("Test bug: Missing description");
685        }
686        System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]");
687
688        Foo foo = new Foo();
689        String fooMsg = foo.toString();
690        System.out.println("\tlogger.log(messageLevel, fooMsg)");
691        System.out.println("\tlogger.<level>(fooMsg)");
692        for (Level loggerLevel : Level.values()) {
693            sink.level = loggerLevel;
694            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
695                String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
696                        + loggerLevel+", messageLevel="+messageLevel;
697                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
698                TestLoggerFinder.LogEvent expected =
699                        TestLoggerFinder.LogEvent.of(
700                            sequencer.get(),
701                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
702                            name, expectedMessageLevel, loggerBundle,
703                            fooMsg, null, (Throwable)null, (Object[])null);
704                logger.log(messageLevel, fooMsg);
705                checkLogEvent(provider, desc, expected);
706            }
707        }
708
709        Supplier<String> supplier = new Supplier<String>() {
710            @Override
711            public String get() {
712                return this.toString();
713            }
714        };
715        System.out.println("\tlogger.log(messageLevel, supplier)");
716        System.out.println("\tlogger.<level>(supplier)");
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, supplier): 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, (ResourceBundle) null,
728                            null, supplier, (Throwable)null, (Object[])null);
729                logger.log(messageLevel, supplier);
730                checkLogEvent(provider, desc, expected);
731            }
732        }
733
734        String format = "two params [{1} {2}]";
735        Object arg1 = foo;
736        Object arg2 = fooMsg;
737        System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
738        for (Level loggerLevel : Level.values()) {
739            sink.level = loggerLevel;
740            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
741                String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
742                        + loggerLevel+", messageLevel="+messageLevel;
743                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
744                TestLoggerFinder.LogEvent expected =
745                        TestLoggerFinder.LogEvent.of(
746                            sequencer.get(),
747                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
748                            name, expectedMessageLevel, loggerBundle,
749                            format, null, (Throwable)null, arg1, arg2);
750                logger.log(messageLevel, format, arg1, arg2);
751                checkLogEvent(provider, desc, expected);
752            }
753        }
754
755        Throwable thrown = new Exception("OK: log me!");
756        System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
757        for (Level loggerLevel : Level.values()) {
758            sink.level = loggerLevel;
759            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
760                String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
761                        + loggerLevel+", messageLevel="+messageLevel;
762                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
763                TestLoggerFinder.LogEvent expected =
764                        TestLoggerFinder.LogEvent.of(
765                            sequencer.get(),
766                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
767                            name, expectedMessageLevel, loggerBundle,
768                            fooMsg, null, thrown, (Object[])null);
769                logger.log(messageLevel, fooMsg, thrown);
770                checkLogEvent(provider, desc, expected);
771            }
772        }
773
774        System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
775        for (Level loggerLevel : Level.values()) {
776            sink.level = loggerLevel;
777            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
778                String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
779                        + loggerLevel+", messageLevel="+messageLevel;
780                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
781                TestLoggerFinder.LogEvent expected =
782                        TestLoggerFinder.LogEvent.of(
783                            sequencer.get(),
784                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
785                            name, expectedMessageLevel, (ResourceBundle)null,
786                            null, supplier, thrown, (Object[])null);
787                logger.log(messageLevel, thrown, supplier);
788                checkLogEvent(provider, desc, expected);
789            }
790        }
791
792        String sourceClass = "blah.Blah";
793        String sourceMethod = "blih";
794        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
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, fooMsg): 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 =
803                    isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
804                        TestLoggerFinder.LogEvent.of(
805                            sequencer.get(),
806                            isLoggable,
807                            name, expectedMessageLevel, loggerBundle,
808                            logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
809                            null, (Throwable)null, (Object[]) null) : null;
810                logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
811                checkLogEvent(provider, desc, expected);
812            }
813        }
814
815        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
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, supplier): 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 = isLoggable ?
824                    TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
825                        TestLoggerFinder.LogEvent.of(
826                            sequencer.get(),
827                            isLoggable,
828                            name, expectedMessageLevel, null, null,
829                            logpMessage(null, sourceClass, sourceMethod, supplier),
830                            (Throwable)null, (Object[]) null)) : null;
831                logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
832                checkLogEvent(provider, desc, expected);
833            }
834        }
835
836        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
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, format, arg1, arg2): 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, format),
851                            null, (Throwable)null, arg1, arg2) : null;
852                logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
853                checkLogEvent(provider, desc, expected);
854            }
855        }
856
857        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
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, fooMsg, thrown): 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 =
866                    isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ?
867                        TestLoggerFinder.LogEvent.of(
868                            sequencer.get(),
869                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
870                            name, expectedMessageLevel, loggerBundle,
871                            logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
872                            null, thrown, (Object[])null) : null;
873                logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
874                checkLogEvent(provider, desc, expected);
875            }
876        }
877
878        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
879        for (Level loggerLevel : Level.values()) {
880            sink.level = loggerLevel;
881            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
882                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
883                        + loggerLevel+", messageLevel="+messageLevel;
884                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
885                boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
886                TestLoggerFinder.LogEvent expected = isLoggable ?
887                    TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
888                        TestLoggerFinder.LogEvent.of(
889                            sequencer.get(),
890                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
891                            name, expectedMessageLevel, null, null,
892                            logpMessage(null, sourceClass, sourceMethod, supplier),
893                            thrown, (Object[])null)) : null;
894                logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
895                checkLogEvent(provider, desc, expected);
896            }
897        }
898
899        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
900        System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
901        for (Level loggerLevel : Level.values()) {
902            sink.level = loggerLevel;
903            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
904                String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
905                        + loggerLevel+", messageLevel="+messageLevel;
906                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
907                TestLoggerFinder.LogEvent expected =
908                        TestLoggerFinder.LogEvent.of(
909                            sequencer.get(),
910                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
911                            name, expectedMessageLevel, bundle,
912                            format, null, (Throwable)null, arg1, arg2);
913                logger.logrb(messageLevel, bundle, format, arg1, arg2);
914                checkLogEvent(provider, desc, expected);
915            }
916        }
917
918        System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
919        for (Level loggerLevel : Level.values()) {
920            sink.level = loggerLevel;
921            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
922                String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
923                        + loggerLevel+", messageLevel="+messageLevel;
924                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
925                TestLoggerFinder.LogEvent expected =
926                        TestLoggerFinder.LogEvent.of(
927                            sequencer.get(),
928                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
929                            name, expectedMessageLevel, bundle,
930                            fooMsg, null, thrown, (Object[])null);
931                logger.logrb(messageLevel, bundle, fooMsg, thrown);
932                checkLogEvent(provider, desc, expected);
933            }
934        }
935
936        System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
937        for (Level loggerLevel : Level.values()) {
938            sink.level = loggerLevel;
939            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
940                String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
941                        + loggerLevel+", messageLevel="+messageLevel;
942                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
943                TestLoggerFinder.LogEvent expected =
944                        TestLoggerFinder.LogEvent.of(
945                            sequencer.get(),
946                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
947                            name, expectedMessageLevel, bundle,
948                            format, null, (Throwable)null, arg1, arg2);
949                logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
950                checkLogEvent(provider, desc, expected);
951            }
952        }
953
954        System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
955        for (Level loggerLevel : Level.values()) {
956            sink.level = loggerLevel;
957            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
958                String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
959                        + loggerLevel+", messageLevel="+messageLevel;
960                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
961                TestLoggerFinder.LogEvent expected =
962                        TestLoggerFinder.LogEvent.of(
963                            sequencer.get(),
964                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
965                            name, expectedMessageLevel, bundle,
966                            fooMsg, null, thrown, (Object[])null);
967                logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
968                checkLogEvent(provider, desc, expected);
969            }
970        }
971    }
972
973    final static class PermissionsBuilder {
974        final Permissions perms;
975        public PermissionsBuilder() {
976            this(new Permissions());
977        }
978        public PermissionsBuilder(Permissions perms) {
979            this.perms = perms;
980        }
981        public PermissionsBuilder add(Permission p) {
982            perms.add(p);
983            return this;
984        }
985        public PermissionsBuilder addAll(PermissionCollection col) {
986            if (col != null) {
987                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
988                    perms.add(e.nextElement());
989                }
990            }
991            return this;
992        }
993        public Permissions toPermissions() {
994            final PermissionsBuilder builder = new PermissionsBuilder();
995            builder.addAll(perms);
996            return builder.perms;
997        }
998    }
999
1000    public static class SimplePolicy extends Policy {
1001        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
1002        final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
1003        final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
1004
1005        final Permissions permissions;
1006        final Permissions allPermissions;
1007        final ThreadLocal<AtomicBoolean> allowControl;
1008        final ThreadLocal<AtomicBoolean> allowAccess;
1009        final ThreadLocal<AtomicBoolean> allowAll;
1010        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
1011                ThreadLocal<AtomicBoolean> allowAccess,
1012                ThreadLocal<AtomicBoolean> allowAll) {
1013            this.allowControl = allowControl;
1014            this.allowAccess = allowAccess;
1015            this.allowAll = allowAll;
1016            permissions = new Permissions();
1017            allPermissions = new PermissionsBuilder()
1018                    .add(new java.security.AllPermission())
1019                    .toPermissions();
1020        }
1021
1022        Permissions getPermissions() {
1023            if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
1024                PermissionsBuilder builder =  new PermissionsBuilder()
1025                        .addAll(permissions);
1026                if (allowControl.get().get()) {
1027                    builder.add(CONTROL);
1028                }
1029                if (allowAccess.get().get()) {
1030                    builder.add(ACCESS_LOGGER);
1031                    builder.add(ACCESS_LOGGING);
1032                }
1033                if (allowAll.get().get()) {
1034                    builder.addAll(allPermissions);
1035                }
1036                return builder.toPermissions();
1037            }
1038            return permissions;
1039        }
1040
1041        @Override
1042        public boolean implies(ProtectionDomain domain, Permission permission) {
1043            return getPermissions().implies(permission);
1044        }
1045
1046        @Override
1047        public PermissionCollection getPermissions(CodeSource codesource) {
1048            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1049        }
1050
1051        @Override
1052        public PermissionCollection getPermissions(ProtectionDomain domain) {
1053            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
1054        }
1055    }
1056}
1057