Index: logback-access/src/test/java/ch/qos/logback/access/jetty/JettyBasicTest.java =================================================================== --- logback-access/src/test/java/ch/qos/logback/access/jetty/JettyBasicTest.java (revision 1828) +++ logback-access/src/test/java/ch/qos/logback/access/jetty/JettyBasicTest.java (working copy) @@ -59,7 +59,7 @@ String result = Util.readToString(connection.getInputStream()); assertEquals("hello world", result); - //Thread.sleep(100); + Thread.sleep(100); ListAppender appender = (ListAppender) requestLogImpl.getAppender("list"); assertTrue(appender.list.size()>0); AccessEvent event = (AccessEvent) appender.list.get(0); Index: logback-classic/src/main/java/ch/qos/logback/classic/Logger.java =================================================================== --- logback-classic/src/main/java/ch/qos/logback/classic/Logger.java (revision 1828) +++ logback-classic/src/main/java/ch/qos/logback/classic/Logger.java (working copy) @@ -196,9 +196,7 @@ // propagate the parent levelInt change to this logger's children // propagate the parent levelInt change to this logger's children if (childrenList != null) { - int len = childrenList.size(); - for (int i = 0; i < len; i++) { - Logger child = (Logger) childrenList.get(i); + for (Logger child : childrenList) { child.handleParentLevelChange(newParentLevel); } } @@ -209,22 +207,24 @@ * Remove all previously added appenders from this logger instance.

