SLF4j 311 - Enable swapping of NOPLogger in SubstituteLoggerFactory
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/SubstitutableLogger.java b/slf4j-api/src/main/java/org/slf4j/helpers/SubstitutableLogger.java
new file mode 100644
index 0000000..468d53b
--- /dev/null
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/SubstitutableLogger.java
@@ -0,0 +1,314 @@
+/**
+ * Copyright (c) 2004-2011 QOS.ch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+package org.slf4j.helpers;
+
+import org.slf4j.Logger;
+import org.slf4j.Marker;
+
+/**
+ * A helper class which delegates all calls to different Loggers. This enables
+ * swapping of the Logger implementation later.
+ *
+ * @author Chetan Mehrotra
+ */
+public class SubstitutableLogger implements Logger {
+
+ private final String name;
+
+ private volatile Logger _delegate;
+
+ public SubstitutableLogger(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isTraceEnabled() {
+ return delegate().isTraceEnabled();
+ }
+
+ public void trace(String msg) {
+ delegate().trace(msg);
+ }
+
+ public void trace(String format, Object arg) {
+ delegate().trace(format, arg);
+ }
+
+ public void trace(String format, Object arg1, Object arg2) {
+ delegate().trace(format, arg1, arg2);
+ }
+
+ public void trace(String format, Object... arguments) {
+ delegate().trace(format, arguments);
+ }
+
+ public void trace(String msg, Throwable t) {
+ delegate().trace(msg, t);
+ }
+
+ public boolean isTraceEnabled(Marker marker) {
+ return delegate().isTraceEnabled(marker);
+ }
+
+ public void trace(Marker marker, String msg) {
+ delegate().trace(marker, msg);
+ }
+
+ public void trace(Marker marker, String format, Object arg) {
+ delegate().trace(marker, format, arg);
+ }
+
+ public void trace(Marker marker, String format, Object arg1, Object arg2) {
+ delegate().trace(marker, format, arg1, arg2);
+ }
+
+ public void trace(Marker marker, String format, Object... arguments) {
+ delegate().trace(marker, format, arguments);
+ }
+
+ public void trace(Marker marker, String msg, Throwable t) {
+ delegate().trace(marker, msg, t);
+ }
+
+ public boolean isDebugEnabled() {
+ return delegate().isDebugEnabled();
+ }
+
+ public void debug(String msg) {
+ delegate().debug(msg);
+ }
+
+ public void debug(String format, Object arg) {
+ delegate().debug(format, arg);
+ }
+
+ public void debug(String format, Object arg1, Object arg2) {
+ delegate().debug(format, arg1, arg2);
+ }
+
+ public void debug(String format, Object... arguments) {
+ delegate().debug(format, arguments);
+ }
+
+ public void debug(String msg, Throwable t) {
+ delegate().debug(msg, t);
+ }
+
+ public boolean isDebugEnabled(Marker marker) {
+ return delegate().isDebugEnabled(marker);
+ }
+
+ public void debug(Marker marker, String msg) {
+ delegate().debug(marker, msg);
+ }
+
+ public void debug(Marker marker, String format, Object arg) {
+ delegate().debug(marker, format, arg);
+ }
+
+ public void debug(Marker marker, String format, Object arg1, Object arg2) {
+ delegate().debug(marker, format, arg1, arg2);
+ }
+
+ public void debug(Marker marker, String format, Object... arguments) {
+ delegate().debug(marker, format, arguments);
+ }
+
+ public void debug(Marker marker, String msg, Throwable t) {
+ delegate().debug(marker, msg, t);
+ }
+
+ public boolean isInfoEnabled() {
+ return delegate().isInfoEnabled();
+ }
+
+ public void info(String msg) {
+ delegate().info(msg);
+ }
+
+ public void info(String format, Object arg) {
+ delegate().info(format, arg);
+ }
+
+ public void info(String format, Object arg1, Object arg2) {
+ delegate().info(format, arg1, arg2);
+ }
+
+ public void info(String format, Object... arguments) {
+ delegate().info(format, arguments);
+ }
+
+ public void info(String msg, Throwable t) {
+ delegate().info(msg, t);
+ }
+
+ public boolean isInfoEnabled(Marker marker) {
+ return delegate().isInfoEnabled(marker);
+ }
+
+ public void info(Marker marker, String msg) {
+ delegate().info(marker, msg);
+ }
+
+ public void info(Marker marker, String format, Object arg) {
+ delegate().info(marker, format, arg);
+ }
+
+ public void info(Marker marker, String format, Object arg1, Object arg2) {
+ delegate().info(marker, format, arg1, arg2);
+ }
+
+ public void info(Marker marker, String format, Object... arguments) {
+ delegate().info(marker, format, arguments);
+ }
+
+ public void info(Marker marker, String msg, Throwable t) {
+ delegate().info(marker, msg, t);
+ }
+
+ public boolean isWarnEnabled() {
+ return delegate().isWarnEnabled();
+ }
+
+ public void warn(String msg) {
+ delegate().warn(msg);
+ }
+
+ public void warn(String format, Object arg) {
+ delegate().warn(format, arg);
+ }
+
+ public void warn(String format, Object arg1, Object arg2) {
+ delegate().warn(format, arg1, arg2);
+ }
+
+ public void warn(String format, Object... arguments) {
+ delegate().warn(format, arguments);
+ }
+
+ public void warn(String msg, Throwable t) {
+ delegate().warn(msg, t);
+ }
+
+ public boolean isWarnEnabled(Marker marker) {
+ return delegate().isWarnEnabled(marker);
+ }
+
+ public void warn(Marker marker, String msg) {
+ delegate().warn(marker, msg);
+ }
+
+ public void warn(Marker marker, String format, Object arg) {
+ delegate().warn(marker, format, arg);
+ }
+
+ public void warn(Marker marker, String format, Object arg1, Object arg2) {
+ delegate().warn(marker, format, arg1, arg2);
+ }
+
+ public void warn(Marker marker, String format, Object... arguments) {
+ delegate().warn(marker, format, arguments);
+ }
+
+ public void warn(Marker marker, String msg, Throwable t) {
+ delegate().warn(marker, msg, t);
+ }
+
+ public boolean isErrorEnabled() {
+ return delegate().isErrorEnabled();
+ }
+
+ public void error(String msg) {
+ delegate().error(msg);
+ }
+
+ public void error(String format, Object arg) {
+ delegate().error(format, arg);
+ }
+
+ public void error(String format, Object arg1, Object arg2) {
+ delegate().error(format, arg1, arg2);
+ }
+
+ public void error(String format, Object... arguments) {
+ delegate().error(format, arguments);
+ }
+
+ public void error(String msg, Throwable t) {
+ delegate().error(msg, t);
+ }
+
+ public boolean isErrorEnabled(Marker marker) {
+ return delegate().isErrorEnabled(marker);
+ }
+
+ public void error(Marker marker, String msg) {
+ delegate().error(marker, msg);
+ }
+
+ public void error(Marker marker, String format, Object arg) {
+ delegate().error(marker, format, arg);
+ }
+
+ public void error(Marker marker, String format, Object arg1, Object arg2) {
+ delegate().error(marker, format, arg1, arg2);
+ }
+
+ public void error(Marker marker, String format, Object... arguments) {
+ delegate().error(marker, format, arguments);
+ }
+
+ public void error(Marker marker, String msg, Throwable t) {
+ delegate().error(marker, msg, t);
+ }
+
+ public void setDelegate(Logger delegate) {
+ this._delegate = delegate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SubstitutableLogger that = (SubstitutableLogger) o;
+
+ if (!name.equals(that.name)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ Logger delegate() {
+ return _delegate != null ? _delegate : NOPLogger.NOP_LOGGER;
+ }
+}
diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java
index 7382ed7..223606e 100644
--- a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java
+++ b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLoggerFactory.java
@@ -26,6 +26,8 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
@@ -33,32 +35,50 @@
/**
* SubstituteLoggerFactory is an trivial implementation of
* {@link ILoggerFactory} which always returns the unique instance of NOPLogger.
- *
+ * <p/>
* <p>
* It used as a temporary substitute for the real ILoggerFactory during its
* auto-configuration which may re-enter LoggerFactory to obtain logger
* instances. See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
- *
+ * <p/>
+ * <p>
+ * Logger implementations can swap out the NOPLogger with actual Logger
+ * implementation once they are properly configured by changing the delegate
+ * in {@link org.slf4j.helpers.SubstitutableLogger}
+ * </p>
+ *
* @author Ceki Gülcü
*/
public class SubstituteLoggerFactory implements ILoggerFactory {
// keep a record of requested logger names
- final List loggerNameList = new ArrayList();
+ final ConcurrentMap<String, SubstitutableLogger> loggers = new ConcurrentHashMap<String, SubstitutableLogger>();
public Logger getLogger(String name) {
- synchronized (loggerNameList) {
- loggerNameList.add(name);
+ SubstitutableLogger logger;
+ synchronized (loggers) {
+ logger = loggers.get(name);
+ if (logger == null) {
+ logger = new SubstitutableLogger(name);
+ loggers.put(name, logger);
+ }
}
- return NOPLogger.NOP_LOGGER;
+ return logger;
}
public List getLoggerNameList() {
- List copy = new ArrayList();
- synchronized (loggerNameList) {
- copy.addAll(loggerNameList);
+ List<String> copy = new ArrayList<String>();
+ synchronized (loggers) {
+ copy.addAll(loggers.keySet());
}
return copy;
}
+ public Iterable<SubstitutableLogger> getLoggers() {
+ return loggers.values();
+ }
+
+ public void clear() {
+ loggers.clear();
+ }
}
diff --git a/slf4j-api/src/test/java/org/slf4j/helpers/SubstitutableLoggerTest.java b/slf4j-api/src/test/java/org/slf4j/helpers/SubstitutableLoggerTest.java
new file mode 100644
index 0000000..f3f5524
--- /dev/null
+++ b/slf4j-api/src/test/java/org/slf4j/helpers/SubstitutableLoggerTest.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2004-2011 QOS.ch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+package org.slf4j.helpers;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.TestCase;
+import org.slf4j.Logger;
+
+/**
+ * @author Chetan Mehrotra
+ */
+public class SubstitutableLoggerTest extends TestCase {
+ private static final Set<String> EXCLUDED_METHODS = new HashSet<String>(Arrays.asList("getName"));
+
+ public void testDelegate() throws Exception {
+ SubstitutableLogger log = new SubstitutableLogger("foo");
+ assertTrue(log.delegate() instanceof NOPLogger);
+
+ Set<String> methodSignatures = determinemethodSignatures(Logger.class);
+ LoggerInvocationHandler ih = new LoggerInvocationHandler();
+ Logger proxyLogger = (Logger) Proxy.newProxyInstance(getClass().getClassLoader(),
+ new Class[]{Logger.class}, ih);
+ log.setDelegate(proxyLogger);
+
+ invokeMethods(log);
+
+ //Assert that all methods are delegated
+ methodSignatures.removeAll(ih.getMethodSignatures());
+ if (!methodSignatures.isEmpty()) {
+ fail("Following methods are not delegated " + methodSignatures.toString());
+ }
+ }
+
+ private void invokeMethods(Logger proxyLogger) throws InvocationTargetException, IllegalAccessException {
+ for (Method m : Logger.class.getDeclaredMethods()) {
+ if (!EXCLUDED_METHODS.contains(m.getName())) {
+ m.invoke(proxyLogger, new Object[m.getParameterTypes().length]);
+ }
+ }
+ }
+
+ private class LoggerInvocationHandler implements InvocationHandler {
+ private final Set<String> methodSignatures = new HashSet<String>();
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ methodSignatures.add(getMethodSignature(method));
+ if (method.getName().startsWith("is")) {
+ return true;
+ }
+ return null;
+ }
+
+ public Set<String> getMethodSignatures() {
+ return methodSignatures;
+ }
+ }
+
+ private static Set<String> determinemethodSignatures(Class<Logger> loggerClass) {
+ Set<String> methodSignatures = new HashSet<String>();
+ for (Method m : loggerClass.getDeclaredMethods()) {
+ if (!EXCLUDED_METHODS.contains(m.getName())) {
+ methodSignatures.add(getMethodSignature(m));
+ }
+ }
+ return methodSignatures;
+ }
+
+ private static String getMethodSignature(Method m) {
+ List<String> result = new ArrayList<String>();
+ result.add(m.getName());
+ for (Class<?> clazz : m.getParameterTypes()) {
+ result.add(clazz.getSimpleName());
+ }
+ return result.toString();
+ }
+}
diff --git a/slf4j-api/src/test/java/org/slf4j/helpers/SubstituteLoggerFactoryTest.java b/slf4j-api/src/test/java/org/slf4j/helpers/SubstituteLoggerFactoryTest.java
new file mode 100644
index 0000000..c6c5a48
--- /dev/null
+++ b/slf4j-api/src/test/java/org/slf4j/helpers/SubstituteLoggerFactoryTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2004-2011 QOS.ch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+package org.slf4j.helpers;
+
+import junit.framework.TestCase;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SubstituteLoggerFactoryTest extends TestCase{
+ private SubstituteLoggerFactory factory = new SubstituteLoggerFactory();
+
+ public void testFactory() {
+ Logger log = factory.getLogger("foo");
+ assertNotNull(log);
+
+ Logger log2 = factory.getLogger("foo");
+ assertTrue("Loggers with same name must be same",log == log2);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testLoggerNameList() {
+ factory.getLogger("foo1");
+ factory.getLogger("foo2");
+
+ Set<String> expectedNames = new HashSet<String>(Arrays.asList("foo1","foo2"));
+ Set<String> actualNames = new HashSet<String>(factory.getLoggerNameList());
+
+ assertEquals(expectedNames, actualNames);
+ }
+
+ public void testLoggers() {
+ factory.getLogger("foo1");
+ factory.getLogger("foo2");
+
+ Set<String> expectedNames = new HashSet<String>(Arrays.asList("foo1","foo2"));
+
+ Set<String> actualNames = new HashSet<String>();
+ for(SubstitutableLogger slog : factory.getLoggers()){
+ actualNames.add(slog.getName());
+ }
+
+ assertEquals(expectedNames, actualNames);
+ }
+
+}