[logback-dev] svn commit: r1966 - in logback/trunk: logback-classic/src/main/java/ch/qos/logback/classic logback-classic/src/main/java/ch/qos/logback/classic/jmx logback-classic/src/main/java/ch/qos/logback/classic/joran/action logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet logback-classic/src/main/java/ch/qos/logback/classic/spi logback-classic/src/test/java/ch/qos/logback/classic logback-classic/src/test/java/ch/qos/logback/classic/db logback-classic/src/test/java/ch/qos/logback/classic/jmx logback-classic/src/test/java/ch/qos/logback/classic/spi logback-classic/src/test/java/ch/qos/logback/classic/util logback-examples/src/main/java/chapter4 logback-examples/src/main/java/chapter4/socket logback-examples/src/main/java/chapter7 logback-site/src/site/pages logback-site/src/site/pages/manual

noreply.ceki at qos.ch noreply.ceki at qos.ch
Wed Nov 12 16:26:29 CET 2008


Author: ceki
Date: Wed Nov 12 16:26:28 2008
New Revision: 1966

Modified:
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationTest.java
   logback/trunk/logback-examples/src/main/java/chapter4/ExitWoes2.java
   logback/trunk/logback-examples/src/main/java/chapter4/socket/SocketClient2.java
   logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java
   logback/trunk/logback-site/src/site/pages/manual/appenders.html
   logback/trunk/logback-site/src/site/pages/manual/jmxConfig.html
   logback/trunk/logback-site/src/site/pages/manual/joran.html
   logback/trunk/logback-site/src/site/pages/manual/mdc.html
   logback/trunk/logback-site/src/site/pages/news.html

Log:
While working on JMXConfigurator, it became apparent that
some components needed to survive resetting of the context. 

For example, when the JMXConfigrator.reload*() methods are invoked, if
the JMXConfigurator instance is unregistered from the MBeans server
while the reload*() method is still running, then a
NullPointerException is thrown from the JMX server. Thus,
JMXConfigurator needs to survive the resetting of the LoggerContext it
is attached to. However, when the host application is stopped (or
restarted), the JMXConfigrator needs to be unregistered from the
MBeans server, otherwise severe memory leaks occur.

Consequently, LoggerContext now offers two related but distinct
methods, reset() and stop(). The former should be called if you just
wish to reset the configuration of the logger context. The former
should be called when you wish to completely stop the LoggerContext so
that it can not be reused ever again.

The LoggerContext.shutdownAndReset method no longer exists. (Simply
deprecating in favor of stop() or reset() seemed too error prone.)


Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java	Wed Nov 12 16:26:28 2008
@@ -63,9 +63,9 @@
 
   boolean started = false;
 
+  
   public LoggerContext() {
     super();
-    System.out.println(" LoggerContext()=============================================================");
     this.loggerCache = new Hashtable<String, Logger>();
     this.loggerContextRemoteView = new LoggerContextRemoteView(this);
     this.root = new Logger(ROOT_NAME, null, this);
@@ -190,25 +190,15 @@
     return loggerContextRemoteView;
   }
 
-  @Override
-  protected void finalize() {
-    System.out.println("**************** LoggerContext finalized");
-  }
+  /**
+   * This method closes all appenders,
+   */
   public void reset() {
     root.recursiveReset();
     clearAllTurboFilters();
     fireOnReset();
-    // TODO is it a good idea to reset the status listeners?
+    resetListenersExceptResetResistant();
     resetStatusListeners();
-    resetListeners();
-  }
-
-  /**
-   * @deprecated Please use reset() method instead
-   */
-
-  public void shutdownAndReset() {
-    reset();
   }
 
   private void resetStatusListeners() {
@@ -269,7 +259,18 @@
     loggerContextListenerList.remove(listener);
   }
 
-  private void resetListeners() {
+  private void resetListenersExceptResetResistant() {
+    List<LoggerContextListener> toRetain = new ArrayList<LoggerContextListener>();
+    
+    for(LoggerContextListener lcl: loggerContextListenerList) {
+      if(lcl.isResetResistant()) {
+        toRetain.add(lcl);
+      }
+    }
+    loggerContextListenerList.retainAll(toRetain);
+  }
+  
+  private void resetAllListeners() {
     loggerContextListenerList.clear();
   }
 
@@ -289,6 +290,12 @@
     }
   }
 
