diff --git a/src/share/classes/java/util/logging/LogManager.java b/src/share/classes/java/util/logging/LogManager.java index 6ecfe69a98101a054a281da75ca1e39b5fffe88b..228d4636f5d0b4ced25035102ea6c8f63ef78b15 100644 --- a/src/share/classes/java/util/logging/LogManager.java +++ b/src/share/classes/java/util/logging/LogManager.java @@ -193,13 +193,22 @@ public class LogManager { // Create and retain Logger for the root of the namespace. manager.rootLogger = manager.new RootLogger(); + // since by design the global manager's userContext and + // systemContext don't have their requiresDefaultLoggers + // flag set - we make sure to add the root logger to + // the global manager's default contexts here. manager.addLogger(manager.rootLogger); - manager.systemContext.addLocalLogger(manager.rootLogger); + manager.systemContext.addLocalLogger(manager.rootLogger, false); + manager.userContext.addLocalLogger(manager.rootLogger, false); // Adding the global Logger. Doing so in the Logger. // would deadlock with the LogManager.. Logger.global.setLogManager(manager); + // Make sure the global logger will be registered in the + // global manager's default contexts. manager.addLogger(Logger.global); + manager.systemContext.addLocalLogger(Logger.global, false); + manager.userContext.addLocalLogger(Logger.global, false); // We don't call readConfiguration() here, as we may be running // very early in the JVM startup sequence. Instead readConfiguration @@ -401,7 +410,11 @@ public class LogManager { if (javaAwtAccess.isMainAppContext()) { context = userContext; } else { - context = new LoggerContext(); + // Create a new LoggerContext for the applet. + // The new logger context has its requiresDefaultLoggers + // flag set to true - so that these loggers will be + // lazily added when the context is firt accessed. + context = new LoggerContext(true); } javaAwtAccess.put(ecx, LoggerContext.class, context); } @@ -508,9 +521,13 @@ public class LogManager { private final Hashtable namedLoggers = new Hashtable<>(); // Tree of named Loggers private final LogNode root; - + private final boolean requiresDefaultLoggers; private LoggerContext() { + this(false); + } + private LoggerContext(boolean requiresDefaultLoggers) { this.root = new LogNode(null, this); + this.requiresDefaultLoggers = requiresDefaultLoggers; } Logger demandLogger(String name, String resourceBundleName) { @@ -519,7 +536,27 @@ public class LogManager { return manager.demandLogger(name, resourceBundleName, null); } + + // Due to subtle deadlock issues getUserContext() no longer + // calls addLocalLogger(rootLogger); + // Therefore - we need to add the default loggers later on. + // Checks that the context is properly initialized + // This is necessary before calling e.g. find(name) + // or getLoggerNames() + // + private void ensureInitialized() { + if (requiresDefaultLoggers) { + // Ensure that the root and global loggers are set. + ensureDefaultLogger(manager.rootLogger); + ensureDefaultLogger(Logger.global); + } + } + + synchronized Logger findLogger(String name) { + // ensure that this context is properly initialized before + // looking for loggers. + ensureInitialized(); LoggerWeakRef ref = namedLoggers.get(name); if (ref == null) { return null; @@ -533,21 +570,76 @@ public class LogManager { return logger; } - synchronized void ensureRootLogger(Logger logger) { - if (logger.getName().isEmpty()) + // This method is called before adding a logger to the + // context. + // 'logger' is the context that will be added. + // This method will ensure that the defaults loggers are added + // before adding 'logger'. + // + private void ensureAllDefaultLoggers(Logger logger) { + if (requiresDefaultLoggers) { + final String name = logger.getName(); + if (!name.isEmpty()) { + ensureDefaultLogger(manager.rootLogger); + } + if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) { + ensureDefaultLogger(Logger.global); + } + } + } + + private void ensureDefaultLogger(Logger logger) { + // Used for lazy addition of root logger and global logger + // to a LoggerContext. + + // This check is simple sanity: we do not want that this + // method be called for anything else than Logger.global + // or owner.rootLogger. + if (!requiresDefaultLoggers || logger == null + || logger != Logger.global && logger != manager.rootLogger) { + + // the case where we have a non null logger which is neither + // Logger.global nor manager.rootLogger indicates a serious + // issue - as ensureDefaultLogger should never be called + // with any other loggers than one of these two (or null - if + // e.g manager.rootLogger is not yet initialized)... + assert logger == null; + return; + } - // during initialization, rootLogger is null when - // instantiating itself RootLogger - if (findLogger("") == null && manager.rootLogger != null) { - addLocalLogger(manager.rootLogger); + // Adds the logger if it's not already there. + if (!namedLoggers.containsKey(logger.getName())) { + // It is important to prevent addLocalLogger to + // call ensureAllDefaultLoggers when we're in the process + // off adding one of those default loggers - as this would + // immediately cause a stack overflow. + // Therefore we must pass addDefaultLoggersIfNeeded=false, + // even if requiresDefaultLoggers is true. + addLocalLogger(logger, false); } } + boolean addLocalLogger(Logger logger) { + // no need to add default loggers if it's not required + return addLocalLogger(logger, requiresDefaultLoggers); + } + // Add a logger to this context. This method will only set its level // and process parent loggers. It doesn't set its handlers. - synchronized boolean addLocalLogger(Logger logger) { - ensureRootLogger(logger); + synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) { + // addDefaultLoggersIfNeeded serves to break recursion when adding + // default loggers. If we're adding one of the default loggers + // (we're being called from ensureDefaultLogger()) then + // addDefaultLoggersIfNeeded will be false: we don't want to + // call ensureAllDefaultLoggers again. + // + // Note: addDefaultLoggersIfNeeded can also be false when + // requiresDefaultLoggers is false - since calling + // ensureAllDefaultLoggers would have no effect in this case. + if (addDefaultLoggersIfNeeded) { + ensureAllDefaultLoggers(logger); + } final String name = logger.getName(); if (name == null) { @@ -615,6 +707,9 @@ public class LogManager { } synchronized Enumeration getLoggerNames() { + // ensure that this context is properly initialized before + // returning logger names. + ensureInitialized(); return namedLoggers.keys(); } diff --git a/test/java/util/logging/LogManagerInstanceTest.java b/test/java/util/logging/LogManagerInstanceTest.java index 3d3714d56566f7849a925d608ff00c96a8d0d30f..c6fbe1e56d7f46496a2c3b7fcaf379898ced4788 100644 --- a/test/java/util/logging/LogManagerInstanceTest.java +++ b/test/java/util/logging/LogManagerInstanceTest.java @@ -63,7 +63,7 @@ public class LogManagerInstanceTest { if (!super.addLogger(root)) throw new RuntimeException("Fail to addLogger " + root); } else { - System.out.println("Root logger already exists"); + throw new RuntimeException("Root logger already exists"); } this.base = root; } diff --git a/test/java/util/logging/TestAppletLoggerContext.java b/test/java/util/logging/TestAppletLoggerContext.java new file mode 100644 index 0000000000000000000000000000000000000000..c7f3d4f49911c2d98400177610a4ca97e491e25a --- /dev/null +++ b/test/java/util/logging/TestAppletLoggerContext.java @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.logging.LoggingPermission; +import sun.misc.JavaAWTAccess; +import sun.misc.SharedSecrets; + +/* + * @test + * @bug 8017174 8010727 + * @summary NPE when using Logger.getAnonymousLogger or + * LogManager.getLogManager().getLogger + * + * @run main/othervm -Dtest.security=off TestAppletLoggerContext LoadingApplet + * @run main/othervm -Dtest.security=on TestAppletLoggerContext LoadingApplet + * @run main/othervm -Dtest.security=off TestAppletLoggerContext LoadingMain + * @run main/othervm -Dtest.security=on TestAppletLoggerContext LoadingMain + * @run main/othervm -Dtest.security=off TestAppletLoggerContext One + * @run main/othervm -Dtest.security=on TestAppletLoggerContext One + * @run main/othervm -Dtest.security=off TestAppletLoggerContext Two + * @run main/othervm -Dtest.security=on TestAppletLoggerContext Two + * @run main/othervm -Dtest.security=off TestAppletLoggerContext Three + * @run main/othervm -Dtest.security=on TestAppletLoggerContext Three + * @run main/othervm -Dtest.security=off TestAppletLoggerContext Four + * @run main/othervm -Dtest.security=on TestAppletLoggerContext Four + * @run main/othervm -Dtest.security=off TestAppletLoggerContext Five + * @run main/othervm -Dtest.security=on TestAppletLoggerContext Five + * @run main/othervm -Dtest.security=off TestAppletLoggerContext Six + * @run main/othervm -Dtest.security=on TestAppletLoggerContext Six + * @run main/othervm -Dtest.security=off TestAppletLoggerContext Seven + * @run main/othervm -Dtest.security=on TestAppletLoggerContext Seven + * @run main/othervm -Dtest.security=off TestAppletLoggerContext + * @run main/othervm -Dtest.security=on TestAppletLoggerContext + */ + +// NOTE: We run in other VM in order to 1. switch security manager and 2. cause +// LogManager class to be loaded anew. +public class TestAppletLoggerContext { + + // Avoids the hassle of dealing with files and system props... + static class SimplePolicy extends Policy { + private final Permissions perms; + public SimplePolicy(Permission... permissions) { + perms = new Permissions(); + for (Permission permission : permissions) { + perms.add(permission); + } + } + @Override + public PermissionCollection getPermissions(CodeSource cs) { + return perms; + } + @Override + public PermissionCollection getPermissions(ProtectionDomain pd) { + return perms; + } + @Override + public boolean implies(ProtectionDomain pd, Permission p) { + return perms.implies(p); + } + } + + // The bridge class initializes the logging system. + // It stubs the applet context in order to simulate context changes. + // + public static class Bridge { + + private static class JavaAWTAccessStub implements JavaAWTAccess { + boolean active = true; + + private static class TestExc { + private final Map map = new HashMap<>(); + void put(Object key, Object v) { map.put(key, v); } + Object get(Object key) { return map.get(key); } + void remove(Object o) { map.remove(o); } + public static TestExc exc(Object o) { + return TestExc.class.cast(o); + } + } + + TestExc exc; + TestExc global = new TestExc(); + + @Override + public Object getContext() { return active ? global : null; } + @Override + public Object getExecutionContext() { return active ? exc : null; } + @Override + public Object get(Object o, Object o1) { return TestExc.exc(o).get(o1); } + @Override + public void put(Object o, Object o1, Object o2) { TestExc.exc(o).put(o1, o2); } + @Override + public void remove(Object o, Object o1) { TestExc.exc(o).remove(o1); } + @Override + public Object get(Object o) { return global.get(o); } + @Override + public void put(Object o, Object o1) { global.put(o, o1); } + @Override + public void remove(Object o) { global.remove(o); } + @Override + public boolean isDisposed() { return false; } + @Override + public boolean isMainAppContext() { return exc == null; } + } + + final static JavaAWTAccessStub javaAwtAccess = new JavaAWTAccessStub(); + public static void init() { + SharedSecrets.setJavaAWTAccess(javaAwtAccess); + if (System.getProperty("test.security", "on").equals("on")) { + Policy p = new SimplePolicy(new LoggingPermission("control", null), + new RuntimePermission("setContextClassLoader"), + new RuntimePermission("shutdownHooks")); + Policy.setPolicy(p); + System.setSecurityManager(new SecurityManager()); + } + } + + public static void changeContext() { + System.out.println("... Switching to a new applet context ..."); + javaAwtAccess.active = true; + javaAwtAccess.exc = new JavaAWTAccessStub.TestExc(); + } + + public static void desactivate() { + System.out.println("... Running with no applet context ..."); + javaAwtAccess.exc = null; + javaAwtAccess.active = false; + } + + public static class CustomAnonymousLogger extends Logger { + public CustomAnonymousLogger() { + this(""); + } + public CustomAnonymousLogger(String name) { + super(null, null); + System.out.println( " LogManager: " +LogManager.getLogManager()); + System.out.println( " getLogger: " +LogManager.getLogManager().getLogger(name)); + setParent(LogManager.getLogManager().getLogger(name)); + } + } + + public static class CustomLogger extends Logger { + CustomLogger(String name) { + super(name, null); + } + } + } + + public static enum TestCase { + LoadingApplet, LoadingMain, One, Two, Three, Four, Five, Six, Seven; + public void test() { + switch(this) { + // When run - each of these two tests must be + // run before any other tests and before each other. + case LoadingApplet: testLoadingApplet(); break; + case LoadingMain: testLoadingMain(); break; + case One: testOne(); break; + case Two: testTwo(); break; + case Three: testThree(); break; + case Four: testFour(); break; + case Five: testFive(); break; + case Six: testSix(); break; + case Seven: testSeven(); break; + } + } + public String describe() { + switch(this) { + case LoadingApplet: + return "Test that when the LogManager class is" + + " loaded in an applet thread first," + + "\n all LoggerContexts are correctly initialized"; + case LoadingMain: + return "Test that when the LogManager class is" + + " loaded in the main thread first," + + "\n all LoggerContexts are correctly initialized"; + case One: + return "Test that Logger.getAnonymousLogger()" + + " and new CustomAnonymousLogger() don't throw NPE"; + case Two: + return "Test that Logger.getLogger(\"\")" + + " does not return null nor throws NPE"; + case Three: + return "Test that LogManager.getLogManager().getLogger(\"\")" + + " does not return null nor throws NPE"; + case Four: + return "Test that Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)" + + " does not return null,\n and that" + + " new CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME)" + + " does not throw NPE"; + case Five: + return "Test that LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME)" + + "\n does not return null nor throws NPE"; + case Six: + return "Test that manager.getLogger(Logger.GLOBAL_LOGGER_NAME)" + + " returns null\n when manager is not the default" + + " LogManager instance.\n" + + "Test adding a new logger named \"global\" in that" + + " non default instance."; + case Seven: return "Test that manager.getLogger(\"\")" + + " returns null\n when manager is not the default" + + " LogManager instance.\n" + + "Test adding a new logger named \"\" in that" + + " non default instance."; + default: return "Undefined"; + } + } + }; + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + Bridge.init(); + EnumSet tests = EnumSet.noneOf(TestCase.class); + for (String arg : args) { + tests.add(TestCase.valueOf(arg)); + } + if (args.length == 0) { + tests = EnumSet.complementOf(EnumSet.of(TestCase.LoadingMain)); + } + final EnumSet loadingTests = + EnumSet.of(TestCase.LoadingApplet, TestCase.LoadingMain); + int testrun = 0; + for (TestCase test : tests) { + if (loadingTests.contains(test)) { + if (testrun > 0) { + throw new UnsupportedOperationException("Test case " + + test + " must be executed first!"); + } + } + System.out.println("Testing "+ test+": "); + System.out.println(test.describe()); + try { + test.test(); + } catch (Exception x) { + throw new Error(String.valueOf(test) + + (System.getSecurityManager() == null ? " without " : " with ") + + "security failed: "+x+"\n "+"FAILED: "+test.describe()+"\n", x); + } finally { + testrun++; + } + Bridge.changeContext(); + System.out.println("PASSED: "+ test); + } + } + + public static void testLoadingApplet() { + Bridge.changeContext(); + + Logger bar = new Bridge.CustomLogger("com.foo.Bar"); + LogManager.getLogManager().addLogger(bar); + assertNotNull(bar.getParent()); + testParent(bar); + testParent(LogManager.getLogManager().getLogger("global")); + testParent(LogManager.getLogManager().getLogger(bar.getName())); + + Bridge.desactivate(); + + Logger foo = new Bridge.CustomLogger("com.foo.Foo"); + boolean b = LogManager.getLogManager().addLogger(foo); + assertEquals(Boolean.TRUE, Boolean.valueOf(b)); + assertNotNull(foo.getParent()); + testParent(foo); + testParent(LogManager.getLogManager().getLogger("global")); + testParent(LogManager.getLogManager().getLogger(foo.getName())); + } + + public static void testLoadingMain() { + Bridge.desactivate(); + + Logger bar = new Bridge.CustomLogger("com.foo.Bar"); + LogManager.getLogManager().addLogger(bar); + assertNotNull(bar.getParent()); + testParent(bar); + testParent(LogManager.getLogManager().getLogger("global")); + testParent(LogManager.getLogManager().getLogger(bar.getName())); + + Bridge.changeContext(); + + Logger foo = new Bridge.CustomLogger("com.foo.Foo"); + boolean b = LogManager.getLogManager().addLogger(foo); + assertEquals(Boolean.TRUE, Boolean.valueOf(b)); + assertNotNull(foo.getParent()); + testParent(foo); + testParent(LogManager.getLogManager().getLogger("global")); + testParent(LogManager.getLogManager().getLogger(foo.getName())); + + } + + public static void testOne() { + for (int i=0; i<3 ; i++) { + Logger logger1 = Logger.getAnonymousLogger(); + Logger logger1b = Logger.getAnonymousLogger(); + Bridge.changeContext(); + Logger logger2 = Logger.getAnonymousLogger(); + Logger logger2b = Logger.getAnonymousLogger(); + Bridge.changeContext(); + Logger logger3 = new Bridge.CustomAnonymousLogger(); + Logger logger3b = new Bridge.CustomAnonymousLogger(); + Bridge.changeContext(); + Logger logger4 = new Bridge.CustomAnonymousLogger(); + Logger logger4b = new Bridge.CustomAnonymousLogger(); + } + } + + + public static void testTwo() { + for (int i=0; i<3 ; i++) { + Logger logger1 = Logger.getLogger(""); + Logger logger1b = Logger.getLogger(""); + assertNotNull(logger1); + assertNotNull(logger1b); + assertEquals(logger1, logger1b); + Bridge.changeContext(); + Logger logger2 = Logger.getLogger(""); + Logger logger2b = Logger.getLogger(""); + assertNotNull(logger2); + assertNotNull(logger2b); + assertEquals(logger2, logger2b); + assertEquals(logger1, logger2); + } + } + + public static void testThree() { + for (int i=0; i<3 ; i++) { + Logger logger1 = LogManager.getLogManager().getLogger(""); + Logger logger1b = LogManager.getLogManager().getLogger(""); + assertNotNull(logger1); + assertNotNull(logger1b); + assertEquals(logger1, logger1b); + Bridge.changeContext(); + Logger logger2 = LogManager.getLogManager().getLogger(""); + Logger logger2b = LogManager.getLogManager().getLogger(""); + assertNotNull(logger2); + assertNotNull(logger2b); + assertEquals(logger2, logger2b); + assertEquals(logger1, logger2); + } + } + + public static void testFour() { + for (int i=0; i<3 ; i++) { + Logger logger1 = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger1b = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNotNull(logger1); + assertNotNull(logger1b); + assertEquals(logger1, logger1b); + Bridge.changeContext(); + + Logger logger2 = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger2b = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNotNull(logger2); + assertNotNull(logger2b); + assertEquals(logger2, logger2b); + + assertEquals(logger1, logger2); + + Bridge.changeContext(); + Logger logger3 = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger3b = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME); + Bridge.changeContext(); + Logger logger4 = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger4b = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME); + } + } + + public static void testFive() { + for (int i=0; i<3 ; i++) { + Logger logger1 = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger1b = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNotNull(logger1); + assertNotNull(logger1b); + assertEquals(logger1, logger1b); + + Bridge.changeContext(); + + Logger logger2 = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger2b = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNotNull(logger2); + assertNotNull(logger2b); + assertEquals(logger2, logger2b); + + assertEquals(logger1, logger2); + } + } + + /** + * This test is designed to test the behavior of additional LogManager instances. + * It must be noted that if the security manager is off, then calling + * Bridge.changeContext() has actually no effect - which explains why we have + * some differences between the cases security manager on & security manager + * off. + **/ + public static void testSix() { + for (int i=0; i<3 ; i++) { + Bridge.desactivate(); + LogManager manager = new LogManager() {}; + Logger logger1 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger1b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNull(logger1); + assertNull(logger1b); + Logger global = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME); + manager.addLogger(global); + Logger logger2 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger2b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNotNull(logger2); + assertNotNull(logger2b); + assertEquals(logger2, global); + assertEquals(logger2b, global); + assertNull(manager.getLogger("")); + assertNull(manager.getLogger("")); + + Bridge.changeContext(); + + // this is not a supported configuration: + // We are in an applet context with several log managers. + // We however need to check our assumptions... + + // Applet context => root logger and global logger are not null. + // root == LogManager.getLogManager().rootLogger + // global == Logger.global + + Logger logger3 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger3b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNotNull(logger3); + assertNotNull(logger3b); + Logger expected = (System.getSecurityManager() != null + ? Logger.getGlobal() + : global); + assertEquals(logger3, expected); // in applet context, we will not see + // the LogManager's custom global logger added above... + assertEquals(logger3b, expected); // in applet context, we will not see + // the LogManager's custom global logger added above... + Logger global2 = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME); + manager.addLogger(global2); // adding a global logger will not work in applet context + // we will always get back the global logger. + // this could be considered as a bug... + Logger logger4 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger4b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNotNull(logger4); + assertNotNull(logger4b); + assertEquals(logger4, expected); // adding a global logger will not work in applet context + assertEquals(logger4b, expected); // adding a global logger will not work in applet context + + Logger logger5 = manager.getLogger(""); + Logger logger5b = manager.getLogger(""); + Logger expectedRoot = (System.getSecurityManager() != null + ? LogManager.getLogManager().getLogger("") + : null); + assertEquals(logger5, expectedRoot); + assertEquals(logger5b, expectedRoot); + + } + } + + /** + * This test is designed to test the behavior of additional LogManager instances. + * It must be noted that if the security manager is off, then calling + * Bridge.changeContext() has actually no effect - which explains why we have + * some differences between the cases security manager on & security manager + * off. + **/ + public static void testSeven() { + for (int i=0; i<3 ; i++) { + Bridge.desactivate(); + LogManager manager = new LogManager() {}; + Logger logger1 = manager.getLogger(""); + Logger logger1b = manager.getLogger(""); + assertNull(logger1); + assertNull(logger1b); + Logger global = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME); + manager.addLogger(global); + Logger logger2 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger2b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + assertNotNull(logger2); + assertNotNull(logger2b); + assertEquals(logger2, global); + assertEquals(logger2b, global); + Logger logger3 = manager.getLogger(""); + Logger logger3b = manager.getLogger(""); + assertNull(logger3); + assertNull(logger3b); + Logger root = new Bridge.CustomLogger(""); + manager.addLogger(root); + Logger logger4 = manager.getLogger(""); + Logger logger4b = manager.getLogger(""); + assertNotNull(logger4); + assertNotNull(logger4b); + assertEquals(logger4, root); + assertEquals(logger4b, root); + + Bridge.changeContext(); + + // this is not a supported configuration: + // We are in an applet context with several log managers. + // We haowever need to check our assumptions... + + // Applet context => root logger and global logger are not null. + // root == LogManager.getLogManager().rootLogger + // global == Logger.global + + Logger logger5 = manager.getLogger(""); + Logger logger5b = manager.getLogger(""); + Logger expectedRoot = (System.getSecurityManager() != null + ? LogManager.getLogManager().getLogger("") + : root); + + assertNotNull(logger5); + assertNotNull(logger5b); + assertEquals(logger5, expectedRoot); + assertEquals(logger5b, expectedRoot); + if (System.getSecurityManager() != null) { + assertNotEquals(logger5, root); + assertNotEquals(logger5b, root); + } + + Logger global2 = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME); + manager.addLogger(global2); // adding a global logger will not work in applet context + // we will always get back the global logger. + // this could be considered as a bug... + Logger logger6 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger logger6b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME); + Logger expectedGlobal = (System.getSecurityManager() != null + ? Logger.getGlobal() + : global); + assertNotNull(logger6); + assertNotNull(logger6b); + assertEquals(logger6, expectedGlobal); // adding a global logger will not work in applet context + assertEquals(logger6b, expectedGlobal); // adding a global logger will not work in applet context + + Logger root2 = new Bridge.CustomLogger(""); + manager.addLogger(root2); // adding a root logger will not work in applet context + // we will always get back the default manager's root logger. + // this could be considered as a bug... + Logger logger7 = manager.getLogger(""); + Logger logger7b = manager.getLogger(""); + assertNotNull(logger7); + assertNotNull(logger7b); + assertEquals(logger7, expectedRoot); // adding a global logger will not work in applet context + assertEquals(logger7b, expectedRoot); // adding a global logger will not work in applet context + assertNotEquals(logger7, root2); + assertNotEquals(logger7b, root2); + } + } + + public static void testParent(Logger logger) { + Logger l = logger; + while (l.getParent() != null) { + l = l.getParent(); + } + assertEquals("", l.getName()); + } + + public static class TestError extends RuntimeException { + public TestError(String msg) { + super(msg); + } + } + + public static void assertNotNull(Object obj) { + if (obj == null) throw new NullPointerException(); + } + + public static void assertNull(Object obj) { + if (obj != null) throw new TestError("Null expected, got "+obj); + } + + public static void assertEquals(Object o1, Object o2) { + if (o1 != o2) { + throw new TestError(o1 + " != " + o2); + } + } + + public static void assertNotEquals(Object o1, Object o2) { + if (o1 == o2) { + throw new TestError(o1 + " == " + o2); + } + } +}