This * is useful when re-reading configuration information. */ - public synchronized void detachAndStopAllAppenders() { + public void detachAndStopAllAppenders() { if (aai != null) { aai.detachAndStopAllAppenders(); } } - public synchronized Appender detachAppender(String name) { + public Appender detachAppender(String name) { if (aai == null) { return null; } return aai.detachAppender(name); } - public synchronized void addAppender(Appender newAppender) { - if (aai == null) { - aai = new AppenderAttachableImpl(); + public void addAppender(Appender newAppender) { + synchronized(this) { + if (aai == null) { + aai = new AppenderAttachableImpl(); + } } aai.addAppender(newAppender); } @@ -236,7 +236,7 @@ return aai.isAttached(appender); } - public synchronized Iterator iteratorForAppenders() { + public Iterator iteratorForAppenders() { if (aai == null) { return Collections.EMPTY_LIST.iterator(); } @@ -287,7 +287,7 @@ /** * Remove the appender passed as parameter form the list of appenders. */ - public synchronized boolean detachAppender(Appender appender) { + public boolean detachAppender(Appender appender) { if (aai == null) { return false; } Index: logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java =================================================================== --- logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java (revision 1828) +++ logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java (working copy) @@ -23,6 +23,7 @@ suite.addTest(ch.qos.logback.core.joran.PackageTest.suite()); suite.addTest(ch.qos.logback.core.appender.PackageTest.suite()); suite.addTest(ch.qos.logback.core.rolling.PackageTest.suite()); + suite.addTest(ch.qos.logback.core.spi.PackageTest.suite()); return suite; } } Index: logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplTest.java =================================================================== --- logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplTest.java (revision 0) +++ logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplTest.java (revision 0) @@ -0,0 +1,185 @@ +package ch.qos.logback.core.spi; + +import junit.framework.TestCase; +import junit.framework.Test; +import junit.framework.TestSuite; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.appender.NOPAppender; +import ch.qos.logback.core.layout.NopLayout; + +import java.util.Iterator; + +/** + * This test case verifies all the methods of AppenderAttableImpl work properly. + * + * @author Ceki + */ +public class AppenderAttachableImplTest extends TestCase { + /** + * To run test suite from command line. + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + /** + * Set up a test suite. + */ + public static Test suite() { + return new TestSuite(AppenderAttachableImplTest.class); + } + + public AppenderAttachableImplTest(String testName) { + super(testName); + } + + private AppenderAttachableImpl at; + + protected void setUp() throws Exception { + super.setUp(); + at = new AppenderAttachableImpl(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + at = null; + } + + public void testAddAppender() throws Exception { + TestEvent event = new TestEvent(); + TestAppender ta = new TestAppender(); + ta.setLayout(new NopLayout()); + ta.start(); + at.addAppender(ta); + ta = new TestAppender(); + ta.setLayout(new NopLayout()); + ta.setName("test"); + ta.start(); + at.addAppender(ta); + int size = at.appendLoopOnAppenders(event); + assertTrue("Incorrect number of appenders", size == 2); + } + + public void testIteratorForAppenders() throws Exception { + TestAppender ta = new TestAppender(); + ta.setLayout(new NopLayout()); + ta.start(); + at.addAppender(ta); + TestAppender tab = new TestAppender(); + tab.setLayout(new NopLayout()); + tab.setName("test"); + tab.start(); + at.addAppender(tab); + Iterator> iter = at.iteratorForAppenders(); + int size = 0; + while (iter.hasNext()) { + ++size; + TestAppender app = iter.next(); + assertTrue("Bad Appender", app == ta || app == tab); + } + assertTrue("Incorrect number of appenders", size == 2); + } + + public void getGetAppender() throws Exception { + TestAppender ta = new TestAppender(); + ta.setLayout(new NopLayout()); + ta.start(); + at.addAppender(ta); + ta.setName("test1"); + TestAppender tab = new TestAppender(); + tab.setLayout(new NopLayout()); + tab.setName("test"); + tab.start(); + at.addAppender(tab); + Appender a = at.getAppender("test"); + assertNotNull("Could not find appender", a); + assertTrue("Wrong appender", a == tab); + a = at.getAppender("test1"); + assertNotNull("Could not find appender", a); + assertTrue("Wrong appender", a == ta); + a = at.getAppender("NotThere"); + assertNull("Appender was returned", a); + } + + public void testIsAttached() throws Exception { + TestAppender ta = new TestAppender(); + ta.setLayout(new NopLayout()); + ta.start(); + at.addAppender(ta); + ta.setName("test1"); + TestAppender tab = new TestAppender(); + tab.setLayout(new NopLayout()); + tab.setName("test"); + tab.start(); + at.addAppender(tab); + assertTrue("Appender is not attached", at.isAttached(ta)); + assertTrue("Appender is not attached", at.isAttached(tab)); + } + + public void testDetachAndStopAllAppenders() throws Exception { + TestAppender ta = new TestAppender(); + ta.setLayout(new NopLayout()); + ta.start(); + at.addAppender(ta); + ta.setName("test1"); + TestAppender tab = new TestAppender(); + tab.setLayout(new NopLayout()); + tab.setName("test"); + tab.start(); + at.addAppender(tab); + assertTrue("Appender was not started", tab.isStarted()); + at.detachAndStopAllAppenders(); + assertNull("Appender was not removed", at.getAppender("test")); + assertFalse("Appender was not stopped", tab.isStarted()); + } + + public void testDetachAppender() throws Exception { + TestAppender ta = new TestAppender(); + ta.setLayout(new NopLayout()); + ta.start(); + at.addAppender(ta); + ta.setName("test1"); + TestAppender tab = new TestAppender(); + tab.setLayout(new NopLayout()); + tab.setName("test"); + tab.start(); + at.addAppender(tab); + assertTrue("Appender not detached", at.detachAppender(tab)); + assertNull("Appender was not removed", at.getAppender("test")); + assertFalse("Appender detach error", at.detachAppender(tab)); + } + + public void testDetachAppenderByName() throws Exception { + TestAppender ta = new TestAppender(); + ta.setLayout(new NopLayout()); + ta.start(); + at.addAppender(ta); + ta.setName("test1"); + TestAppender tab = new TestAppender(); + tab.setLayout(new NopLayout()); + tab.setName("test"); + tab.start(); + at.addAppender(tab); + Appender a = at.detachAppender("test"); + assertNotNull("Appender not detached", a); + a = at.detachAppender("test1"); + assertNotNull("Appender not detached", a); + a = at.detachAppender("test1"); + assertNull("Appender detach error", a); + } + + private static class TestEvent { + + } + + + private static class TestAppender extends AppenderBase { + + protected void append(TestEvent event) { + + } + + } +} \ No newline at end of file Index: logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java =================================================================== --- logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java (revision 0) +++ logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java (revision 0) @@ -0,0 +1,15 @@ +package ch.qos.logback.core.spi; + +import junit.framework.TestCase; +import junit.framework.Test; +import junit.framework.TestSuite; + + +public class PackageTest extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(); + suite.addTestSuite(AppenderAttachableImplTest.class); + return suite; + } +} Index: logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachableImpl.java =================================================================== --- logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachableImpl.java (revision 1828) +++ logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachableImpl.java (working copy) @@ -9,20 +9,29 @@ */ package ch.qos.logback.core.spi; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; import ch.qos.logback.core.Appender; /** - * A straightforward implementation of the {@link AppenderAttachable} interface. + * An implementation of the {@link AppenderAttachable} interface. This implementation would be + * much simpler if the name attribute of the Appender interface was required and was immutable. + * Since the name is not required to be set and might be changed or set after attaching it, this + * class must account for those rare occurances. * * @author Ceki Gülcü */ public class AppenderAttachableImpl implements AppenderAttachable { - final private List> appenderList = new ArrayList>(); + private final ConcurrentMap> appenders = + new ConcurrentHashMap>(); + private AtomicLong counter = new AtomicLong(); + private static final String APPENDER_NAME_ROOT = "__APPENDER__:"; /** * Attach an appender. If the appender is already in the list in won't be @@ -33,21 +42,21 @@ if (newAppender == null) { throw new IllegalArgumentException("Cannot null as an appener"); } - if (!appenderList.contains(newAppender)) { - appenderList.add(newAppender); + String name = newAppender.getName(); + if (name == null) + { + name = APPENDER_NAME_ROOT + counter.getAndIncrement(); } + appenders.putIfAbsent(name, newAppender); } /** * Call the doAppend method on all attached appenders. */ public int appendLoopOnAppenders(E e) { - int size = 0; - Appender appender; - - size = appenderList.size(); - for (int i = 0; i < size; i++) { - appender = (Appender) appenderList.get(i); + Collection> coll = appenders.values(); + int size = coll.size(); + for (Appender appender : coll) { appender.doAppend(e); } return size; @@ -60,7 +69,7 @@ * @return Enumeration An enumeration of attached appenders. */ public Iterator iteratorForAppenders() { - return appenderList.iterator(); + return appenders.values().iterator(); } /** @@ -74,15 +83,13 @@ if (name == null) { return null; } - - int size = appenderList.size(); - Appender appender; - - for (int i = 0; i < size; i++) { - appender = appenderList.get(i); - - if (name.equals(appender.getName())) { - return appender; + Appender appender = appenders.get(name); + if (appender != null) { + return appender; + } + for (Appender a : appenders.values()) { + if (name.equals(a.getName())) { + return a; } } @@ -100,17 +107,18 @@ return false; } - int size = appenderList.size(); - Appender a; - - for (int i = 0; i < size; i++) { - a = (Appender) appenderList.get(i); - + String name = appender.getName(); + if (name != null) { + Appender a = appenders.get(name); + if (a != null && a == appender) { + return true; + } + } + for (Appender a : appenders.values()) { if (a == appender) { return true; } } - return false; } @@ -118,14 +126,10 @@ * Remove and stop all previously attached appenders. */ public void detachAndStopAllAppenders() { - int len = appenderList.size(); - - for (int i = 0; i < len; i++) { - Appender a = (Appender) appenderList.get(i); - a.stop(); + for (Map.Entry> entry : appenders.entrySet()) { + entry.getValue().stop(); + appenders.remove(entry.getKey(), entry.getValue()); } - - appenderList.clear(); } /** @@ -136,7 +140,16 @@ if (appender == null) { return false; } - return appenderList.remove(appender); + String name = appender.getName(); + if (appenders.containsKey(name)) { + return appenders.remove(name, appender); + } + for (Map.Entry> entry : appenders.entrySet()) { + if (appender == entry.getValue()) { + return appenders.remove(entry.getKey(), appender); + } + } + return false; } /** @@ -147,12 +160,13 @@ if (name == null) { return null; } - - int size = appenderList.size(); - - for (int i = 0; i < size; i++) { - if (name.equals((appenderList.get(i)).getName())) { - return appenderList.remove(i); + Appender a = appenders.remove(name); + if (a != null) { + return a; + } + for (Map.Entry> entry : appenders.entrySet()) { + if (name.equals(entry.getValue().getName())) { + return appenders.remove(entry.getKey()); } } return null;