logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sde...@apache.org
Subject svn commit: r1178304 [3/6] - in /logging/chainsaw/trunk: ./ src/main/java/org/apache/log4j/ src/main/java/org/apache/log4j/db/ src/main/java/org/apache/log4j/db/dialect/ src/main/java/org/apache/log4j/helpers/ src/main/java/org/apache/log4j/net/ src/ma...
Date Mon, 03 Oct 2011 06:15:43 GMT
Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketHubReceiver.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketHubReceiver.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketHubReceiver.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketHubReceiver.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,409 @@
+/*
+ * 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 org.apache.log4j.plugins.Plugin;
+import org.apache.log4j.plugins.Receiver;
+import org.apache.log4j.spi.LoggerRepository;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+  SocketHubReceiver receives a remote logging event on a configured
+  socket and "posts" it to a LoggerRepository as if the event was
+  generated locally. This class is designed to receive events from
+  the SocketHubAppender class (or classes that send compatible events).
+
+  <p>Once the event has been "posted", it will be handled by the
+  appenders currently configured in the LoggerRespository.
+
+  @author Mark Womack
+  @author Ceki G&uuml;lc&uuml;
+  @author Paul Smith (psmith@apache.org)
+*/
+public class SocketHubReceiver
+extends Receiver implements SocketNodeEventListener, PortBased {
+
+    /**
+     * Default reconnection delay.
+     */
+  static final int DEFAULT_RECONNECTION_DELAY   = 30000;
+
+    /**
+     * Host.
+     */
+  protected String host;
+
+    /**
+     * Port.
+     */
+  protected int port;
+    /**
+     * Reconnection delay.
+     */
+  protected int reconnectionDelay = DEFAULT_RECONNECTION_DELAY;
+
+  /**
+   * The MulticastDNS zone advertised by a SocketHubReceiver
+   */
+  public static final String ZONE = "_log4j_obj_tcpconnect_receiver.local.";
+
+    /**
+     * Active.
+     */
+  protected boolean active = false;
+
+    /**
+     * Connector.
+     */
+  protected Connector connector;
+
+    /**
+     * Socket.
+     */
+  protected SocketNode13 socketNode;
+
+    /**
+     * Listener list.
+     */
+  private List listenerList = Collections.synchronizedList(new ArrayList());
+
+  private boolean advertiseViaMulticastDNS;
+  private ZeroConfSupport zeroConf;
+
+    /**
+     * Create new instance.
+     */
+  public SocketHubReceiver() {
+     super();
+  }
+
+    /**
+     * Create new instance.
+     * @param h host
+     * @param p port
+     */
+  public SocketHubReceiver(final String h,
+                           final int p) {
+    super();
+    host = h;
+    port = p;
+  }
+
+    /**
+     * Create new instance.
+     * @param h host
+     * @param p port
+     * @param repo logger repository
+     */
+  public SocketHubReceiver(final String h,
+                           final int p,
+                           final LoggerRepository repo) {
+    super();
+    host = h;
+    port = p;
+    repository = repo;
+  }
+
+  /**
+   * Adds a SocketNodeEventListener to this receiver to be notified
+   * of SocketNode events.
+   * @param l listener
+   */
+  public void addSocketNodeEventListener(final SocketNodeEventListener l) {
+    listenerList.add(l);
+  }
+
+  /**
+   * Removes a specific SocketNodeEventListener from this instance
+   * so that it will no  longer be notified of SocketNode events.
+   * @param l listener
+   */
+  public void removeSocketNodeEventListener(
+          final SocketNodeEventListener l) {
+    listenerList.remove(l);
+  }
+
+  /**
+    Get the remote host to connect to for logging events.
+    @return host
+   */
+  public String getHost() {
+    return host;
+  }
+
+  /**
+   * Configures the Host property, this will require activateOptions
+   * to be called for this to take effect.
+   * @param remoteHost address of remote host.
+   */
+  public void setHost(final String remoteHost) {
+    this.host = remoteHost;
+  }
+  /**
+    Set the remote host to connect to for logging events.
+   Equivalent to setHost.
+   @param remoteHost address of remote host.
+   */
+  public void setPort(final String remoteHost) {
+    host = remoteHost;
+  }
+
+  /**
+    Get the remote port to connect to for logging events.
+   @return port
+   */
+  public int getPort() {
+    return port;
+  }
+
+  /**
+    Set the remote port to connect to for logging events.
+    @param p port
+   */
+  public void setPort(final int p) {
+    this.port = p;
+  }
+
+  /**
+     The <b>ReconnectionDelay</b> option takes a positive integer
+     representing the number of milliseconds to wait between each
+     failed connection attempt to the server. The default value of
+     this option is 30000 which corresponds to 30 seconds.
+
+     <p>Setting this option to zero turns off reconnection
+     capability.
+   @param delay milliseconds to wait or zero to not reconnect.
+   */
+  public void setReconnectionDelay(final int delay) {
+    int oldValue = this.reconnectionDelay;
+    this.reconnectionDelay = delay;
+    firePropertyChange("reconnectionDelay", oldValue, this.reconnectionDelay);
+  }
+
+  /**
+     Returns value of the <b>ReconnectionDelay</b> option.
+   @return value of reconnection delay option.
+   */
+  public int getReconnectionDelay() {
+    return reconnectionDelay;
+  }
+
+  /**
+   * 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(final Plugin testPlugin) {
+    if (testPlugin != null && testPlugin instanceof SocketHubReceiver) {
+      SocketHubReceiver sReceiver = (SocketHubReceiver) testPlugin;
+
+      return (port == sReceiver.getPort()
+              && host.equals(sReceiver.getHost())
+              && reconnectionDelay == sReceiver.getReconnectionDelay()
+              && super.isEquivalent(testPlugin));
+    }
+    return false;
+  }
+
+  /**
+    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()) {
+      setActive(true);
+      if (advertiseViaMulticastDNS) {
+        zeroConf = new ZeroConfSupport(ZONE, port, getName());
+        zeroConf.advertise();
+      }
+
+      fireConnector(false);
+    }
+  }
+
+  /**
+    Called when the receiver should be stopped. Closes the socket */
+  public synchronized void shutdown() {
+    // mark this as no longer running
+    active = false;
+
+    // close the socket
+    try {
+      if (socketNode != null) {
+        socketNode.close();
+        socketNode = null;
+      }
+    } catch (Exception e) {
+      getLogger().info("Excpetion closing socket", e);
+      // ignore for now
+    }
+
+    // stop the connector
+    if (connector != null) {
+      connector.interrupted = true;
+      connector = null;  // allow gc
+    }
+    if (advertiseViaMulticastDNS) {
+        zeroConf.unadvertise();
+    }
+  }
+
+  /**
+    Listen for a socketClosedEvent from the SocketNode. Reopen the
+    socket if this receiver is still active.
+   @param e exception not used.
+   */
+  public void socketClosedEvent(final Exception e) {
+    // if it is a non-normal closed event
+    // we clear the connector object here
+    // so that it actually does reconnect if the
+    // remote socket dies.
+    if (e != null) {
+      connector = null;
+      fireConnector(true);
+    }
+  }
+
+    /**
+     * Fire connectors.
+     * @param isReconnect true if reconnect.
+     */
+  private synchronized void fireConnector(final boolean isReconnect) {
+    if (active && connector == null) {
+      getLogger().debug("Starting a new connector thread.");
+      connector = new Connector(isReconnect);
+      connector.setDaemon(true);
+      connector.setPriority(Thread.MIN_PRIORITY);
+      connector.start();
+    }
+  }
+
+    /**
+     * Set socket.
+     * @param newSocket new value for socket.
+     */
+  private synchronized void setSocket(final Socket newSocket) {
+    connector = null;
+    socketNode = new SocketNode13(newSocket, this);
+    socketNode.addSocketNodeEventListener(this);
+
+    synchronized (listenerList) {
+        for (Iterator iter = listenerList.iterator(); iter.hasNext();) {
+            SocketNodeEventListener listener =
+                    (SocketNodeEventListener) iter.next();
+            socketNode.addSocketNodeEventListener(listener);
+        }
+    }
+    new Thread(socketNode).start();
+  }
+
+  public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+      this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+  }
+
+  public boolean isAdvertiseViaMulticastDNS() {
+      return advertiseViaMulticastDNS;
+  }
+
+  /**
+   The Connector will reconnect when the server becomes available
+   again.  It does this by attempting to open a new connection every
+   <code>reconnectionDelay</code> milliseconds.
+
+   <p>It stops trying whenever a connection is established. It will
+   restart to try reconnect to the server when previpously open
+   connection is droppped.
+
+   @author  Ceki G&uuml;lc&uuml;
+   */
+  private final class Connector extends Thread {
+
+      /**
+       * Interruption status.
+       */
+    boolean interrupted = false;
+      /**
+       * If true, then delay on next iteration.
+       */
+    boolean doDelay;
+
+      /**
+       * Create new instance.
+       * @param isReconnect true if reconnecting.
+       */
+    public Connector(final boolean isReconnect) {
+      super();
+      doDelay = isReconnect;
+    }
+
+      /**
+       * Attempt to connect until interrupted.
+       */
+    public void run() {
+      while (!interrupted) {
+        try {
+          if (doDelay) {
+            getLogger().debug("waiting for " + reconnectionDelay
+              + " milliseconds before reconnecting.");
+            sleep(reconnectionDelay);
+          }
+          doDelay = true;
+          getLogger().debug("Attempting connection to " + host);
+          Socket s = new Socket(host, port);
+          setSocket(s);
+          getLogger().debug(
+                  "Connection established. Exiting connector thread.");
+          break;
+        } catch (InterruptedException e) {
+          getLogger().debug("Connector interrupted. Leaving loop.");
+          return;
+        } catch (java.net.ConnectException e) {
+          getLogger().debug("Remote host {} refused connection.", host);
+        } catch (IOException e) {
+          getLogger().debug("Could not connect to {}. Exception is {}.",
+                  host, e);
+        }
+      }
+    }
+  }
+
+    /**
+     * This method does nothing.
+     * @param remoteInfo remote info.
+     */
+  public void socketOpened(final String remoteInfo) {
+
+    // This method does nothing.
+  }
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketNode13.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketNode13.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketNode13.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketNode13.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,299 @@
+/*
+ * 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.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.Constants;
+import org.apache.log4j.plugins.Pauseable;
+import org.apache.log4j.plugins.Receiver;
+import org.apache.log4j.spi.ComponentBase;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+// Contributors:  Moses Hohman <mmhohman@rainbow.uchicago.edu>
+
+/**
+   Read {@link LoggingEvent} objects sent from a remote client using
+   Sockets (TCP). These logging events are logged according to local
+   policy, as if they were generated locally.
+
+   <p>For example, the socket node might decide to log events to a
+   local file and also resent them to a second socket node.
+
+    Implementation lifted from org.apache.log4j.net.SocketNode
+    in log4j 1.3 and renamed to prevent collision with
+    log4j 1.2 implementation.
+
+    @author  Ceki G&uuml;lc&uuml;
+    @author  Paul Smith (psmith@apache.org)
+
+
+*/
+public class SocketNode13 extends ComponentBase implements Runnable, Pauseable {
+
+    /**
+     * Paused state.
+     */
+  private boolean paused;
+    /**
+     * Closed state.
+     */
+  private boolean closed;
+    /**
+     * Socket.
+     */
+  private Socket socket;
+    /**
+     * Receiver.
+     */
+  private Receiver receiver;
+    /**
+     * List of listeners.
+     */
+  private List listenerList = Collections.synchronizedList(new ArrayList());
+
+
+
+  /**
+    Constructor for socket and logger repository.
+   @param s socket
+   @param hierarchy logger repository
+   */
+  public SocketNode13(final Socket s,
+                    final LoggerRepository hierarchy) {
+    super();
+    this.socket = s;
+    this.repository = hierarchy;
+  }
+
+  /**
+    Constructor for socket and receiver.
+   @param s socket
+   @param r receiver
+   */
+  public SocketNode13(final Socket s, final Receiver r) {
+    super();
+    this.socket = s;
+    this.receiver = r;
+  }
+
+  /**
+   * Set the event listener on this node.
+   *
+   * @deprecated Now supports mutliple listeners, this method
+   * simply invokes the removeSocketNodeEventListener() to remove
+   * the listener, and then readds it.
+   * @param l listener
+   */
+  public void setListener(final SocketNodeEventListener l) {
+    removeSocketNodeEventListener(l);
+    addSocketNodeEventListener(l);
+  }
+
+  /**
+   * Adds the listener to the list of listeners to be notified of the
+   * respective event.
+   * @param listener the listener to add to the list
+   */
+  public void addSocketNodeEventListener(
+          final SocketNodeEventListener listener) {
+    listenerList.add(listener);
+  }
+
+  /**
+   * Removes the registered Listener from this instances list of
+   * listeners.  If the listener has not been registered, then invoking
+   * this method has no effect.
+   *
+   * @param listener the SocketNodeEventListener to remove
+   */
+  public void removeSocketNodeEventListener(
+          final SocketNodeEventListener listener) {
+    listenerList.remove(listener);
+  }
+
+
+    /**
+     * Deserialize events from socket until interrupted.
+     */
+  public void run() {
+    LoggingEvent event;
+    Logger remoteLogger;
+    Exception listenerException = null;
+    ObjectInputStream ois = null;
+
+    try {
+      ois =
+        new ObjectInputStream(
+          new BufferedInputStream(socket.getInputStream()));
+    } catch (Exception e) {
+      ois = null;
+      listenerException = e;
+      getLogger().error("Exception opening ObjectInputStream to " + socket, e);
+    }
+
+    if (ois != null) {
+
+      String hostName = socket.getInetAddress().getHostName();
+      String remoteInfo = hostName + ":" + socket.getPort();
+
+      /**
+       * notify the listener that the socket has been
+       * opened and this SocketNode is ready and waiting
+       */
+      fireSocketOpened(remoteInfo);
+
+      try {
+        while (!isClosed()) {
+          // read an event from the wire
+          event = (LoggingEvent) ois.readObject();
+          event.setProperty(Constants.HOSTNAME_KEY, hostName);
+          // store the known remote info in an event property
+          event.setProperty("log4j.remoteSourceInfo", remoteInfo);
+
+          // if configured with a receiver, tell it to post the event
+          if (!isPaused() && !isClosed()) {
+            if ((receiver != null)) {
+              receiver.doPost(event);
+
+              // else post it via the hierarchy
+            } else {
+              // get a logger from the hierarchy. The name of the logger
+              // is taken to be the name contained in the event.
+              remoteLogger = repository.getLogger(event.getLoggerName());
+
+              //event.logger = remoteLogger;
+              // apply the logger-level filter
+              if (event
+                .getLevel()
+                .isGreaterOrEqual(remoteLogger.getEffectiveLevel())) {
+                // finally log the event as if was generated locally
+                remoteLogger.callAppenders(event);
+              }
+            }
+          } else {
+            //we simply discard this event.
+          }
+        }
+      } catch (java.io.EOFException e) {
+        getLogger().info("Caught java.io.EOFException closing connection.");
+        listenerException = e;
+      } catch (java.net.SocketException e) {
+        getLogger().info("Caught java.net.SocketException closing connection.");
+        listenerException = e;
+      } catch (IOException e) {
+        getLogger().info("Caught java.io.IOException: " + e);
+        getLogger().info("Closing connection.");
+        listenerException = e;
+      } catch (Exception e) {
+        getLogger().error("Unexpected exception. Closing connection.", e);
+        listenerException = e;
+      }
+    }
+
+    // close the socket
+    try {
+      if (ois != null) {
+        ois.close();
+      }
+    } catch (Exception e) {
+      //getLogger().info("Could not close connection.", e);
+    }
+
+    // send event to listener, if configured
+    if (listenerList.size() > 0 && !isClosed()) {
+      fireSocketClosedEvent(listenerException);
+    }
+  }
+
+  /**
+   * Notifies all registered listeners regarding the closing of the Socket.
+   * @param listenerException listener exception
+   */
+  private void fireSocketClosedEvent(final Exception listenerException) {
+    synchronized (listenerList) {
+        for (Iterator iter = listenerList.iterator(); iter.hasNext();) {
+            SocketNodeEventListener snel =
+                    (SocketNodeEventListener) iter.next();
+            if (snel != null) {
+                snel.socketClosedEvent(listenerException);
+            }
+        }
+    }
+  }
+
+  /**
+   * Notifies all registered listeners regarding the opening of a Socket.
+   * @param remoteInfo remote info
+   */
+  private void fireSocketOpened(final String remoteInfo) {
+    synchronized (listenerList) {
+        for (Iterator iter = listenerList.iterator(); iter.hasNext();) {
+            SocketNodeEventListener snel =
+                    (SocketNodeEventListener) iter.next();
+            if (snel != null) {
+                snel.socketOpened(remoteInfo);
+            }
+        }
+    }
+  }
+
+    /**
+     * Sets if node is paused.
+     * @param b new value
+     */
+  public void setPaused(final boolean b) {
+    this.paused = b;
+  }
+
+    /**
+     * Get if node is paused.
+     * @return true if pause.
+     */
+  public boolean isPaused() {
+    return this.paused;
+  }
+
+    /**
+     * Close the node and underlying socket
+     */
+  public void close() throws IOException {
+    getLogger().debug("closing socket");
+    this.closed = true;
+    socket.close();
+    fireSocketClosedEvent(null);
+  }
+  
+    /**
+     * Get if node is closed.
+     * @return true if closed.
+     */
+  public boolean isClosed() {
+    return this.closed;
+  }
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketNodeEventListener.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketNodeEventListener.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketNodeEventListener.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketNodeEventListener.java Mon Oct  3 06:15:40 2011
@@ -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.net;
+
+import java.util.EventListener;
+
+/**
+  Interface used to listen for {@link SocketNode} related
+  events. Clients register an instance of the interface and the
+  instance is called back when the various events occur.
+
+  @author Mark Womack
+  @author Paul Smith (psmith@apache.org)
+*/
+public interface SocketNodeEventListener extends EventListener {
+
+  /**
+   * Called when the SocketNode is created and begins awaiting data.
+   *  @param remoteInfo remote info
+   */
+  void socketOpened(String remoteInfo);
+
+  /**
+    Called when the socket the node was given has been closed.
+    @param e exception
+   */
+  void socketClosedEvent(Exception e);
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketReceiver.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketReceiver.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketReceiver.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/SocketReceiver.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,479 @@
+/*
+ * 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.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+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;
+
+
+/**
+  SocketReceiver receives a remote logging event on a configured
+  socket and "posts" it to a LoggerRepository as if the event was
+  generated locally. This class is designed to receive events from
+  the SocketAppender class (or classes that send compatible events).
+
+  <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 (sdeboy@apache.org)
+  @author Paul Smith (psmith@apache.org)
+*/
+public class SocketReceiver extends Receiver implements Runnable, PortBased,
+  Pauseable {
+    /**
+     * socket map.
+     */
+  private Map socketMap = new HashMap();
+    /**
+     * Paused.
+     */
+  private boolean paused;
+    /**
+     * Thread.
+     */
+  private Thread rThread;
+    /**
+     * Port.
+     */
+  protected int port;
+    /**
+     * Server socket.
+     */
+  private ServerSocket serverSocket;
+    /**
+     * Socket list.
+     */
+  private Vector socketList = new Vector();
+
+  /**
+   * The MulticastDNS zone advertised by a SocketReceiver
+   */
+  public static final String ZONE = "_log4j_obj_tcpaccept_receiver.local.";
+
+    /**
+     * Listener.
+     */
+  private SocketNodeEventListener listener = null;
+    /**
+     * Listeners.
+     */
+  private List listenerList = Collections.synchronizedList(new ArrayList());
+  private boolean advertiseViaMulticastDNS;
+  private ZeroConfSupport zeroConf;
+
+    /**
+     * Create new instance.
+     */
+  public SocketReceiver() {
+        super();
+  }
+
+    /**
+     * Create new instance.
+     * @param p port
+     */
+  public SocketReceiver(final int p) {
+    super();
+    port = p;
+  }
+
+    /**
+     * Create new instance.
+     * @param p port
+     * @param repo logger repository
+     */
+  public SocketReceiver(final int p, final LoggerRepository repo) {
+    super();
+    this.port = p;
+    repository = repo;
+  }
+
+    /** {@inheritDoc} */
+  public int getPort() {
+    return port;
+  }
+
+  /** {@inheritDoc} */
+  public void setPort(final int p) {
+    port = p;
+  }
+
+  /**
+   * 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(final Plugin testPlugin) {
+    if ((testPlugin != null) && testPlugin instanceof SocketReceiver) {
+      SocketReceiver sReceiver = (SocketReceiver) testPlugin;
+
+      return (port == sReceiver.getPort() && super.isEquivalent(testPlugin));
+    }
+
+    return false;
+  }
+
+  /**
+    Starts the SocketReceiver with the current options. */
+  public void activateOptions() {
+    if (!isActive()) {
+      //      shutdown();
+      rThread = new Thread(this);
+      rThread.setDaemon(true);
+      rThread.start();
+      if (advertiseViaMulticastDNS) {
+        zeroConf = new ZeroConfSupport(ZONE, port, getName());
+        zeroConf.advertise();
+      }
+
+      active = true;
+    }
+  }
+
+  /**
+   * Called when the receiver should be stopped. Closes the
+   * server socket and all of the open sockets.
+   */
+  public synchronized void shutdown() {
+    getLogger().debug(getName() + " received shutdown request");
+
+    // mark this as no longer running
+    active = false;
+
+    if (rThread != null) {
+      rThread.interrupt();
+      rThread = null;
+    }
+    if (advertiseViaMulticastDNS) {
+        zeroConf.unadvertise();
+    }
+
+    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(getName() + " doShutdown called");
+
+    // close the server socket
+    closeServerSocket();
+
+    // close all of the accepted sockets
+    closeAllAcceptedSockets();
+  }
+
+  /**
+   * 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
+    socketMap.clear();
+    socketList.clear();
+  }
+
+  /**
+    Sets the flag to indicate if receiver is active or not.
+   @param b new value
+   */
+  protected synchronized void setActive(final boolean b) {
+    active = b;
+  }
+
+  public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+      this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+  }
+
+  public boolean isAdvertiseViaMulticastDNS() {
+      return advertiseViaMulticastDNS;
+  }
+
+  /**
+    Loop, accepting new socket connections. */
+  public void run() {
+    /**
+     * Ensure we start fresh.
+     */
+    closeServerSocket();
+    closeAllAcceptedSockets();
+
+    // 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;
+
+      return;
+    }
+
+    Socket socket = null;
+
+    try {
+      getLogger().debug("in run-about to enter while not interrupted loop");
+
+      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);
+
+          SocketNode13 node = new SocketNode13(socket, this);
+          synchronized (listenerList) {
+            for (Iterator iter = listenerList.iterator();
+                 iter.hasNext();) {
+                SocketNodeEventListener l =
+                        (SocketNodeEventListener) iter.next();
+                node.addSocketNodeEventListener(l);
+            }
+          }
+          socketMap.put(socket, node);
+          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");
+      }
+    } catch (Exception e) {
+      getLogger().warn(
+        "exception while watching socket server in SocketReceiver ("
+        + this.getName() + "), stopping");
+    }
+
+    getLogger().debug("{} has exited the not interrupted loop", getName());
+
+    // socket not watched because we a no longer running
+    // so close it now.
+    if (socket != null) {
+      try {
+        socket.close();
+      } catch (IOException e1) {
+        getLogger().warn("socket exception caught - socket closed");
+      }
+    }
+
+    getLogger().debug("{} is exiting main run loop", getName());
+  }
+
+  /**
+   * Returns a Vector of SocketDetail representing the IP/Domain name
+   * of the currently connected sockets that this receiver has
+   * been responsible for creating.
+   * @return Vector of SocketDetails
+   */
+  public Vector getConnectedSocketDetails() {
+    Vector details = new Vector(socketList.size());
+
+    for (Enumeration enumeration = socketList.elements();
+         enumeration.hasMoreElements();
+            ) {
+      Socket socket = (Socket) enumeration.nextElement();
+      details.add(
+        new SocketDetail(socket, (SocketNode13) socketMap.get(socket)));
+    }
+
+    return details;
+  }
+
+  /**
+   * Returns the currently configured SocketNodeEventListener that
+   * will be automatically set for each SocketNode created.
+   * @return SocketNodeEventListener currently configured
+   *
+   * @deprecated This receiver now supports multiple listeners
+   */
+  public SocketNodeEventListener getListener() {
+    return listener;
+  }
+
+  /**
+   * Adds the listener to the list of listeners to be notified of the
+   * respective event.
+   * @param l the listener to add to the list
+   */
+  public void addSocketNodeEventListener(
+          final SocketNodeEventListener l) {
+    listenerList.add(l);
+  }
+
+  /**
+   * Removes the registered Listener from this instances list of
+   * listeners.  If the listener has not been registered, then invoking
+   * this method has no effect.
+   *
+   * @param l the SocketNodeEventListener to remove
+   */
+  public void removeSocketNodeEventListener(
+          final SocketNodeEventListener l) {
+    listenerList.remove(l);
+  }
+
+  /**
+   * Sets the SocketNodeEventListener that will be used for each
+   * created SocketNode.
+   * @param l the listener to set on each creation of a SocketNode
+   * @deprecated This receiver now supports multiple listeners and
+   * so this method simply removes the listener (if there already)
+   * and readds it to the list.
+   *
+   * The passed listener will also be returned via the getListener()
+   * method still, but this is also deprecated
+   */
+  public void setListener(final SocketNodeEventListener l) {
+    removeSocketNodeEventListener(l);
+    addSocketNodeEventListener(l);
+    this.listener = l;
+  }
+
+    /** {@inheritDoc} */
+  public boolean isPaused() {
+    return paused;
+  }
+
+    /** {@inheritDoc} */
+  public void setPaused(final boolean b) {
+    paused = b;
+  }
+
+    /**
+     * Socket detail.
+     */
+  private static final class SocketDetail implements AddressBased, PortBased,
+    Pauseable {
+      /**
+       * Address.
+       */
+    private String address;
+      /**
+       * Port.
+       */
+    private int port;
+      /**
+       * Socket node.
+       */
+    private SocketNode13 socketNode;
+
+      /**
+       * Create new instance.
+       * @param socket socket
+       * @param node socket node
+       */
+    private SocketDetail(final Socket socket,
+                         final SocketNode13 node) {
+      super();
+      this.address = socket.getInetAddress().getHostName();
+      this.port = socket.getPort();
+      this.socketNode = node;
+    }
+
+      /** {@inheritDoc} */
+    public String getAddress() {
+      return address;
+    }
+
+      /** {@inheritDoc} */
+    public int getPort() {
+      return port;
+    }
+
+      /** {@inheritDoc} */
+    public String getName() {
+      return "Socket";
+    }
+
+      /** {@inheritDoc} */
+    public boolean isActive() {
+      return true;
+    }
+
+      /** {@inheritDoc} */
+    public boolean isPaused() {
+      return socketNode.isPaused();
+    }
+
+      /** {@inheritDoc} */
+    public void setPaused(final boolean b) {
+      socketNode.setPaused(b);
+    }
+  }
+    /** {@inheritDoc} */
+  public void doPost(final LoggingEvent event) {
+    if (!isPaused()) {
+      super.doPost(event);
+    }
+  }
+
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/UDPAppender.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/UDPAppender.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/UDPAppender.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/UDPAppender.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,330 @@
+/*
+ * 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 org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.Constants;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.xml.XMLLayout;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+
+
+/**
+ *  Sends log information as a UDP datagrams.
+ *
+ *  <p>The UDPAppender is meant to be used as a diagnostic logging tool
+ *  so that logging can be monitored by a simple UDP client.
+ *
+ *  <p>Messages are not sent as LoggingEvent objects but as text after
+ *  applying the designated Layout.
+ *
+ *  <p>The port and remoteHost properties can be set in configuration properties.
+ *  By setting the remoteHost to a broadcast address any number of clients can
+ *  listen for log messages.
+ *
+ *  <p>This was inspired and really extended/copied from {@link SocketAppender}.
+ *  Please see the docs for the proper credit to the authors of that class.
+ *
+ *  @author  <a href="mailto:kbrown@versatilesolutions.com">Kevin Brown</a>
+ *  @author Scott Deboy <sdeboy@apache.org>
+ */
+public class UDPAppender extends AppenderSkeleton implements PortBased{
+  /**
+    * The default port number for the UDP packets, 9991.
+  */
+  public static final int DEFAULT_PORT = 9991;
+
+  /**
+     We remember host name as String in addition to the resolved
+     InetAddress so that it can be returned via getOption().
+  */
+  String hostname;
+  String remoteHost;
+  String application;
+  String encoding;
+  InetAddress address;
+  int port = DEFAULT_PORT;
+  DatagramSocket outSocket;
+
+  /**
+   * The MulticastDNS zone advertised by a UDPAppender
+   */
+  public static final String ZONE = "_log4j_xml_udp_appender.local.";
+
+  // if there is something irrecoverably wrong with the settings, there is no
+  // point in sending out packeets.
+  boolean inError = false;
+  private boolean advertiseViaMulticastDNS;
+  private ZeroConfSupport zeroConf;
+
+    public UDPAppender() {
+      super(false);
+  }
+
+  /**
+     Sends UDP packets to the <code>address</code> and <code>port</code>.
+  */
+  public UDPAppender(final InetAddress address, final int port) {
+    super(false);
+    this.address = address;
+    this.remoteHost = address.getHostName();
+    this.port = port;
+    activateOptions();
+  }
+
+  /**
+     Sends UDP packets to the <code>address</code> and <code>port</code>.
+  */
+  public UDPAppender(final String host, final int port) {
+    super(false);
+    this.port = port;
+    this.address = getAddressByName(host);
+    this.remoteHost = host;
+    activateOptions();
+  }
+
+  /**
+     Open the UDP sender for the <b>RemoteHost</b> and <b>Port</b>.
+  */
+  public void activateOptions() {
+    try {
+      hostname = InetAddress.getLocalHost().getHostName();
+    } catch (UnknownHostException uhe) {
+      try {
+        hostname = InetAddress.getLocalHost().getHostAddress();
+      } catch (UnknownHostException uhe2) {
+        hostname = "unknown";
+      }
+    }
+
+    //allow system property of application to be primary
+    if (application == null) {
+      application = System.getProperty(Constants.APPLICATION_KEY);
+    } else {
+      if (System.getProperty(Constants.APPLICATION_KEY) != null) {
+        application = application + "-" + System.getProperty(Constants.APPLICATION_KEY);
+      }
+    }
+
+    if(remoteHost != null) {
+      address = getAddressByName(remoteHost);
+      connect(address, port);
+    } else {
+      String err = "The RemoteHost property is required for SocketAppender named "+ name;
+      LogLog.error(err);
+      throw new IllegalStateException(err);
+    }
+
+    if (layout == null) {
+        layout = new XMLLayout();
+    }
+
+    if (advertiseViaMulticastDNS) {
+      zeroConf = new ZeroConfSupport(ZONE, port, getName());
+      zeroConf.advertise();
+    }
+
+    super.activateOptions();
+  }
+
+  /**
+     Close this appender.
+     <p>This will mark the appender as closed and
+     call then {@link #cleanUp} method.
+  */
+  public synchronized void close() {
+    if (closed) {
+      return;
+    }
+
+    if (advertiseViaMulticastDNS) {
+      zeroConf.unadvertise();
+    }
+      
+    this.closed = true;
+    cleanUp();
+  }
+
+  /**
+     Close the UDP Socket and release the underlying
+     connector thread if it has been created
+   */
+  public void cleanUp() {
+    if (outSocket != null) {
+      try {
+        outSocket.close();
+      } catch (Exception e) {
+        LogLog.error("Could not close outSocket.", e);
+      }
+
+      outSocket = null;
+    }
+  }
+
+  void connect(InetAddress address, int port) {
+    if (this.address == null) {
+      return;
+    }
+
+    try {
+      // First, close the previous connection if any.
+      cleanUp();
+      outSocket = new DatagramSocket();
+      outSocket.connect(address, port);
+    } catch (IOException e) {
+      LogLog.error(
+        "Could not open UDP Socket for sending.", e);
+      inError = true;
+    }
+  }
+
+  public void append(LoggingEvent event) {
+    if(inError) {
+      return;
+    }
+    
+    if (event == null) {
+      return;
+    }
+
+    if (address == null) {
+      return;
+    }
+
+    if (outSocket != null) {
+      event.setProperty(Constants.HOSTNAME_KEY, hostname);
+      if (application != null) {
+        event.setProperty(Constants.APPLICATION_KEY, application);
+      }
+
+      try {
+        StringBuffer buf = new StringBuffer(layout.format(event));
+
+        byte[] payload;
+        if(encoding == null) {
+          payload = buf.toString().getBytes();
+        } else {
+          payload = buf.toString().getBytes(encoding);
+        }
+
+        DatagramPacket dp =
+           new DatagramPacket(payload, payload.length, address, port);
+        outSocket.send(dp);
+      } catch (IOException e) {
+        outSocket = null;
+        LogLog.warn("Detected problem with UDP connection: " + e);
+      }
+    }
+  }
+
+  public boolean isActive() {
+    return !inError;
+  }
+  
+  InetAddress getAddressByName(String host) {
+    try {
+      return InetAddress.getByName(host);
+    } catch (Exception e) {
+      LogLog.error("Could not find address of [" + host + "].", e);
+      return null;
+    }
+  }
+
+  /**
+     The UDPAppender uses layouts. Hence, this method returns
+     <code>true</code>.
+  */
+  public boolean requiresLayout() {
+    return true;
+  }
+
+  /**
+     The <b>RemoteHost</b> option takes a string value which should be
+     the host name or ipaddress to send the UDP packets.
+   */
+  public void setRemoteHost(String host) {
+    remoteHost = host;
+  }
+
+  /**
+     Returns value of the <b>RemoteHost</b> option.
+   */
+  public String getRemoteHost() {
+    return remoteHost;
+  }
+
+  /**
+     The <b>App</b> option takes a string value which should be the name of the application getting logged.
+     If property was already set (via system property), don't set here.
+   */
+  public void setApplication(String app) {
+    this.application = app;
+  }
+
+  /**
+     Returns value of the <b>App</b> option.
+   */
+  public String getApplication() {
+    return application;
+  }
+
+  /**
+     The <b>Encoding</b> option specifies how the bytes are encoded.  If this option is not specified, 
+     the System encoding is used.
+   */
+  public void setEncoding(String encoding) {
+    this.encoding = encoding;
+  }
+
+  /**
+     Returns value of the <b>Encoding</b> option.
+   */
+  public String getEncoding() {
+    return encoding;
+  }
+
+    /**
+     The <b>Port</b> option takes a positive integer representing
+     the port where UDP packets will be sent.
+   */
+  public void setPort(int port) {
+    this.port = port;
+  }
+
+  /**
+     Returns value of the <b>Port</b> option.
+   */
+  public int getPort() {
+    return port;
+  }
+
+  public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+    this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+  }
+
+  public boolean isAdvertiseViaMulticastDNS() {
+    return advertiseViaMulticastDNS;
+  }
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/UDPReceiver.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/UDPReceiver.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/UDPReceiver.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/UDPReceiver.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,281 @@
+/*
+ * 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.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.log4j.plugins.Pauseable;
+import org.apache.log4j.plugins.Receiver;
+import org.apache.log4j.spi.Decoder;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ *  Receive LoggingEvents encoded with an XMLLayout, convert the XML data to a
+ *  LoggingEvent and post the LoggingEvent.
+ *
+ *  @author Scott Deboy <sdeboy@apache.org>
+ *
+ */
+public class UDPReceiver extends Receiver implements PortBased, Pauseable {
+  private static final int PACKET_LENGTH = 16384;
+  private UDPReceiverThread receiverThread;
+  private String encoding;
+
+  //default to log4j xml decoder
+  private String decoder = "org.apache.log4j.xml.XMLDecoder";
+  private Decoder decoderImpl;
+  protected boolean paused;
+  private transient boolean closed = false;
+  private int port;
+  private DatagramSocket socket;
+  UDPHandlerThread handlerThread;
+  private boolean advertiseViaMulticastDNS;
+  private ZeroConfSupport zeroConf;
+
+  /**
+   * The MulticastDNS zone advertised by a UDPReceiver
+   */
+  public static final String ZONE = "_log4j_xml_udp_receiver.local.";
+
+
+    public int getPort() {
+    return port;
+  }
+
+  public void setPort(int port) {
+    this.port = port;
+  }
+
+  /**
+   * The <b>Encoding</b> option specifies how the bytes are encoded.  If this 
+   * option is not specified, the system encoding will be used.
+   * */
+  public void setEncoding(String encoding) {
+    this.encoding = encoding;
+  }
+
+  /**
+   * Returns value of the <b>Encoding</b> option.
+   */
+  public String getEncoding() {
+    return encoding;
+  }
+
+  public String getDecoder() {
+    return decoder;
+  }
+
+  public void setDecoder(String decoder) {
+    this.decoder = decoder;
+  }
+
+  public boolean isPaused() {
+    return paused;
+  }
+
+  public void setPaused(boolean b) {
+    paused = b;
+  }
+
+  public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+    this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+  }
+
+  public boolean isAdvertiseViaMulticastDNS() {
+    return advertiseViaMulticastDNS;
+  }
+
+  public synchronized void shutdown() {
+    if(closed == true) {
+      return;
+    }
+    closed = true;
+    active = false;
+    // Closing the datagram socket will unblock the UDPReceiverThread if it is
+    // was waiting to receive data from the socket.
+    if (socket != null) {
+    	socket.close();
+    }
+
+    if (advertiseViaMulticastDNS) {
+      zeroConf.unadvertise();
+    }
+      
+    try {
+      if(handlerThread != null) {
+      	handlerThread.close();
+        handlerThread.join();
+      }
+      if(receiverThread != null) {
+        receiverThread.join();
+      }
+    } catch(InterruptedException ie) {
+    }
+  }
+
+  /**
+    Returns true if this receiver is active. */
+//  public synchronized boolean isActive() {
+//    return isActive;
+//}
+
+  public void activateOptions() {
+    try {
+      Class c = Class.forName(decoder);
+      Object o = c.newInstance();
+
+      if (o instanceof Decoder) {
+        this.decoderImpl = (Decoder) o;
+      }
+    } catch (ClassNotFoundException cnfe) {
+      getLogger().warn("Unable to find decoder", cnfe);
+    } catch (IllegalAccessException iae) {
+      getLogger().warn("Could not construct decoder", iae);
+    } catch (InstantiationException ie) {
+      getLogger().warn("Could not construct decoder", ie);
+    }
+
+    try {
+      socket = new DatagramSocket(port);
+      receiverThread = new UDPReceiverThread();
+      receiverThread.start();
+      handlerThread = new UDPHandlerThread();
+      handlerThread.start();
+      if (advertiseViaMulticastDNS) {
+        zeroConf = new ZeroConfSupport(ZONE, port, getName());
+        zeroConf.advertise();
+      }
+      active = true;
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+  }
+
+  class UDPHandlerThread extends Thread {
+    private List list = new ArrayList();
+
+    public UDPHandlerThread() {
+      setDaemon(true);
+    }
+
+    public void append(String data) {
+      synchronized (list) {
+        list.add(data);
+        list.notify();
+      }
+    }
+
+    /**
+     * Allow the UDPHandlerThread to wakeup and exit gracefully.
+     */
+    void close() {
+      synchronized(list) {
+      	list.notify();
+      }
+    }
+
+    public void run() {
+      ArrayList list2 = new ArrayList();
+
+      while (!UDPReceiver.this.closed) {
+        synchronized (list) {
+          try {
+            while (!UDPReceiver.this.closed && list.size() == 0) {
+              list.wait(300);
+            }
+
+            if (list.size() > 0) {
+              list2.addAll(list);
+              list.clear();
+            }
+          } catch (InterruptedException ie) {
+          }
+        }
+
+        if (list2.size() > 0) {
+          Iterator iter = list2.iterator();
+
+          while (iter.hasNext()) {
+            String data = (String) iter.next();
+            List v = decoderImpl.decodeEvents(data);
+
+            if (v != null) {
+              Iterator eventIter = v.iterator();
+
+              while (eventIter.hasNext()) {
+                if (!isPaused()) {
+                  doPost((LoggingEvent) eventIter.next());
+                }
+              }
+            }
+          }
+
+          list2.clear();
+        } else {
+          try {
+            synchronized (this) {
+              wait(1000);
+            }
+          } catch (InterruptedException ie) {
+          }
+        }
+      } // while
+      getLogger().debug(UDPReceiver.this.getName()+ "'s handler thread is exiting");
+    } // run
+  } // UDPHandlerThread
+
+  class UDPReceiverThread extends Thread {
+    public UDPReceiverThread() {
+      setDaemon(true);
+    }
+    
+    public void run() {
+      byte[] b = new byte[PACKET_LENGTH];
+      DatagramPacket p = new DatagramPacket(b, b.length);
+
+      while (!UDPReceiver.this.closed) {
+        try {
+          socket.receive(p);
+          
+          //this string constructor which accepts a charset throws an exception if it is 
+          //null
+          if (encoding == null) {
+            handlerThread.append(
+              new String(p.getData(), 0, p.getLength()));
+          } else {
+            handlerThread.append(
+              new String(p.getData(), 0, p.getLength(), encoding));
+          }
+        } catch (SocketException se) {
+          //disconnected
+        } catch (IOException ioe) {
+          ioe.printStackTrace();
+        }
+      }
+
+      //LogLog.debug(UDPReceiver.this.getName() + "'s thread is ending.");
+    }
+  }
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/XMLSocketNode.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/XMLSocketNode.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/XMLSocketNode.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/XMLSocketNode.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,205 @@
+/*
+ * 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 org.apache.log4j.*;
+import org.apache.log4j.helpers.Constants;
+import org.apache.log4j.plugins.Receiver;
+import org.apache.log4j.spi.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.net.Socket;
+
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+   Read {@link LoggingEvent} objects sent from a remote client using XML over
+   Sockets (TCP). These logging events are logged according to local
+   policy, as if they were generated locally.
+
+   <p>For example, the socket node might decide to log events to a
+   local file and also resent them to a second socket node.
+
+    @author  Scott Deboy <sdeboy@apache.org>;
+
+    @since 0.8.4
+*/
+public class XMLSocketNode extends ComponentBase implements Runnable {
+  Socket socket;
+  Receiver receiver;
+  Decoder decoder;
+  SocketNodeEventListener listener;
+
+  /**
+    Constructor for socket and logger repository. */
+  public XMLSocketNode(
+    String decoder, Socket socket, LoggerRepository hierarchy) {
+    this.repository = hierarchy;
+    try {
+      Class c = Class.forName(decoder);
+      Object o = c.newInstance();
+
+      if (o instanceof Decoder) {
+        this.decoder = (Decoder) o;
+      }
+    } catch (ClassNotFoundException cnfe) {
+      getLogger().warn("Unable to find decoder", cnfe);
+    } catch (IllegalAccessException iae) {
+      getLogger().warn("Unable to construct decoder", iae);
+    } catch (InstantiationException ie) {
+      getLogger().warn("Unable to construct decoder", ie);
+    }
+
+    this.socket = socket;
+  }
+
+  /**
+    Constructor for socket and reciever. */
+  public XMLSocketNode(String decoder, Socket socket, Receiver receiver) {
+    try {
+      Class c = Class.forName(decoder);
+      Object o = c.newInstance();
+
+      if (o instanceof Decoder) {
+        this.decoder = (Decoder) o;
+      }
+    } catch (ClassNotFoundException cnfe) {
+      getLogger().warn("Unable to find decoder", cnfe);
+    } catch (IllegalAccessException iae) {
+      getLogger().warn("Unable to construct decoder", iae);
+    } catch (InstantiationException ie) {
+      getLogger().warn("Unable to construct decoder", ie);
+    }
+
+    this.socket = socket;
+    this.receiver = receiver;
+  }
+
+  /**
+    Set the event listener on this node. */
+  public void setListener(SocketNodeEventListener _listener) {
+    listener = _listener;
+  }
+
+  public void run() {
+    Logger remoteLogger;
+    Exception listenerException = null;
+    InputStream is = null;
+
+    if ((this.receiver == null) || (this.decoder == null)) {
+      is = null;
+      listenerException =
+        new Exception(
+          "No receiver or decoder provided.  Cannot process xml socket events");
+      getLogger().error(
+        "Exception constructing XML Socket Receiver", listenerException);
+    }
+
+    try {
+      is = socket.getInputStream();
+    } catch (Exception e) {
+      is = null;
+      listenerException = e;
+      getLogger().error("Exception opening ObjectInputStream to " + socket, e);
+    }
+
+    if (is != null) {
+        String hostName = socket.getInetAddress().getHostName();
+        String remoteInfo = hostName + ":" + socket.getPort();
+    	
+      try {
+        //read data from the socket
+        //it's up to the individual decoder to handle incomplete event data
+        while (true) {
+          byte[] b = new byte[1024];
+          int length = is.read(b);
+          if (length == -1) {
+            getLogger().info(
+              "no bytes read from stream - closing connection.");
+            break;
+          }
+          List v = decoder.decodeEvents(new String(b, 0, length));
+
+          if (v != null) {
+            Iterator iter = v.iterator();
+
+            while (iter.hasNext()) {
+              LoggingEvent e = (LoggingEvent) iter.next();
+              e.setProperty(Constants.HOSTNAME_KEY, hostName);
+
+              // store the known remote info in an event property
+              e.setProperty("log4j.remoteSourceInfo", remoteInfo);
+
+              // if configured with a receiver, tell it to post the event
+              if (receiver != null) {
+                receiver.doPost(e);
+
+                // else post it via the hierarchy
+              } else {
+                // get a logger from the hierarchy. The name of the logger
+                // is taken to be the name contained in the event.
+                remoteLogger = repository.getLogger(e.getLoggerName());
+
+                //event.logger = remoteLogger;
+                // apply the logger-level filter
+                if (
+                  e.getLevel().isGreaterOrEqual(
+                      remoteLogger.getEffectiveLevel())) {
+                  // finally log the event as if was generated locally
+                  remoteLogger.callAppenders(e);
+                }
+              }
+            }
+          }
+        }
+      } catch (java.io.EOFException e) {
+        getLogger().info("Caught java.io.EOFException closing connection.");
+        listenerException = e;
+      } catch (java.net.SocketException e) {
+        getLogger().info(
+          "Caught java.net.SocketException closing connection.");
+        listenerException = e;
+      } catch (IOException e) {
+        getLogger().info("Caught java.io.IOException: " + e);
+        getLogger().info("Closing connection.");
+        listenerException = e;
+      } catch (Exception e) {
+        getLogger().error("Unexpected exception. Closing connection.", e);
+        listenerException = e;
+      }
+    }
+
+    // close the socket
+    try {
+      if (is != null) {
+        is.close();
+      }
+    } catch (Exception e) {
+      //logger.info("Could not close connection.", e);
+    }
+
+    // send event to listener, if configured
+    if (listener != null) {
+      listener.socketClosedEvent(listenerException);
+    }
+  }
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/net/XMLSocketReceiver.java Mon Oct  3 06:15:40 2011
@@ -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 <sdeboy@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);
+    }
+  }
+
+
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/Pauseable.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/Pauseable.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/Pauseable.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/Pauseable.java Mon Oct  3 06:15:40 2011
@@ -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();
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/Plugin.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/Plugin.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/Plugin.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/Plugin.java Mon Oct  3 06:15:40 2011
@@ -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();
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/PluginEvent.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/PluginEvent.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/PluginEvent.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/PluginEvent.java Mon Oct  3 06:15:40 2011
@@ -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();
+  }
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/PluginListener.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/PluginListener.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/PluginListener.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/plugins/PluginListener.java Mon Oct  3 06:15:40 2011
@@ -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);
+}



Mime
View raw message