Added ZeroConf support to most of the network-based appenders and receivers.

To enable ZeroConf advertising of an appender or receiver, a user can now:
 - add jmdns jar to their classpath 
 - set the 'advertiseViaMulticastDNS' param to 'true'

Also added support for discovery of appenders in Chainsaw (Chainsaw will automatically configure a matching receiver when connected).
Supported appenders:
    SocketAppender
    SocketHubAppender (ZeroConfSocketHubAppender is still functional)
    UDPAppender
    MulticastAppender
Supported receivers:
    SocketReceiver
    SocketHubReceiver (also receives events from ZeroConfSocketHubAppender)
    XMLSocketReceiver (can receive events sent over TCP by other logging frameworks)
    UDPReceiver
    MulticastReceiver

Implementation details:
 - removed Zeroconf4log4j class, jmdns access is now through the ZeroConfSupport class (class provides support of JmDNS and ServiceInfo creation via reflection, and supports both jmdns 1.0 and jmdns 3.1 apis)
 - ZeroConfSupport class is now used by ZeroConfSocketHubAppender, Chainsaw and all appenders & receivers that can advertise their configuration via ZeroConf
 - added new 'advertiseViaMulticastDNS' param to the appenders and receivers that support ZeroConf
 - updated the ZeroConf site documentation
 - updated release notes
 - updated the ZeroConfPlugin html file
 - tested appenders with 1.0 and 3.1 jmdns jars
 - updated log4j references in poms to log4j 1.2.16-snapshot where necessary 

For those wanting to add ZeroConf support to third-party appenders and have Chainsaw discover the appenders, the service info naming convention is described here: https://issues.apache.org/bugzilla/show_bug.cgi?id=48907


git-svn-id: https://svn.apache.org/repos/asf/logging/chainsaw/trunk@924176 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 66f2f56..6a01631 100644
--- a/pom.xml
+++ b/pom.xml
@@ -349,7 +349,7 @@
     <dependency>

       <groupId>log4j</groupId>

       <artifactId>log4j</artifactId>

-      <version>1.2.16</version>

+      <version>1.2.16-SNAPSHOT</version>

     </dependency>

     <dependency>

       <groupId>xstream</groupId>

diff --git a/src/main/java/org/apache/log4j/chainsaw/zeroconf/ZeroConfPlugin.java b/src/main/java/org/apache/log4j/chainsaw/zeroconf/ZeroConfPlugin.java
index 6b54d3e..446a7d5 100644
--- a/src/main/java/org/apache/log4j/chainsaw/zeroconf/ZeroConfPlugin.java
+++ b/src/main/java/org/apache/log4j/chainsaw/zeroconf/ZeroConfPlugin.java
@@ -10,8 +10,10 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 
 import javax.jmdns.JmDNS;
 import javax.jmdns.ServiceEvent;
@@ -41,12 +43,17 @@
 import org.apache.log4j.chainsaw.icons.ChainsawIcons;
 import org.apache.log4j.chainsaw.plugins.GUIPluginSkeleton;
 import org.apache.log4j.chainsaw.prefs.SettingsManager;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.net.MulticastReceiver;
 import org.apache.log4j.net.SocketHubReceiver;
-import org.apache.log4j.net.ZeroConfSocketHubAppender;
-import org.apache.log4j.net.Zeroconf4log4j;
+import org.apache.log4j.net.SocketReceiver;
+import org.apache.log4j.net.UDPReceiver;
+import org.apache.log4j.net.XMLSocketReceiver;
+import org.apache.log4j.net.ZeroConfSupport;
 import org.apache.log4j.plugins.Plugin;
 import org.apache.log4j.plugins.PluginEvent;
 import org.apache.log4j.plugins.PluginListener;
+import org.apache.log4j.plugins.Receiver;
 import org.apache.log4j.spi.LoggerRepositoryEx;
 
 import com.thoughtworks.xstream.XStream;
@@ -62,9 +69,6 @@
  * TODO add the
  * default Zone, and the list of user-specified zones to a preferenceModel
  * 
- * To run this in trial mode, first run {@link ZeroConfSocketHubAppenderTestBed}, then
- * run this class' main(..) method.
- * 
  * @author psmith
  * 
  */
@@ -80,8 +84,6 @@
     
     private final JScrollPane scrollPane = new JScrollPane(deviceTable);
 
-    private JmDNS jmDNS;
-
     private ZeroConfPreferenceModel preferenceModel;
     
     private Map serviceInfoToReceiveMap = new HashMap();
