I made a slight adjustment to the pre-Java-5 solution and replaced it (unconditionally here, though this could easily be made conditional) with a Java 5 implementation.

Jess Holle wrote:
We as many others have been bitten by the synchronization nuances of log4j.

Attached is a proposed patch to reduce the scope of the problem. No, this is not yet regression tested due to the issues I e-mailed about previously with the regression test suite, but I'd like to spur some discussion as to what's wrong with such a change, etc.

I was considering using Java 5 features here and could, but the key issue here does not appear to require such fanciness to address.

--
Jess Holle

# This patch file was generated by NetBeans IDE
# Following Index: paths are relative to: D:\User 
Profiles\jessh.PTCNET\Desktop\log4jsvn
# This patch can be applied using context Tools: Patch action on respective 
folder.
# It uses platform neutral UTF-8 encoding and \n newlines.
# Above lines and this line are ignored by the patching process.
Index: src/main/java/org/apache/log4j/Category.java
--- src/main/java/org/apache/log4j/Category.java Base (BASE)
+++ src/main/java/org/apache/log4j/Category.java Locally Modified (Based On 
LOCAL)
@@ -36,7 +36,7 @@
 import org.apache.log4j.spi.LoggerRepository;
 import org.apache.log4j.spi.HierarchyEventListener;
 import org.apache.log4j.helpers.NullEnumeration;
-import org.apache.log4j.helpers.AppenderAttachableImpl;
+import org.apache.log4j.helpers.AppenderAttachableImpl5;
 
 import java.util.Enumeration;
 import java.util.MissingResourceException;
@@ -123,7 +123,7 @@
   protected LoggerRepository repository;
 
 
