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.io.ByteArrayOutputStream;
24import java.io.IOException;
25import java.io.PrintStream;
26import java.io.UncheckedIOException;
27import java.security.AccessControlException;
28import java.security.CodeSource;
29import java.security.Permission;
30import java.security.PermissionCollection;
31import java.security.Permissions;
32import java.security.Policy;
33import java.security.ProtectionDomain;
34import java.util.Collections;
35import java.util.Enumeration;
36import java.util.HashMap;
37import java.util.Map;
38import java.util.ResourceBundle;
39import java.util.stream.Stream;
40import java.util.concurrent.ConcurrentHashMap;
41import java.util.concurrent.atomic.AtomicBoolean;
42import java.util.concurrent.atomic.AtomicLong;
43import java.util.function.Supplier;
44import java.lang.System.LoggerFinder;
45import java.lang.System.Logger;
46import java.lang.System.Logger.Level;
47import java.lang.reflect.InvocationTargetException;
48import java.lang.reflect.Method;
49import java.security.AccessController;
50import java.security.PrivilegedAction;
51import java.util.Locale;
52import java.util.Objects;
53import java.util.concurrent.atomic.AtomicReference;
54import java.util.function.Function;
55import jdk.internal.logger.DefaultLoggerFinder;
56import jdk.internal.logger.SimpleConsoleLogger;
57import sun.util.logging.PlatformLogger;
58
59/**
60 * @test
61 * @bug     8140364 8145686
62 * @summary JDK implementation specific unit test for the base DefaultLoggerFinder.
63 *          Tests the behavior of DefaultLoggerFinder and SimpleConsoleLogger
64 *          implementation.
65 * @modules java.base/sun.util.logging
66 *          java.base/jdk.internal.logger
67 * @build AccessSystemLogger BaseDefaultLoggerFinderTest CustomSystemClassLoader
68 * @run  driver AccessSystemLogger
69 * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOSECURITY
70 * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOPERMISSIONS
71 * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHPERMISSIONS
72 * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHCUSTOMWRAPPERS
73 * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHREFLECTION
74 * @author danielfuchs
75 */
76public class BaseDefaultLoggerFinderTest {
77
78    static final RuntimePermission LOGGERFINDER_PERMISSION =
79                new RuntimePermission("loggerFinder");
80    final static boolean VERBOSE = false;
81    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
82        @Override
83        protected AtomicBoolean initialValue() {
84            return  new AtomicBoolean(false);
85        }
86    };
87    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
88        @Override
89        protected AtomicBoolean initialValue() {
90            return  new AtomicBoolean(false);
91        }
92    };
93
94    final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
95    static final Class<?>[] providerClass;
96    static {
97        try {
98            providerClass = new Class<?>[] {
99                ClassLoader.getSystemClassLoader().loadClass("BaseDefaultLoggerFinderTest$BaseLoggerFinder"),
100            };
101        } catch (ClassNotFoundException ex) {
102            throw new ExceptionInInitializerError(ex);
103        }
104    }
105
106    /**
107     * What our test provider needs to implement.
108     */
109    public static interface TestLoggerFinder {
110        public final static AtomicBoolean fails = new AtomicBoolean();
111        public final static AtomicReference<String> conf = new AtomicReference<>("");
112        public final static AtomicLong sequencer = new AtomicLong();
113
114
115        public Logger getLogger(String name, Module caller);
116        public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
117        void setLevel(Logger logger, Level level, Module caller);
118        void setLevel(Logger logger, PlatformLogger.Level level, Module caller);
119        PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger);
120    }
121
122    public static class BaseLoggerFinder extends DefaultLoggerFinder implements TestLoggerFinder {
123
124        static final RuntimePermission LOGGERFINDER_PERMISSION =
125                    new RuntimePermission("loggerFinder");
126        public BaseLoggerFinder() {
127            if (fails.get()) {
128                throw new RuntimeException("Simulate exception while loading provider");
129            }
130        }
131
132        @Override
133        public void setLevel(Logger logger, Level level, Module caller) {
134            PrivilegedAction<Void> pa = () -> {
135                setLevel(logger, PlatformLogger.toPlatformLevel(level), caller);
136                return null;
137            };
138            AccessController.doPrivileged(pa);
139        }
140
141        @Override
142        public void setLevel(Logger logger, PlatformLogger.Level level, Module caller) {
143            PrivilegedAction<Logger> pa = () -> demandLoggerFor(logger.getName(), caller);
144            Logger impl = AccessController.doPrivileged(pa);
145            SimpleConsoleLogger.class.cast(impl)
146                    .getLoggerConfiguration()
147                    .setPlatformLevel(level);
148        }
149
150        @Override
151        public PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger) {
152            PrivilegedAction<PlatformLogger.Bridge> pa = () ->
153                PlatformLogger.Bridge.convert(logger);
154            return AccessController.doPrivileged(pa);
155        }
156
157    }
158
159    public static class MyBundle extends ResourceBundle {
160
161        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
162
163        @Override
164        protected Object handleGetObject(String key) {
165            if (key.contains(" (translated)")) {
166                throw new RuntimeException("Unexpected key: " + key);
167            }
168            return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
169        }
170
171        @Override
172        public Enumeration<String> getKeys() {
173            return Collections.enumeration(map.keySet());
174        }
175
176    }
177    public static class MyLoggerBundle extends MyBundle {
178
179    }
180
181    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS,
182                           WITHCUSTOMWRAPPERS, WITHREFLECTION};
183
184    static void setSecurityManager() {
185        if (System.getSecurityManager() == null) {
186            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
187            System.setSecurityManager(new SecurityManager());
188        }
189    }
190
191    static TestLoggerFinder getLoggerFinder(Class<?> expectedClass) {
192        LoggerFinder provider = null;
193        try {
194            TestLoggerFinder.sequencer.incrementAndGet();
195            provider = LoggerFinder.getLoggerFinder();
196        } catch(AccessControlException a) {
197            throw a;
198        }
199        ErrorStream.errorStream.store();
200        System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
201        expectedClass.cast(provider);
202        return TestLoggerFinder.class.cast(provider);
203    }
204
205
206    static class ErrorStream extends PrintStream {
207
208        static AtomicBoolean forward = new AtomicBoolean();
209        ByteArrayOutputStream out;
210        String saved = "";
211        public ErrorStream(ByteArrayOutputStream out) {
212            super(out);
213            this.out = out;
214        }
215
216        @Override
217        public void write(int b) {
218            super.write(b);
219            if (forward.get()) err.write(b);
220        }
221
222        @Override
223        public void write(byte[] b) throws IOException {
224            super.write(b);
225            if (forward.get()) err.write(b);
226        }
227
228        @Override
229        public void write(byte[] buf, int off, int len) {
230            super.write(buf, off, len);
231            if (forward.get()) err.write(buf, off, len);
232        }
233
234        public String peek() {
235            flush();
236            return out.toString();
237        }
238
239        public String drain() {
240            flush();
241            String res = out.toString();
242            out.reset();
243            return res;
244        }
245
246        public void store() {
247            flush();
248            saved = out.toString();
249            out.reset();
250        }
251
252        public void restore() {
253            out.reset();
254            try {
255                out.write(saved.getBytes());
256            } catch(IOException io) {
257                throw new UncheckedIOException(io);
258            }
259        }
260
261        static final PrintStream err = System.err;
262        static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
263    }
264
265    private static StringBuilder appendProperty(StringBuilder b, String name) {
266        String value = System.getProperty(name);
267        if (value == null) return b;
268        return b.append(name).append("=").append(value).append('\n');
269    }
270
271    static class CustomLoggerWrapper implements Logger {
272
273        Logger impl;
274        public CustomLoggerWrapper(Logger logger) {
275            this.impl = Objects.requireNonNull(logger);
276        }
277
278
279        @Override
280        public String getName() {
281            return impl.getName();
282        }
283
284        @Override
285        public boolean isLoggable(Level level) {
286            return impl.isLoggable(level);
287        }
288
289        @Override
290        public void log(Level level, ResourceBundle rb, String string, Throwable thrwbl) {
291            impl.log(level, rb, string, thrwbl);
292        }
293
294        @Override
295        public void log(Level level, ResourceBundle rb, String string, Object... os) {
296            impl.log(level, rb, string, os);
297        }
298
299        @Override
300        public void log(Level level, Object o) {
301            impl.log(level, o);
302        }
303
304        @Override
305        public void log(Level level, String string) {
306            impl.log(level, string);
307        }
308
309        @Override
310        public void log(Level level, Supplier<String> splr) {
311            impl.log(level, splr);
312        }
313
314        @Override
315        public void log(Level level, String string, Object... os) {
316           impl.log(level, string, os);
317        }
318
319        @Override
320        public void log(Level level, String string, Throwable thrwbl) {
321            impl.log(level, string, thrwbl);
322        }
323
324        @Override
325        public void log(Level level, Supplier<String> splr, Throwable thrwbl) {
326            Logger.super.log(level, splr, thrwbl);
327        }
328
329        @Override
330        public String toString() {
331            return super.toString() + "(impl=" + impl + ")";
332        }
333
334    }
335    /**
336     * The ReflectionLoggerWrapper additionally makes it possible to verify
337     * that code which use reflection to call System.Logger will be skipped
338     * when looking for the calling method.
339     */
340    static class ReflectionLoggerWrapper implements Logger {
341
342        Logger impl;
343        public ReflectionLoggerWrapper(Logger logger) {
344            this.impl = Objects.requireNonNull(logger);
345        }
346
347        private Object invoke(Method m, Object... params) {
348            try {
349                return m.invoke(impl, params);
350            } catch (IllegalAccessException | IllegalArgumentException
351                    | InvocationTargetException ex) {
352                throw new RuntimeException(ex);
353            }
354        }
355
356        @Override
357        public String getName() {
358            return impl.getName();
359        }
360
361        @Override
362        public boolean isLoggable(Level level) {
363            return impl.isLoggable(level);
364        }
365
366        @Override
367        public void log(Level level, ResourceBundle rb, String string, Throwable thrwbl) {
368            try {
369                invoke(System.Logger.class.getMethod(
370                        "log", Level.class, ResourceBundle.class, String.class, Throwable.class),
371                        level, rb, string, thrwbl);
372            } catch (NoSuchMethodException ex) {
373                throw new RuntimeException(ex);
374            }
375        }
376
377        @Override
378        public void log(Level level, ResourceBundle rb, String string, Object... os) {
379            try {
380                invoke(System.Logger.class.getMethod(
381                        "log", Level.class, ResourceBundle.class, String.class, Object[].class),
382                        level, rb, string, os);
383            } catch (NoSuchMethodException ex) {
384                throw new RuntimeException(ex);
385            }
386        }
387
388        @Override
389        public void log(Level level, String string) {
390            try {
391                invoke(System.Logger.class.getMethod(
392                        "log", Level.class, String.class),
393                        level, string);
394            } catch (NoSuchMethodException ex) {
395                throw new RuntimeException(ex);
396            }
397        }
398
399        @Override
400        public void log(Level level, String string, Object... os) {
401            try {
402                invoke(System.Logger.class.getMethod(
403                        "log", Level.class, String.class, Object[].class),
404                        level, string, os);
405            } catch (NoSuchMethodException ex) {
406                throw new RuntimeException(ex);
407            }
408        }
409
410        @Override
411        public void log(Level level, String string, Throwable thrwbl) {
412            try {
413                invoke(System.Logger.class.getMethod(
414                        "log", Level.class, String.class, Throwable.class),
415                        level, string, thrwbl);
416            } catch (NoSuchMethodException ex) {
417                throw new RuntimeException(ex);
418            }
419        }
420
421
422        @Override
423        public String toString() {
424            return super.toString() + "(impl=" + impl + ")";
425        }
426
427    }
428
429
430    public static void main(String[] args) {
431        if (args.length == 0) {
432            args = new String[] {
433                //"NOSECURITY",
434                "NOPERMISSIONS",
435                "WITHPERMISSIONS",
436                "WITHCUSTOMWRAPPERS",
437                "WITHREFLECTION"
438            };
439        }
440        Locale.setDefault(Locale.ENGLISH);
441        System.setErr(ErrorStream.errorStream);
442        //System.setProperty("jdk.logger.finder.error", "ERROR");
443        //System.setProperty("jdk.logger.finder.singleton", "true");
444        //System.setProperty("test.fails", "true");
445        TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
446        StringBuilder c = new StringBuilder();
447        appendProperty(c, "jdk.logger.packages");
448        appendProperty(c, "jdk.logger.finder.error");
449        appendProperty(c, "jdk.logger.finder.singleton");
450        appendProperty(c, "test.fails");
451        TestLoggerFinder.conf.set(c.toString());
452        try {
453            test(args);
454        } finally {
455            try {
456                System.setErr(ErrorStream.err);
457            } catch (Error | RuntimeException x) {
458                x.printStackTrace(ErrorStream.err);
459            }
460        }
461    }
462
463
464    public static void test(String[] args) {
465
466        final Class<?> expectedClass = jdk.internal.logger.DefaultLoggerFinder.class;
467
468        System.out.println("Declared provider class: " + providerClass[0]
469                + "[" + providerClass[0].getClassLoader() + "]");
470
471        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
472            TestLoggerFinder provider;
473            ErrorStream.errorStream.restore();
474            switch (testCase) {
475                case NOSECURITY:
476                    System.out.println("\n*** Without Security Manager\n");
477                    System.out.println(TestLoggerFinder.conf.get());
478                    provider = getLoggerFinder(expectedClass);
479                    if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
480                        throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
481                    }
482                    test(provider, true);
483                    System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
484                    break;
485                case NOPERMISSIONS:
486                    System.out.println("\n*** With Security Manager, without permissions\n");
487                    System.out.println(TestLoggerFinder.conf.get());
488                    setSecurityManager();
489                    try {
490                        provider = getLoggerFinder(expectedClass);
491                        throw new RuntimeException("Expected exception not raised");
492                    } catch (AccessControlException x) {
493                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
494                            throw new RuntimeException("Unexpected permission check", x);
495                        }
496                        final boolean control = allowControl.get().get();
497                        try {
498                            allowControl.get().set(true);
499                            provider = getLoggerFinder(expectedClass);
500                            if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
501                                throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
502                            }
503                        } finally {
504                            allowControl.get().set(control);
505                        }
506                    }
507                    test(provider, false);
508                    System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
509                    break;
510                case WITHPERMISSIONS:
511                    System.out.println("\n*** With Security Manager, with control permission\n");
512                    System.out.println(TestLoggerFinder.conf.get());
513                    setSecurityManager();
514                    final boolean control = allowControl.get().get();
515                    try {
516                        allowControl.get().set(true);
517                        provider = getLoggerFinder(expectedClass);
518                        if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
519                            throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
520                        }
521                        test(provider, true);
522                    } finally {
523                        allowControl.get().set(control);
524                    }
525                    break;
526                case WITHCUSTOMWRAPPERS:
527                    System.out.println("\n*** With Security Manager, with control permission and custom Wrapper\n");
528                    System.out.println(TestLoggerFinder.conf.get());
529                    setSecurityManager();
530                    final boolean previous = allowControl.get().get();
531                    try {
532                        allowControl.get().set(true);
533                        provider = getLoggerFinder(expectedClass);
534                        if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
535                            throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
536                        }
537                        test(provider, CustomLoggerWrapper::new, true);
538                    } finally {
539                        allowControl.get().set(previous);
540                    }
541                    break;
542                case WITHREFLECTION:
543                    System.out.println("\n*** With Security Manager,"
544                            + " with control permission,"
545                            + " using reflection while logging\n");
546                    System.out.println(TestLoggerFinder.conf.get());
547                    setSecurityManager();
548                    final boolean before = allowControl.get().get();
549                    try {
550                        allowControl.get().set(true);
551                        provider = getLoggerFinder(expectedClass);
552                        if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
553                            throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
554                        }
555                        test(provider, ReflectionLoggerWrapper::new, true);
556                    } finally {
557                        allowControl.get().set(before);
558                    }
559                    break;
560                default:
561                    throw new RuntimeException("Unknown test case: " + testCase);
562            }
563        });
564        System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
565    }
566
567    public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
568        test(provider, Function.identity(), hasRequiredPermissions);
569    }
570
571    public static void test(TestLoggerFinder provider, Function<Logger, Logger> wrapper, boolean hasRequiredPermissions) {
572
573        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
574        final Map<Logger, String> loggerDescMap = new HashMap<>();
575
576        System.Logger sysLogger = wrapper.apply(accessSystemLogger.getLogger("foo"));
577        loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
578        System.Logger localizedSysLogger = wrapper.apply(accessSystemLogger.getLogger("fox", loggerBundle));
579        loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
580        System.Logger appLogger = wrapper.apply(System.getLogger("bar"));
581        loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
582        System.Logger localizedAppLogger = wrapper.apply(System.getLogger("baz", loggerBundle));
583        loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
584
585        testLogger(provider, loggerDescMap, "foo", null, sysLogger, accessSystemLogger.getClass());
586        testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger, accessSystemLogger.getClass());
587        testLogger(provider, loggerDescMap, "foo", null, appLogger, BaseDefaultLoggerFinderTest.class);
588        testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger, BaseDefaultLoggerFinderTest.class);
589    }
590
591    public static class Foo {
592
593    }
594
595    static void verbose(String msg) {
596       if (VERBOSE) {
597           System.out.println(msg);
598       }
599    }
600
601    // Calls the 8 methods defined on Logger and verify the
602    // parameters received by the underlying TestProvider.LoggerImpl
603    // logger.
604    private static void testLogger(TestLoggerFinder provider,
605            Map<Logger, String> loggerDescMap,
606            String name,
607            ResourceBundle loggerBundle,
608            Logger logger,
609            Class<?> callerClass) {
610
611        System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
612        AtomicLong sequencer = TestLoggerFinder.sequencer;
613
614        Module caller = callerClass.getModule();
615        Foo foo = new Foo();
616        String fooMsg = foo.toString();
617        for (Level loggerLevel : Level.values()) {
618            provider.setLevel(logger, loggerLevel, caller);
619            for (Level messageLevel : Level.values()) {
620                ErrorStream.errorStream.drain();
621                String desc = "logger.log(messageLevel, foo): loggerLevel="
622                        + loggerLevel+", messageLevel="+messageLevel;
623                sequencer.incrementAndGet();
624                logger.log(messageLevel, foo);
625                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
626                    if (!ErrorStream.errorStream.peek().isEmpty()) {
627                        throw new RuntimeException("unexpected event in queue for "
628                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
629                    }
630                } else {
631                    String logged = ErrorStream.errorStream.drain();
632                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
633                        || !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
634                        throw new RuntimeException("mismatch for " + desc
635                                + "\n\texpected:" + "\n<<<<\n"
636                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
637                                + messageLevel.getName() + " " + fooMsg
638                                + "\n>>>>"
639                                + "\n\t  actual:"
640                                + "\n<<<<\n" + logged + ">>>>\n");
641                    } else {
642                        verbose("Got expected results for "
643                                + desc + "\n<<<<\n" + logged + ">>>>\n");
644                    }
645                }
646            }
647        }
648
649        String msg = "blah";
650        for (Level loggerLevel : Level.values()) {
651            provider.setLevel(logger, loggerLevel, caller);
652            for (Level messageLevel : Level.values()) {
653                String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
654                        + loggerLevel+", messageLevel="+messageLevel;
655                sequencer.incrementAndGet();
656                logger.log(messageLevel, msg);
657                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
658                    if (!ErrorStream.errorStream.peek().isEmpty()) {
659                        throw new RuntimeException("unexpected event in queue for "
660                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
661                    }
662                } else {
663                    String logged = ErrorStream.errorStream.drain();
664                    String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
665                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
666                        || !logged.contains(messageLevel.getName() + ": " + msgText)) {
667                        throw new RuntimeException("mismatch for " + desc
668                                + "\n\texpected:" + "\n<<<<\n"
669                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
670                                + messageLevel.getName() + " " + msgText
671                                + "\n>>>>"
672                                + "\n\t  actual:"
673                                + "\n<<<<\n" + logged + ">>>>\n");
674                    } else {
675                        verbose("Got expected results for "
676                                + desc + "\n<<<<\n" + logged + ">>>>\n");
677                    }
678                }
679            }
680        }
681
682        Supplier<String> fooSupplier = new Supplier<String>() {
683            @Override
684            public String get() {
685                return this.toString();
686            }
687        };
688
689        for (Level loggerLevel : Level.values()) {
690            provider.setLevel(logger, loggerLevel, caller);
691            for (Level messageLevel : Level.values()) {
692                String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
693                        + loggerLevel+", messageLevel="+messageLevel;
694                sequencer.incrementAndGet();
695                logger.log(messageLevel, fooSupplier);
696                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
697                    if (!ErrorStream.errorStream.peek().isEmpty()) {
698                        throw new RuntimeException("unexpected event in queue for "
699                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
700                    }
701                } else {
702                    String logged = ErrorStream.errorStream.drain();
703                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
704                        || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
705                        throw new RuntimeException("mismatch for " + desc
706                                + "\n\texpected:" + "\n<<<<\n"
707                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
708                                + messageLevel.getName() + " " + fooSupplier.get()
709                                + "\n>>>>"
710                                + "\n\t  actual:"
711                                + "\n<<<<\n" + logged + ">>>>\n");
712                    } else {
713                        verbose("Got expected results for "
714                                + desc + "\n<<<<\n" + logged + ">>>>\n");
715                    }
716                }
717            }
718        }
719
720
721        String format = "two params [{1} {2}]";
722        Object arg1 = foo;
723        Object arg2 = msg;
724        for (Level loggerLevel : Level.values()) {
725            provider.setLevel(logger, loggerLevel, caller);
726            for (Level messageLevel : Level.values()) {
727                String desc = "logger.log(messageLevel, format, params...): loggerLevel="
728                        + loggerLevel+", messageLevel="+messageLevel;
729                sequencer.incrementAndGet();
730                logger.log(messageLevel, format, foo, msg);
731                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
732                    if (!ErrorStream.errorStream.peek().isEmpty()) {
733                        throw new RuntimeException("unexpected event in queue for "
734                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
735                    }
736                } else {
737                    String logged = ErrorStream.errorStream.drain();
738                    String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
739                    String text = java.text.MessageFormat.format(msgFormat, foo, msg);
740                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
741                        || !logged.contains(messageLevel.getName() + ": " + text)) {
742                        throw new RuntimeException("mismatch for " + desc
743                                + "\n\texpected:" + "\n<<<<\n"
744                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
745                                + messageLevel.getName() + " " + text
746                                + "\n>>>>"
747                                + "\n\t  actual:"
748                                + "\n<<<<\n" + logged + ">>>>\n");
749                    } else {
750                        verbose("Got expected results for "
751                                + desc + "\n<<<<\n" + logged + ">>>>\n");
752                    }
753                }
754            }
755        }
756
757        Throwable thrown = new Exception("OK: log me!");
758        for (Level loggerLevel : Level.values()) {
759            provider.setLevel(logger, loggerLevel, caller);
760            for (Level messageLevel : Level.values()) {
761                String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
762                        + loggerLevel+", messageLevel="+messageLevel;
763                sequencer.incrementAndGet();
764                logger.log(messageLevel, msg, thrown);
765                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
766                    if (!ErrorStream.errorStream.peek().isEmpty()) {
767                        throw new RuntimeException("unexpected event in queue for "
768                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
769                    }
770                } else {
771                    String logged = ErrorStream.errorStream.drain();
772                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
773                    thrown.printStackTrace(new PrintStream(baos));
774                    String text = baos.toString();
775                    String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
776                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
777                        || !logged.contains(messageLevel.getName() + ": " + msgText)
778                        || !logged.contains(text)) {
779                        throw new RuntimeException("mismatch for " + desc
780                                + "\n\texpected:" + "\n<<<<\n"
781                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
782                                + messageLevel.getName() + " " + msgText +"\n"
783                                + text
784                                + ">>>>"
785                                + "\n\t  actual:"
786                                + "\n<<<<\n" + logged + ">>>>\n");
787                    } else {
788                        verbose("Got expected results for "
789                                + desc + "\n<<<<\n" + logged + ">>>>\n");
790                    }
791                }
792            }
793        }
794
795
796        for (Level loggerLevel : Level.values()) {
797            provider.setLevel(logger, loggerLevel, caller);
798            for (Level messageLevel : Level.values()) {
799                String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
800                        + loggerLevel+", messageLevel="+messageLevel;
801                sequencer.incrementAndGet();
802                logger.log(messageLevel, fooSupplier, thrown);
803                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
804                    if (!ErrorStream.errorStream.peek().isEmpty()) {
805                        throw new RuntimeException("unexpected event in queue for "
806                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
807                    }
808                } else {
809                    String logged = ErrorStream.errorStream.drain();
810                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
811                    thrown.printStackTrace(new PrintStream(baos));
812                    String text = baos.toString();
813                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
814                        || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
815                        || !logged.contains(text)) {
816                        throw new RuntimeException("mismatch for " + desc
817                                + "\n\texpected:" + "\n<<<<\n"
818                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
819                                + messageLevel.getName() + " " + fooSupplier.get() +"\n"
820                                + text
821                                + ">>>>"
822                                + "\n\t  actual:"
823                                + "\n<<<<\n" + logged + ">>>>\n");
824                    } else {
825                        verbose("Got expected results for "
826                                + desc + "\n<<<<\n" + logged + ">>>>\n");
827                    }
828                }
829            }
830        }
831
832        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
833        for (Level loggerLevel : Level.values()) {
834            provider.setLevel(logger, loggerLevel, caller);
835            for (Level messageLevel : Level.values()) {
836                String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
837                        + loggerLevel+", messageLevel="+messageLevel;
838                sequencer.incrementAndGet();
839                logger.log(messageLevel, bundle, format, foo, msg);
840                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
841                    if (!ErrorStream.errorStream.peek().isEmpty()) {
842                        throw new RuntimeException("unexpected event in queue for "
843                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
844                    }
845                } else {
846                    String logged = ErrorStream.errorStream.drain();
847                    String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
848                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
849                        || !logged.contains(messageLevel.getName() + ": " + text)) {
850                        throw new RuntimeException("mismatch for " + desc
851                                + "\n\texpected:" + "\n<<<<\n"
852                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
853                                + messageLevel.getName() + " " + text
854                                + "\n>>>>"
855                                + "\n\t  actual:"
856                                + "\n<<<<\n" + logged + ">>>>\n");
857                    } else {
858                        verbose("Got expected results for "
859                                + desc + "\n<<<<\n" + logged + ">>>>\n");
860                    }
861                }
862            }
863        }
864
865        for (Level loggerLevel : Level.values()) {
866            provider.setLevel(logger, loggerLevel, caller);
867            for (Level messageLevel : Level.values()) {
868                String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
869                        + loggerLevel+", messageLevel="+messageLevel;
870                sequencer.incrementAndGet();
871                logger.log(messageLevel, bundle, msg, thrown);
872                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
873                    if (!ErrorStream.errorStream.peek().isEmpty()) {
874                        throw new RuntimeException("unexpected event in queue for "
875                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
876                    }
877                } else {
878                    String logged = ErrorStream.errorStream.drain();
879                    String textMsg = bundle.getString(msg);
880                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
881                    thrown.printStackTrace(new PrintStream(baos));
882                    String text = baos.toString();
883                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
884                        || !logged.contains(messageLevel.getName() + ": " + textMsg)
885                        || !logged.contains(text)) {
886                        throw new RuntimeException("mismatch for " + desc
887                                + "\n\texpected:" + "\n<<<<\n"
888                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
889                                + messageLevel.getName() + " " + textMsg +"\n"
890                                + text
891                                + ">>>>"
892                                + "\n\t  actual:"
893                                + "\n<<<<\n" + logged + ">>>>\n");
894                    } else {
895                        verbose("Got expected results for "
896                                + desc + "\n<<<<\n" + logged + ">>>>\n");
897                    }
898                }
899            }
900        }
901
902    }
903
904    final static class PermissionsBuilder {
905        final Permissions perms;
906        public PermissionsBuilder() {
907            this(new Permissions());
908        }
909        public PermissionsBuilder(Permissions perms) {
910            this.perms = perms;
911        }
912        public PermissionsBuilder add(Permission p) {
913            perms.add(p);
914            return this;
915        }
916        public PermissionsBuilder addAll(PermissionCollection col) {
917            if (col != null) {
918                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
919                    perms.add(e.nextElement());
920                }
921            }
922            return this;
923        }
924        public Permissions toPermissions() {
925            final PermissionsBuilder builder = new PermissionsBuilder();
926            builder.addAll(perms);
927            return builder.perms;
928        }
929    }
930
931    public static class SimplePolicy extends Policy {
932        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
933        final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
934
935        final Permissions permissions;
936        final ThreadLocal<AtomicBoolean> allowControl;
937        final ThreadLocal<AtomicBoolean> allowAccess;
938        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
939            this.allowControl = allowControl;
940            this.allowAccess = allowAccess;
941            permissions = new Permissions();
942            permissions.add(new RuntimePermission("setIO"));
943        }
944
945        Permissions getPermissions() {
946            if (allowControl.get().get() || allowAccess.get().get()) {
947                PermissionsBuilder builder =  new PermissionsBuilder()
948                        .addAll(permissions);
949                if (allowControl.get().get()) {
950                    builder.add(CONTROL);
951                }
952                if (allowAccess.get().get()) {
953                    builder.add(ACCESS);
954                }
955                return builder.toPermissions();
956            }
957            return permissions;
958        }
959
960        @Override
961        public boolean implies(ProtectionDomain domain, Permission permission) {
962            return getPermissions().implies(permission);
963        }
964
965        @Override
966        public PermissionCollection getPermissions(CodeSource codesource) {
967            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
968        }
969
970        @Override
971        public PermissionCollection getPermissions(ProtectionDomain domain) {
972            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
973        }
974    }
975}
976