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