+  private void fireOnStop() {
+    for (LoggerContextListener listener : loggerContextListenerList) {
+      listener.onStop(this);
+    }
+  }
+  
   // === end listeners ==============================================
 
   public boolean isStarted() {
@@ -302,11 +309,13 @@
 
   public void stop() {
     reset();
+    fireOnStop();
+    resetAllListeners();
     started = false;
   }
 
-//  @Override
-//  public String toString() {
-//    return this.getClass().getName() + "[" + getName() + "]";
-//  }
+   @Override
+  public String toString() {
+    return this.getClass().getName() + "[" + getName() + "]";
+  }
 }

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java	Wed Nov 12 16:26:28 2008
@@ -56,39 +56,42 @@
   MBeanServer mbs;
   ObjectName objectName;
   String objectNameAsString;
-  
+
+  // whether to output status events on the console when reloading the
+  // configuration
+  boolean debug = true;
+
   boolean started;
 
-  @Override
-  public void finalize() {
-    System.out.println("....... finalize() "+this);
-  }
-  
   public JMXConfigurator(LoggerContext loggerContext, MBeanServer mbs,
       ObjectName objectName) {
-    System.out.println("....... constructor() "+this);
     started = true;
     this.context = loggerContext;
     this.loggerContext = loggerContext;
     this.mbs = mbs;
     this.objectName = objectName;
     this.objectNameAsString = objectName.toString();
-    removePreviousInstanceAsListener();
-    loggerContext.addListener(this);
-
+    if (previouslyRegisteredListenerWithSameObjectName()) {
+      addError("Previously registered JMXConfigurator named ["
+          + objectNameAsString + "] in the logger context named ["
+          + loggerContext.getName() + "]");
+    } else {
+      // register as a listener only if there are no homonyms
+      loggerContext.addListener(this);
+    }
   }
 
-  private void removePreviousInstanceAsListener() {
+  private boolean previouslyRegisteredListenerWithSameObjectName() {
     List<LoggerContextListener> lcll = loggerContext.getCopyOfListenerList();
     for (LoggerContextListener lcl : lcll) {
       if (lcl instanceof JMXConfigurator) {
         JMXConfigurator jmxConfigurator = (JMXConfigurator) lcl;
         if (objectName.equals(jmxConfigurator.objectName)) {
-          addInfo("Removing previous JMXConfigurator from the logger context listener list");
-          loggerContext.removeListener(lcl);
+          return true;
         }
       }
     }
+    return false;
   }
 
   public void reloadDefaultConfiguration() throws JoranException {
@@ -121,23 +124,31 @@
     StatusManager sm = loggerContext.getStatusManager();
     sm.add(statusListener);
   }
-  
+
+  void removeStatusListener(StatusListener statusListener) {
+    StatusManager sm = loggerContext.getStatusManager();
+    sm.remove(statusListener);
+  }
+
   public void reloadByURL(URL url) throws JoranException {
     StatusListenerAsList statusListenerAsList = new StatusListenerAsList();
 
     addStatusListener(statusListenerAsList);
     addInfo("Resetting context: " + loggerContext.getName());
     loggerContext.reset();
+    // after a reset the statusListenerAsList gets removed as a listener
     addStatusListener(statusListenerAsList);
 
-    
     try {
       JoranConfigurator configurator = new JoranConfigurator();
       configurator.setContext(loggerContext);
       configurator.doConfigure(url);
       addInfo("Context: " + loggerContext.getName() + " reloaded.");
     } finally {
-      StatusPrinter.print(statusListenerAsList.getStatusList());
+      removeStatusListener(statusListenerAsList);
+      if (debug) {
+        StatusPrinter.print(statusListenerAsList.getStatusList());
+      }
     }
   }
 
@@ -219,23 +230,23 @@
   }
 
   /**
-   * When the associated LoggerContext is reset, this configurator must be
+   * When the associated LoggerContext is stopped, this configurator must be
    * unregistered
    */