@@ -97,13 +99,19 @@
     });  
     
     private JMenuItem nothingToConnectTo = new JMenuItem("No devices discovered");
-    
+    private static final String MULTICAST_APPENDER_SERVICE_NAME = "_log4j_xml_mcast_appender.local.";
+    private static final String UDP_APPENDER_SERVICE_NAME = "_log4j_xml_udp_appender.local.";
+    private static final String XML_SOCKET_APPENDER_SERVICE_NAME = "_log4j_xml_tcpconnect_appender.local.";
+    private static final String SOCKET_APPENDER_SERVICE_NAME = "_log4j_obj_tcpconnect_appender.local.";
+    private static final String SOCKETHUB_APPENDER_SERVICE_NAME = "_log4j_obj_tcpaccept_appender.local.";
+    private JmDNS jmDNS;
+
     public ZeroConfPlugin() {
         setName("Zeroconf");
     }
 
     public void shutdown() {
-        Zeroconf4log4j.shutdown();
+        jmDNS.close();
         save();
     }
 
@@ -123,14 +131,10 @@
 
     public void activateOptions() {
         setLayout(new BorderLayout());
-        jmDNS = Zeroconf4log4j.getInstance();
+        jmDNS = (JmDNS) ZeroConfSupport.getJMDNSInstance();
 
-        jmDNS.addServiceListener(
-                ZeroConfSocketHubAppender.DEFAULT_ZEROCONF_ZONE,
-                new ZeroConfServiceListener());
+        registerServiceListenersForAppenders();
 
-        jmDNS.addServiceListener(ZeroConfSocketHubAppender.DEFAULT_ZEROCONF_ZONE, discoveredDevices);
-        
         deviceTable.addMouseListener(new ConnectorMouseListener());
 
         
@@ -179,7 +183,28 @@
         discoveredDevices.setZeroConfPreferenceModel(preferenceModel);
         discoveredDevices.setZeroConfPluginParent(this);
     }
-    
+
+    private void registerServiceListenersForAppenders()
+    {
+        Set serviceNames = new HashSet();
+        serviceNames.add(MULTICAST_APPENDER_SERVICE_NAME);
+        serviceNames.add(SOCKET_APPENDER_SERVICE_NAME);
+        serviceNames.add(SOCKETHUB_APPENDER_SERVICE_NAME);
+        serviceNames.add(UDP_APPENDER_SERVICE_NAME);
+        serviceNames.add(XML_SOCKET_APPENDER_SERVICE_NAME);
+
+        for (Iterator iter = serviceNames.iterator(); iter.hasNext();) {
+            String serviceName = iter.next().toString();
+            jmDNS.addServiceListener(
+                    serviceName,
+                    new ZeroConfServiceListener());
+
+            jmDNS.addServiceListener(serviceName, discoveredDevices);
+        }
+
+        //now add each appender constant
+    }
+
     /**
      * Sets the icon of this parent container (a JTabbedPane, we hope
      *
@@ -286,7 +311,7 @@
             connectToMenu.insert(connectToDeviceMenuItem,0);
         }
 //         if the device name is one of the autoconnect devices, then connect immediately
-        if (preferenceModel.getAutoConnectDevices().contains(name)) {
+        if (preferenceModel != null && preferenceModel.getAutoConnectDevices() != null && preferenceModel.getAutoConnectDevices().contains(name)) {
             new Thread(new Runnable() {
 
                 public void run() {
@@ -432,15 +457,12 @@
      */
     private void connectTo(ServiceInfo info) {
         LOG.info("Connection request for " + info);
-        int port = info.getPort();
-        String hostAddress = info.getHostAddress();
-       
-//        TODO handle different receivers than just SocketHubReceiver
-        SocketHubReceiver receiver = new SocketHubReceiver();
-        receiver.setHost(hostAddress);
-        receiver.setPort(port);
-        receiver.setName(info.getName());
-        
+        //Chainsaw can construct receivers from discovered appenders
+        Receiver receiver = getReceiver(info);
+        //if null, unable to resolve the service name..no-op
+        if (receiver == null) {
+            return;
+        }
         ((LoggerRepositoryEx)LogManager.getLoggerRepository()).getPluginRegistry().addPlugin(receiver);
         receiver.activateOptions();
         LOG.info("Receiver '" + receiver.getName() + "' has been started");
@@ -460,6 +482,69 @@
 //        discoveredDevices.fireContentsChanged();
     }
 