-  AppenderAttachableImpl aai;
+  AppenderAttachableImpl5 aai;
 
   /** Additivity is set to true by default, that is children inherit
       the appenders of their ancestors by default. If this variable is
@@ -159,7 +159,7 @@
   public
   void addAppender(Appender newAppender) {
     if(aai == null) {
-      aai = new AppenderAttachableImpl();
+      aai = new AppenderAttachableImpl5();
     }
     aai.addAppender(newAppender);
     repository.fireAddAppenderEvent(this, newAppender);
@@ -200,16 +200,15 @@
     int writes = 0;
 
     for(Category c = this; c != null; c=c.parent) {
-      // Protected against simultaneous call to addAppender, removeAppender,...
-      synchronized(c) {
-       if(c.aai != null) {
-         writes += c.aai.appendLoopOnAppenders(event);
+      // copy into field, so null check and usage are against same value
+      AppenderAttachableImpl5  cAai = c.aai;
+      if (cAai != null) {
+        writes += cAai.appendLoopOnAppenders(event);
        }
-       if(!c.additive) {
+      if (!c.additive) {
          break;
        }
       }
-    }
 
     if(writes == 0) {
       repository.emitNoAppenderWarning(this);
Index: src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
--- src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java Base 
(BASE)
+++ src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java Locally 
Modified (Based On LOCAL)
@@ -53,21 +53,29 @@
   }
 
   /**
-     Call the <code>doAppend</code> method on all attached appenders.  */
+     Call the <code>doAppend</code> method on all attached appenders.
+     Implementations should be thread safe against other methods on this class.
+   */
   public
   int appendLoopOnAppenders(LoggingEvent event) {
-    int size = 0;
-    Appender appender;
-
-    if(appenderList != null) {
-      size = appenderList.size();
+    // copy into field, so null check and usage are against same value    
+    Vector  appenderList = this.appenderList;
+    if ( appenderList != null ) {
+      Appender  appenders[];
+      synchronized ( appenderList ) {
+        int size = appenderList.size();
+        if ( size == 0 )
+          return 0;
+        appenders = (Appender[]) appenderList.toArray( new Appender[size] );
+      }
+      int size = appenders.length;
       for(int i = 0; i < size; i++) {
-       appender = (Appender) appenderList.elementAt(i);
-       appender.doAppend(event);
+        appenders[i].doAppend(event);
       }
-    }    
     return size;
   }
+    return 0;
+  }
 
 
   /**
Index: src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl5.java
--- src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl5.java Locally 
New
+++ src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl5.java Locally 
New
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.Appender;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+   A straightforward CopyOnWriteArrayList-based implementation of the
+   [EMAIL PROTECTED] AppenderAttachable} interface.
+   <p>
+   Note that althought the implementation uses CopyOnWriteArrayList (and thus
+   requires Java 5, on the whole it is no more thread safe than
+   AppenderAttachableImpl (which was not thread-safe despite using Vector).
+   The main benefit of using CopyOnWriteArrayList is that
+   appendLoopOnAppenders(LoggingEvent) no longer requires <i>any</i>
+   synchronization or array copying.
+   <p>
+   This class is final simply as I see no need to extend it at this time.
+
+   @author Jess Holle
+ */
+public final class AppenderAttachableImpl5 implements AppenderAttachable {  
+
+  /** Array of appenders. */
+  private CopyOnWriteArrayList<Appender>  appenderList;
+
+  /**
+     Attach an appender. If the appender is already in the list in
+     won't be added again.
+  */
+  public
+  void addAppender(Appender newAppender) {
+    // Null values for newAppender parameter are strictly forbidden.
+    if(newAppender == null)
+      return;
+    
+    if(appenderList == null)
+      appenderList = new CopyOnWriteArrayList<Appender>();
+    if(!appenderList.contains(newAppender))
+      appenderList.add(newAppender);
+  }
+
+  /**
+     Call the <code>doAppend</code> method on all attached appenders.
+     This method is thread-safe against other methods on this class.
+   */
+  public
+  int appendLoopOnAppenders(LoggingEvent event) {
+    // copy into field, so null check and usage are against same value and to 
reduce field access
+    CopyOnWriteArrayList<Appender>  appenderList = this.appenderList;
+    if ( ( appenderList != null ) && !appenderList.isEmpty() ) {
+      int size = 0;
+      for ( Appender appender : appenderList ) {
+        appender.doAppend(event);
+        ++size;
+      }
+      return size;
+    }
+    return 0;
+  }
+
+  /**
+     Get all attached appenders as an Enumeration. If there are no
+     attached appenders <code>null</code> is returned.
+     
+     @return Enumeration An enumeration of attached appenders.
+   */
+  public
+  Enumeration getAllAppenders() {
+    if(appenderList == null)
+      return null;
+    else 
+      return Collections.enumeration( appenderList );    
+  }
+
+  /**
+     Look for an attached appender named as <code>name</code>.
+
+     <p>Return the appender with that name if in the list. Return null
+     otherwise.  
+     
+   */
+  public
+  Appender getAppender(String name) {
+     if(appenderList == null || name == null)
+      return null;
+
+     for ( Appender appender : appenderList )
+       if(name.equals(appender.getName()))
+         return appender;
+     return null;    
+  }
+
+  /**
+     Returns <code>true</code> if the specified appender is in the
+     list of attached appenders, <code>false</code> otherwise.
+
+     @since 1.2 */
+  public 
+  boolean isAttached(Appender appender) {
+    if(appenderList == null || appender == null)
+      return false;
+
+    for ( Appender a : appenderList )
+      if(a == appender)
+        return true;
+    return false;    
+  }
+
+  /**
+   * Remove and close all previously attached appenders.
+   * */
+  public
+  void removeAllAppenders() {
+    if(appenderList != null) {
+      for ( Appender a : appenderList )
+             a.close();
+      appenderList.clear();
+      appenderList = null;
+    }
+  }
+
+  /**
+     Remove the appender passed as parameter form the list of attached
+     appenders.  */
+  public
+  void removeAppender(Appender appender) {
+    if(appender == null || appenderList == null) 
+      return;
+    appenderList.remove(appender);    
+  }
+
+ /**
+    Remove the appender with the name passed as parameter form the
+    list of appenders.  
+  */
+  public
+  void removeAppender(String name) {
+    if(name == null || appenderList == null)
+      return;
+    for ( Appender appender : appenderList )
+      if(name.equals(appender.getName())) {
+        appenderList.remove(appender);
+        break;
+      }
+  }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to