-  public void onReset(LoggerContext context) {
-    if(!started) {
-      addInfo("onReset() method called on a stopped JMXActivator [" + objectNameAsString + "]");;
+  public void onStop(LoggerContext context) {
+    if (!started) {
+      addInfo("onStop() method called on a stopped JMXActivator ["
+          + objectNameAsString + "]");
+      return;
     }
-    System.out.println("Unregistering JMXConfigurator");
-    
     if (mbs.isRegistered(objectName)) {
       try {
         addInfo("Unregistering mbean [" + objectNameAsString + "]");
         mbs.unregisterMBean(objectName);
       } catch (InstanceNotFoundException e) {
         // this is theoretically impossible
-        addError("Unable to find a verifiably registered mbean [" + objectNameAsString
-            + "]", e);
+        addError("Unable to find a verifiably registered mbean ["
+            + objectNameAsString + "]", e);
       } catch (MBeanRegistrationException e) {
         addError("Failed to unregister [" + objectNameAsString + "]", e);
       }
@@ -245,9 +256,21 @@
     }
     stop();
   }
-  
+
+  public void onReset(LoggerContext context) {
+    addInfo("onReset() method called JMXActivator [" + objectNameAsString + "]");
+  }
+
+  /**
+   * JMXConfigrator should not be removed subsequent to a LoggerContext reset.
+   * 
+   * @return
+   */
+  public boolean isResetResistant() {
+    return true;
+  }
+
   private void clearFields() {
-    System.out.println("Clearing fields");
     mbs = null;
     objectName = null;
     loggerContext = null;
@@ -257,12 +280,13 @@
     started = false;
     clearFields();
   }
+
   public void onStart(LoggerContext context) {
     // nop
   }
 
-//  @Override
-//  public String toString() {
-//    return this.getClass().getName() + "(" + context.getName() + ")";
-//  }
+  @Override
+  public String toString() {
+    return this.getClass().getName() + "(" + context.getName() + ")";
+  }
 }

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java	Wed Nov 12 16:26:28 2008
@@ -9,8 +9,6 @@
  */
 package ch.qos.logback.classic.jmx;
 
-import java.lang.management.ManagementFactory;
-
 import javax.management.InstanceNotFoundException;
 import javax.management.MBeanRegistrationException;
 import javax.management.MBeanServer;
@@ -25,8 +23,8 @@
 
   static final String DOMAIN = "ch.qos.logback.classic";
 
-  static public String getObjectNameFor(Context context, Class type) {
-    String objectNameAsStr = DOMAIN + ":Name=" + context.getName() + ",Type="
+  static public String getObjectNameFor(String contextName, Class type) {
+    String objectNameAsStr = DOMAIN + ":Name=" + contextName + ",Type="
         + type.getName();
     return objectNameAsStr;
   }
@@ -46,25 +44,17 @@
     }
   }
 
