src/share/classes/java/util/logging/LogManager.java
Print this page
*** 142,152 ****
* @since 1.4
*/
public class LogManager {
// The global LogManager object
! private static LogManager manager;
private Properties props = new Properties();
private final static Level defaultLevel = Level.INFO;
// The map of the registered listeners. The map value is the registration
--- 142,152 ----
* @since 1.4
*/
public class LogManager {
// The global LogManager object
! private static final LogManager manager;
private Properties props = new Properties();
private final static Level defaultLevel = Level.INFO;
// The map of the registered listeners. The map value is the registration
*** 167,226 ****
private boolean initializedGlobalHandlers = true;
// True if JVM death is imminent and the exit hook has been called.
private boolean deathImminent;
static {
! AccessController.doPrivileged(new PrivilegedAction<Object>() {
! public Object run() {
String cname = null;
try {
cname = System.getProperty("java.util.logging.manager");
if (cname != null) {
try {
! Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
! manager = (LogManager) clz.newInstance();
} catch (ClassNotFoundException ex) {
! Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
! manager = (LogManager) clz.newInstance();
}
}
} catch (Exception ex) {
System.err.println("Could not load Logmanager \"" + cname + "\"");
ex.printStackTrace();
}
! if (manager == null) {
! manager = new 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, false);
- manager.userContext.addLocalLogger(manager.rootLogger, false);
-
- // Adding the global Logger. Doing so in the Logger.<clinit>
- // would deadlock with the LogManager.<clinit>.
- // Do not call Logger.getGlobal() here as this might trigger
- // the deadlock too.
- @SuppressWarnings("deprecation")
- final Logger global = Logger.global;
- global.setLogManager(manager);
-
- // Make sure the global logger will be registered in the
- // global manager's default contexts.
- manager.addLogger(global);
- manager.systemContext.addLocalLogger(global, false);
- manager.userContext.addLocalLogger(global, false);
-
- // We don't call readConfiguration() here, as we may be running
- // very early in the JVM startup sequence. Instead readConfiguration
- // will be called lazily in getLogManager().
- return null;
}
});
}
--- 167,203 ----
private boolean initializedGlobalHandlers = true;
// True if JVM death is imminent and the exit hook has been called.
private boolean deathImminent;
static {
! manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() {
! @Override
! public LogManager run() {
! LogManager mgr = null;
String cname = null;
try {
cname = System.getProperty("java.util.logging.manager");
if (cname != null) {
try {
! Class<?> clz = ClassLoader.getSystemClassLoader()
! .loadClass(cname);
! mgr = (LogManager) clz.newInstance();
} catch (ClassNotFoundException ex) {
! Class<?> clz = Thread.currentThread()
! .getContextClassLoader().loadClass(cname);
! mgr = (LogManager) clz.newInstance();
}
}
} catch (Exception ex) {
System.err.println("Could not load Logmanager \"" + cname + "\"");
ex.printStackTrace();
}
! if (mgr == null) {
! mgr = new LogManager();
}
+ return mgr;
}
});
}
*** 233,242 ****
--- 210,220 ----
* keeping a strong reference to an application classloader.
*/
this.setContextClassLoader(null);
}
+ @Override
public void run() {
// This is to ensure the LogManager.<clinit> is completed
// before synchronized block. Otherwise deadlocks are possible.
LogManager mgr = manager;
*** 269,284 ****
// We do not need to register shutdownHook.
}
}
/**
* Returns the global LogManager object.
* @return the global LogManager object
*/
public static LogManager getLogManager() {
if (manager != null) {
! manager.readPrimordialConfiguration();
}
return manager;
}
private void readPrimordialConfiguration() {
--- 247,316 ----
// We do not need to register shutdownHook.
}
}
/**
+ * Lazy initialization: if this instance of manager is the global
+ * manager then this method will read the initial configuration and
+ * add the root logger and global logger by calling addLogger().
+ *
+ * Note that it is subtly different from what we do in LoggerContext.
+ * In LoggerContext we're patching up the logger context tree in order to add
+ * the root and global logger *to the context tree*.
+ *
+ * For this to work, addLogger() must have already have been called
+ * once on the LogManager instance for the default logger being
+ * added.
+ *
+ * This is why ensureLogManagerInitialized() needs to be called before
+ * any logger is added to any logger context.
+ *
+ **/
+ private boolean initializedCalled=false;
+ final synchronized void ensureLogManagerInitialized() {
+ final LogManager owner = this;
+ if (initializedCalled || owner != manager) {
+ // we don't want to do this twice, and we don't want to do
+ // this on private manager instances.
+ return;
+ }
+ initializedCalled = true;
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ if (rootLogger == null) {
+ // Read configuration. This was previously triggered
+ // by the new RootLogger() constructor - but no longer.
+ owner.readPrimordialConfiguration();
+
+ // Create and retain Logger for the root of the namespace.
+ owner.rootLogger = owner.new RootLogger();
+ owner.addLogger(owner.rootLogger);
+
+ // Adding the global Logger. Doing so in the Logger.<clinit>
+ // would deadlock with the LogManager.<clinit>.
+ // Do not call Logger.getGlobal() here as this might trigger
+ // the deadlock too.
+ @SuppressWarnings("deprecation")
+ final Logger global = Logger.global;
+
+ // Make sure the global logger will be registered in the
+ // global manager
+ owner.addLogger(global);
+ }
+ return null;
+ }
+ });
+ }
+
+ /**
* Returns the global LogManager object.
* @return the global LogManager object
*/
public static LogManager getLogManager() {
if (manager != null) {
! manager.ensureLogManagerInitialized();
}
return manager;
}
private void readPrimordialConfiguration() {
*** 293,302 ****
--- 325,335 ----
}
readPrimordialConfiguration = true;
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ @Override
public Void run() throws Exception {
readConfiguration();
// Platform loggers begin to delegate to java.util.logging.Logger
sun.util.logging.PlatformLogger.redirectPlatformLoggers();
*** 304,313 ****
--- 337,347 ----
}
});
} catch (Exception ex) {
// System.err.println("Can't read logging configuration:");
// ex.printStackTrace();
+ assert false : "Exception raised while reading logging configuration: " + ex;
}
}
}
}
}
*** 390,400 ****
}
}
}
// LoggerContext maps from AppContext
! private static WeakHashMap<Object, LoggerContext> contextsMap = null;
// Returns the LoggerContext for the user code (i.e. application or AppContext).
// Loggers are isolated from each AppContext.
private LoggerContext getUserContext() {
LoggerContext context = null;
--- 424,434 ----
}
}
}
// LoggerContext maps from AppContext
! private WeakHashMap<Object, LoggerContext> contextsMap = null;
// Returns the LoggerContext for the user code (i.e. application or AppContext).
// Loggers are isolated from each AppContext.
private LoggerContext getUserContext() {
LoggerContext context = null;
*** 412,437 ****
contextsMap = new WeakHashMap<>();
}
context = contextsMap.get(ecx);
if (context == null) {
// 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);
contextsMap.put(ecx, context);
}
}
}
}
// for standalone app, return userContext
return context != null ? context : userContext;
}
private List<LoggerContext> contexts() {
List<LoggerContext> cxs = new ArrayList<>();
! cxs.add(systemContext);
cxs.add(getUserContext());
return cxs;
}
// Find or create a specified logger instance. If a logger has
--- 446,473 ----
contextsMap = new WeakHashMap<>();
}
context = contextsMap.get(ecx);
if (context == null) {
// Create a new LoggerContext for the applet.
! context = new LoggerContext();
contextsMap.put(ecx, context);
}
}
}
}
// for standalone app, return userContext
return context != null ? context : userContext;
}
+ // The system context.
+ final LoggerContext getSystemContext() {
+ return systemContext;
+ }
+
private List<LoggerContext> contexts() {
List<LoggerContext> cxs = new ArrayList<>();
! cxs.add(getSystemContext());
cxs.add(getUserContext());
return cxs;
}
// Find or create a specified logger instance. If a logger has
*** 448,458 ****
// readConfiguration, and other methods.
Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
Logger result = getLogger(name);
if (result == null) {
// only allocate the new logger once
! Logger newLogger = new Logger(name, resourceBundleName, caller);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
return newLogger;
--- 484,494 ----
// readConfiguration, and other methods.
Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
Logger result = getLogger(name);
if (result == null) {
// only allocate the new logger once
! Logger newLogger = new Logger(name, resourceBundleName, caller, this);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
return newLogger;
*** 475,485 ****
return result;
}
Logger demandSystemLogger(String name, String resourceBundleName) {
// Add a system logger in the system context's namespace
! final Logger sysLogger = systemContext.demandLogger(name, resourceBundleName);
// Add the system logger to the LogManager's namespace if not exist
// so that there is only one single logger of the given name.
// System loggers are visible to applications unless a logger of
// the same name has been added.
--- 511,521 ----
return result;
}
Logger demandSystemLogger(String name, String resourceBundleName) {
// Add a system logger in the system context's namespace
! final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName);
// Add the system logger to the LogManager's namespace if not exist
// so that there is only one single logger of the given name.
// System loggers are visible to applications unless a logger of
// the same name has been added.
*** 499,508 ****
--- 535,545 ----
// LogManager will set the sysLogger's handlers via LogManager.addLogger method.
if (logger != sysLogger && sysLogger.getHandlers().length == 0) {
// if logger already exists but handlers not set
final Logger l = logger;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
public Void run() {
for (Handler hdl : l.getHandlers()) {
sysLogger.addHandler(hdl);
}
return null;
*** 517,544 ****
// context. The system context is used to maintain the namespace for
// all system loggers and is queried by the system code. If a system logger
// doesn't exist in the user context, it'll also be added to the user context.
// The user context is queried by the user code and all other loggers are
// added in the user context.
! static class LoggerContext {
// Table of named Loggers that maps names to Loggers.
private final Hashtable<String,LoggerWeakRef> 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) {
// a LogManager subclass may have its own implementation to add and
// get a Logger. So delegate to the LogManager to do the work.
! return manager.demandLogger(name, resourceBundleName, null);
}
// Due to subtle deadlock issues getUserContext() no longer
// calls addLocalLogger(rootLogger);
--- 554,609 ----
// context. The system context is used to maintain the namespace for
// all system loggers and is queried by the system code. If a system logger
// doesn't exist in the user context, it'll also be added to the user context.
// The user context is queried by the user code and all other loggers are
// added in the user context.
! class LoggerContext {
// Table of named Loggers that maps names to Loggers.
private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
// Tree of named Loggers
private final LogNode root;
private LoggerContext() {
this.root = new LogNode(null, this);
! }
!
!
! // Tells whether default loggers are required in this context.
! // If true, the default loggers will be lazily added.
! final boolean requiresDefaultLoggers() {
! final boolean requiresDefaultLoggers = (getOwner() == manager);
! if (requiresDefaultLoggers) {
! getOwner().ensureLogManagerInitialized();
! }
! return requiresDefaultLoggers;
! }
!
! // This context's LogManager.
! final LogManager getOwner() {
! return LogManager.this;
! }
!
! // This context owner's root logger, which if not null, and if
! // the context requires default loggers, will be added to the context
! // logger's tree.
! final Logger getRootLogger() {
! return getOwner().rootLogger;
! }
!
! // The global logger, which if not null, and if
! // the context requires default loggers, will be added to the context
! // logger's tree.
! final Logger getGlobalLogger() {
! @SuppressWarnings("deprecated") // avoids initialization cycles.
! final Logger global = Logger.global;
! return global;
}
Logger demandLogger(String name, String resourceBundleName) {
// a LogManager subclass may have its own implementation to add and
// get a Logger. So delegate to the LogManager to do the work.
! final LogManager owner = getOwner();
! return owner.demandLogger(name, resourceBundleName, null);
}
// Due to subtle deadlock issues getUserContext() no longer
// calls addLocalLogger(rootLogger);
*** 546,559 ****
// 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) {
--- 611,624 ----
// 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(getRootLogger());
! ensureDefaultLogger(getGlobalLogger());
}
}
synchronized Logger findLogger(String name) {
*** 578,594 ****
// '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) {
--- 643,659 ----
// '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(getRootLogger());
if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
! ensureDefaultLogger(getGlobalLogger());
! }
}
}
}
private void ensureDefaultLogger(Logger logger) {
*** 596,607 ****
// 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
--- 661,672 ----
// 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 != LogManager.this.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
*** 623,633 ****
}
}
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, boolean addDefaultLoggersIfNeeded) {
--- 688,698 ----
}
}
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, boolean addDefaultLoggersIfNeeded) {
*** 661,675 ****
}
}
// We're adding a new logger.
// Note that we are creating a weak reference here.
! ref = manager.new LoggerWeakRef(logger);
namedLoggers.put(name, ref);
// Apply any initial level defined for the new logger.
! Level level = manager.getLevelProperty(name + ".level", null);
if (level != null) {
doSetLevel(logger, level);
}
// instantiation of the handler is done in the LogManager.addLogger
--- 726,742 ----
}
}
// We're adding a new logger.
// Note that we are creating a weak reference here.
! final LogManager owner = getOwner();
! logger.setLogManager(owner);
! ref = owner.new LoggerWeakRef(logger);
namedLoggers.put(name, ref);
// Apply any initial level defined for the new logger.
! Level level = owner.getLevelProperty(name + ".level", null);
if (level != null) {
doSetLevel(logger, level);
}
// instantiation of the handler is done in the LogManager.addLogger
*** 717,730 ****
}
// If logger.getUseParentHandlers() returns 'true' and any of the logger's
// parents have levels or handlers defined, make sure they are instantiated.
private void processParentHandlers(final Logger logger, final String name) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
! if (logger != manager.rootLogger) {
! boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true);
if (!useParent) {
logger.setUseParentHandlers(false);
}
}
return null;
--- 784,799 ----
}
// If logger.getUseParentHandlers() returns 'true' and any of the logger's
// parents have levels or handlers defined, make sure they are instantiated.
private void processParentHandlers(final Logger logger, final String name) {
+ final LogManager owner = getOwner();
AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
public Void run() {
! if (logger != owner.rootLogger) {
! boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
if (!useParent) {
logger.setUseParentHandlers(false);
}
}
return null;
*** 736,747 ****
int ix2 = name.indexOf(".", ix);
if (ix2 < 0) {
break;
}
String pname = name.substring(0, ix2);
! if (manager.getProperty(pname + ".level") != null ||
! manager.getProperty(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
demandLogger(pname, null);
}
ix = ix2+1;
--- 805,816 ----
int ix2 = name.indexOf(".", ix);
if (ix2 < 0) {
break;
}
String pname = name.substring(0, ix2);
! if (owner.getProperty(pname + ".level") != null ||
! owner.getProperty(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
demandLogger(pname, null);
}
ix = ix2+1;
*** 777,796 ****
}
return node;
}
}
! static class SystemLoggerContext extends LoggerContext {
// Add a system logger in the system context's namespace as well as
// in the LogManager's namespace if not exist so that there is only
// one single logger of the given name. System loggers are visible
// to applications unless a logger of the same name has been added.
Logger demandLogger(String name, String resourceBundleName) {
Logger result = findLogger(name);
if (result == null) {
// only allocate the new system logger once
! Logger newLogger = new Logger(name, resourceBundleName);
do {
if (addLocalLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
result = newLogger;
--- 846,866 ----
}
return node;
}
}
! final class SystemLoggerContext extends LoggerContext {
// Add a system logger in the system context's namespace as well as
// in the LogManager's namespace if not exist so that there is only
// one single logger of the given name. System loggers are visible
// to applications unless a logger of the same name has been added.
+ @Override
Logger demandLogger(String name, String resourceBundleName) {
Logger result = findLogger(name);
if (result == null) {
// only allocate the new system logger once
! Logger newLogger = new Logger(name, resourceBundleName, null, getOwner());
do {
if (addLocalLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
result = newLogger;
*** 820,829 ****
--- 890,900 ----
// only be modified by trusted code.
private void loadLoggerHandlers(final Logger logger, final String name,
final String handlersPropertyName)
{
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
String names[] = parseClassNames(handlersPropertyName);
for (int i = 0; i < names.length; i++) {
String word = names[i];
try {
*** 1012,1021 ****
--- 1083,1093 ----
return;
}
// There is a security manager. Raise privilege before
// calling setLevel.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
logger.setLevel(level);
return null;
}});
}
*** 1030,1039 ****
--- 1102,1112 ----
return;
}
// There is a security manager. Raise privilege before
// calling setParent.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
logger.setParent(parent);
return null;
}});
}
*** 1127,1146 ****
}
File f = new File(fname, "lib");
f = new File(f, "logging.properties");
fname = f.getCanonicalPath();
}
! InputStream in = new FileInputStream(fname);
! BufferedInputStream bin = new BufferedInputStream(in);
try {
readConfiguration(bin);
} finally {
- if (in != null) {
in.close();
}
}
- }
/**
* Reset the logging configuration.
* <p>
* For all named loggers, the reset operation removes and closes
--- 1200,1217 ----
}
File f = new File(fname, "lib");
f = new File(f, "logging.properties");
fname = f.getCanonicalPath();
}
! final InputStream in = new FileInputStream(fname);
! final BufferedInputStream bin = new BufferedInputStream(in);
try {
readConfiguration(bin);
} finally {
in.close();
}
}
/**
* Reset the logging configuration.
* <p>
* For all named loggers, the reset operation removes and closes
*** 1199,1209 ****
if (hands == null) {
return new String[0];
}
hands = hands.trim();
int ix = 0;
! Vector<String> result = new Vector<>();
while (ix < hands.length()) {
int end = ix;
while (end < hands.length()) {
if (Character.isWhitespace(hands.charAt(end))) {
break;
--- 1270,1280 ----
if (hands == null) {
return new String[0];
}
hands = hands.trim();
int ix = 0;
! final List<String> result = new ArrayList<>();
while (ix < hands.length()) {
int end = ix;
while (end < hands.length()) {
if (Character.isWhitespace(hands.charAt(end))) {
break;
*** 1469,1500 ****
}
// We use a subclass of Logger for the root logger, so
// that we only instantiate the global handlers when they
// are first needed.
! private class RootLogger extends Logger {
private RootLogger() {
! super("", null);
setLevel(defaultLevel);
}
public void log(LogRecord record) {
// Make sure that the global handlers have been instantiated.
initializeGlobalHandlers();
super.log(record);
}
public void addHandler(Handler h) {
initializeGlobalHandlers();
super.addHandler(h);
}
public void removeHandler(Handler h) {
initializeGlobalHandlers();
super.removeHandler(h);
}
public Handler[] getHandlers() {
initializeGlobalHandlers();
return super.getHandlers();
}
}
--- 1540,1578 ----
}
// We use a subclass of Logger for the root logger, so
// that we only instantiate the global handlers when they
// are first needed.
! private final class RootLogger extends Logger {
private RootLogger() {
! // We do not the protected Logger two args constructor here,
! // to avoid calling LOgManager.getLogManager() from within the
! // RootLogger constructor.
! super("", null, null, LogManager.this);
setLevel(defaultLevel);
}
+ @Override
public void log(LogRecord record) {
// Make sure that the global handlers have been instantiated.
initializeGlobalHandlers();
super.log(record);
}
+ @Override
public void addHandler(Handler h) {
initializeGlobalHandlers();
super.addHandler(h);
}
+ @Override
public void removeHandler(Handler h) {
initializeGlobalHandlers();
super.removeHandler(h);
}
+ @Override
public Handler[] getHandlers() {
initializeGlobalHandlers();
return super.getHandlers();
}
}