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:13 UTC

[36/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/JMSReceiverBeanInfo.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/JMSReceiverBeanInfo.java b/src/main/java/org/apache/log4j/net/JMSReceiverBeanInfo.java
new file mode 100644
index 0000000..eec19d3
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/JMSReceiverBeanInfo.java
@@ -0,0 +1,52 @@
+/*
+ * 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.beans.PropertyDescriptor;
+import java.beans.SimpleBeanInfo;
+
+
+/**
+ * BeanInfo class for the JMSReceiver.
+ *
+ * @author Paul Smith <ps...@apache.org>
+ *
+ */
+public class JMSReceiverBeanInfo extends SimpleBeanInfo {
+
+    /* (non-Javadoc)
+     * @see java.beans.BeanInfo#getPropertyDescriptors()
+     */
+    public PropertyDescriptor[] getPropertyDescriptors() {
+
+        try {
+
+            return new PropertyDescriptor[] {
+                new PropertyDescriptor("name", JMSReceiver.class),
+                new PropertyDescriptor("topicFactoryName", JMSReceiver.class),
+                new PropertyDescriptor("topicName", JMSReceiver.class),
+                new PropertyDescriptor("threshold", JMSReceiver.class),
+                new PropertyDescriptor("jndiPath", JMSReceiver.class),
+                new PropertyDescriptor("userId",
+                        JMSReceiver.class),
+            };
+        } catch (Exception e) {
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/MulticastAppender.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/MulticastAppender.java b/src/main/java/org/apache/log4j/net/MulticastAppender.java
new file mode 100644
index 0000000..de002c5
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/MulticastAppender.java
@@ -0,0 +1,345 @@
+/*
+ * 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.InetAddress;
+import java.net.MulticastSocket;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+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;
+
+
+/**
+ *  Multicast-based Appender.  Works in conjunction with the MulticastReceiver, which expects
+ *  a LoggingEvent encoded using XMLLayout. 
+ * 
+ *  Sends log information as a multicast datagrams.
+ *
+ *  <p>Messages are not sent as LoggingEvent objects but as text after
+ *  applying XMLLayout.
+ *
+ *  <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 <sd...@apache.org>
+ * 
+ */
+public class MulticastAppender extends AppenderSkeleton implements PortBased {
+  /**
+     The default port number for the multicast packets. (9991).
+  */
+  static final int DEFAULT_PORT = 9991;
+
+  /**
+   * The MulticastDNS zone advertised by a MulticastAppender
+   * the MulticastAppender also adds a 'multicastAddress' property with the multicast address value as a string
+   */
+  public static final String ZONE = "_log4j_xml_mcast_appender.local.";
+
+  /**
+     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;
+  int timeToLive;
+  InetAddress address;
+  int port = DEFAULT_PORT;
+  MulticastSocket outSocket;
+  private String encoding;
+
+  private boolean locationInfo = false;
+  private boolean advertiseViaMulticastDNS;
+  private ZeroConfSupport zeroConf;
+
+  public MulticastAppender() {
+     super(false);
+  }
+
+  /**
+     Open the multicast 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);
+    } else {
+      String err = "The RemoteHost property is required for MulticastAppender named "+ name;
+      LogLog.error(err);
+      throw new IllegalStateException(err);
+    }
+
+    if (layout == null) {
+        layout = new XMLLayout();
+    }
+      
+    if (advertiseViaMulticastDNS) {
+        Map properties = new HashMap();
+        properties.put("multicastAddress", remoteHost);
+        zeroConf = new ZeroConfSupport(ZONE, port, getName(), properties);
+        zeroConf.advertise();
+    }
+    connect();
+    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;
+    }
+
+    this.closed = true;
+    if (advertiseViaMulticastDNS) {
+        zeroConf.unadvertise();
+    }
+    cleanUp();
+  }
+
+  /**
+     Close the 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() {
+    if (this.address == null) {
+      return;
+    }
+
+    try {
+      // First, close the previous connection if any.
+      cleanUp();
+      outSocket = new MulticastSocket();
+      outSocket.setTimeToLive(timeToLive);
+    } catch (IOException e) {
+      LogLog.error("Error in connect method of MulticastAppender named "+name, e);
+    }
+  }
+
+  public void append(LoggingEvent event) {
+    if (event == null) {
+      return;
+    }
+
+    if(locationInfo) {
+	   event.getLocationInformation();
+	}
+
+    if (outSocket != null) {
+        event.setProperty(Constants.HOSTNAME_KEY, hostname);
+
+        if (application != null) {
+          event.setProperty(Constants.APPLICATION_KEY, application);
+        }
+      
+		if(locationInfo) {
+		   event.getLocationInformation();
+		}
+
+
+      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 Multicast connection: " + e);
+      }
+    }
+  }
+
+  InetAddress getAddressByName(String host) {
+    try {
+      return InetAddress.getByName(host);
+    } catch (Exception e) {
+      LogLog.error("Could not find address of [" + host + "].", e);
+      return null;
+    }
+  }
+
+  /**
+     The <b>RemoteHost</b> option takes a string value which should be
+     the host name or ipaddress to send the multicast packets.
+   */
+  public void setRemoteHost(String host) {
+    remoteHost = host;
+  }
+
+  /**
+     Returns value of the <b>RemoteHost</b> option.
+   */
+  public String getRemoteHost() {
+    return remoteHost;
+  }
+
+  /**
+  The <b>LocationInfo</b> option takes a boolean value. If true,
+  the information sent to the remote host will include location
+  information. By default no location information is sent to the server.
+   */
+  public void setLocationInfo(boolean locationInfo) {
+	  this.locationInfo = locationInfo;
+  }
+
+  /**
+   * Returns value of the <b>LocationInfo</b> option.
+   */
+  public boolean getLocationInfo() {
+	  return locationInfo;
+  }
+
+  /**
+      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>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>Time to live</b> option takes a positive integer representing
+     the time to live value.
+   */
+  public void setTimeToLive(int timeToLive) {
+    this.timeToLive = timeToLive;
+  }
+
+  /**
+     Returns value of the <b>Time to Live</b> option.
+   */
+  public int getTimeToLive() {
+    return timeToLive;
+  }
+
+  /**
+     The <b>Port</b> option takes a positive integer representing
+     the port where multicast 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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.log4j.net.NetworkBased#isActive()
+   */
+  public boolean isActive() {
+    // TODO handle active/inactive
+    return true;
+  }
+
+    /**
+     * Gets whether appender requires a layout.
+     * @return false
+     */
+  public boolean requiresLayout() {
+      return true;
+  }
+
+  public boolean isAdvertiseViaMulticastDNS() {
+      return advertiseViaMulticastDNS;
+  }
+
+  public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+      this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/MulticastReceiver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/MulticastReceiver.java b/src/main/java/org/apache/log4j/net/MulticastReceiver.java
new file mode 100644
index 0000000..2dfcec2
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/MulticastReceiver.java
@@ -0,0 +1,276 @@
+/*
+ * 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.InetAddress;
+import java.net.MulticastSocket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+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;
+
+
+/**
+ *  Multicast-based receiver.  Accepts LoggingEvents encoded using
+ *  MulticastAppender and XMLLayout. The the XML data is converted
+ *  back to a LoggingEvent and is posted.
+ *
+ *  @author Scott Deboy <sd...@apache.org>
+ *
+ */
+public class MulticastReceiver extends Receiver implements PortBased,
+  AddressBased, Pauseable {
+  private static final int PACKET_LENGTH = 16384;
+  private int port;
+  private String address;
+  private String encoding;
+  private MulticastSocket socket = null;
+
+  //default to log4j xml decoder
+  private String decoder = "org.apache.log4j.xml.XMLDecoder";
+  private Decoder decoderImpl;
+  private MulticastHandlerThread handlerThread;
+  private MulticastReceiverThread receiverThread;
+  private boolean paused;
+  private boolean advertiseViaMulticastDNS;
+  private ZeroConfSupport zeroConf;
+
+  /**
+   * The MulticastDNS zone advertised by a MulticastReceiver
+   */
+  public static final String ZONE = "_log4j_xml_mcast_receiver.local.";
+
+  public String getDecoder() {
+    return decoder;
+  }
+
+  public void setDecoder(String decoder) {
+    this.decoder = decoder;
+  }
+
+  public int getPort() {
+    return port;
+  }
+
+  public void setPort(int port) {
+    this.port = port;
+  }
+
+  public String getAddress() {
+    return address;
+  }
+
+  /**
+      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 synchronized void shutdown() {
+    active = false;
+    if (advertiseViaMulticastDNS) {
+        zeroConf.unadvertise();
+    }
+    if (handlerThread != null) {
+        handlerThread.interrupt();
+    }
+    if (receiverThread != null) {
+        receiverThread.interrupt();
+    }
+    if (socket != null) {
+        socket.close();
+    }
+  }
+
+  public void setAddress(String address) {
+    this.address = address;
+  }
+
+  public boolean isPaused() {
+    return paused;
+  }
+
+  public void setPaused(boolean b) {
+    paused = b;
+  }
+
+  public void activateOptions() {
+    InetAddress addr = null;
+
+    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 {
+      addr = InetAddress.getByName(address);
+    } catch (UnknownHostException uhe) {
+      uhe.printStackTrace();
+    }
+
+    try {
+      active = true;
+      socket = new MulticastSocket(port);
+      socket.joinGroup(addr);
+      receiverThread = new MulticastReceiverThread();
+      receiverThread.start();
+      handlerThread = new MulticastHandlerThread();
+      handlerThread.start();
+      if (advertiseViaMulticastDNS) {
+        zeroConf = new ZeroConfSupport(ZONE, port, getName());
+        zeroConf.advertise();
+      }
+
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+  }
+
+    public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+        this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+    }
+
+    public boolean isAdvertiseViaMulticastDNS() {
+        return advertiseViaMulticastDNS;
+    }
+
+    class MulticastHandlerThread extends Thread {
+    private List list = new ArrayList();
+
+    public MulticastHandlerThread() {
+      setDaemon(true);
+    }
+
+    public void append(String data) {
+      synchronized (list) {
+        list.add(data);
+        list.notify();
+      }
+    }
+
+    public void run() {
+      ArrayList list2 = new ArrayList();
+
+      while (isAlive()) {
+        synchronized (list) {
+          try {
+            while (list.size() == 0) {
+              list.wait();
+            }
+
+            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.trim());
+
+            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) {
+          }
+        }
+      }
+    }
+  }
+
+  class MulticastReceiverThread extends Thread {
+    public MulticastReceiverThread() {
+      setDaemon(true);
+    }
+
+    public void run() {
+      active = true;
+
+      byte[] b = new byte[PACKET_LENGTH];
+      DatagramPacket p = new DatagramPacket(b, b.length);
+
+      while (active) {
+        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();
+        }
+      }
+
+      getLogger().debug("{}'s thread is ending.", MulticastReceiver.this.getName());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/MulticastReceiverBeanInfo.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/MulticastReceiverBeanInfo.java b/src/main/java/org/apache/log4j/net/MulticastReceiverBeanInfo.java
new file mode 100644
index 0000000..4dec14c
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/MulticastReceiverBeanInfo.java
@@ -0,0 +1,51 @@
+/*
+ * 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.beans.PropertyDescriptor;
+import java.beans.SimpleBeanInfo;
+
+
+/**
+ * BeanInfo class for the meta-data of the MulticastReceiver.
+ *
+ * @author Paul Smith <ps...@apache.org>
+ *
+ */
+public class MulticastReceiverBeanInfo extends SimpleBeanInfo {
+
+    /* (non-Javadoc)
+     * @see java.beans.BeanInfo#getPropertyDescriptors()
+     */
+    public PropertyDescriptor[] getPropertyDescriptors() {
+
+        try {
+
+            return new PropertyDescriptor[] {
+                new PropertyDescriptor("name", MulticastReceiver.class),
+                new PropertyDescriptor("address", MulticastReceiver.class),
+                new PropertyDescriptor("port", MulticastReceiver.class),
+                new PropertyDescriptor("threshold", MulticastReceiver.class),
+                new PropertyDescriptor("decoder", MulticastReceiver.class),
+                new PropertyDescriptor("advertiseViaMulticastDNS", MulticastReceiver.class),
+            };
+        } catch (Exception e) {
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/NetworkBased.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/NetworkBased.java b/src/main/java/org/apache/log4j/net/NetworkBased.java
new file mode 100644
index 0000000..9c5153f
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/NetworkBased.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.net;
+
+/**
+ * The parent of all the Network based interfaces.
+ *
+ * @author Paul Smith (psmith@apache.org)
+ *
+ */
+public interface NetworkBased {
+
+    /**
+     * Get name.
+     * @return name.
+     */
+  String getName();
+
+    /**
+     * Get if item is active.
+     * @return if true, item is active.
+     */
+  boolean isActive();
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/PortBased.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/PortBased.java b/src/main/java/org/apache/log4j/net/PortBased.java
new file mode 100644
index 0000000..c7c1f97
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/PortBased.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+
+/**
+ * Net based entities that 'work with' a Port should consider implementing this
+ * interface so that they can be treated generically.
+ *
+ * @author Paul Smith (psmith@apache.org)
+ *
+ */
+public interface PortBased extends NetworkBased {
+  /**
+   * Returns the Port # that this net based thing is using.
+   * @return int port number
+   */
+  int getPort();
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/SocketHubReceiver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/SocketHubReceiver.java b/src/main/java/org/apache/log4j/net/SocketHubReceiver.java
new file mode 100644
index 0000000..85058e8
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/SocketHubReceiver.java
@@ -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.
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/SocketNode13.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/SocketNode13.java b/src/main/java/org/apache/log4j/net/SocketNode13.java
new file mode 100644
index 0000000..e27c68e
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/SocketNode13.java
@@ -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 <mm...@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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/SocketNodeEventListener.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/SocketNodeEventListener.java b/src/main/java/org/apache/log4j/net/SocketNodeEventListener.java
new file mode 100644
index 0000000..6d17602
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/SocketNodeEventListener.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.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);
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/SocketReceiver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/SocketReceiver.java b/src/main/java/org/apache/log4j/net/SocketReceiver.java
new file mode 100644
index 0000000..9d4aac9
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/SocketReceiver.java
@@ -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);
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/UDPAppender.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/UDPAppender.java b/src/main/java/org/apache/log4j/net/UDPAppender.java
new file mode 100644
index 0000000..55bc6f1
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/UDPAppender.java
@@ -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 <sd...@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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/UDPReceiver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/UDPReceiver.java b/src/main/java/org/apache/log4j/net/UDPReceiver.java
new file mode 100644
index 0000000..a8c7375
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/UDPReceiver.java
@@ -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 <sd...@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.");
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/08c7be5c/src/main/java/org/apache/log4j/net/XMLSocketNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/log4j/net/XMLSocketNode.java b/src/main/java/org/apache/log4j/net/XMLSocketNode.java
new file mode 100644
index 0000000..95ab638
--- /dev/null
+++ b/src/main/java/org/apache/log4j/net/XMLSocketNode.java
@@ -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 <sd...@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);
+    }
+  }
+}