-  public static JMXConfigurator register(LoggerContext loggerContext,
-      ObjectName objectName, Object caller) {
-    try {
-      MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
-
-      JMXConfigurator jmxConfigurator = new JMXConfigurator(loggerContext, mbs,
-          objectName);
+  public static boolean isRegistered(MBeanServer mbs, ObjectName objectName) {
+    return mbs.isRegistered(objectName);
+  }
 
-      if (mbs.isRegistered(objectName)) {
-        StatusUtil.addWarn(loggerContext, caller,
-            "Unregistering existing MBean named ["
-                + objectName.getCanonicalName() + "]");
-        mbs.unregisterMBean(objectName);
-      }
+  public static void createAndRegisterJMXConfigurator(
+      MBeanServer mbs, LoggerContext loggerContext,
+      JMXConfigurator jmxConfigurator, ObjectName objectName, Object caller) {
+    try {
       mbs.registerMBean(jmxConfigurator, objectName);
-      return jmxConfigurator;
     } catch (Exception e) {
       StatusUtil.addError(loggerContext, caller, "Failed to create mbean", e);
-      return null;
     }
   }
 

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java	Wed Nov 12 16:26:28 2008
@@ -1,5 +1,8 @@
 package ch.qos.logback.classic.joran.action;
 
+import java.lang.management.ManagementFactory;
+
+import javax.management.MBeanServer;
 import javax.management.ObjectName;
 
 import org.xml.sax.Attributes;
@@ -15,7 +18,7 @@
 public class JMXConfiguratorAction extends Action {
 
   static final String OBJECT_NAME_ATTRIBUTE_NAME = "objectName";
-  static final String SUFFIX_ATTRIBUTE_NAME = "suffix";
+  static final String CONTEXT_NAME_ATTRIBUTE_NAME = "contextName";
   static final char JMX_NAME_SEPARATOR = ',';
   
   @Override
@@ -23,31 +26,44 @@
       throws ActionException {
     addInfo("begin");
 
+
+    String contextName = context.getName();
+    String contextNameAttributeVal = attributes
+    .getValue(CONTEXT_NAME_ATTRIBUTE_NAME);
+    if(!OptionHelper.isEmpty(contextNameAttributeVal)) {
+      contextName = contextNameAttributeVal;
+    }
+
     String objectNameAsStr;
     String objectNameAttributeVal = attributes
         .getValue(OBJECT_NAME_ATTRIBUTE_NAME);
-    String suffixAttributeVal = attributes
-    .getValue(SUFFIX_ATTRIBUTE_NAME);
-
     if (OptionHelper.isEmpty(objectNameAttributeVal)) {
-      objectNameAsStr = MBeanUtil.getObjectNameFor((LoggerContext) context,
+      objectNameAsStr = MBeanUtil.getObjectNameFor(contextName,
           JMXConfigurator.class);
     } else {
       objectNameAsStr = objectNameAttributeVal;
     }
 
-    if(!OptionHelper.isEmpty(suffixAttributeVal)) {
-      if(suffixAttributeVal.indexOf(0) != JMX_NAME_SEPARATOR) {
-        objectNameAsStr += JMX_NAME_SEPARATOR;
-      }
-      objectNameAsStr += suffixAttributeVal;
-    }
-    
     ObjectName objectName = MBeanUtil.string2ObjectName(context, this,
         objectNameAsStr);
-
-    if (objectName != null) {
-      MBeanUtil.register((LoggerContext) context, objectName, this);
+    if (objectName == null) {
+      addError("Failed to for ObjectName for ["+objectNameAsStr+"]");
+      return;
+    }
+    
+    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+    if(!MBeanUtil.isRegistered(mbs, objectName)) {
+      // register only of the named JMXConfigurator has not been previously
+      // registered. Unregistering an MBean within invocation of itself
+      // caused jconsole to throw an NPE. (This occurs when the reload* method
+      // unregisters the 
+      JMXConfigurator jmxConfigurator = new JMXConfigurator((LoggerContext) context, mbs,
+          objectName);
+      try {     
+        mbs.registerMBean(jmxConfigurator, objectName);
+      } catch (Exception e) {
+        addError("Failed to create mbean", e);
+      }
     }
 
   }

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java	Wed Nov 12 16:26:28 2008
@@ -42,8 +42,9 @@
       LoggerContext context = selector.detachLoggerContext(loggerContextName);
       if (context != null) {
         Logger logger = context.getLogger(LoggerContext.ROOT_NAME);
-        logger.warn("Shutting down context " + loggerContextName);
-        context.reset();
+        logger.warn("Stopping logger context " + loggerContextName);
+        // when the web-app is destroyed, its logger context should be stopped
+        context.stop();
       } else {
         System.out.println("No context named " + loggerContextName + " was found.");
       }

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java	Wed Nov 12 16:26:28 2008
@@ -13,7 +13,14 @@
 
 public interface LoggerContextListener {
   
-  public void onReset(LoggerContext context);
-  public void onStart(LoggerContext context);
 
+  /**
+   * Some listeners should not be removed when the LoggerContext is
+   * reset. Such listeners are said to be reset resistant.
+   * @return whether this listener is reset resistant or not.
+   */
+  public boolean isResetResistant();
+  public void onStart(LoggerContext context);
+  public void onReset(LoggerContext context);
+  public void onStop(LoggerContext context);
 }

Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java	(original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java	Wed Nov 12 16:26:28 2008
@@ -14,7 +14,7 @@
   LoggerContext context;
   Logger logger;
   Marker blueMarker = MarkerFactory.getMarker(BLUE);
-  
+
   @Override
   protected void setUp() throws Exception {
     super.setUp();
@@ -40,7 +40,7 @@
     filter.start();
     context.addTurboFilter(filter);
   }
-  
+
   private void addAcceptBLUEFilter() {
     MarkerFilter filter = new MarkerFilter();
     filter.setMarker(BLUE);
@@ -48,7 +48,7 @@
     filter.start();
     context.addTurboFilter(filter);
   }
-  
+
   private void addDenyBLUEFilter() {
     MarkerFilter filter = new MarkerFilter();
     filter.setMarker(BLUE);
@@ -56,8 +56,6 @@
     filter.start();
     context.addTurboFilter(filter);
   }
-  
-  
 
   public void testIsDebugEnabledWithYesFilter() {
     addYesFilter();
@@ -131,13 +129,13 @@
     assertFalse(logger.isDebugEnabled(blueMarker));
   }
 
-  public void testLoggingContextShutdownAndReset() {
+  public void testLoggingContextReset() {
     addYesFilter();
     assertNotNull(context.getTurboFilterList().get(0));
     context.reset();
     assertEquals(0, context.getTurboFilterList().size());
   }
-  
+
 }
 
 class YesFilter extends TurboFilter {

Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java	(original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java	Wed Nov 12 16:26:28 2008
@@ -46,7 +46,8 @@
 
   @After
   public void tearDown() throws Exception {
-    lc.reset();
+    // lc will never be used again
+    lc.stop();
   }
 
   public void doTest(String configFile) throws JoranException {

Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java	(original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java	Wed Nov 12 16:26:28 2008
@@ -2,6 +2,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -23,21 +24,23 @@
 
 public class JMXConfiguratorTest {
 
-  static MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+  MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
   LoggerContext lc = new LoggerContext();
   Logger testLogger  = lc.getLogger(this.getClass());
 
   List<LoggerContextListener> listenerList;
   int diff = RandomUtil.getPositiveInt();
 
+  
   @Before
   public void setUp() throws Exception {
     lc.setName("context-" + diff);
+    assertNotNull(mbs);
   }
 
   @After
   public void tearDown() throws Exception {
-    lc.reset();
+    lc.stop();
   }
 
   @Override
@@ -46,45 +49,69 @@
   }
 
   @Test
-  public void contextListening() {
-    String objectNameAsStr = "ch.qos.logback.toto" + ":Name=" + lc.getName()
+  public void contextReset() throws Exception {
+    String randomizedObjectNameAsStr = "ch.qos.logback."+diff + ":Name=" + lc.getName()
         + ",Type=" + this.getClass().getName();
-    ObjectName on = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr);
 
-    MBeanUtil.register(lc, on, this);
+    ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, randomizedObjectNameAsStr);
+    JMXConfigurator jmxConfigurator = new JMXConfigurator(lc, mbs, objectName);
+    mbs.registerMBean(jmxConfigurator, objectName);
+    
     listenerList = lc.getCopyOfListenerList();
     assertEquals(1, listenerList.size());
+    
     lc.reset();
+    
+    // check that after lc.reset, jmxConfigurator should still be
+    // registered as a listener in the loggerContext and also as an
+    // MBean in mbs
     listenerList = lc.getCopyOfListenerList();
-    assertEquals(0, listenerList.size());
+    assertEquals(1, listenerList.size());
+    assertTrue(listenerList.contains(jmxConfigurator));
 
-    MBeanUtil.register(lc, on, this);
+    assertTrue(mbs.isRegistered(objectName));
+  }
+  
+  @Test
+  public void contextStop() throws Exception {
+    String randomizedObjectNameAsStr = "ch.qos.logback."+diff + ":Name=" + lc.getName()
+        + ",Type=" + this.getClass().getName();
+
+    ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, randomizedObjectNameAsStr);
+    JMXConfigurator jmxConfigurator = new JMXConfigurator(lc, mbs, objectName);
+    mbs.registerMBean(jmxConfigurator, objectName);
+    
     listenerList = lc.getCopyOfListenerList();
     assertEquals(1, listenerList.size());
+    
+    lc.stop();
+    
+    // check that after lc.stop, jmxConfigurator is no longer
+    // registered as a listener in the loggerContext nor as an
+    // MBean in mbs
+    listenerList = lc.getCopyOfListenerList();
+    assertEquals(0, listenerList.size());
 
+    assertFalse(mbs.isRegistered(objectName));
   }
 
   @Test
-  public void testRemovalOfPreviousIntanceFromTheContextListenerList() {
+  public void testNonRemovalOfPreviousIntanceFromTheContextListenerList() {
     String objectNameAsStr = "ch.qos.logback.toto" + ":Name=" + lc.getName()
         + ",Type=" + this.getClass().getName();
-
-    ObjectName on = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr);
-    JMXConfigurator jmxConfigurator0 = MBeanUtil.register(lc, on, this);
-
+    ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr);
+    JMXConfigurator jmxConfigurator0 = new JMXConfigurator(lc, mbs, objectName);
+    
     listenerList = lc.getCopyOfListenerList();
-    assertEquals(1, listenerList.size());
     assertTrue(listenerList.contains(jmxConfigurator0));
 
-    JMXConfigurator jmxConfigurator1 = MBeanUtil.register(lc, on, this);
+    JMXConfigurator jmxConfigurator1 = new JMXConfigurator(lc, mbs, objectName);
     listenerList = lc.getCopyOfListenerList();
     assertEquals(1, listenerList.size());
-    assertFalse("old configurator should be absent", listenerList
+    assertTrue("old configurator should be present", listenerList
         .contains(jmxConfigurator0));
-    assertTrue("new configurator should be present", listenerList
+    assertFalse("new configurator should be absent", listenerList
         .contains(jmxConfigurator1));
-
-    // StatusPrinter.print(lc);
   }
 
   @Test

Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java	(original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java	Wed Nov 12 16:26:28 2008
@@ -4,16 +4,33 @@
 
 public class BasicContextListener implements LoggerContextListener {
 
-  boolean updated = false;
+  enum UpdateType { NONE, START, RESET, STOP};
+  
+  UpdateType updateType = UpdateType.NONE;
   LoggerContext context;
   
+  boolean resetResistant;
+  
+  public void setResetResistant(boolean resetResistant) {
+    this.resetResistant = resetResistant;
+  }
+  
   public void onReset(LoggerContext context) {
-    updated = true;
+    updateType =  UpdateType.RESET;
     this.context = context;
     
   }
   public void onStart(LoggerContext context) {
-    updated = true;
+    updateType =  UpdateType.START;;
     this.context = context;
   }
+  
+  public void onStop(LoggerContext context) {
+    updateType =  UpdateType.STOP;;
+    this.context = context;
+  }
+  
+  public boolean isResetResistant() {
+    return resetResistant;
+  }
 }

Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java	(original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java	Wed Nov 12 16:26:28 2008
@@ -2,6 +2,7 @@
 
 import junit.framework.TestCase;
 import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.BasicContextListener.UpdateType;
 
 public class ContextListenerTest extends TestCase {
   
@@ -17,13 +18,27 @@
   
   public void testNotifyOnReset() {
     context.reset();
-    assertTrue(listener.updated);
+    assertEquals(UpdateType.RESET, listener.updateType);
     assertEquals(listener.context, context);
   }
+
+  public void testNotifyOnStopResistant() {
+    listener.setResetResistant(true);
+    context.stop();
+    assertEquals(UpdateType.STOP, listener.updateType);
+    assertEquals(listener.context, context);
+  }
+
+  public void testNotifyOnStopNotResistant() {
+    context.stop();
+    assertEquals(UpdateType.RESET, listener.updateType);
+    assertEquals(listener.context, context);
+  }
+
   
   public void testNotifyOnStart() {
     context.start();
-    assertTrue(listener.updated);
+    assertEquals(UpdateType.START, listener.updateType);
     assertEquals(listener.context, context);
   }
 }

Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationTest.java	(original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationTest.java	Wed Nov 12 16:26:28 2008
@@ -46,7 +46,7 @@
   public void tearDown() throws Exception {
     System.clearProperty(ContextInitializer.CONFIG_FILE_PROPERTY);
     System.clearProperty(ContextInitializer.STATUS_LISTENER_CLASS);
-    lc.reset();
+    lc.reset(); // we are going to need this context
   }
 
   @Test
@@ -73,7 +73,7 @@
       assertTrue(appender instanceof ConsoleAppender);
     }
     {
-      lc.reset();
+      lc.stop();
       Appender appender = root.getAppender("STDOUT");
       assertNull(appender);
     }
@@ -88,7 +88,7 @@
   }
   
   public void doAutoConfigFromSystemProperties(String val) throws JoranException {
-    //lc.shutdownAndReset();
+    //lc.reset();
     System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, val);
     new ContextInitializer(lc).autoConfig();
     Appender appender = root.getAppender("AUTO_BY_SYSTEM_PROPERTY");

Modified: logback/trunk/logback-examples/src/main/java/chapter4/ExitWoes2.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter4/ExitWoes2.java	(original)
+++ logback/trunk/logback-examples/src/main/java/chapter4/ExitWoes2.java	Wed Nov 12 16:26:28 2008
@@ -42,7 +42,7 @@
 
     logger.debug("Hello world.");
     
-    lc.reset();
+    lc.stop();
     
     StatusPrinter.print(lc);
   }

Modified: logback/trunk/logback-examples/src/main/java/chapter4/socket/SocketClient2.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter4/socket/SocketClient2.java	(original)
+++ logback/trunk/logback-examples/src/main/java/chapter4/socket/SocketClient2.java	Wed Nov 12 16:26:28 2008
@@ -45,7 +45,7 @@
     if (configFile.endsWith(".xml")) {
       LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
       JoranConfigurator configurator = new JoranConfigurator();
-      lc.reset();
+      lc.stop();
       configurator.setContext(lc);
       configurator.doConfigure(configFile);
     }

Modified: logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java	(original)
+++ logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java	Wed Nov 12 16:26:28 2008
@@ -80,7 +80,7 @@
     try {
       JoranConfigurator configurator = new JoranConfigurator();
       configurator.setContext(lc);
-      lc.reset();
+      lc.stop();
       URL url = Loader.getResourceBySelfClassLoader("chapter7/simpleMDC.xml");
       configurator.doConfigure(url);
     } catch (JoranException je) {

Modified: logback/trunk/logback-site/src/site/pages/manual/appenders.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/appenders.html	(original)
+++ logback/trunk/logback-site/src/site/pages/manual/appenders.html	Wed Nov 12 16:26:28 2008
@@ -306,7 +306,7 @@
 
   public static void main(String[] args) throws Exception {
     LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
-    lc.shutdownAndReset(); // we want to override the default-config.
+    lc.reset(); // we want to override the default-config.
     WriterAppender&lt;LoggingEvent> writerAppender = new WriterAppender&lt;LoggingEvent>();
     writerAppender.setContext(lc);
     writerAppender.setLayout(new EchoLayout&lt;&lt;LoggingEvent>());
@@ -335,7 +335,7 @@
 	the underlying output stream. As astonishing as this may seem,
 	running <code>ExitWoes1</code> will not produce any data in the file
 	<em>exitWoes1.log</em> because the Java VM does not flush output
-	streams when it exits.  Calling the <code>shutdownAndReset()</code>
+	streams when it exits.  Calling the <code>stop()</code>
 	method of a <code>LoggerContext</code> ensures that all appenders in
 	the hierarchy are closed and their buffers are flushed. The <code><a
 	href="../xref/chapter4/ExitWoes2.html">ExitWoes2</a></code> class
@@ -1212,31 +1212,34 @@
 			lost due to server unavailability.
 		</p>
 		
-		<p>
-			Even if a <code>SocketAppender</code> is no longer attached to any logger, 
-			it will not be garbage collected in the presence of a connector thread. 
-			A connector thread exists only if the connection to the server is down. 
-			To avoid this garbage collection problem, you should close the <code>SocketAppender</code> 
-			explicitly. Long lived applications which create/destroy many 
-			<code>SocketAppender</code> instances should be aware of this 
-			garbage collection problem. Most other applications can safely ignore it. 
-			If the JVM hosting the <code>SocketAppender</code> exits before the 
-			<code>SocketAppender</code> is closed, either explicitly or subsequent 
-			to garbage collection, then there might be untransmitted data in the 
-			pipe which may be lost. This is a common problem on Windows based systems.  
-			To avoid lost data, it is usually sufficient to <code>close()</code> the 
-			<code>SocketAppender</code> either explicitly or by calling the 
-			<code>LoggerContext</code>'s <code>shutdownAndReset()</code> method before exiting the application.
-		</p>
-		
-		<p>
-			The remote server is identified by the <span class="option">RemoteHost</span> and 
-			<span class="option">Port</span> options. 
-			<code>SocketAppender</code> options are listed in the following table.
+		<p>Even if a <code>SocketAppender</code> is no longer attached to
+		any logger, it will not be garbage collected in the presence of a
+		connector thread.  A connector thread exists only if the
+		connection to the server is down.  To avoid this garbage
+		collection problem, you should close the
+		<code>SocketAppender</code> explicitly. Long lived applications
+		which create/destroy many <code>SocketAppender</code> instances
+		should be aware of this garbage collection problem. Most other
+		applications can safely ignore it.  If the JVM hosting the
+		<code>SocketAppender</code> exits before the
+		<code>SocketAppender</code> is closed, either explicitly or
+		subsequent to garbage collection, then there might be
+		untransmitted data in the pipe which may be lost. This is a common
+		problem on Windows based systems.  To avoid lost data, it is
+		usually sufficient to <code>close()</code> the
+		<code>SocketAppender</code> either explicitly or by calling the
+		<code>LoggerContext</code>'s <code>stop()</code>
+		method before exiting the application.
+		</p>
+		
+		<p>The remote server is identified by the <span
+		class="option">RemoteHost</span> and <span
+		class="option">Port</span> options.  <code>SocketAppender</code>
+		options are listed in the following table.
 		</p>
 
-	<table class="bodyTable">
-			<tr class="a">
+    <table class="bodyTable">
+      <tr class="a">
 			<th>Option Name</th>
 			<th>Type</th>
 			<th>Description</th>

Modified: logback/trunk/logback-site/src/site/pages/manual/jmxConfig.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/jmxConfig.html	(original)
+++ logback/trunk/logback-site/src/site/pages/manual/jmxConfig.html	Wed Nov 12 16:26:28 2008
@@ -12,9 +12,7 @@
     <title>JMX Configuration</title>
   </head>
   <body>
-    <script type="text/javascript">
-      prefix='../';	
-    </script>
+    <script type="text/javascript">prefix='../';</script>
     <script src="../templates/header.js" type="text/javascript"></script>
     <div id="left">
       <script src="../templates/left.js" type="text/javascript"></script>
@@ -46,7 +44,7 @@
     your logback configuration file, as shown below:
 		</p>
 
-<div class="source"><pre>&lt;configuration>
+    <p class="source">&lt;configuration>
   <b>&lt;jmxConfigurator /></b>
 
   &lt;appender name="console" class="ch.qos.logback.classic.ConsoleAppender">
@@ -59,7 +57,7 @@
     &lt;level value="debug"/>
     &lt;appender-ref ref="console" />
   &lt;/root>  
-&lt;/configuration></pre></div>
+&lt;/configuration></p>
 		
     <p>After you connect to your server with <em>jconsole</em>, on the
     MBeans panel, under "ch.qos.logback.classic.jmx.Configurator"
@@ -126,7 +124,7 @@
 
   public void contextDestroyed(ServletContextEvent sce) {
     <b>LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
-    <b>lc.reset();</b>
+    <b>lc.stop();</b>
   }
 
   public void contextInitialized(ServletContextEvent sce) {
@@ -134,10 +132,6 @@
 } </p>
 
 
-  
-
-
-
     <!-- ============ Multiple web-applications  ================== -->
 
     

Modified: logback/trunk/logback-site/src/site/pages/manual/joran.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/joran.html	(original)
+++ logback/trunk/logback-site/src/site/pages/manual/joran.html	Wed Nov 12 16:26:28 2008
@@ -383,7 +383,7 @@
       configurator.setContext(lc);
       // the context was probably already configured by default configuration 
       // rules
-      lc.shutdownAndReset(); 
+      lc.reset(); 
       configurator.doConfigure(args[0]);
     } catch (JoranException je) {
        je.printStackTrace();

Modified: logback/trunk/logback-site/src/site/pages/manual/mdc.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/mdc.html	(original)
+++ logback/trunk/logback-site/src/site/pages/manual/mdc.html	Wed Nov 12 16:26:28 2008
@@ -365,7 +365,7 @@
         LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
         JoranConfigurator configurator = new JoranConfigurator();
         configurator.setContext(lc);
-        lc.shutdownAndReset();
+        lc.reset();
         configurator.doConfigure(args[0]);
       } catch (JoranException je) {
         je.printStackTrace();

Modified: logback/trunk/logback-site/src/site/pages/news.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/news.html	(original)
+++ logback/trunk/logback-site/src/site/pages/news.html	Wed Nov 12 16:26:28 2008
@@ -135,8 +135,8 @@
 
   <p>The TurboFilterChain in a LoggerContext is <a
   href="http://svn.qos.ch/viewvc?view=rev&amp;revision=1678">now
-  cleared</a> when the shutdownAndReset method is called. This problem
-  was reported on May 1st, 2008, by Julia Hu on the logback
+  cleared</a> when the <code>reset</code>() method is called. This
+  problem was reported on May 1st, 2008, by Julia Hu on the logback
   developpers list.
   </p>
 


More information about the logback-dev mailing list