You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2017/05/30 23:49:12 UTC

[35/50] [abbrv] logging-chainsaw git commit: Moved component and receivers companion sources to the Chainsaw source tree - those companions won't be released.

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java b/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java
new file mode 100644
index 0000000..cd37dc4
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java
@@ -0,0 +1,316 @@
+/*
+ * 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.net;
+
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.log4j.plugins.Pauseable;
+import org.apache.log4j.plugins.Plugin;
+import org.apache.log4j.plugins.Receiver;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+  XMLSocketReceiver receives a remote logging event via XML on a configured
+  socket and "posts" it to a LoggerRepository as if the event were
+  generated locally. This class is designed to receive events from
+  the XMLSocketAppender class (or classes that send compatible events).
+  <p>
+  This receiver supports log files created using log4j's XMLLayout, as well as java.util.logging
+  XMLFormatter (via the org.apache.log4j.spi.Decoder interface).
+  <p>
+  By default, log4j's XMLLayout is supported (no need to specify a decoder in that case).
+  <p>
+  To configure this receiver to support java.util.logging's XMLFormatter, specify a 'decoder' param
+  of org.apache.log4j.xml.UtilLoggingXMLDecoder.
+  <p>
+  Once the event has been "posted", it will be handled by the
+  appenders currently configured in the LoggerRespository.
+
+  @author Mark Womack
+  @author Scott Deboy <sd...@apache.org>
+
+*/
+public class XMLSocketReceiver extends Receiver implements Runnable, PortBased, Pauseable {
+  private boolean paused;
+  //default to log4j xml decoder
+  protected String decoder = "org.apache.log4j.xml.XMLDecoder";
+  private ServerSocket serverSocket;
+  private List socketList = new Vector();
+  private Thread rThread;
+  public static final int DEFAULT_PORT = 4448;
+  protected int port = DEFAULT_PORT;
+  private boolean advertiseViaMulticastDNS;
+  private ZeroConfSupport zeroConf;
+
+  /**
+   * The MulticastDNS zone advertised by an XMLSocketReceiver
+   */
+  public static final String ZONE = "_log4j_xml_tcpaccept_receiver.local.";
+
+  /*
+   * Log4j doesn't provide an XMLSocketAppender, but the MulticastDNS zone that should be advertised by one is:
+   * _log4j_xml_tcpconnect_appender.local.
+   */
+
+  public XMLSocketReceiver() {
+  }
+
+  public XMLSocketReceiver(int _port) {
+    port = _port;
+  }
+
+  public XMLSocketReceiver(int _port, LoggerRepository _repository) {
+    port = _port;
+    repository = _repository;
+  }
+
+  /**
+    Get the port to receive logging events on. */
+  public int getPort() {
+    return port;
+  }
+
+  /**
+    Set the port to receive logging events on. */
+  public void setPort(int _port) {
+    port = _port;
+  }
+
+  public String getDecoder() {
+    return decoder;
+  }
+
+  /**
+   *Specify the class name implementing org.apache.log4j.spi.Decoder that can process the file.
+   */
+  public void setDecoder(String _decoder) {
+    decoder = _decoder;
+  }
+
+  public boolean isPaused() {
+    return paused;
+  }
+
+  public void setPaused(boolean b) {
+    paused = b;
+  }
+
+  /**
+   * Returns true if the receiver is the same class and they are
+   * configured for the same properties, and super class also considers
+   * them to be equivalent. This is used by PluginRegistry when determining
+   * if the a similarly configured receiver is being started.
+   * 
+   * @param testPlugin The plugin to test equivalency against.
+   * @return boolean True if the testPlugin is equivalent to this plugin.
+   */
+  public boolean isEquivalent(Plugin testPlugin) {
+    if ((testPlugin != null) && testPlugin instanceof XMLSocketReceiver) {
+      XMLSocketReceiver sReceiver = (XMLSocketReceiver) testPlugin;
+
+      return (port == sReceiver.getPort() && super.isEquivalent(testPlugin));
+    }
+
+    return false;
+  }
+
+  public int hashCode() {
+  	
+  	int result = 37 * (repository != null? repository.hashCode():0);
+  	result = result * 37 + port;
+  	return (result * 37 + (getName() != null? getName().hashCode():0));
+  }
+
+  /**
+    Sets the flag to indicate if receiver is active or not.
+   @param b new value
+   */
+  protected synchronized void setActive(final boolean b) {
+    active = b;
+  }
+
+  /**
+    Starts the SocketReceiver with the current options. */
+  public void activateOptions() {
+    if (!isActive()) {
+      rThread = new Thread(this);
+      rThread.setDaemon(true);
+      rThread.start();
+
+      if (advertiseViaMulticastDNS) {
+        zeroConf = new ZeroConfSupport(ZONE, port, getName());
+        zeroConf.advertise();
+      }
+
+      active = true;
+    }
+  }
+
+  public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+    this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+  }
+
+  public boolean isAdvertiseViaMulticastDNS() {
+    return advertiseViaMulticastDNS;
+  }
+
+  /**
+    Called when the receiver should be stopped. Closes the
+    server socket and all of the open sockets. */
+  public synchronized void shutdown() {
+    // mark this as no longer running
+    active = false;
+
+    if (rThread != null) {
+      rThread.interrupt();
+      rThread = null;
+    }
+    doShutdown();
+  }
+
+    /**
+     * Does the actual shutting down by closing the server socket
+     * and any connected sockets that have been created.
+     */
+    private synchronized void doShutdown() {
+      active = false;
+
+      getLogger().debug("{} doShutdown called", getName());
+
+      // close the server socket
+      closeServerSocket();
+
+      // close all of the accepted sockets
+      closeAllAcceptedSockets();
+
+      if (advertiseViaMulticastDNS) {
+          zeroConf.unadvertise();
+      }
+    }
+
+    /**
+      * Closes the server socket, if created.
+      */
+     private void closeServerSocket() {
+       getLogger().debug("{} closing server socket", getName());
+
+       try {
+         if (serverSocket != null) {
+           serverSocket.close();
+         }
+       } catch (Exception e) {
+         // ignore for now
+       }
+
+       serverSocket = null;
+     }
+
+    /**
+      * Closes all the connected sockets in the List.
+      */
+     private synchronized void closeAllAcceptedSockets() {
+       for (int x = 0; x < socketList.size(); x++) {
+         try {
+           ((Socket) socketList.get(x)).close();
+         } catch (Exception e) {
+           // ignore for now
+         }
+       }
+
+       // clear member variables
+       socketList.clear();
+     }
+
+  /**
+    Loop, accepting new socket connections. */
+  public void run() {
+      /**
+        * Ensure we start fresh.
+        */
+    getLogger().debug("performing socket cleanup prior to entering loop for {}",  name);
+    closeServerSocket();
+    closeAllAcceptedSockets();
+    getLogger().debug("socket cleanup complete for {}", name);       
+    active = true;
+
+    // start the server socket
+    try {
+      serverSocket = new ServerSocket(port);
+    } catch (Exception e) {
+      getLogger().error(
+        "error starting SocketReceiver (" + this.getName()
+        + "), receiver did not start", e);
+      active = false;
+      doShutdown();
+
+      return;
+    }
+
+    Socket socket = null;
+
+    try {
+      getLogger().debug("in run-about to enter while isactiveloop");
+
+      active = true;
+
+      while (!rThread.isInterrupted()) {
+        // if we have a socket, start watching it
+        if (socket != null) {
+          getLogger().debug("socket not null - creating and starting socketnode");
+          socketList.add(socket);
+
+          XMLSocketNode node = new XMLSocketNode(decoder, socket, this);
+          node.setLoggerRepository(this.repository);
+          new Thread(node).start();
+          socket = null;
+        }
+
+        getLogger().debug("waiting to accept socket");
+
+        // wait for a socket to open, then loop to start it
+        socket = serverSocket.accept();
+        getLogger().debug("accepted socket");
+      }
+
+      // socket not watched because we a no longer running
+      // so close it now.
+      if (socket != null) {
+        socket.close();
+      }
+    } catch (Exception e) {
+      getLogger().warn(
+        "socket server disconnected, stopping");
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.log4j.plugins.Receiver#doPost(org.apache.log4j.spi.LoggingEvent)
+   */
+  public void doPost(LoggingEvent event) {
+    if(!isPaused()){
+      super.doPost(event);
+    }
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/Pauseable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/plugins/Pauseable.java b/src/main/java/org/apache/log4j/plugins/Pauseable.java
new file mode 100644
index 0000000..6268ba3
--- /dev/null
+++ b/src/main/java/org/apache/log4j/plugins/Pauseable.java
@@ -0,0 +1,39 @@
+/*
+ * 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.plugins;
+
+
+/**
+ * Instances of this interface can be paused, and resumed.
+ *
+ * @author Paul Smith (psmith@apache.org)
+ *
+ */
+public interface Pauseable {
+    /**
+     * Set paused state.
+     * @param paused new value
+     */
+  void setPaused(boolean paused);
+
+    /**
+     * Get paused state.
+     * @return paused state.
+     */
+  boolean isPaused();
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/Plugin.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/plugins/Plugin.java b/src/main/java/org/apache/log4j/plugins/Plugin.java
new file mode 100644
index 0000000..e1b6a6f
--- /dev/null
+++ b/src/main/java/org/apache/log4j/plugins/Plugin.java
@@ -0,0 +1,144 @@
+/*
+ * 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.plugins;
+
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.OptionHandler;
+
+import java.beans.PropertyChangeListener;
+
+
+/**
+ * Defines the required interface for all Plugin objects.
+ * <p/>
+ * <p>A plugin implements some specific functionality to extend
+ * the log4j framework.  Each plugin is associated with a specific
+ * LoggerRepository, which it then uses/acts upon.  The functionality
+ * of the plugin is up to the developer.
+ * <p/>
+ * <p>Examples of plugins are Receiver and Watchdog. Receiver plugins
+ * allow for remote logging events to be received and processed by
+ * a repository as if the event was sent locally. Watchdog plugins
+ * allow for a repository to be reconfigured when some "watched"
+ * configuration data changes.
+ *
+ * @author Mark Womack (mwomack@apache.org)
+ * @author Nicko Cadell
+ * @author Paul Smith (psmith@apache.org)
+ */
+public interface Plugin extends OptionHandler {
+    /**
+     * Gets the name of the plugin.
+     *
+     * @return String the name of the plugin.
+     */
+    String getName();
+
+    /**
+     * Sets the name of the plugin.
+     *
+     * @param name the name of the plugin.
+     */
+    void setName(String name);
+
+    /**
+     * Gets the logger repository for this plugin.
+     *
+     * @return the logger repository to which this plugin is attached.
+     */
+    LoggerRepository getLoggerRepository();
+
+    /**
+     * Sets the logger repository used by this plugin. This
+     * repository will be used by the plugin functionality.
+     *
+     * @param repository the logger repository to attach this plugin to.
+     */
+    void setLoggerRepository(LoggerRepository repository);
+
+    /**
+     * Adds a PropertyChangeListener to this instance which is
+     * notified only by changes of the property with name propertyName.
+     *
+     * @param propertyName
+     *    the name of the property in standard JavaBean syntax
+     *    (e.g. for setName(), property="name")
+     * @param l listener
+     */
+    void addPropertyChangeListener(
+            String propertyName, PropertyChangeListener l);
+
+    /**
+     * Adds a PropertyChangeListener that will be notified of all property
+     * changes.
+     *
+     * @param l The listener to add.
+     */
+    void addPropertyChangeListener(PropertyChangeListener l);
+
+    /**
+     * Removes a specific PropertyChangeListener from this instances
+     * registry that has been mapped to be notified of all property
+     * changes.
+     *
+     * @param l The listener to remove.
+     */
+    void removePropertyChangeListener(PropertyChangeListener l);
+
+    /**
+     * Removes a specific PropertyChangeListener from this instance's
+     * registry which has been previously registered to be notified
+     * of only a specific property change.
+     *
+     * @param propertyName property name, may not be null.
+     * @param l listener to be removed.
+     */
+    void removePropertyChangeListener(
+            String propertyName, PropertyChangeListener l);
+
+    /**
+     * True if the plugin is active and running.
+     *
+     * @return boolean true if the plugin is currently active.
+     */
+    boolean isActive();
+
+    /**
+     * Returns true if the testPlugin is considered to be "equivalent" to the
+     * this plugin.  The equivalency test is at the discretion of the plugin
+     * implementation.  The PluginRegistry will use this method when starting
+     * new plugins to see if a given plugin is considered equivalent to an
+     * already running plugin with the same name.  If they are considered to
+     * be equivalent, the currently running plugin will be left in place, and
+     * the new plugin will not be started.
+     * <p/>
+     * It is possible to override the equals() method, however this has
+     * more meaning than is required for this simple test and would also
+     * require the overriding of the hashCode() method as well.  All of this
+     * is more work than is needed, so this simple method is used instead.
+     *
+     * @param testPlugin The plugin to test equivalency against.
+     * @return Returns true if testPlugin is considered to be equivelent.
+     */
+    boolean isEquivalent(Plugin testPlugin);
+
+    /**
+     * Call when the plugin should be stopped.
+     */
+    void shutdown();
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/PluginEvent.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/plugins/PluginEvent.java b/src/main/java/org/apache/log4j/plugins/PluginEvent.java
new file mode 100644
index 0000000..1843034
--- /dev/null
+++ b/src/main/java/org/apache/log4j/plugins/PluginEvent.java
@@ -0,0 +1,47 @@
+/*
+ * 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.plugins;
+
+import java.util.EventObject;
+
+
+/**
+ * All Plugin events are encapsulated in this class, which
+ * simply contains the source Plugin, but may in future include more
+ * information.
+ *
+ * @author Paul Smith
+ */
+public class PluginEvent extends EventObject {
+    /**
+     * @param source The source plugin of the event
+     */
+    PluginEvent(final Plugin source) {
+        super(source);
+    }
+
+    /**
+     * Returns the source Plugin of this event, which is simple
+     * the getSource() method casted to Plugin for convenience.
+     *
+     * @return Plugin source of this event
+     */
+    public Plugin getPlugin() {
+        return (Plugin) getSource();
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/PluginListener.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/plugins/PluginListener.java b/src/main/java/org/apache/log4j/plugins/PluginListener.java
new file mode 100644
index 0000000..11f628e
--- /dev/null
+++ b/src/main/java/org/apache/log4j/plugins/PluginListener.java
@@ -0,0 +1,43 @@
+/*
+ * 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.plugins;
+
+import java.util.EventListener;
+
+
+/**
+ * PluginListeners are notified when plugins are started or stopped
+ * by the PluginRegistry.
+ *
+ * @author Paul Smith (psmith@apache.org)
+ */
+public interface PluginListener extends EventListener {
+    /**
+     * Notification that plugin has started.
+     * @param e event
+     */
+  void pluginStarted(PluginEvent e);
+
+    /**
+     * Notification that plugin has stopped.
+     * @param e event
+     */
+  void pluginStopped(PluginEvent e);
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/PluginRegistry.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/plugins/PluginRegistry.java b/src/main/java/org/apache/log4j/plugins/PluginRegistry.java
new file mode 100644
index 0000000..d34f885
--- /dev/null
+++ b/src/main/java/org/apache/log4j/plugins/PluginRegistry.java
@@ -0,0 +1,303 @@
+/*
+ * 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.plugins;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggerRepositoryEx;
+import org.apache.log4j.spi.LoggerRepositoryEventListener;
+
+
+/**
+ * This is a registry for Plugin instances. It provides methods to
+ * start and stop plugin objects individually and to stop all
+ * plugins for a repository.
+ *
+ * @author Mark Womack
+ * @author Paul Smith
+ */
+public final class PluginRegistry {
+    /**
+     * The pluginMap is keyed by plugin name and contains plugins as values.
+     * key=plugin.getName, value=plugin
+     */
+    private final Map pluginMap;
+    /**
+     * Logger repository.
+     */
+    private final LoggerRepositoryEx loggerRepository;
+
+    /**
+     * the listener used to listen for repository events.
+     */
+    private final RepositoryListener listener = new RepositoryListener();
+    /**
+     * List of listeners.
+     */
+    private final List listenerList =
+            Collections.synchronizedList(new ArrayList());
+
+    /**
+     * Creates a new instance.
+     * @param repository logger repository.
+     */
+    public PluginRegistry(final LoggerRepositoryEx repository) {
+        super();
+        pluginMap = new HashMap();
+        this.loggerRepository = repository;
+        this.loggerRepository.addLoggerRepositoryEventListener(listener);
+    }
+
+    /**
+     * Get logger repository.
+     * @return logger repository.
+     */
+    public LoggerRepositoryEx getLoggerRepository() {
+        return loggerRepository;
+    }
+
+
+    /**
+     * Returns true if the specified name is already taken by
+     * an existing Plugin registered within the scope of the specified
+     * LoggerRepository.
+     *
+     * @param name The name to check the repository for
+     * @return true if the name is already in use, otherwise false
+     */
+    public boolean pluginNameExists(final String name) {
+        synchronized (pluginMap) {
+            return pluginMap.containsKey(name);
+        }
+    }
+
+
+    /**
+     * Adds a plugin to the plugin registry.
+     * If a plugin with the same name exists
+     * already, it is shutdown and removed.
+     *
+     * @param plugin the plugin to add.
+     */
+    public void addPlugin(final Plugin plugin) {
+        // put plugin into the repository's reciever map
+        synchronized (pluginMap) {
+            String name = plugin.getName();
+
+            // make sure the plugin has reference to repository
+            plugin.setLoggerRepository(getLoggerRepository());
+
+            Plugin existingPlugin = (Plugin) pluginMap.get(name);
+            if (existingPlugin != null) {
+                existingPlugin.shutdown();
+            }
+
+            // put the new plugin into the map
+            pluginMap.put(name, plugin);
+            firePluginStarted(plugin);
+        }
+    }
+
+
+    /**
+     * Calls the pluginStarted method on every registered PluginListener.
+     *
+     * @param plugin The plugin that has been started.
+     */
+    private void firePluginStarted(final Plugin plugin) {
+        PluginEvent e = null;
+        synchronized (listenerList) {
+            for (Iterator iter = listenerList.iterator(); iter.hasNext();) {
+                PluginListener l = (PluginListener) iter.next();
+                if (e == null) {
+                    e = new PluginEvent(plugin);
+                }
+                l.pluginStarted(e);
+            }
+        }
+    }
+
+
+    /**
+     * Calls the pluginStopped method for every registered PluginListner.
+     *
+     * @param plugin The plugin that has been stopped.
+     */
+    private void firePluginStopped(final Plugin plugin) {
+        PluginEvent e = null;
+        synchronized (listenerList) {
+            for (Iterator iter = listenerList.iterator(); iter.hasNext();) {
+                PluginListener l = (PluginListener) iter.next();
+                if (e == null) {
+                    e = new PluginEvent(plugin);
+                }
+                l.pluginStopped(e);
+            }
+        }
+    }
+
+
+    /**
+     * Returns all the plugins for a given repository.
+     *
+     * @return List list of plugins from the repository.
+     */
+    public List getPlugins() {
+        synchronized (pluginMap) {
+            List pluginList = new ArrayList(pluginMap.size());
+            Iterator iter = pluginMap.values().iterator();
+
+            while (iter.hasNext()) {
+                pluginList.add(iter.next());
+            }
+            return pluginList;
+        }
+    }
+
+
+    /**
+     * Returns all the plugins for a given repository that are instances
+     * of a certain class.
+     *
+     * @param pluginClass the class the plugin must implement to be selected.
+     * @return List list of plugins from the repository.
+     */
+    public List getPlugins(final Class pluginClass) {
+        synchronized (pluginMap) {
+            List pluginList = new ArrayList(pluginMap.size());
+            Iterator iter = pluginMap.values().iterator();
+
+            while (iter.hasNext()) {
+                Object plugin = iter.next();
+
+                if (pluginClass.isInstance(plugin)) {
+                    pluginList.add(plugin);
+                }
+            }
+            return pluginList;
+        }
+    }
+
+
+    /**
+     * Stops a plugin by plugin name and repository.
+     *
+     * @param pluginName the name of the plugin to stop.
+     * @return Plugin the plugin, if stopped, or null if the
+     *         the plugin was not found in the registry.
+     */
+    public Plugin stopPlugin(final String pluginName) {
+        synchronized (pluginMap) {
+            Plugin plugin = (Plugin) pluginMap.get(pluginName);
+
+            if (plugin == null) {
+                return null;
+            }
+
+            // shutdown the plugin
+            plugin.shutdown();
+
+            // remove it from the plugin map
+            pluginMap.remove(pluginName);
+            firePluginStopped(plugin);
+
+            // return it for future use
+            return plugin;
+        }
+    }
+
+    /**
+     * Stops all plugins in the given logger repository.
+     */
+    public void stopAllPlugins() {
+        synchronized (pluginMap) {
+            // remove the listener for this repository
+            loggerRepository.removeLoggerRepositoryEventListener(listener);
+
+            Iterator iter = pluginMap.values().iterator();
+
+            while (iter.hasNext()) {
+                Plugin plugin = (Plugin) iter.next();
+                plugin.shutdown();
+                firePluginStopped(plugin);
+            }
+        }
+    }
+
+
+    /**
+     * Adds a PluginListener to this registry to be notified
+     * of PluginEvents.
+     *
+     * @param l PluginListener to add to this registry
+     */
+    public void addPluginListener(final PluginListener l) {
+        listenerList.add(l);
+    }
+
+
+    /**
+     * Removes a particular PluginListener from this registry
+     * such that it will no longer be notified of PluginEvents.
+     *
+     * @param l PluginListener to remove
+     */
+    public void removePluginListener(final PluginListener l) {
+        listenerList.remove(l);
+    }
+
+    /**
+     * Internal class used to handle listener events from repositories.
+     */
+    private class RepositoryListener implements LoggerRepositoryEventListener {
+        /**
+         * Stops all plugins associated with the repository being reset.
+         *
+         * @param repository the repository that was reset.
+         */
+        public void configurationResetEvent(final LoggerRepository repository) {
+            PluginRegistry.this.stopAllPlugins();
+        }
+
+
+        /**
+         * Called when the repository configuration is changed.
+         *
+         * @param repository the repository that was changed.
+         */
+        public void configurationChangedEvent(
+                final LoggerRepository repository) {
+            // do nothing with this event
+        }
+
+
+        /**
+         * Stops all plugins associated with the repository being shutdown.
+         *
+         * @param repository the repository being shutdown.
+         */
+        public void shutdownEvent(final LoggerRepository repository) {
+            PluginRegistry.this.stopAllPlugins();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/PluginSkeleton.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/plugins/PluginSkeleton.java b/src/main/java/org/apache/log4j/plugins/PluginSkeleton.java
new file mode 100644
index 0000000..2660473
--- /dev/null
+++ b/src/main/java/org/apache/log4j/plugins/PluginSkeleton.java
@@ -0,0 +1,222 @@
+/*
+ * 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.plugins;
+
+import org.apache.log4j.spi.ComponentBase;
+import org.apache.log4j.spi.LoggerRepository;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+
+/**
+ * A convienent abstract class for plugin subclasses that implements
+ * the basic methods of the Plugin interface. Subclasses are required
+ * to implement the isActive(), activateOptions(), and shutdown()
+ * methods.
+ * <p/>
+ * <p>Developers are not required to subclass PluginSkeleton to
+ * develop their own plugins (they are only required to implement the
+ * Plugin interface), but it provides a convenient base class to start
+ * from.
+ * <p/>
+ * Contributors: Nicko Cadell
+ *
+ * @author Mark Womack (mwomack@apache.org)
+ * @author Paul Smith (psmith@apache.org)
+ */
+public abstract class PluginSkeleton extends ComponentBase implements Plugin {
+    /**
+     * Name of this plugin.
+     */
+    protected String name = "plugin";
+
+    /**
+     * Active state of plugin.
+     */
+    protected boolean active;
+
+    /**
+     * This is a delegate that does all the PropertyChangeListener
+     * support.
+     */
+    private PropertyChangeSupport propertySupport =
+            new PropertyChangeSupport(this);
+
+    /**
+     * Construct new instance.
+     */
+    protected PluginSkeleton() {
+        super();
+    }
+
+    /**
+     * Gets the name of the plugin.
+     *
+     * @return String the name of the plugin.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the name of the plugin and notifies
+     * PropertyChangeListeners of the change.
+     *
+     * @param newName the name of the plugin to set.
+     */
+    public void setName(final String newName) {
+        String oldName = this.name;
+        this.name = newName;
+        propertySupport.firePropertyChange("name", oldName, this.name);
+    }
+
+    /**
+     * Gets the logger repository for this plugin.
+     *
+     * @return LoggerRepository the logger repository this plugin will affect.
+     */
+    public LoggerRepository getLoggerRepository() {
+        return repository;
+    }
+
+    /**
+     * Sets the logger repository used by this plugin and notifies a
+     * relevant PropertyChangeListeners registered. This
+     * repository will be used by the plugin functionality.
+     *
+     * @param repository the logger repository that this plugin should affect.
+     */
+    public void setLoggerRepository(final LoggerRepository repository) {
+        Object oldValue = this.repository;
+        this.repository = repository;
+        firePropertyChange("loggerRepository", oldValue, this.repository);
+    }
+
+    /**
+     * Returns whether this plugin is Active or not.
+     *
+     * @return true/false
+     */
+    public synchronized boolean isActive() {
+        return active;
+    }
+
+    /**
+     * Returns true if the plugin has the same name and logger repository as the
+     * testPlugin passed in.
+     *
+     * @param testPlugin The plugin to test equivalency against.
+     * @return Returns true if testPlugin is considered to be equivalent.
+     */
+    public boolean isEquivalent(final Plugin testPlugin) {
+        return (repository == testPlugin.getLoggerRepository())
+                && ((this.name == null && testPlugin.getName() == null)
+                || (this.name != null
+                           && name.equals(testPlugin.getName())))
+                && this.getClass().equals(testPlugin.getClass());
+    }
+
+    /**
+     * Add property change listener.
+     * @param listener listener.
+     */
+    public final void addPropertyChangeListener(
+            final PropertyChangeListener listener) {
+        propertySupport.addPropertyChangeListener(listener);
+    }
+
+    /**
+     * Add property change listener for one property only.
+     * @param propertyName property name.
+     * @param listener listener.
+     */
+    public final void addPropertyChangeListener(
+            final String propertyName,
+            final PropertyChangeListener listener) {
+        propertySupport.addPropertyChangeListener(propertyName, listener);
+    }
+
+    /**
+     * Remove property change listener.
+     * @param listener listener.
+     */
+    public final void removePropertyChangeListener(
+            final PropertyChangeListener listener) {
+        propertySupport.removePropertyChangeListener(listener);
+    }
+
+    /**
+     * Remove property change listener on a specific property.
+     * @param propertyName property name.
+     * @param listener listener.
+     */
+    public final void removePropertyChangeListener(
+            final String propertyName,
+            final PropertyChangeListener listener) {
+        propertySupport.removePropertyChangeListener(propertyName, listener);
+    }
+
+    /**
+     * Fire a property change event to appropriate listeners.
+     * @param evt change event.
+     */
+    protected final void firePropertyChange(
+            final PropertyChangeEvent evt) {
+        propertySupport.firePropertyChange(evt);
+    }
+
+    /**
+     * Fire property change event to appropriate listeners.
+     * @param propertyName property name.
+     * @param oldValue old value.
+     * @param newValue new value.
+     */
+    protected final void firePropertyChange(
+            final String propertyName,
+            final boolean oldValue,
+            final boolean newValue) {
+        propertySupport.firePropertyChange(propertyName, oldValue, newValue);
+    }
+
+    /**
+     * Fire property change event to appropriate listeners.
+     * @param propertyName property name.
+     * @param oldValue old value.
+     * @param newValue new value.
+     */
+    protected final void firePropertyChange(
+            final String propertyName,
+            final int oldValue, final int newValue) {
+        propertySupport.firePropertyChange(propertyName, oldValue, newValue);
+    }
+
+    /**
+     * Fire property change event to appropriate listeners.
+     * @param propertyName property name.
+     * @param oldValue old value.
+     * @param newValue new value.
+     */
+    protected final void firePropertyChange(
+            final String propertyName,
+            final Object oldValue,
+            final Object newValue) {
+        propertySupport.firePropertyChange(propertyName, oldValue, newValue);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/plugins/Receiver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/plugins/Receiver.java b/src/main/java/org/apache/log4j/plugins/Receiver.java
new file mode 100644
index 0000000..d78ae59
--- /dev/null
+++ b/src/main/java/org/apache/log4j/plugins/Receiver.java
@@ -0,0 +1,131 @@
+/*
+ * 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.plugins;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.Thresholdable;
+
+
+/**
+ * Defines the base class for Receiver plugins.
+ * <p/>
+ * <p>Just as Appenders send logging events outside of the log4j
+ * environment (to files, to smtp, to sockets, etc), Receivers bring
+ * logging events inside the log4j environment.
+ * <p/>
+ * <p>Receivers are meant to support the receiving of
+ * remote logging events from another process. For example,
+ * SocketAppender "appends" a logging event to a socket, configured
+ * for a specific host and port number.  On the receiving side of
+ * the socket can be a SocketReceiver object.  The SocketReceiver
+ * object receives the logging event, and then "posts" it to the
+ * log4j environment (LoggerRepository) on the receiving machine, to
+ * be handled by the configured appenders, etc.  The various
+ * settings in this environment (Logger levels, Appender filters &
+ * thresholds) are applied to the received logging event.
+ * <p/>
+ * <p>Receivers can also be used to "import" log messages from other
+ * logging packages into the log4j environment.
+ * <p/>
+ * <p>Receivers can be configured to post events to a given
+ * LoggerRepository.
+ * <p/>
+ * <p>Subclasses of Receiver must implement the isActive(),
+ * activateOptions(), and shutdown() methods. The doPost() method
+ * is provided to standardize the "import" of remote events into
+ * the repository.
+ *
+ * @author Mark Womack
+ * @author Ceki G&uuml;lc&uuml;
+ * @author Paul Smith (psmith@apache.org)
+ */
+public abstract class Receiver extends PluginSkeleton implements Thresholdable {
+    /**
+     * Threshold level.
+     */
+    protected Level thresholdLevel;
+
+    /**
+     * Create new instance.
+     */
+    protected Receiver() {
+        super();
+    }
+
+    /**
+     * Sets the receiver theshold to the given level.
+     *
+     * @param level The threshold level events must equal or be greater
+     *              than before further processing can be done.
+     */
+    public void setThreshold(final Level level) {
+        Level oldValue = this.thresholdLevel;
+        thresholdLevel = level;
+        firePropertyChange("threshold", oldValue, this.thresholdLevel);
+    }
+
+    /**
+     * Gets the current threshold setting of the receiver.
+     *
+     * @return Level The current threshold level of the receiver.
+     */
+    public Level getThreshold() {
+        return thresholdLevel;
+    }
+
+    /**
+     * Returns true if the given level is equals or greater than the current
+     * threshold value of the receiver.
+     *
+     * @param level The level to test against the receiver threshold.
+     * @return boolean True if level is equal or greater than the
+     *         receiver threshold.
+     */
+    public boolean isAsSevereAsThreshold(final Level level) {
+        return ((thresholdLevel == null)
+                || level.isGreaterOrEqual(thresholdLevel));
+    }
+
+    /**
+     * Posts the logging event to a logger in the configured logger
+     * repository.
+     *
+     * @param event the log event to post to the local log4j environment.
+     */
+    public void doPost(final LoggingEvent event) {
+        // if event does not meet threshold, exit now
+        if (!isAsSevereAsThreshold(event.getLevel())) {
+            return;
+        }
+
+        // get the "local" logger for this event from the
+        // configured repository.
+        Logger localLogger =
+                getLoggerRepository().getLogger(event.getLoggerName());
+
+        // if the logger level is greater or equal to the level
+        // of the event, use the logger to append the event.
+        if (event.getLevel()
+                .isGreaterOrEqual(localLogger.getEffectiveLevel())) {
+            // call the loggers appenders to process the event
+            localLogger.callAppenders(event);
+        }
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java b/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
new file mode 100644
index 0000000..4fca465
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
@@ -0,0 +1,85 @@
+/*
+ * 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.rewrite;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This policy rewrites events where the message of the
+ * original event implementes java.util.Map.
+ * All other events are passed through unmodified.
+ * If the map contains a "message" entry, the value will be
+ * used as the message for the rewritten event.  The rewritten
+ * event will have a property set that is the combination of the
+ * original property set and the other members of the message map.
+ * If both the original property set and the message map
+ * contain the same entry, the value from the message map
+ * will overwrite the original property set.
+ *
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the MapFilter from log4j 1.3. 
+ */
+public class MapRewritePolicy implements RewritePolicy {
+    /**
+     * {@inheritDoc}
+     */
+    public LoggingEvent rewrite(final LoggingEvent source) {
+        Object msg = source.getMessage();
+        if (msg instanceof Map) {
+            Map props = new HashMap(source.getProperties());
+            Map eventProps = (Map) msg;
+            //
+            //   if the map sent in the logging request
+            //      has "message" entry, use that as the message body
+            //      otherwise, use the entire map.
+            //
+            Object newMsg = eventProps.get("message");
+            if (newMsg == null) {
+                newMsg = msg;
+            }
+
+            for(Iterator iter = eventProps.entrySet().iterator();
+                    iter.hasNext();
+                  ) {
+                Map.Entry entry = (Map.Entry) iter.next();
+                if (!("message".equals(entry.getKey()))) {
+                    props.put(entry.getKey(), entry.getValue());
+                }
+            }
+
+            return new LoggingEvent(
+                    source.getFQNOfLoggerClass(),
+                    source.getLogger() != null ? source.getLogger(): Logger.getLogger(source.getLoggerName()), 
+                    source.getTimeStamp(),
+                    source.getLevel(),
+                    newMsg,
+                    source.getThreadName(),
+                    source.getThrowableInformation(),
+                    source.getNDC(),
+                    source.getLocationInformation(),
+                    props);
+        } else {
+            return source;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java b/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
new file mode 100644
index 0000000..535736c
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
@@ -0,0 +1,93 @@
+/*
+ * 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.rewrite;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This policy rewrites events by adding
+ * a user-specified list of properties to the event.
+ * Existing properties are not modified.
+ *
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the PropertyFilter from log4j 1.3.
+ */
+
+public class PropertyRewritePolicy implements RewritePolicy {
+    private Map properties = Collections.EMPTY_MAP;
+    public PropertyRewritePolicy() {
+    }
+
+    /**
+     * Set a string representing the property name/value pairs.
+     * 
+     * Form: propname1=propvalue1,propname2=propvalue2
+     * 
+     * @param props
+     */
+    public void setProperties(String props) {
+        Map hashTable = new HashMap();
+        StringTokenizer pairs = new StringTokenizer(props, ",");
+        while (pairs.hasMoreTokens()) {
+            StringTokenizer entry = new StringTokenizer(pairs.nextToken(), "=");
+            hashTable.put(entry.nextElement().toString().trim(), entry.nextElement().toString().trim());
+        }
+        synchronized(this) {
+            properties = hashTable;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public LoggingEvent rewrite(final LoggingEvent source) {
+        if (!properties.isEmpty()) {
+            Map rewriteProps = new HashMap(source.getProperties());
+            for(Iterator iter = properties.entrySet().iterator();
+                    iter.hasNext();
+                    ) {
+                Map.Entry entry = (Map.Entry) iter.next();
+                if (!rewriteProps.containsKey(entry.getKey())) {
+                    rewriteProps.put(entry.getKey(), entry.getValue());
+                }
+            }
+
+            return new LoggingEvent(
+                    source.getFQNOfLoggerClass(),
+                    source.getLogger() != null ? source.getLogger(): Logger.getLogger(source.getLoggerName()), 
+                    source.getTimeStamp(),
+                    source.getLevel(),
+                    source.getMessage(),
+                    source.getThreadName(),
+                    source.getThrowableInformation(),
+                    source.getNDC(),
+                    source.getLocationInformation(),
+                    rewriteProps);
+        }
+        return source;
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/ReflectionRewritePolicy.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/rewrite/ReflectionRewritePolicy.java b/src/main/java/org/apache/log4j/rewrite/ReflectionRewritePolicy.java
new file mode 100644
index 0000000..f1a4cc5
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rewrite/ReflectionRewritePolicy.java
@@ -0,0 +1,89 @@
+/*
+ * 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.rewrite;
+
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This policy rewrites events by evaluating any
+ * JavaBean properties on the message object and adding them
+ * to the event properties.  If the message object has a
+ * message property, the value of that property will be
+ * used as the message for the rewritten event and will
+ * not be added to the event properties.  Values from the
+ * JavaBean properties will replace any existing property
+ * with the same name.
+ *
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the ReflectionFilter from log4j 1.3. 
+ */
+public class ReflectionRewritePolicy implements RewritePolicy {
+    /**
+     * {@inheritDoc}
+     */
+    public LoggingEvent rewrite(final LoggingEvent source) {
+        Object msg = source.getMessage();
+        if (!(msg instanceof String)) {
+            Object newMsg = msg;
+            Map rewriteProps = new HashMap(source.getProperties());
+
+            try {
+                PropertyDescriptor[] props = Introspector.getBeanInfo(
+                        msg.getClass(), Object.class).getPropertyDescriptors();
+                if (props.length > 0) {
+                    for (int i=0;i<props.length;i++) {
+                        try {
+                            Object propertyValue =
+                                props[i].getReadMethod().invoke(msg,
+                                        (Object[]) null);
+                            if ("message".equalsIgnoreCase(props[i].getName())) {
+                                newMsg = propertyValue;
+                            } else {
+                                rewriteProps.put(props[i].getName(), propertyValue);
+                            }
+                        } catch (Exception e) {
+                            LogLog.warn("Unable to evaluate property " +
+                                    props[i].getName(), e);
+                        }
+                    }
+                    return new LoggingEvent(
+                            source.getFQNOfLoggerClass(),
+                            source.getLogger() != null ? source.getLogger(): Logger.getLogger(source.getLoggerName()),
+                            source.getTimeStamp(),
+                            source.getLevel(),
+                            newMsg,
+                            source.getThreadName(),
+                            source.getThrowableInformation(),
+                            source.getNDC(),
+                            source.getLocationInformation(),
+                            rewriteProps);
+                }
+            } catch (Exception e) {
+                LogLog.warn("Unable to get property descriptors", e);
+            }
+
+        }
+        return source;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/RewriteAppender.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/rewrite/RewriteAppender.java b/src/main/java/org/apache/log4j/rewrite/RewriteAppender.java
new file mode 100644
index 0000000..368ecf9
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rewrite/RewriteAppender.java
@@ -0,0 +1,199 @@
+/*
+ * 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.rewrite;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.AppenderAttachableImpl;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.xml.UnrecognizedElementHandler;
+import org.w3c.dom.Element;
+
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * This appender forwards a logging request to another
+ * appender after possibly rewriting the logging event.
+ *
+ * This appender (with the appropriate policy)
+ * replaces the MapFilter, PropertyFilter and ReflectionFilter
+ * from log4j 1.3.
+ */
+public class RewriteAppender extends AppenderSkeleton
+     implements AppenderAttachable, UnrecognizedElementHandler {
+    /**
+     * Rewrite policy.
+     */
+    private RewritePolicy policy;
+    /**
+     * Nested appenders.
+     */
+    private final AppenderAttachableImpl appenders;
+
+    public RewriteAppender() {
+        appenders = new AppenderAttachableImpl();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void append(final LoggingEvent event) {
+        LoggingEvent rewritten = event;
+        if (policy != null) {
+            rewritten = policy.rewrite(event);
+        }
+        if (rewritten != null) {
+            synchronized (appenders) {
+              appenders.appendLoopOnAppenders(rewritten);
+            }
+        }
+    }
+
+    /**
+     * Add appender.
+     *
+     * @param newAppender appender to add, may not be null.
+     */
+    public void addAppender(final Appender newAppender) {
+      synchronized (appenders) {
+        appenders.addAppender(newAppender);
+      }
+    }
+
+    /**
+     * Get iterator over attached appenders.
+     * @return iterator or null if no attached appenders.
+     */
+    public Enumeration getAllAppenders() {
+      synchronized (appenders) {
+        return appenders.getAllAppenders();
+      }
+    }
+
+    /**
+     * Get appender by name.
+     *
+     * @param name name, may not be null.
+     * @return matching appender or null.
+     */
+    public Appender getAppender(final String name) {
+      synchronized (appenders) {
+        return appenders.getAppender(name);
+      }
+    }
+
+
+    /**
+     * Close this <code>AsyncAppender</code> by interrupting the dispatcher
+     * thread which will process all pending events before exiting.
+     */
+    public void close() {
+      closed = true;
+      //
+      //    close all attached appenders.
+      //
+      synchronized (appenders) {
+        Enumeration iter = appenders.getAllAppenders();
+
+        if (iter != null) {
+          while (iter.hasMoreElements()) {
+            Object next = iter.nextElement();
+
+            if (next instanceof Appender) {
+              ((Appender) next).close();
+            }
+          }
+        }
+      }
+    }
+
+    /**
+     * Determines if specified appender is attached.
+     * @param appender appender.
+     * @return true if attached.
+     */
+    public boolean isAttached(final Appender appender) {
+      synchronized (appenders) {
+        return appenders.isAttached(appender);
+      }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean requiresLayout() {
+      return false;
+    }
+
+    /**
+     * Removes and closes all attached appenders.
+     */
+    public void removeAllAppenders() {
+      synchronized (appenders) {
+        appenders.removeAllAppenders();
+      }
+    }
+
+    /**
+     * Removes an appender.
+     * @param appender appender to remove.
+     */
+    public void removeAppender(final Appender appender) {
+      synchronized (appenders) {
+        appenders.removeAppender(appender);
+      }
+    }
+
+    /**
+     * Remove appender by name.
+     * @param name name.
+     */
+    public void removeAppender(final String name) {
+      synchronized (appenders) {
+        appenders.removeAppender(name);
+      }
+    }
+
+
+    public void setRewritePolicy(final RewritePolicy rewritePolicy) {
+        policy = rewritePolicy;
+    }
+    /**
+     * {@inheritDoc}
+     */
+    public boolean parseUnrecognizedElement(final Element element,
+                                            final Properties props) throws Exception {
+        final String nodeName = element.getNodeName();
+        if ("rewritePolicy".equals(nodeName)) {
+            Object rewritePolicy =
+                    org.apache.log4j.xml.DOMConfigurator.parseElement(
+                            element, props, RewritePolicy.class);
+            if (rewritePolicy != null) {
+                if (rewritePolicy instanceof OptionHandler) {
+                    ((OptionHandler) rewritePolicy).activateOptions();
+                }
+                this.setRewritePolicy((RewritePolicy) rewritePolicy);
+            }
+            return true;
+        }
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java b/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java
new file mode 100644
index 0000000..bb40507
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java
@@ -0,0 +1,37 @@
+package org.apache.log4j.rewrite;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/*
+* 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.
+*/
+
+/**
+ * This interface is implemented to provide a rewrite
+ * strategy for RewriteAppender.  RewriteAppender will
+ * call the rewrite method with a source logging event.
+ * The strategy may return that event, create a new event
+ * or return null to suppress the logging request.
+ */
+public interface RewritePolicy {
+    /**
+     * Rewrite a logging event.
+     * @param source a logging event that may be returned or
+     * used to create a new logging event.
+     * @return a logging event or null to suppress processing.
+     */
+    LoggingEvent rewrite(final LoggingEvent source);
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/scheduler/Job.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/scheduler/Job.java b/src/main/java/org/apache/log4j/scheduler/Job.java
new file mode 100644
index 0000000..a0e9be4
--- /dev/null
+++ b/src/main/java/org/apache/log4j/scheduler/Job.java
@@ -0,0 +1,36 @@
+/*
+ * 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.scheduler;
+
+
+/**
+ * Job is a very simple interface. It only has a single method {@link #execute}
+ * which is called by the {@link Scheduler} when a task is ready for execution.
+ * <p/>
+ * It is assumed that the execution context
+ * are contained within the implementing
+ * {@link Job} itself.
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ */
+public interface Job {
+    /**
+     * Execute job.
+     */
+    void execute();
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/scheduler/Scheduler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/scheduler/Scheduler.java b/src/main/java/org/apache/log4j/scheduler/Scheduler.java
new file mode 100644
index 0000000..72809f7
--- /dev/null
+++ b/src/main/java/org/apache/log4j/scheduler/Scheduler.java
@@ -0,0 +1,307 @@
+/*
+ * 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.scheduler;
+
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * A simple but still useful implementation of a Scheduler (in memory only).
+ * <p/>
+ * This implementation will work very well when the number of scheduled job is
+ * small, say less than 100 jobs. If a larger number of events need to be
+ * scheduled, than a better adapted data structure for the jobList can give
+ * improved performance.
+ *
+ * @author Ceki
+ */
+public class Scheduler extends Thread {
+
+    /**
+     * Job list.
+     */
+    List jobList;
+    /**
+     * If set true, scheduler has or should shut down.
+     */
+    boolean shutdown = false;
+
+    /**
+     * Create new instance.
+     */
+    public Scheduler() {
+        super();
+        jobList = new Vector();
+    }
+
+    /**
+     * Find the index of a given job.
+     * @param job job
+     * @return -1 if the job could not be found.
+     */
+    int findIndex(final Job job) {
+        int size = jobList.size();
+        boolean found = false;
+
+        int i = 0;
+        for (; i < size; i++) {
+            ScheduledJobEntry se = (ScheduledJobEntry) jobList.get(i);
+            if (se.job == job) {
+                found = true;
+                break;
+            }
+        }
+        if (found) {
+            return i;
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Delete the given job.
+     * @param job job.
+     * @return true if the job could be deleted, and
+     * false if the job could not be found or if the Scheduler is about to
+     * shutdown in which case deletions are not permitted.
+     */
+    public synchronized boolean delete(final Job job) {
+        // if already shutdown in the process of shutdown, there is no
+        // need to remove Jobs as they will never be executed.
+        if (shutdown) {
+            return false;
+        }
+        int i = findIndex(job);
+        if (i != -1) {
+            ScheduledJobEntry se = (ScheduledJobEntry) jobList.remove(i);
+            if (se.job != job) { // this should never happen
+                new IllegalStateException("Internal programming error");
+            }
+            // if the job is the first on the list,
+            // then notify the scheduler thread to schedule a new job
+            if (i == 0) {
+                this.notifyAll();
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    /**
+     * Schedule a {@link Job} for execution at system time given by
+     * the <code>desiredTime</code> parameter.
+     * @param job job to schedule.
+     * @param desiredTime desired time of execution.
+     */
+    public synchronized void schedule(final Job job,
+                                      final long desiredTime) {
+        schedule(new ScheduledJobEntry(job, desiredTime));
+    }
+
+    /**
+     * Schedule a {@link Job} for execution at system time given by
+     * the <code>desiredTime</code> parameter.
+     * <p/>
+     * The job will be rescheduled. It will execute with a frequency determined
+     * by the period parameter.
+     * @param job job to schedule.
+     * @param desiredTime desired time of execution.
+     * @param period repeat period.
+     */
+    public synchronized void schedule(final Job job,
+                                      final long desiredTime,
+                                      final long period) {
+        schedule(new ScheduledJobEntry(job, desiredTime, period));
+    }
+
+    /**
+     * Change the period of a job. The original job must exist for its period
+     * to be changed.
+     * <p/>
+     * The method returns true if the period could be changed, and false
+     * otherwise.
+     * @param job job.
+     * @param newPeriod new repeat period.
+     * @return true if period could be changed.
+     */
+    public synchronized boolean changePeriod(final Job job,
+                                             final long newPeriod) {
+        if (newPeriod <= 0) {
+            throw new IllegalArgumentException(
+                    "Period must be an integer langer than zero");
+        }
+
+        int i = findIndex(job);
+        if (i == -1) {
+            return false;
+        } else {
+            ScheduledJobEntry se = (ScheduledJobEntry) jobList.get(i);
+            se.period = newPeriod;
+            return true;
+        }
+    }
+
+    /**
+     * Schedule a job.
+     * @param newSJE new job entry.
+     */
+    private synchronized void schedule(final ScheduledJobEntry newSJE) {
+        // disallow new jobs after shutdown
+        if (shutdown) {
+            return;
+        }
+        int max = jobList.size();
+        long desiredExecutionTime = newSJE.desiredExecutionTime;
+
+        // find the index i such that timeInMillis < jobList[i]
+        int i = 0;
+        for (; i < max; i++) {
+
+            ScheduledJobEntry sje = (ScheduledJobEntry) jobList.get(i);
+
+            if (desiredExecutionTime < sje.desiredExecutionTime) {
+                break;
+            }
+        }
+        jobList.add(i, newSJE);
+        // if the jobList was empty, then notify the scheduler thread
+        if (i == 0) {
+            this.notifyAll();
+        }
+    }
+
+    /**
+     * Shut down scheduler.
+     */
+    public synchronized void shutdown() {
+        shutdown = true;
+    }
+
+    /**
+     * Run scheduler.
+     */
+    public synchronized void run() {
+        while (!shutdown) {
+            if (jobList.isEmpty()) {
+                linger();
+            } else {
+                ScheduledJobEntry sje = (ScheduledJobEntry) jobList.get(0);
+                long now = System.currentTimeMillis();
+                if (now >= sje.desiredExecutionTime) {
+                    executeInABox(sje.job);
+                    jobList.remove(0);
+                    if (sje.period > 0) {
+                        sje.desiredExecutionTime = now + sje.period;
+                        schedule(sje);
+                    }
+                } else {
+                    linger(sje.desiredExecutionTime - now);
+                }
+            }
+        }
+        // clear out the job list to facilitate garbage collection
+        jobList.clear();
+        jobList = null;
+        System.out.println("Leaving scheduler run method");
+    }
+
+    /**
+     * We do not want a single failure to affect the whole scheduler.
+     * @param job job to execute.
+     */
+    void executeInABox(final Job job) {
+        try {
+            job.execute();
+        } catch (Exception e) {
+            System.err.println("The execution of the job threw an exception");
+            e.printStackTrace(System.err);
+        }
+    }
+
+    /**
+     * Wait for notification.
+     */
+    void linger() {
+        try {
+            while (jobList.isEmpty() && !shutdown) {
+                this.wait();
+            }
+        } catch (InterruptedException ie) {
+            shutdown = true;
+        }
+    }
+
+    /**
+     * Wait for notification or time to elapse.
+     * @param timeToLinger time to linger.
+     */
+    void linger(final long timeToLinger) {
+        try {
+            this.wait(timeToLinger);
+        } catch (InterruptedException ie) {
+            shutdown = true;
+        }
+    }
+
+    /**
+     * Represents an entry in job scheduler.
+     */
+    static final class ScheduledJobEntry {
+        /**
+         * Desired execution time.
+         */
+        long desiredExecutionTime;
+        /**
+         * Job to run.
+         */
+        Job job;
+        /**
+         * Repeat period.
+         */
+        long period = 0;
+
+        /**
+         * Create new instance.
+         * @param job job
+         * @param desiredTime desired time.
+         */
+        ScheduledJobEntry(final Job job, final long desiredTime) {
+            this(job, desiredTime, 0);
+        }
+
+        /**
+         * Create new instance.
+         * @param job job
+         * @param desiredTime desired time
+         * @param period repeat period
+         */
+        ScheduledJobEntry(final Job job,
+                          final long desiredTime,
+                          final long period) {
+            super();
+            this.desiredExecutionTime = desiredTime;
+            this.job = job;
+            this.period = period;
+        }
+    }
+
+}
+
+

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/Component.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/spi/Component.java b/src/main/java/org/apache/log4j/spi/Component.java
new file mode 100644
index 0000000..42ef29a
--- /dev/null
+++ b/src/main/java/org/apache/log4j/spi/Component.java
@@ -0,0 +1,37 @@
+/*
+ * 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.spi;
+
+
+/**
+ * A common interface shared by log4j components.
+ *
+ * @author Ceki Gulcu
+ */
+public interface Component {
+
+
+    /**
+     * Set owning logger repository for this component. This operation can
+     * only be performed once.
+     * Once set, a subsequent attempt will throw an IllegalStateException.
+     *
+     * @param repository The repository where this appender is attached.
+     */
+    void setLoggerRepository(LoggerRepository repository);
+
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/ComponentBase.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/spi/ComponentBase.java b/src/main/java/org/apache/log4j/spi/ComponentBase.java
new file mode 100644
index 0000000..78932f7
--- /dev/null
+++ b/src/main/java/org/apache/log4j/spi/ComponentBase.java
@@ -0,0 +1,126 @@
+/*
+ * 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.spi;
+
+import org.apache.log4j.ULogger;
+import org.apache.log4j.Logger;
+
+
+/**
+ * Most log4j components derive from this class.
+ *
+ * @author Ceki Gulcu
+ */
+public class ComponentBase implements Component {
+
+    /**
+     * Error count limit.
+     */
+    private static final int ERROR_COUNT_LIMIT = 3;
+
+    /**
+     * Logger repository.
+     */
+    protected LoggerRepository repository;
+    /**
+     * Logger.
+     */
+    private ULogger logger;
+    /**
+     * Error count.
+     */
+    private int errorCount = 0;
+
+    /**
+     * Construct a new instance.
+     */
+    protected ComponentBase() {
+        super();
+    }
+
+
+    /**
+     * Called by derived classes when they deem that the component has recovered
+     * from an erroneous state.
+     */
+    protected void resetErrorCount() {
+        errorCount = 0;
+    }
+
+    /**
+     * Set the owning repository. The owning repository cannot be set more than
+     * once.
+     *
+     * @param repository repository
+     */
+    public void setLoggerRepository(final LoggerRepository repository) {
+        if (this.repository == null) {
+            this.repository = repository;
+        } else if (this.repository != repository) {
+            throw new IllegalStateException("Repository has been already set");
+        }
+    }
+
+    /**
+     * Return the LoggerRepository to which this component is attached.
+     *
+     * @return Owning LoggerRepository
+     */
+    protected LoggerRepository getLoggerRepository() {
+        return repository;
+    }
+
+    /**
+     * Return an instance specific logger to be used by the component itself.
+     * This logger is not intended to be accessed by the end-user, hence the
+     * protected keyword.
+     * <p/>
+     * <p>In case the repository for this component is not set,
+     * this implementations returns a {@link SimpleULogger} instance.
+     *
+     * @return A ULogger instance.
+     */
+    protected ULogger getLogger() {
+        if (logger == null) {
+            if (repository != null) {
+                Logger l = repository.getLogger(this.getClass().getName());
+                if (l instanceof ULogger) {
+                    logger = (ULogger) l;
+                } else {
+                    logger = new Log4JULogger(l);
+                }
+            } else {
+                logger = SimpleULogger.getLogger(this.getClass().getName());
+            }
+        }
+        return logger;
+    }
+
+    /**
+     * Frequently called methods in log4j components can invoke this method in
+     * order to avoid flooding the output when logging lasting error conditions.
+     *
+     * @return a regular logger, or a NOPLogger if called too frequently.
+     */
+    protected ULogger getNonFloodingLogger() {
+        if (errorCount++ >= ERROR_COUNT_LIMIT) {
+            return NOPULogger.NOP_LOGGER;
+        } else {
+            return getLogger();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/Decoder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/spi/Decoder.java b/src/main/java/org/apache/log4j/spi/Decoder.java
new file mode 100644
index 0000000..d4686ad
--- /dev/null
+++ b/src/main/java/org/apache/log4j/spi/Decoder.java
@@ -0,0 +1,63 @@
+/*
+ * 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.spi;
+
+
+import java.io.IOException;
+
+import java.net.URL;
+
+import java.util.Map;
+import java.util.Vector;
+
+
+/**
+ *  Allow LoggingEvents to be reconstructed from a different format
+ * (usually XML).
+ *
+ *  @author Scott Deboy (sdeboy@apache.org)
+ */
+public interface Decoder {
+    /**
+     * Decode events from document.
+     * @param document document to decode.
+     * @return list of LoggingEvent instances.
+     */
+  Vector decodeEvents(String document);
+
+    /**
+     * Decode event from string.
+     * @param event string representation of event
+     * @return event
+     */
+  LoggingEvent decode(String event);
+
+    /**
+     * Decode event from document retreived from URL.
+     * @param url url of document
+     * @return list of LoggingEvent instances.
+     * @throws IOException if IO error resolving document.
+     */
+  Vector decode(URL url) throws IOException;
+
+    /**
+     * Sets additional properties.
+     * @param additionalProperties map of additional properties.
+     */
+  void setAdditionalProperties(Map additionalProperties);
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/spi/ErrorItem.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/spi/ErrorItem.java b/src/main/java/org/apache/log4j/spi/ErrorItem.java
new file mode 100644
index 0000000..f6f3686
--- /dev/null
+++ b/src/main/java/org/apache/log4j/spi/ErrorItem.java
@@ -0,0 +1,172 @@
+/*
+ * 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.spi;
+
+import java.io.PrintStream;
+
+/**
+ * Used to store special log4j errors which cannot be logged using internal
+ * logging. Such errors include those occurring during the initial phases
+ * of log4j configuration or errors emanating from core components such as
+ * Logger or Hierarchy.
+ *
+ * @author Ceki Gulcu
+ */
+public class ErrorItem {
+    /**
+     * Message.
+     */
+  String message;
+    /**
+     * Column.
+     */
+  int colNumber = -1;
+    /**
+     * Line number.
+     */
+  int lineNumber = -1;
+    /**
+     * Exception.
+     */
+  Throwable exception;
+
+    /**
+     * Create new instance.
+     * @param message message
+     * @param e exception
+     */
+  public ErrorItem(final String message, final Exception e) {
+    super();
+    this.message = message;
+    exception = e;
+  }
+
+    /**
+     * Creaet new instance.
+     * @param message message.
+     */
+  public ErrorItem(final String message) {
+    this(message, null);
+  }
+
+    /**
+     * Get column number.
+     * @return column number.
+     */
+  public int getColNumber() {
+    return colNumber;
+  }
+
+    /**
+     * Set column number.
+     * @param colNumber new column number.
+     */
+  public void setColNumber(int colNumber) {
+    this.colNumber = colNumber;
+  }
+
+    /**
+     * Get exception.
+     * @return exception.
+     */
+  public Throwable getException() {
+    return exception;
+  }
+
+    /**
+     * Set exception.
+     * @param exception exception
+     */
+  public void setException(final Throwable exception) {
+    this.exception = exception;
+  }
+
+    /**
+     * Get line number.
+     * @return line number.
+     */
+  public int getLineNumber() {
+    return lineNumber;
+  }
+
+    /**
+     * Set line number.
+     * @param lineNumber line number.
+     */
+  public void setLineNumber(final int lineNumber) {
+    this.lineNumber = lineNumber;
+  }
+
+    /**
+     * Get message.
+     * @return message.
+     */
+  public String getMessage() {
+    return message;
+  }
+
+    /**
+     * Set message.
+     * @param message message.
+     */
+  public void setMessage(final String message) {
+    this.message = message;
+  }
+
+    /**
+     * String representation of ErrorItem.
+     * @return string.
+     */
+  public String toString() {
+    String str =
+      "Reported error: \"" + message + "\"";
+
+    if (lineNumber != -1) {
+      str += " at line " + lineNumber + " column " + colNumber;
+    }
+    if (exception != null) {
+      str += (" with exception " + exception);
+    }
+    return str;
+  }
+
+  /**
+   * Dump the details of this ErrorItem to System.out.
+   */
+  public void dump() {
+    dump(System.out);
+  }
+  
+  /**
+   * Dump the details of this ErrorItem on the specified {@link PrintStream}.
+   * @param ps print stream.
+   */
+  public void dump(final PrintStream ps) {
+    String str =
+      "Reported error: \"" + message + "\"";
+
+    if (lineNumber != -1) {
+      str += " at line " + lineNumber + " column " + colNumber;
+    }
+    ps.println(str);
+
+    if (exception != null) {
+      exception.printStackTrace(ps);
+    }
+  }
+}