+    private Receiver getReceiver(ServiceInfo info) {
+        String zone = info.getType();
+        int port = info.getPort();
+        String hostAddress = info.getHostAddress();
+        String name = info.getName();
+        String decoderClass = info.getPropertyString("decoder");
+
+        //MulticastAppender
+        if (MULTICAST_APPENDER_SERVICE_NAME.equals(zone)) {
+            MulticastReceiver receiver = new MulticastReceiver();
+            //this needs to be a multicast address, not the host address, so we need to use a property
+            receiver.setAddress(info.getPropertyString("multicastAddress"));
+            receiver.setPort(port);
+            receiver.setName(name + "-receiver");
+            if (decoderClass != null && !decoderClass.equals("")) {
+                receiver.setDecoder(decoderClass);
+            }
+
+            return receiver;
+        }
+        //UDPAppender
+        if (UDP_APPENDER_SERVICE_NAME.equals(zone)) {
+            UDPReceiver receiver = new UDPReceiver();
+            receiver.setPort(port);
+            receiver.setName(name + "-receiver");
+            if (decoderClass != null && !decoderClass.equals("")) {
+                receiver.setDecoder(decoderClass);
+            }
+            return receiver;
+        }
+
+        //non-log4j XML-based socketappender
+        if (XML_SOCKET_APPENDER_SERVICE_NAME.equals(zone)) {
+            XMLSocketReceiver receiver = new XMLSocketReceiver();
+            receiver.setPort(port);
+            receiver.setName(name + "-receiver");
+            if (decoderClass != null && !decoderClass.equals("")) {
+                receiver.setDecoder(decoderClass);
+            }
+            return receiver;
+        }
+
+        //SocketAppender
+        if (SOCKET_APPENDER_SERVICE_NAME.equals(zone)) {
+            SocketReceiver receiver = new SocketReceiver();
+            receiver.setPort(port);
+            receiver.setName(name + "-receiver");
+            return receiver;
+        }
+
+        //SocketHubAppender
+        if (SOCKETHUB_APPENDER_SERVICE_NAME.equals(zone)) {
+            SocketHubReceiver receiver = new SocketHubReceiver();
+            receiver.setHost(hostAddress);
+            receiver.setPort(port);
+            receiver.setName(name + "-receiver");
+            return receiver;
+        }
+        //not recognized
+        LogLog.debug("Unable to find receiver for appender with service name: " + zone);
+        return null;
+    }
+
     /**
      * Finds the matching JMenuItem based on name, may return null if there is no match.
      * 
diff --git a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html b/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
index 6a0e361..7d69a8a 100644
--- a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
+++ b/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
@@ -10,6 +10,23 @@
 <b>NOTE:</b> The mechanism and format used to persist settings in Chainsaw is subject to change.  If you are experiencing problems displaying events in Chainsaw, please delete everything in the $user.dir/.chainsaw directory and restart Chainsaw.
 <br>
 <h1>1.99.99</h1>
+<h2>16 Mar 2010</h2>
+<ul>
+<li>
+Added ZeroConf support to most of the network-based appenders and receivers - just add the jmdns jar to your classpath and set the 'advertiseViaMulticastDNS' param to 'true'.  Also added
+support for discovery of these appenders in Chainsaw (once you 'connect' to an advertised appender, Chainsaw will automatically configure a matching receiver).<br>
+Supported appenders:
+    <ul>SocketAppender</ul>
+    <ul>SocketHubAppender (ZeroConfSocketHubAppender is still functional)</ul>
+    <ul>UDPAppender</ul>
+    <ul>MulticastAppender</ul>
+Supported receivers:
+    <ul>SocketReceiver</ul>
+    <ul>SocketHubReceiver (also receives events from ZeroConfSocketHubAppender)</ul>
+    <ul>XMLSocketReceiver (can receive events sent over TCP by other logging frameworks)</ul>
+    <ul>UDPReceiver</ul>
+    <ul>MulticastReceiver</ul>
+</ul>
 <h2>14 Mar 2010</h2>
 <ul>
 <li>
diff --git a/src/main/resources/org/apache/log4j/chainsaw/zeroconf/ZeroConfPlugin.html b/src/main/resources/org/apache/log4j/chainsaw/zeroconf/ZeroConfPlugin.html
index 5bb3e1b..39e5983 100644
--- a/src/main/resources/org/apache/log4j/chainsaw/zeroconf/ZeroConfPlugin.html
+++ b/src/main/resources/org/apache/log4j/chainsaw/zeroconf/ZeroConfPlugin.html
@@ -20,7 +20,7 @@
 Zeroconf is an open-standard that many vendors are using to reduce cost of configuration.  The <b>JmDNS</b> project
 is an Apache 2.0 licensed library that implements the ZeroConf protocol.  Chainsaw and log4j use
 JmDNS to broadcast Appender information so that a matching Receiver can be easily created to connect to each other
-to stream logging messages into Chainsaw.  
+to stream logging messages into Chainsaw.
 </p>
 <p>
 <ul>
@@ -30,19 +30,16 @@
 </p>
 <h2>Interesting... So what do I need to use ZeroConf with Chainsaw & log4j?</h2>
 <p>
-The log4j team has created an additional JAR file that can be added to your existing
-application to enable ZeroConf configuration. You just need to download the <code>log4j-zeroconf.jar</code> and the JmDNS package (you must use
-at least version 1.0 of JmDNS) and add 
-both JARs to your application's classpath. </p>
-<p> Then change your references from <code>SocketHubAppender</code>
-to <code><b>ZeroConf</b>SocketHubAppender</code>. (We recommend you change the 'name' propety from it's default to make it easy for you to use, other
-wise it's name will appear as the default 'SocketHubAppender').
-</p>
+Most of the network-based appenders are ZeroConf-capable - all you have to do to use ZeroConf is to
+download the JmDNS package (at least version 1.0), add the JmDNS jar to your application's classpath, and set the "advertiseViaMulticastDNS"
+parameter to 'true' for any of the appenders or receivers you want to advertise (most of the time you will want to advertise appenders, so they can be
+automatically detected by Chainsaw).</p>
 <p>You're log4j.xml configuration file could be as simple as this:</p>
 <pre>
 &lt;log4j:configuration debug="false" threshold="debug"  xmlns:log4j="http://jakarta.apache.org/log4j/&gt;
-    &lt;appender name="zeroconf" class="org.apache.log4j.net.ZeroConfSocketHubAppender"&gt;
+    &lt;appender name="zeroconf" class="org.apache.log4j.net.SocketHubAppender"&gt;
         &lt;param name="Name" value="MyZeroConfSockeHubAppender" /&gt;
+        &lt;param name="advertiseViaMulticastDNS" value="true" /&gt;
     &lt;/appender&gt;
     &lt;!--ROOT Logger--&gt;
     &lt;root&gt;
@@ -52,12 +49,16 @@
 &lt;/log4j:configuration&gt;
 </pre>
 <p>
-Once you have started your application using this new appender, then from within Chainsaw, the ZeroConf tab will show detected
-applications.  You can simply double click on the detected app, and Chainsaw will connect a Receiver to it.  You can even tick the "Auto-Connect"
+Once you have started your application using this configuration, from within Chainsaw, the ZeroConf tab will show detected
+applications.  You can simply double click on the detected appender and Chainsaw will connect a Receiver to it.  You can even tick the "Auto-Connect"
 check box, and next time Chainsaw see's this application, it will automatically connect to it.
 </p>
+<p>Most of the network-based Receivers are also ZeroConf capable, giving ZeroConf-aware appenders or applications
+the ability to discover the receivers and configure appenders to connect to those Receivers.
+</p>
 <h2>Cool... But I have some applications that use log4j 1.2.x.  Will it still work?</h2>
-<p>Yes.  We have designed a backward compatible mechanism to expose log4j 1.2.x SocketHubAppender so that Chainsaw can detect it automatically.  
+<p>Yes.  We have designed a backward compatible mechanism to expose prior versions of log4j's SocketHubAppender (ZeroConfSocketHubAppender)
+so that Chainsaw can detect it automatically.
 </p>
 
 <h2>But... Surely there's a catch?</h2>
diff --git a/src/site/apt/zeroconf.apt b/src/site/apt/zeroconf.apt
index 2b0c597..1424f68 100644
--- a/src/site/apt/zeroconf.apt
+++ b/src/site/apt/zeroconf.apt
@@ -14,21 +14,45 @@
 ~~ limitations under the License.
 ZeroConf - Zero Configuration
 
-	Chainsaw has ZeroConf elements embedded within it, but you'll need to add a few things to your application to enable your application for ZeroConf.
+    Chainsaw can use ZeroConf to discover advertised appenders and automatically configure a matching receiver, but you must update your configuration in order to advertise your appender via ZeroConf.
+    
+    If you are using a newer version of log4j (1.2.16 or greater), most of the network-based appenders are capable of advertising their configurations via ZeroConf, but have this
+capability disabled by default.
+    To advertise an appender via ZeroConf:
+        [[1]] Download {{{http://sourceforge.net/projects/jmdns/}JmDNS}}
 
-	Download:                                            
-	
-		[[1]] {{{http://logging.apache.org/log4j/docs/webstart/chainsaw/log4j-zeroconf.zip}log4j ZeroConf extension}}
-		
-		[[2]] {{{http://sourceforge.net/projects/jmdns/}JmDNS}}
-		
-		[[3]] Add the <log4j-zeroconf.jar> and the <jmdns.jar> from these bundles and add them to your application's classpath. 
-		
-		[[4]] Modify your log4j configuration so that it use the ZeroConfSocketHubAppender.  
-		
-	Here is a complete log4j.xml file that you can use as a base:
-	
-+-------------------------------+						
+        [[2]] Add the <jmdns.jar> to your application's classpath
+
+        [[3]] Modify your log4j configuration so that the appender is set to advertise its configuration via ZeroConf (by setting the 'advertiseViaMulticastDNS' parameter to 'true')
+
+    Here is a complete log4j.xml file that you can use as a base SocketAppender configuration which advertises the appender via ZeroConf:</p>
+
++-------------------------------+
+<log4j:configuration debug="false" threshold="debug"  xmlns:log4j="http://jakarta.apache.org/log4j/">
+    <appender name="socketAppender" class="org.apache.log4j.net.SocketAppender">
+        <param name="Name" value="MySockeAppender" />
+        <param name="advertiseViaMulticastDNS" value="true" />
+    </appender>
+    <!--ROOT Logger-->
+    <root>
+        <level value="INFO" />
+        <appender-ref ref="socketAppender" />
+    </root>
+</log4j:configuration>
++-------------------------------+
+
+    If you are using an older version of log4j (prior to 1.2.16), ZeroConfSocketHubAppender is a ZeroConf-capable appender which is backward compatible with the prior versions of SocketHubAppender and can advertise the appender configuration via ZeroConf:</p>
+        [[1]] Download {{{http://sourceforge.net/projects/jmdns/}JmDNS}}
+
+        [[2]] Add the <i>jmdns.jar</i> to your application's classpath
+
+        [[3]] Download {{{http://logging.apache.org/log4j/docs/webstart/chainsaw/log4j-zeroconf.zip}log4j ZeroConf extension (provides ZeroConfSocketHubAppender)}}
+
+        [[4]] Add the <log4j-zeroconf.jar> to your application's classpath
+
+    Here is a complete log4j.xml file that you can use as a base ZeroConfSocketAppender configuration which advertises the appender via ZeroConf:</p>
+
++-------------------------------+
 <log4j:configuration debug="false" threshold="debug"  xmlns:log4j="http://jakarta.apache.org/log4j/">
     <appender name="zeroconf" class="org.apache.log4j.net.ZeroConfSocketHubAppender">
         <param name="Name" value="MyZeroConfSockeHubAppender" />
@@ -41,12 +65,8 @@
 </log4j:configuration>
 +-------------------------------+
 
-	Once configured and your applicatiion started, you should be able to click on the Zeroconf tab inside Chainsaw, and see the "MyZeroConfSocketHubAppender" listed.  If you double click on the row, Chainsaw will automatically connect to your application and start receiving events.  You can tick the 'auto-connect' option to have Chainsaw immediately connect as soon as it sees your application started.  Great for Dev/QA environment.
-	
-	The Zeroconf-enabled SocketHubAppender broadcasts it's existence via a multicast protocol, passing enough information for Chainsaw to be able to connect to it.
-	
-Firewalls
+    Once you have enabled ZeroConf in your log4j configuration and started your application, you should be able to click on the Zeroconf tab inside Chainsaw and see the advertised appenders listed. If you double click on a row,
+Chainsaw will automatically connect to your application and start receiving events. You can tick the 'auto-connect' option to have Chainsaw immediately connect as soon as it sees your application started. Great for Dev/QA environment.</p>
 
-	Multicast protocols generally don't pass through firewall, so in a production environment Zeroconf won't work.
-	
-	
\ No newline at end of file
+Firewalls
+    Multicast protocols generally don't pass through firewall, so in a production environment Zeroconf won't work.</p>