ソースを参照

ZOOKEEPER-678. Gui browser application to view and edit the contents of a zookeeper instance

git-svn-id: https://svn.apache.org/repos/asf/hadoop/zookeeper/trunk@920604 13f79535-47bb-0310-9956-ffa450edef68
Patrick D. Hunt 15 年 前
コミット
32a5d09b3b
52 ファイル変更5349 行追加6 行削除
  1. 15 6
      CHANGES.txt
  2. 3 0
      src/contrib/zooinspector/NOTICE.txt
  3. 94 0
      src/contrib/zooinspector/README.txt
  4. 152 0
      src/contrib/zooinspector/build.xml
  5. 5 0
      src/contrib/zooinspector/config/defaultConnectionSettings.cfg
  6. 3 0
      src/contrib/zooinspector/config/defaultNodeVeiwers.cfg
  7. BIN
      src/contrib/zooinspector/icons/edtsrclkup_co.gif
  8. BIN
      src/contrib/zooinspector/icons/file_obj.gif
  9. BIN
      src/contrib/zooinspector/icons/fldr_obj.gif
  10. BIN
      src/contrib/zooinspector/icons/info_obj.gif
  11. BIN
      src/contrib/zooinspector/icons/jspdecl.gif
  12. BIN
      src/contrib/zooinspector/icons/launch_run.gif
  13. BIN
      src/contrib/zooinspector/icons/launch_stop.gif
  14. BIN
      src/contrib/zooinspector/icons/new_con.gif
  15. BIN
      src/contrib/zooinspector/icons/refresh.gif
  16. BIN
      src/contrib/zooinspector/icons/save_edit.gif
  17. BIN
      src/contrib/zooinspector/icons/search_next.gif
  18. BIN
      src/contrib/zooinspector/icons/search_prev.gif
  19. BIN
      src/contrib/zooinspector/icons/trash.gif
  20. 45 0
      src/contrib/zooinspector/ivy.xml
  21. BIN
      src/contrib/zooinspector/lib/jtoaster-1.0.4.jar
  22. 9 0
      src/contrib/zooinspector/lib/log4j.properties
  23. 202 0
      src/contrib/zooinspector/licences/Apache Software Licence v2.0.txt
  24. 261 0
      src/contrib/zooinspector/licences/epl-v10.html
  25. 66 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/ZooInspector.java
  26. 50 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java
  27. 39 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/DataEncryptionManager.java
  28. 37 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/NodeViewersChangeListener.java
  29. 80 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java
  30. 321 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java
  31. 118 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorIconResources.java
  32. 605 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java
  33. 140 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java
  34. 361 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java
  35. 362 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java
  36. 21 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html
  37. 187 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java
  38. 146 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java
  39. 186 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java
  40. 138 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java
  41. 38 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java
  42. 37 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java
  43. 120 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java
  44. 139 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java
  45. 852 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java
  46. 33 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeManager.java
  47. 43 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeTreeManager.java
  48. 99 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorReadOnlyManager.java
  49. 288 0
      src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java
  50. 18 0
      src/contrib/zooinspector/zooInspector-dev.sh
  51. 18 0
      src/contrib/zooinspector/zooInspector.cmd
  52. 18 0
      src/contrib/zooinspector/zooInspector.sh

+ 15 - 6
CHANGES.txt

@@ -270,24 +270,29 @@ IMPROVEMENTS:
 
   ZOOKEEPER-425. Add OSGi metadata to zookeeper.jar (david bosschaert via breed)
 
-  ZOOKEEPER-599. Changes to FLE and QuorumCnxManager to support Observers (fpj via breed)
+  ZOOKEEPER-599. Changes to FLE and QuorumCnxManager to support Observers
+  (fpj via breed)
 
   ZOOKEEPER-506. QuorumBase should use default leader election (fpj via breed)
 
   ZOOKEEPER-633. Fetch netty using ivy for bookkeeper (giri via fpj)
 
-  ZOOKEEPER-544. improve client testability - allow test client to access connected server location (phunt via breed)
+  ZOOKEEPER-544. improve client testability - allow test client to access
+  connected server location (phunt via breed)
 
-  ZOOKEEPER-426. Windows versions of zookeeper scripts (David Bosschaert via breed)
+  ZOOKEEPER-426. Windows versions of zookeeper scripts
+  (David Bosschaert via breed)
 
   ZOOKEEPER-638. upgrade ivy to 2.1.0 final from 2.1.0 release
   candidate (phunt via breed)
 
   ZOOKEEPER-648. Fix releaseaudit warning count to zero (phunt via henry)
 
-  ZOOKEEPER-626. ensure the c/java cli's print xid/sessionid/etc... in hex (pat via mahadev)
+  ZOOKEEPER-626. ensure the c/java cli's print xid/sessionid/etc... in hex
+  (pat via mahadev)
 
-  ZOOKEEPER-655. StringBuffer -> StringBuilder - conversion of references as necessary (Kay Kay via henry)
+  ZOOKEEPER-655. StringBuffer -> StringBuilder - conversion of references as
+  necessary (Kay Kay via henry)
 
   ZOOKEEPER-612. Make Zookeeper C client can be compiled by gcc of early
   version (qian via mahadev)
@@ -331,10 +336,14 @@ NEW FEATURES:
 
   ZOOKEEPER-368. Observers: core functionality (henry robinson via mahadev)
 
-  ZOOKEEPER-496. zookeeper-tree utility for export, import and incremental updates (anirban roy via breed)
+  ZOOKEEPER-496. zookeeper-tree utility for export, import and incremental
+  updates (anirban roy via breed)
 
   ZOOKEEPER-572. add ability for operator to examine state of watches
   currently registered with a server (phunt via mahadev)
+
+  ZOOKEEPER-678. Gui browser application to view and edit the contents of a
+  zookeeper instance (Colin Goodheart-Smithe via phunt)
   
 Release 3.2.0 - 2009-06-30
 

+ 3 - 0
src/contrib/zooinspector/NOTICE.txt

@@ -0,0 +1,3 @@
+This contrib module includes icons available under the Eclipse Public Licence Version 1.0
+. from the Eclipse Java Devlopment Platform. 
+The lib sub-directory includes a binary only jar library developed at http://sourceforge.net/projects/jtoaster/

+ 94 - 0
src/contrib/zooinspector/README.txt

@@ -0,0 +1,94 @@
+==========================================
+ZooInspector - Browser and Editor for ZooKeeper Instances
+Author: Colin Goodheart-Smithe
+Date: February 2010
+==========================================
+
+ZooInspector is a Java Swing based application for browsing and editing ZooKeeper instances.
+
+Contents
+--------
+	- Features
+	- Pre-requisites
+	- Build Instructions
+	- Using ZooInspector
+	- Creating and Using Plugins
+	
+Features
+--------
+	Below is a list of features in the current release of ZooInspector.
+	- Load connection settings from a zookeeper properties file
+	- Plugable DataEncryptionManagers to specify how data should be encrypted and decrypted in the Zookeeper instance
+	- Browseable tree view of the ZooKeeper instance
+	- View the data in a node
+	- View the ACL's currently applied to a node
+	- View the metadata for a node (Version, Number of Children, Last modified Tiem, etc.)
+	- Plugable NodeViewers interface
+	- Ability to save/load and set default Node Viewers
+	
+Pre-requisites
+--------------
+	- The main zookeeper build script must have been run before building this module
+	
+Build Instructions
+------------------
+	1. Open a command line.
+	2. cd into this directory
+	3. Run command: ant
+	4. ZooInspector will be built to ../../../build/contrib/ZooInspector
+	5. Copy zookeeper-3.x.x.jar into the lib sub-directory (if you are using zookeeper-3.3.0.jar it will have been
+       copied to this directory during the build
+	6. By default the zookeeper.cmd and zookeeper.sh files expect zookeeper-3.3.0.jar.  If you are using another version
+	   you will need to change these files to point to the zookeeper-3.x.x.jar you copied to the lib directory
+	7. To run ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux).  If you are using 
+	   zookeeper-3.3.0.jar and do not require any classpath changes you can run the zookeeper-dev-ZooInspector.jar
+	   directly
+
+Using ZooInspector
+------------------
+	To start ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux).  If you are using 
+	zookeeper-3.3.0.jar and do not require any classpath changes you can run the zookeeper-dev-ZooInspector.jar
+	directly.
+	
+	Click the play button on the toolbar to bring up the connection dialog.  From here you can enter connection 
+	information for your zookeeper instance.  You can also load the connection properties from a file.  This file can 
+	have the format as a normal zookeeper properties file (i.e. hosts and timeout key-value pairs) and van optional have
+	an encryptionManager key-value pair to specify the DataEncryptionManager to use for this connection 
+	(DataEncryptionManagers are explained in further detail in the 'Creating and Using Plugins' section below).  You can
+	also set the entered information as the defaults so that when you first start ZooInspector these settings are 
+	automatically loaded into this dialog.  Pressing the OK button with connect to your ZooKeeper instance and show the
+	current node tree on the left of the main panel.
+	
+	Clicking a node in the node tree will load the data for that node into the node viewers.  Three node viewers are 
+	currently distributed with ZooInspector:
+		1. Node Data - This enables you to see the data current stored on that node.  This data can be modified and 
+		   saved.  The data is decrypted and encrypted using the DataEncryptionManager specified on the connection 
+		   dialog.
+		2. Node Metadata - This enables you to see the metadata associiated with this node.  This is Essentially the data
+		   obtained from the Stat object for this node.
+		3. Node ACLs - This allows you to see the ACLs currently applied to this node.  Currently there is no ability
+		   to change the ACLs on a node, but it is a feature I would like to add.
+	Other custom Node Viewers can be added, this is explained in the 'Creating and Using Plugins' section below.
+	
+
+Creating and Using Plugins
+--------------------------
+	There are two types of plugin which can be used with ZooInspector:
+		1. DataEncryptionManager - This specifies how data should be encrypted and decrypted when working with a 
+		   zookeeper instance.
+		2. ZooInspectorNodeViewer - This is a GUI panel which provides a view of visualisation on a node.
+	More information on these interfaces can be found in the javadocs for this module.
+	
+	To use a plugin in ZooInspector, build the plugin to a jar and copy the jar to the lib sub-directory.  Edit the 
+	zooInspector.cmd and/or zooInspector.sh files to include your new jar on the classpath and run ZooInspector.
+	
+	For DataEncryptionManagers, click the play button to open the connection dialog and enter the full class name of 
+	your DataEncryptionManager in the 'Data Encryption Manager' field.  You can make this Data Encryption Manager the 
+	default by clicking 'Set As Default'.  Click the 'OK' button to instantiate and use your plugin.
+	
+	For ZooInspectorNodeViewers, Click the 'Change Node Viewers' button on the toolbar (looks like a tree with a pencil)
+	and enter the full classname for your Node Viewer in the field left of the 'Add' button, then click the 'Add' 
+	button.  The Node Viewer will be instantiated and should appear in the list.  You can change the order of the Node 
+	viewers by clicking the up and dpwn buttons and delete a Node Viewer by clicking the delete button.  You can save 
+	to configuration to a file or set it as the default if necessary. Then click the 'OK' button and your Node Viewer 
+	should appear in the tabs on the right of the main panel.

+ 152 - 0
src/contrib/zooinspector/build.xml

@@ -0,0 +1,152 @@
+<!--
+   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.
+-->
+
+<project name="ZooInspector" default="jar">
+	<import file="../build-contrib.xml" />
+
+
+	<target name="setjarname">
+		<property name="jarname" value="${build.dir}/zookeeper-${version}-${name}.jar" />
+	</target>
+
+	<target name="init" depends="checkMainCompiled, zookeeperbuildcontrib.init">
+		<mkdir dir="${build.dir}/licences" />
+		<copy todir="${build.dir}/licences">
+			<fileset dir="${basedir}/licences" />
+		</copy>
+		<mkdir dir="${build.dir}/icons" />
+		<copy todir="${build.dir}/icons">
+			<fileset dir="${basedir}/icons" />
+		</copy>
+		<mkdir dir="${build.dir}/config" />
+		<copy todir="${build.dir}/config">
+			<fileset dir="${basedir}/config" />
+		</copy>
+		<copy todir="${build.dir}/lib">
+			<fileset file="${basedir}/lib/jtoaster-1.0.4.jar" />
+			<fileset file="${basedir}/lib/log4j.properties" />
+		</copy>
+		<copy todir="${build.dir}/lib">
+			<fileset file="../../../build/zookeeper-3.3.0.jar" />
+		</copy>
+		<copy todir="${build.dir}">
+			<fileset dir="${basedir}" includes="*.*" excludes="build.xml,ivy.xml" />
+		</copy>
+	</target>
+
+	<!-- Override jar target to specify main class -->
+	<target name="jar" depends="setjarname, compile">
+		<echo message="contrib: ${name}" />
+
+		<jar jarfile="${jarname}">
+			<manifest>
+				<attribute name="Main-Class" value="org.apache.zookeeper.inspector.ZooInspector" />
+				<attribute name="Class-Path" value="lib/log4j-1.2.15.jar lib/TableLayout-20050920.jar lib/zookeeper-3.3.0.jar lib/jToaster-1.0.4.jar lib" />
+				<attribute name="Built-By" value="${user.name}" />
+				<attribute name="Built-At" value="${build.time}" />
+				<attribute name="Built-On" value="${host.name}" />
+				<attribute name="Implementation-Title" value="org.apache.zookeeper" />
+				<attribute name="Implementation-Version" value="${revision}" />
+				<attribute name="Implementation-Vendor" value="The Apache Software Foundation" />
+			</manifest>
+			<fileset file="${zk.root}/LICENSE.txt" />
+			<fileset dir="${build.classes}" />
+			<fileset dir="${basedir}/src/java" excludes="**/*.jar, **/*.java"/>
+		</jar>
+	</target>
+
+	<target name="compile" depends="ivy-retrieve,zookeeperbuildcontrib.compile" />
+
+	<target name="test" depends="checkMainTestCompiled,compile-test,test-init,test-category,junit.run" />
+
+	<target name="compile-test" depends="ivy-retrieve-test,compile">
+		<property name="target.jdk" value="${ant.java.version}" />
+		<property name="src.test.local" location="${basedir}/test" />
+		<mkdir dir="${build.test}" />
+		<javac srcdir="${src.test.local}" destdir="${build.test}" target="${target.jdk}" debug="on">
+			<classpath refid="classpath" />
+			<classpath>
+				<pathelement location="${zk.root}/build/test/classes" />
+			</classpath>
+		</javac>
+	</target>
+
+	<target name="test-init" depends="jar,compile-test">
+		<delete dir="${test.log.dir}" />
+		<delete dir="${test.tmp.dir}" />
+		<delete dir="${test.data.dir}" />
+		<mkdir dir="${test.log.dir}" />
+		<mkdir dir="${test.tmp.dir}" />
+		<mkdir dir="${test.data.dir}" />
+	</target>
+
+	<target name="test-category">
+		<property name="test.category" value="" />
+	</target>
+
+	<target name="junit.run">
+		<echo message="${test.src.dir}" />
+		<junit showoutput="${test.output}" printsummary="${test.junit.printsummary}" haltonfailure="${test.junit.haltonfailure}" fork="yes" forkmode="${test.junit.fork.mode}" maxmemory="${test.junit.maxmem}" dir="${basedir}" timeout="${test.timeout}" errorProperty="tests.failed" failureProperty="tests.failed">
+			<sysproperty key="build.test.dir" value="${test.tmp.dir}" />
+			<sysproperty key="test.data.dir" value="${test.data.dir}" />
+			<sysproperty key="log4j.configuration" value="file:${basedir}/conf/log4j.properties" />
+			<classpath refid="classpath" />
+			<classpath>
+				<pathelement path="${build.test}" />
+				<pathelement location="${zk.root}/build/test/classes" />
+			</classpath>
+			<formatter type="${test.junit.output.format}" />
+			<batchtest todir="${test.log.dir}" unless="testcase">
+				<fileset dir="${test.src.dir}" includes="**/*${test.category}Test.java" />
+			</batchtest>
+			<batchtest todir="${test.log.dir}" if="testcase">
+				<fileset dir="${test.src.dir}" includes="**/${testcase}.java" />
+			</batchtest>
+		</junit>
+		<fail if="tests.failed">Tests failed!</fail>
+	</target>
+
+	<target name="package" depends="jar, zookeeperbuildcontrib.package" unless="skip.contrib">
+
+		<copy file="${basedir}/build.xml" todir="${dist.dir}/contrib/${name}" />
+
+		<mkdir dir="${dist.dir}/contrib/${name}/src" />
+		<copy todir="${dist.dir}/contrib/${name}/src">
+			<fileset dir="${basedir}/src" />
+		</copy>
+		<mkdir dir="${dist.dir}/contrib/${name}/licences" />
+		<copy todir="${dist.dir}/contrib/${name}/licences">
+			<fileset dir="${basedir}/licences" />
+		</copy>
+		<mkdir dir="${dist.dir}/contrib/${name}/icons" />
+		<copy todir="${dist.dir}/contrib/${name}/icons">
+			<fileset dir="${basedir}/icons" />
+		</copy>
+		<mkdir dir="${dist.dir}/contrib/${name}/config" />
+		<copy todir="${dist.dir}/contrib/${name}/config">
+			<fileset dir="${basedir}/config" />
+		</copy>
+		<copy todir="${dist.dir}/contrib/${name}/lib">
+			<fileset file="${basedir}/lib/jtoaster-1.0.4.jar" />
+		</copy>
+		<copy todir="${dist.dir}/contrib/${name}/lib">
+			<fileset file="../../../build/zookeeper-3.3.0.jar" />
+		</copy>
+	</target>
+
+</project>
+

+ 5 - 0
src/contrib/zooinspector/config/defaultConnectionSettings.cfg

@@ -0,0 +1,5 @@
+#Default connection for ZooInspector
+#Sun Feb 28 14:46:55 GMT 2010
+hosts=localhost\:2181
+encryptionManager=org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager
+timeout=5000

+ 3 - 0
src/contrib/zooinspector/config/defaultNodeVeiwers.cfg

@@ -0,0 +1,3 @@
+org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerData
+org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerMetaData
+org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerACL

BIN
src/contrib/zooinspector/icons/edtsrclkup_co.gif


BIN
src/contrib/zooinspector/icons/file_obj.gif


BIN
src/contrib/zooinspector/icons/fldr_obj.gif


BIN
src/contrib/zooinspector/icons/info_obj.gif


BIN
src/contrib/zooinspector/icons/jspdecl.gif


BIN
src/contrib/zooinspector/icons/launch_run.gif


BIN
src/contrib/zooinspector/icons/launch_stop.gif


BIN
src/contrib/zooinspector/icons/new_con.gif


BIN
src/contrib/zooinspector/icons/refresh.gif


BIN
src/contrib/zooinspector/icons/save_edit.gif


BIN
src/contrib/zooinspector/icons/search_next.gif


BIN
src/contrib/zooinspector/icons/search_prev.gif


BIN
src/contrib/zooinspector/icons/trash.gif


+ 45 - 0
src/contrib/zooinspector/ivy.xml

@@ -0,0 +1,45 @@
+<!--
+   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.
+-->
+
+<ivy-module version="2.0"
+            xmlns:e="http://ant.apache.org/ivy/extra">
+
+  <info organisation="org.apache.zookeeper"
+        module="${name}" revision="${version}">
+    <license name="Apache 2.0"/>
+    <ivyauthor name="Apache Hadoop" url="http://hadoop.apache.org"/>
+    <description>ZooInspector</description>
+  </info>
+
+  <configurations defaultconfmapping="default">
+    <conf name="default"/>
+    <conf name="test"/>
+    <conf name="releaseaudit" visibility="private" description="Artifacts required for releaseaudit target"/>
+  </configurations>
+
+  <dependencies>
+    <dependency org="log4j" name="log4j" rev="1.2.15" transitive="false"/>
+    <dependency org="junit" name="junit" rev="4.7" conf="test->default"/>
+        <dependency org="org.apache.rat" name="apache-rat-tasks" 
+                rev="0.6" conf="releaseaudit->default"/>
+    <dependency org="commons-lang" name="commons-lang" 
+                rev="2.4" conf="releaseaudit->default"/>
+    <dependency org="commons-collections" name="commons-collections" 
+                rev="3.1" conf="releaseaudit->default"/>
+  </dependencies>
+
+</ivy-module>

BIN
src/contrib/zooinspector/lib/jtoaster-1.0.4.jar


+ 9 - 0
src/contrib/zooinspector/lib/log4j.properties

@@ -0,0 +1,9 @@
+# ***** Set root logger level to INFO and it appender to stdout.
+log4j.rootLogger=info, stdout
+
+# ***** stdout is set to be a ConsoleAppender.
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+# ***** stdout uses PatternLayout.
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+# ***** Pattern to output the caller's file name and line number.
+log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

+ 202 - 0
src/contrib/zooinspector/licences/Apache Software Licence v2.0.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.

+ 261 - 0
src/contrib/zooinspector/licences/epl-v10.html

@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Public License - Version 1.0</title>
+<style type="text/css">
+  body {
+    size: 8.5in 11.0in;
+    margin: 0.25in 0.5in 0.25in 0.5in;
+    tab-interval: 0.5in;
+    }
+  p {  	
+    margin-left: auto;
+    margin-top:  0.5em;
+    margin-bottom: 0.5em;
+    }
+  p.list {
+  	margin-left: 0.5in;
+    margin-top:  0.05em;
+    margin-bottom: 0.05em;
+    }
+  </style>
+
+</head>
+
+<body lang="EN-US">
+
+<h2>Eclipse Public License - v 1.0</h2>
+
+<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR
+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
+AGREEMENT.</p>
+
+<p><b>1. DEFINITIONS</b></p>
+
+<p>&quot;Contribution&quot; means:</p>
+
+<p class="list">a) in the case of the initial Contributor, the initial
+code and documentation distributed under this Agreement, and</p>
+<p class="list">b) in the case of each subsequent Contributor:</p>
+<p class="list">i) changes to the Program, and</p>
+<p class="list">ii) additions to the Program;</p>
+<p class="list">where such changes and/or additions to the Program
+originate from and are distributed by that particular Contributor. A
+Contribution 'originates' from a Contributor if it was added to the
+Program by such Contributor itself or anyone acting on such
+Contributor's behalf. Contributions do not include additions to the
+Program which: (i) are separate modules of software distributed in
+conjunction with the Program under their own license agreement, and (ii)
+are not derivative works of the Program.</p>
+
+<p>&quot;Contributor&quot; means any person or entity that distributes
+the Program.</p>
+
+<p>&quot;Licensed Patents&quot; mean patent claims licensable by a
+Contributor which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program.</p>
+
+<p>&quot;Program&quot; means the Contributions distributed in accordance
+with this Agreement.</p>
+
+<p>&quot;Recipient&quot; means anyone who receives the Program under
+this Agreement, including all Contributors.</p>
+
+<p><b>2. GRANT OF RIGHTS</b></p>
+
+<p class="list">a) Subject to the terms of this Agreement, each
+Contributor hereby grants Recipient a non-exclusive, worldwide,
+royalty-free copyright license to reproduce, prepare derivative works
+of, publicly display, publicly perform, distribute and sublicense the
+Contribution of such Contributor, if any, and such derivative works, in
+source code and object code form.</p>
+
+<p class="list">b) Subject to the terms of this Agreement, each
+Contributor hereby grants Recipient a non-exclusive, worldwide,
+royalty-free patent license under Licensed Patents to make, use, sell,
+offer to sell, import and otherwise transfer the Contribution of such
+Contributor, if any, in source code and object code form. This patent
+license shall apply to the combination of the Contribution and the
+Program if, at the time the Contribution is added by the Contributor,
+such addition of the Contribution causes such combination to be covered
+by the Licensed Patents. The patent license shall not apply to any other
+combinations which include the Contribution. No hardware per se is
+licensed hereunder.</p>
+
+<p class="list">c) Recipient understands that although each Contributor
+grants the licenses to its Contributions set forth herein, no assurances
+are provided by any Contributor that the Program does not infringe the
+patent or other intellectual property rights of any other entity. Each
+Contributor disclaims any liability to Recipient for claims brought by
+any other entity based on infringement of intellectual property rights
+or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to
+secure any other intellectual property rights needed, if any. For
+example, if a third party patent license is required to allow Recipient
+to distribute the Program, it is Recipient's responsibility to acquire
+that license before distributing the Program.</p>
+
+<p class="list">d) Each Contributor represents that to its knowledge it
+has sufficient copyright rights in its Contribution, if any, to grant
+the copyright license set forth in this Agreement.</p>
+
+<p><b>3. REQUIREMENTS</b></p>
+
+<p>A Contributor may choose to distribute the Program in object code
+form under its own license agreement, provided that:</p>
+
+<p class="list">a) it complies with the terms and conditions of this
+Agreement; and</p>
+
+<p class="list">b) its license agreement:</p>
+
+<p class="list">i) effectively disclaims on behalf of all Contributors
+all warranties and conditions, express and implied, including warranties
+or conditions of title and non-infringement, and implied warranties or
+conditions of merchantability and fitness for a particular purpose;</p>
+
+<p class="list">ii) effectively excludes on behalf of all Contributors
+all liability for damages, including direct, indirect, special,
+incidental and consequential damages, such as lost profits;</p>
+
+<p class="list">iii) states that any provisions which differ from this
+Agreement are offered by that Contributor alone and not by any other
+party; and</p>
+
+<p class="list">iv) states that source code for the Program is available
+from such Contributor, and informs licensees how to obtain it in a
+reasonable manner on or through a medium customarily used for software
+exchange.</p>
+
+<p>When the Program is made available in source code form:</p>
+
+<p class="list">a) it must be made available under this Agreement; and</p>
+
+<p class="list">b) a copy of this Agreement must be included with each
+copy of the Program.</p>
+
+<p>Contributors may not remove or alter any copyright notices contained
+within the Program.</p>
+
+<p>Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.</p>
+
+<p><b>4. COMMERCIAL DISTRIBUTION</b></p>
+
+<p>Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use of
+the Program, the Contributor who includes the Program in a commercial
+product offering should do so in a manner which does not create
+potential liability for other Contributors. Therefore, if a Contributor
+includes the Program in a commercial product offering, such Contributor
+(&quot;Commercial Contributor&quot;) hereby agrees to defend and
+indemnify every other Contributor (&quot;Indemnified Contributor&quot;)
+against any losses, damages and costs (collectively &quot;Losses&quot;)
+arising from claims, lawsuits and other legal actions brought by a third
+party against the Indemnified Contributor to the extent caused by the
+acts or omissions of such Commercial Contributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In
+order to qualify, an Indemnified Contributor must: a) promptly notify
+the Commercial Contributor in writing of such claim, and b) allow the
+Commercial Contributor to control, and cooperate with the Commercial
+Contributor in, the defense and any related settlement negotiations. The
+Indemnified Contributor may participate in any such claim at its own
+expense.</p>
+
+<p>For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.</p>
+
+<p><b>5. NO WARRANTY</b></p>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
+ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement , including but not limited to
+the risks and costs of program errors, compliance with applicable laws,
+damage to or loss of data, programs or equipment, and unavailability or
+interruption of operations.</p>
+
+<p><b>6. DISCLAIMER OF LIABILITY</b></p>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
+
+<p><b>7. GENERAL</b></p>
+
+<p>If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.</p>
+
+<p>If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other
+software or hardware) infringes such Recipient's patent(s), then such
+Recipient's rights granted under Section 2(b) shall terminate as of the
+date such litigation is filed.</p>
+
+<p>All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of time
+after becoming aware of such noncompliance. If all Recipient's rights
+under this Agreement terminate, Recipient agrees to cease use and
+distribution of the Program as soon as reasonably practicable. However,
+Recipient's obligations under this Agreement and any licenses granted by
+Recipient relating to the Program shall continue and survive.</p>
+
+<p>Everyone is permitted to copy and distribute copies of this
+Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The
+Agreement Steward reserves the right to publish new versions (including
+revisions) of this Agreement from time to time. No one other than the
+Agreement Steward has the right to modify this Agreement. The Eclipse
+Foundation is the initial Agreement Steward. The Eclipse Foundation may
+assign the responsibility to serve as the Agreement Steward to a
+suitable separate entity. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version
+of the Agreement is published, Contributor may elect to distribute the
+Program (including its Contributions) under the new version. Except as
+expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
+rights or licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under this
+Agreement are reserved.</p>
+
+<p>This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No party
+to this Agreement will bring a legal action under this Agreement more
+than one year after the cause of action arose. Each party waives its
+rights to a jury trial in any resulting litigation.</p>
+
+</body>
+
+</html>

+ 66 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/ZooInspector.java

@@ -0,0 +1,66 @@
+/**
+ * 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.zookeeper.inspector;
+
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.UIManager;
+
+import org.apache.zookeeper.inspector.gui.ZooInspectorPanel;
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+import org.apache.zookeeper.inspector.manager.ZooInspectorManagerImpl;
+
+/**
+ * 
+ */
+public class ZooInspector {
+    /**
+     * @param args
+     *            - not used. The value of these parameters will have no effect
+     *            on the application
+     */
+    public static void main(String[] args) {
+        try {
+            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+            JFrame frame = new JFrame("ZooInspector");
+            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+            final ZooInspectorPanel zooInspectorPanel = new ZooInspectorPanel(
+                    new ZooInspectorManagerImpl());
+            frame.addWindowListener(new WindowAdapter() {
+                @Override
+                public void windowClosed(WindowEvent e) {
+                    super.windowClosed(e);
+                    zooInspectorPanel.disconnect(true);
+                }
+            });
+
+            frame.setContentPane(zooInspectorPanel);
+            frame.setSize(1024, 768);
+            frame.setVisible(true);
+        } catch (Exception e) {
+            LoggerFactory.getLogger().error(
+                    "Error occurred loading ZooInspector", e);
+            JOptionPane.showMessageDialog(null,
+                    "ZooInspector failed to start: " + e.getMessage(), "Error",
+                    JOptionPane.ERROR_MESSAGE);
+        }
+    }
+}

+ 50 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java

@@ -0,0 +1,50 @@
+/**
+ * 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.zookeeper.inspector.encryption;
+
+/**
+ *
+ */
+public class BasicDataEncryptionManager implements DataEncryptionManager {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.encryption.DataEncryptionManager#decryptData
+     * (byte[])
+     */
+    public String decryptData(byte[] encrypted) throws Exception {
+        return new String(encrypted);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.encryption.DataEncryptionManager#encryptData
+     * (java.lang.String)
+     */
+    public byte[] encryptData(String data) throws Exception {
+        if (data == null) {
+            return new byte[0];
+        }
+        return data.getBytes();
+    }
+
+}

+ 39 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/DataEncryptionManager.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.zookeeper.inspector.encryption;
+
+/**
+ * A class which describes how data should be encrypted and decrypted
+ */
+public interface DataEncryptionManager {
+    /**
+     * @param data
+     *            - the data to be encrypted
+     * @return the encrypted data
+     * @throws Exception
+     */
+    public byte[] encryptData(String data) throws Exception;
+
+    /**
+     * @param encrypted
+     *            - the data to be decrypted
+     * @return the decrypted data
+     * @throws Exception
+     */
+    public String decryptData(byte[] encrypted) throws Exception;
+}

+ 37 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/NodeViewersChangeListener.java

@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.inspector.gui;
+
+import java.util.List;
+
+import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer;
+
+/**
+ * A Listener for changes to the configuration of which node viewers are shown
+ */
+public interface NodeViewersChangeListener {
+    /**
+     * Called when the node viewers configuration is changed (i.e node viewers
+     * are added, removed or the order of the node viewers is changed)
+     * 
+     * @param newViewers
+     *            - a {@link List} of {@link ZooInspectorNodeViewer}s which are
+     *            to be shown
+     */
+    public void nodeViewersChanged(List<ZooInspectorNodeViewer> newViewers);
+}

+ 80 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java

@@ -0,0 +1,80 @@
+/**
+ * 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.zookeeper.inspector.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
+
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+
+/**
+ * The About Dialog for the application
+ */
+public class ZooInspectorAboutDialog extends JDialog {
+    /**
+     * @param frame
+     *            - the Frame from which the dialog is displayed
+     */
+    public ZooInspectorAboutDialog(Frame frame) {
+        super(frame);
+        this.setLayout(new BorderLayout());
+        this.setIconImage(ZooInspectorIconResources.getInformationIcon()
+                .getImage());
+        this.setTitle("About ZooInspector");
+        this.setModal(true);
+        this.setAlwaysOnTop(true);
+        this.setResizable(false);
+        JPanel panel = new JPanel();
+        panel.setLayout(new BorderLayout());
+        JEditorPane aboutPane = new JEditorPane();
+        aboutPane.setEditable(false);
+        aboutPane.setOpaque(false);
+        java.net.URL aboutURL = ZooInspectorAboutDialog.class
+                .getResource("about.html");
+        try {
+            aboutPane.setPage(aboutURL);
+        } catch (IOException e) {
+            LoggerFactory.getLogger().error(
+                    "Error loading about.html, file may be corrupt", e);
+        }
+        panel.add(aboutPane, BorderLayout.CENTER);
+        panel.setPreferredSize(new Dimension(600, 200));
+        JPanel buttonsPanel = new JPanel();
+        buttonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
+        JButton okButton = new JButton("OK");
+        okButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                ZooInspectorAboutDialog.this.dispose();
+            }
+        });
+        buttonsPanel.add(okButton);
+        this.add(panel, BorderLayout.CENTER);
+        this.add(buttonsPanel, BorderLayout.SOUTH);
+        this.pack();
+    }
+}

+ 321 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java

@@ -0,0 +1,321 @@
+/**
+ * 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.zookeeper.inspector.gui;
+
+import java.awt.BorderLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Map.Entry;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+import org.apache.zookeeper.inspector.manager.Pair;
+
+/**
+ * The connection properties dialog. This is used to determine the settings for
+ * connecting to a zookeeper instance
+ */
+public class ZooInspectorConnectionPropertiesDialog extends JDialog {
+
+    private final HashMap<String, JComponent> components;
+
+    /**
+     * @param lastConnectionProps
+     *            - the last connection properties used. if this is the first
+     *            conneciton since starting the applications this will be the
+     *            default settings
+     * @param connectionPropertiesTemplateAndLabels
+     *            - the connection properties and labels to show in this dialog
+     * @param zooInspectorPanel
+     *            - the {@link ZooInspectorPanel} linked to this dialog
+     */
+    public ZooInspectorConnectionPropertiesDialog(
+            Properties lastConnectionProps,
+            Pair<Map<String, List<String>>, Map<String, String>> connectionPropertiesTemplateAndLabels,
+            final ZooInspectorPanel zooInspectorPanel) {
+        final Map<String, List<String>> connectionPropertiesTemplate = connectionPropertiesTemplateAndLabels
+                .getKey();
+        final Map<String, String> connectionPropertiesLabels = connectionPropertiesTemplateAndLabels
+                .getValue();
+        this.setLayout(new BorderLayout());
+        this.setTitle("Connection Settings");
+        this.setModal(true);
+        this.setAlwaysOnTop(true);
+        this.setResizable(false);
+        final JPanel options = new JPanel();
+        final JFileChooser fileChooser = new JFileChooser();
+        options.setLayout(new GridBagLayout());
+        int i = 0;
+        components = new HashMap<String, JComponent>();
+        for (Entry<String, List<String>> entry : connectionPropertiesTemplate
+                .entrySet()) {
+            int rowPos = 2 * i + 1;
+            JLabel label = new JLabel(connectionPropertiesLabels.get(entry
+                    .getKey()));
+            GridBagConstraints c1 = new GridBagConstraints();
+            c1.gridx = 0;
+            c1.gridy = rowPos;
+            c1.gridwidth = 1;
+            c1.gridheight = 1;
+            c1.weightx = 0;
+            c1.weighty = 0;
+            c1.anchor = GridBagConstraints.WEST;
+            c1.fill = GridBagConstraints.HORIZONTAL;
+            c1.insets = new Insets(5, 5, 5, 5);
+            c1.ipadx = 0;
+            c1.ipady = 0;
+            options.add(label, c1);
+            if (entry.getValue().size() == 0) {
+                JTextField text = new JTextField();
+                GridBagConstraints c2 = new GridBagConstraints();
+                c2.gridx = 2;
+                c2.gridy = rowPos;
+                c2.gridwidth = 1;
+                c2.gridheight = 1;
+                c2.weightx = 0;
+                c2.weighty = 0;
+                c2.anchor = GridBagConstraints.WEST;
+                c2.fill = GridBagConstraints.HORIZONTAL;
+                c2.insets = new Insets(5, 5, 5, 5);
+                c2.ipadx = 0;
+                c2.ipady = 0;
+                options.add(text, c2);
+                components.put(entry.getKey(), text);
+            } else if (entry.getValue().size() == 1) {
+                JTextField text = new JTextField(entry.getValue().get(0));
+                GridBagConstraints c2 = new GridBagConstraints();
+                c2.gridx = 2;
+                c2.gridy = rowPos;
+                c2.gridwidth = 1;
+                c2.gridheight = 1;
+                c2.weightx = 0;
+                c2.weighty = 0;
+                c2.anchor = GridBagConstraints.WEST;
+                c2.fill = GridBagConstraints.HORIZONTAL;
+                c2.insets = new Insets(5, 5, 5, 5);
+                c2.ipadx = 0;
+                c2.ipady = 0;
+                options.add(text, c2);
+                components.put(entry.getKey(), text);
+            } else {
+                List<String> list = entry.getValue();
+                JComboBox combo = new JComboBox(list.toArray(new String[list
+                        .size()]));
+                combo.setSelectedItem(list.get(0));
+                GridBagConstraints c2 = new GridBagConstraints();
+                c2.gridx = 2;
+                c2.gridy = rowPos;
+                c2.gridwidth = 1;
+                c2.gridheight = 1;
+                c2.weightx = 0;
+                c2.weighty = 0;
+                c2.anchor = GridBagConstraints.WEST;
+                c2.fill = GridBagConstraints.HORIZONTAL;
+                c2.insets = new Insets(5, 5, 5, 5);
+                c2.ipadx = 0;
+                c2.ipady = 0;
+                options.add(combo, c2);
+                components.put(entry.getKey(), combo);
+            }
+            i++;
+        }
+        loadConnectionProps(lastConnectionProps);
+        JPanel buttonsPanel = new JPanel();
+        buttonsPanel.setLayout(new GridBagLayout());
+        JButton loadPropsFileButton = new JButton("Load from file");
+        loadPropsFileButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                int result = fileChooser
+                        .showOpenDialog(ZooInspectorConnectionPropertiesDialog.this);
+                if (result == JFileChooser.APPROVE_OPTION) {
+                    File propsFilePath = fileChooser.getSelectedFile();
+                    Properties props = new Properties();
+                    try {
+                        FileReader reader = new FileReader(propsFilePath);
+                        try {
+                            props.load(reader);
+                            loadConnectionProps(props);
+                        } finally {
+                            reader.close();
+                        }
+                    } catch (IOException ex) {
+                        LoggerFactory
+                                .getLogger()
+                                .error(
+                                        "An Error occurred loading connection properties from file",
+                                        ex);
+                        JOptionPane
+                                .showMessageDialog(
+                                        ZooInspectorConnectionPropertiesDialog.this,
+                                        "An Error occurred loading connection properties from file",
+                                        "Error", JOptionPane.ERROR_MESSAGE);
+                    }
+                    options.revalidate();
+                    options.repaint();
+                }
+
+            }
+        });
+        GridBagConstraints c3 = new GridBagConstraints();
+        c3.gridx = 0;
+        c3.gridy = 0;
+        c3.gridwidth = 1;
+        c3.gridheight = 1;
+        c3.weightx = 0;
+        c3.weighty = 1;
+        c3.anchor = GridBagConstraints.SOUTHWEST;
+        c3.fill = GridBagConstraints.NONE;
+        c3.insets = new Insets(5, 5, 5, 5);
+        c3.ipadx = 0;
+        c3.ipady = 0;
+        buttonsPanel.add(loadPropsFileButton, c3);
+        JButton saveDefaultPropsFileButton = new JButton("Set As Default");
+        saveDefaultPropsFileButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+
+                Properties connectionProps = getConnectionProps();
+                try {
+                    zooInspectorPanel
+                            .setdefaultConnectionProps(connectionProps);
+                } catch (IOException ex) {
+                    LoggerFactory
+                            .getLogger()
+                            .error(
+                                    "An Error occurred saving the default connection properties file",
+                                    ex);
+                    JOptionPane
+                            .showMessageDialog(
+                                    ZooInspectorConnectionPropertiesDialog.this,
+                                    "An Error occurred saving the default connection properties file",
+                                    "Error", JOptionPane.ERROR_MESSAGE);
+                }
+            }
+        });
+        GridBagConstraints c6 = new GridBagConstraints();
+        c6.gridx = 1;
+        c6.gridy = 0;
+        c6.gridwidth = 1;
+        c6.gridheight = 1;
+        c6.weightx = 1;
+        c6.weighty = 1;
+        c6.anchor = GridBagConstraints.SOUTHWEST;
+        c6.fill = GridBagConstraints.NONE;
+        c6.insets = new Insets(5, 5, 5, 5);
+        c6.ipadx = 0;
+        c6.ipady = 0;
+        buttonsPanel.add(saveDefaultPropsFileButton, c6);
+        JButton okButton = new JButton("OK");
+        okButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                ZooInspectorConnectionPropertiesDialog.this.dispose();
+                Properties connectionProps = getConnectionProps();
+                zooInspectorPanel.connect(connectionProps);
+            }
+        });
+        GridBagConstraints c4 = new GridBagConstraints();
+        c4.gridx = 2;
+        c4.gridy = 0;
+        c4.gridwidth = 1;
+        c4.gridheight = 1;
+        c4.weightx = 0;
+        c4.weighty = 1;
+        c4.anchor = GridBagConstraints.SOUTH;
+        c4.fill = GridBagConstraints.HORIZONTAL;
+        c4.insets = new Insets(5, 5, 5, 5);
+        c4.ipadx = 0;
+        c4.ipady = 0;
+        buttonsPanel.add(okButton, c4);
+        JButton cancelButton = new JButton("Cancel");
+        cancelButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                ZooInspectorConnectionPropertiesDialog.this.dispose();
+            }
+        });
+        GridBagConstraints c5 = new GridBagConstraints();
+        c5.gridx = 3;
+        c5.gridy = 0;
+        c5.gridwidth = 1;
+        c5.gridheight = 1;
+        c5.weightx = 0;
+        c5.weighty = 1;
+        c5.anchor = GridBagConstraints.SOUTH;
+        c5.fill = GridBagConstraints.HORIZONTAL;
+        c5.insets = new Insets(5, 5, 5, 5);
+        c5.ipadx = 0;
+        c5.ipady = 0;
+        buttonsPanel.add(cancelButton, c5);
+        this.add(options, BorderLayout.CENTER);
+        this.add(buttonsPanel, BorderLayout.SOUTH);
+        this.pack();
+    }
+
+    private void loadConnectionProps(Properties props) {
+        if (props != null) {
+            for (Object key : props.keySet()) {
+                String propsKey = (String) key;
+                if (components.containsKey(propsKey)) {
+                    JComponent component = components.get(propsKey);
+                    String value = props.getProperty(propsKey);
+                    if (component instanceof JTextField) {
+                        ((JTextField) component).setText(value);
+                    } else if (component instanceof JComboBox) {
+                        ((JComboBox) component).setSelectedItem(value);
+                    }
+                }
+            }
+        }
+    }
+
+    private Properties getConnectionProps() {
+        Properties connectionProps = new Properties();
+        for (Entry<String, JComponent> entry : components.entrySet()) {
+            String value = null;
+            JComponent component = entry.getValue();
+            if (component instanceof JTextField) {
+                value = ((JTextField) component).getText();
+            } else if (component instanceof JComboBox) {
+                value = ((JComboBox) component).getSelectedItem().toString();
+            }
+            connectionProps.put(entry.getKey(), value);
+        }
+        return connectionProps;
+    }
+}

+ 118 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorIconResources.java

@@ -0,0 +1,118 @@
+/**
+ * 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.zookeeper.inspector.gui;
+
+import javax.swing.ImageIcon;
+
+/**
+ * A class containing static methods for retrieving {@link ImageIcon}s used in
+ * the application
+ */
+public class ZooInspectorIconResources {
+
+    /**
+     * @return file icon
+     */
+    public static ImageIcon getTreeLeafIcon() {
+        return new ImageIcon("icons/file_obj.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return folder open icon
+     */
+    public static ImageIcon getTreeOpenIcon() {
+        return new ImageIcon("icons/fldr_obj.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return folder closed icon
+     */
+    public static ImageIcon getTreeClosedIcon() {
+        return new ImageIcon("icons/fldr_obj.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return connect icon
+     */
+    public static ImageIcon getConnectIcon() {
+        return new ImageIcon("icons/launch_run.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return disconnect icon
+     */
+    public static ImageIcon getDisconnectIcon() {
+        return new ImageIcon("icons/launch_stop.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return save icon
+     */
+    public static ImageIcon getSaveIcon() {
+        return new ImageIcon("icons/save_edit.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return add icon
+     */
+    public static ImageIcon getAddNodeIcon() {
+        return new ImageIcon("icons/new_con.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return delete icon
+     */
+    public static ImageIcon getDeleteNodeIcon() {
+        return new ImageIcon("icons/trash.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return refresh icon
+     */
+    public static ImageIcon getRefreshIcon() {
+        return new ImageIcon("icons/refresh.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return information icon
+     */
+    public static ImageIcon getInformationIcon() {
+        return new ImageIcon("icons/info_obj.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return node viewers icon
+     */
+    public static ImageIcon getChangeNodeViewersIcon() {
+        return new ImageIcon("icons/edtsrclkup_co.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return up icon
+     */
+    public static ImageIcon getUpIcon() {
+        return new ImageIcon("icons/search_prev.gif"); //$NON-NLS-1$
+    }
+
+    /**
+     * @return down icon
+     */
+    public static ImageIcon getDownIcon() {
+        return new ImageIcon("icons/search_next.gif"); //$NON-NLS-1$
+    }
+}

+ 605 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java

@@ -0,0 +1,605 @@
+/**
+ * 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.zookeeper.inspector.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.datatransfer.Transferable;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.DropMode;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.TransferHandler;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer;
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+import org.apache.zookeeper.inspector.manager.ZooInspectorManager;
+
+/**
+ * A {@link JDialog} for configuring which {@link ZooInspectorNodeViewer}s to
+ * show in the application
+ */
+public class ZooInspectorNodeViewersDialog extends JDialog implements
+        ListSelectionListener {
+
+    private final JButton upButton;
+    private final JButton downButton;
+    private final JButton removeButton;
+    private final JButton addButton;
+    private final JList viewersList;
+    private final JButton saveFileButton;
+    private final JButton loadFileButton;
+    private final JButton setDefaultsButton;
+    private final JFileChooser fileChooser = new JFileChooser(new File("."));
+
+    /**
+     * @param frame
+     *            - the Frame from which the dialog is displayed
+     * @param currentViewers
+     *            - the {@link ZooInspectorNodeViewer}s to show
+     * @param listeners
+     *            - the {@link NodeViewersChangeListener}s which need to be
+     *            notified of changes to the node viewers configuration
+     * @param manager
+     *            - the {@link ZooInspectorManager} for the application
+     * 
+     */
+    public ZooInspectorNodeViewersDialog(Frame frame,
+            final List<ZooInspectorNodeViewer> currentViewers,
+            final Collection<NodeViewersChangeListener> listeners,
+            final ZooInspectorManager manager) {
+        super(frame);
+        final List<ZooInspectorNodeViewer> newViewers = new ArrayList<ZooInspectorNodeViewer>(
+                currentViewers);
+        this.setLayout(new BorderLayout());
+        this.setIconImage(ZooInspectorIconResources.getChangeNodeViewersIcon()
+                .getImage());
+        this.setTitle("About ZooInspector");
+        this.setModal(true);
+        this.setAlwaysOnTop(true);
+        this.setResizable(true);
+        final JPanel panel = new JPanel();
+        panel.setLayout(new GridBagLayout());
+        viewersList = new JList();
+        DefaultListModel model = new DefaultListModel();
+        for (ZooInspectorNodeViewer viewer : newViewers) {
+            model.addElement(viewer);
+        }
+        viewersList.setModel(model);
+        viewersList.setCellRenderer(new DefaultListCellRenderer() {
+            @Override
+            public Component getListCellRendererComponent(JList list,
+                    Object value, int index, boolean isSelected,
+                    boolean cellHasFocus) {
+                ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) value;
+                JLabel label = (JLabel) super.getListCellRendererComponent(
+                        list, value, index, isSelected, cellHasFocus);
+                label.setText(viewer.getTitle());
+                return label;
+            }
+        });
+        viewersList.setDropMode(DropMode.INSERT);
+        viewersList.enableInputMethods(true);
+        viewersList.setDragEnabled(true);
+        viewersList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        viewersList.getSelectionModel().addListSelectionListener(this);
+        viewersList.setTransferHandler(new TransferHandler() {
+
+            @Override
+            public boolean canImport(TransferHandler.TransferSupport info) {
+                // we only import NodeViewers
+                if (!info
+                        .isDataFlavorSupported(ZooInspectorNodeViewer.nodeViewerDataFlavor)) {
+                    return false;
+                }
+
+                JList.DropLocation dl = (JList.DropLocation) info
+                        .getDropLocation();
+                if (dl.getIndex() == -1) {
+                    return false;
+                }
+                return true;
+            }
+
+            @Override
+            public boolean importData(TransferHandler.TransferSupport info) {
+                JList.DropLocation dl = (JList.DropLocation) info
+                        .getDropLocation();
+                DefaultListModel listModel = (DefaultListModel) viewersList
+                        .getModel();
+                int index = dl.getIndex();
+                boolean insert = dl.isInsert();
+                // Get the string that is being dropped.
+                Transferable t = info.getTransferable();
+                String data;
+                try {
+                    data = (String) t
+                            .getTransferData(ZooInspectorNodeViewer.nodeViewerDataFlavor);
+                } catch (Exception e) {
+                    return false;
+                }
+                try {
+                    ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class
+                            .forName(data).newInstance();
+                    if (listModel.contains(viewer)) {
+                        listModel.removeElement(viewer);
+                    }
+                    if (insert) {
+                        listModel.add(index, viewer);
+                    } else {
+                        listModel.set(index, viewer);
+                    }
+                    return true;
+                } catch (Exception e) {
+                    LoggerFactory.getLogger().error(
+                            "Error instantiating class: " + data, e);
+                    return false;
+                }
+
+            }
+
+            @Override
+            public int getSourceActions(JComponent c) {
+                return MOVE;
+            }
+
+            @Override
+            protected Transferable createTransferable(JComponent c) {
+                JList list = (JList) c;
+                ZooInspectorNodeViewer value = (ZooInspectorNodeViewer) list
+                        .getSelectedValue();
+                return value;
+            }
+        });
+        JScrollPane scroller = new JScrollPane(viewersList);
+        GridBagConstraints c1 = new GridBagConstraints();
+        c1.gridx = 0;
+        c1.gridy = 0;
+        c1.gridwidth = 3;
+        c1.gridheight = 3;
+        c1.weightx = 0;
+        c1.weighty = 1;
+        c1.anchor = GridBagConstraints.CENTER;
+        c1.fill = GridBagConstraints.BOTH;
+        c1.insets = new Insets(5, 5, 5, 5);
+        c1.ipadx = 0;
+        c1.ipady = 0;
+        panel.add(scroller, c1);
+        upButton = new JButton(ZooInspectorIconResources.getUpIcon());
+        downButton = new JButton(ZooInspectorIconResources.getDownIcon());
+        removeButton = new JButton(ZooInspectorIconResources
+                .getDeleteNodeIcon());
+        addButton = new JButton(ZooInspectorIconResources.getAddNodeIcon());
+        upButton.setEnabled(false);
+        downButton.setEnabled(false);
+        removeButton.setEnabled(false);
+        addButton.setEnabled(true);
+        upButton.setToolTipText("Move currently selected node viewer up");
+        downButton.setToolTipText("Move currently selected node viewer down");
+        removeButton.setToolTipText("Remove currently selected node viewer");
+        addButton.setToolTipText("Add node viewer");
+        final JTextField newViewerTextField = new JTextField();
+        GridBagConstraints c2 = new GridBagConstraints();
+        c2.gridx = 3;
+        c2.gridy = 0;
+        c2.gridwidth = 1;
+        c2.gridheight = 1;
+        c2.weightx = 0;
+        c2.weighty = 0;
+        c2.anchor = GridBagConstraints.NORTH;
+        c2.fill = GridBagConstraints.HORIZONTAL;
+        c2.insets = new Insets(5, 5, 5, 5);
+        c2.ipadx = 0;
+        c2.ipady = 0;
+        panel.add(upButton, c2);
+        GridBagConstraints c3 = new GridBagConstraints();
+        c3.gridx = 3;
+        c3.gridy = 2;
+        c3.gridwidth = 1;
+        c3.gridheight = 1;
+        c3.weightx = 0;
+        c3.weighty = 0;
+        c3.anchor = GridBagConstraints.NORTH;
+        c3.fill = GridBagConstraints.HORIZONTAL;
+        c3.insets = new Insets(5, 5, 5, 5);
+        c3.ipadx = 0;
+        c3.ipady = 0;
+        panel.add(downButton, c3);
+        GridBagConstraints c4 = new GridBagConstraints();
+        c4.gridx = 3;
+        c4.gridy = 1;
+        c4.gridwidth = 1;
+        c4.gridheight = 1;
+        c4.weightx = 0;
+        c4.weighty = 0;
+        c4.anchor = GridBagConstraints.NORTH;
+        c4.fill = GridBagConstraints.HORIZONTAL;
+        c4.insets = new Insets(5, 5, 5, 5);
+        c4.ipadx = 0;
+        c4.ipady = 0;
+        panel.add(removeButton, c4);
+        GridBagConstraints c5 = new GridBagConstraints();
+        c5.gridx = 0;
+        c5.gridy = 3;
+        c5.gridwidth = 3;
+        c5.gridheight = 1;
+        c5.weightx = 0;
+        c5.weighty = 0;
+        c5.anchor = GridBagConstraints.CENTER;
+        c5.fill = GridBagConstraints.BOTH;
+        c5.insets = new Insets(5, 5, 5, 5);
+        c5.ipadx = 0;
+        c5.ipady = 0;
+        panel.add(newViewerTextField, c5);
+        GridBagConstraints c6 = new GridBagConstraints();
+        c6.gridx = 3;
+        c6.gridy = 3;
+        c6.gridwidth = 1;
+        c6.gridheight = 1;
+        c6.weightx = 0;
+        c6.weighty = 0;
+        c6.anchor = GridBagConstraints.CENTER;
+        c6.fill = GridBagConstraints.BOTH;
+        c6.insets = new Insets(5, 5, 5, 5);
+        c6.ipadx = 0;
+        c6.ipady = 0;
+        panel.add(addButton, c6);
+        upButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                DefaultListModel listModel = (DefaultListModel) viewersList
+                        .getModel();
+                ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList
+                        .getSelectedValue();
+                int index = viewersList.getSelectedIndex();
+                if (listModel.contains(viewer)) {
+                    listModel.removeElementAt(index);
+                    listModel.insertElementAt(viewer, index - 1);
+                    viewersList.setSelectedValue(viewer, true);
+                }
+            }
+        });
+        downButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                DefaultListModel listModel = (DefaultListModel) viewersList
+                        .getModel();
+                ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList
+                        .getSelectedValue();
+                int index = viewersList.getSelectedIndex();
+                if (listModel.contains(viewer)) {
+                    listModel.removeElementAt(index);
+                    listModel.insertElementAt(viewer, index + 1);
+                    viewersList.setSelectedValue(viewer, true);
+                }
+            }
+        });
+        removeButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                DefaultListModel listModel = (DefaultListModel) viewersList
+                        .getModel();
+                ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList
+                        .getSelectedValue();
+                int index = viewersList.getSelectedIndex();
+                if (listModel.contains(viewer)) {
+                    listModel.removeElement(viewer);
+                    viewersList
+                            .setSelectedIndex(index == listModel.size() ? index - 1
+                                    : index);
+                }
+            }
+        });
+        addButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                String className = newViewerTextField.getText();
+                if (className == null || className.length() == 0) {
+                    JOptionPane
+                            .showMessageDialog(
+                                    ZooInspectorNodeViewersDialog.this,
+                                    "Please enter the full class name for a Node Viewer and click the add button",
+                                    "Input Error", JOptionPane.ERROR_MESSAGE);
+                } else {
+                    try {
+                        DefaultListModel listModel = (DefaultListModel) viewersList
+                                .getModel();
+                        ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class
+                                .forName(className).newInstance();
+                        if (listModel.contains(viewer)) {
+                            JOptionPane
+                                    .showMessageDialog(
+                                            ZooInspectorNodeViewersDialog.this,
+                                            "Node viewer already exists.  Each node viewer can only be added once.",
+                                            "Input Error",
+                                            JOptionPane.ERROR_MESSAGE);
+                        } else {
+                            listModel.addElement(viewer);
+                        }
+                    } catch (Exception ex) {
+                        LoggerFactory
+                                .getLogger()
+                                .error(
+                                        "An error occurred while instaniating the node viewer. ",
+                                        ex);
+                        JOptionPane.showMessageDialog(
+                                ZooInspectorNodeViewersDialog.this,
+                                "An error occurred while instaniating the node viewer: "
+                                        + ex.getMessage(), "Error",
+                                JOptionPane.ERROR_MESSAGE);
+                    }
+                }
+            }
+        });
+        saveFileButton = new JButton("Save");
+        loadFileButton = new JButton("Load");
+        setDefaultsButton = new JButton("Set As Defaults");
+        saveFileButton
+                .setToolTipText("Save current node viewer configuration to file");
+        loadFileButton
+                .setToolTipText("Load node viewer configuration frm file");
+        setDefaultsButton
+                .setToolTipText("Set current configuration asd defaults");
+        GridBagConstraints c7 = new GridBagConstraints();
+        c7.gridx = 0;
+        c7.gridy = 4;
+        c7.gridwidth = 1;
+        c7.gridheight = 1;
+        c7.weightx = 1;
+        c7.weighty = 0;
+        c7.anchor = GridBagConstraints.WEST;
+        c7.fill = GridBagConstraints.VERTICAL;
+        c7.insets = new Insets(5, 5, 5, 5);
+        c7.ipadx = 0;
+        c7.ipady = 0;
+        panel.add(saveFileButton, c7);
+        GridBagConstraints c8 = new GridBagConstraints();
+        c8.gridx = 1;
+        c8.gridy = 4;
+        c8.gridwidth = 1;
+        c8.gridheight = 1;
+        c8.weightx = 0;
+        c8.weighty = 0;
+        c8.anchor = GridBagConstraints.WEST;
+        c8.fill = GridBagConstraints.VERTICAL;
+        c8.insets = new Insets(5, 5, 5, 5);
+        c8.ipadx = 0;
+        c8.ipady = 0;
+        panel.add(loadFileButton, c8);
+        GridBagConstraints c9 = new GridBagConstraints();
+        c9.gridx = 2;
+        c9.gridy = 4;
+        c9.gridwidth = 1;
+        c9.gridheight = 1;
+        c9.weightx = 0;
+        c9.weighty = 0;
+        c9.anchor = GridBagConstraints.WEST;
+        c9.fill = GridBagConstraints.VERTICAL;
+        c9.insets = new Insets(5, 5, 5, 5);
+        c9.ipadx = 0;
+        c9.ipady = 0;
+        panel.add(setDefaultsButton, c9);
+        saveFileButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                int result = fileChooser
+                        .showSaveDialog(ZooInspectorNodeViewersDialog.this);
+                if (result == JFileChooser.APPROVE_OPTION) {
+                    File selectedFile = fileChooser.getSelectedFile();
+                    int answer = JOptionPane.YES_OPTION;
+                    if (selectedFile.exists()) {
+                        answer = JOptionPane
+                                .showConfirmDialog(
+                                        ZooInspectorNodeViewersDialog.this,
+                                        "The specified file already exists.  do you want to overwrite it?",
+                                        "Confirm Overwrite",
+                                        JOptionPane.YES_NO_OPTION,
+                                        JOptionPane.WARNING_MESSAGE);
+                    }
+                    if (answer == JOptionPane.YES_OPTION) {
+                        DefaultListModel listModel = (DefaultListModel) viewersList
+                                .getModel();
+                        List<String> nodeViewersClassNames = new ArrayList<String>();
+                        Object[] modelContents = listModel.toArray();
+                        for (Object o : modelContents) {
+                            nodeViewersClassNames
+                                    .add(((ZooInspectorNodeViewer) o)
+                                            .getClass().getCanonicalName());
+                        }
+                        try {
+                            manager.saveNodeViewersFile(selectedFile,
+                                    nodeViewersClassNames);
+                        } catch (IOException ex) {
+                            LoggerFactory
+                                    .getLogger()
+                                    .error(
+                                            "Error saving node veiwer configuration from file.",
+                                            ex);
+                            JOptionPane.showMessageDialog(
+                                    ZooInspectorNodeViewersDialog.this,
+                                    "Error saving node veiwer configuration from file: "
+                                            + ex.getMessage(), "Error",
+                                    JOptionPane.ERROR_MESSAGE);
+                        }
+                    }
+                }
+            }
+        });
+        loadFileButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                int result = fileChooser
+                        .showOpenDialog(ZooInspectorNodeViewersDialog.this);
+                if (result == JFileChooser.APPROVE_OPTION) {
+                    try {
+                        List<String> nodeViewersClassNames = manager
+                                .loadNodeViewersFile(fileChooser
+                                        .getSelectedFile());
+                        List<ZooInspectorNodeViewer> nodeViewers = new ArrayList<ZooInspectorNodeViewer>();
+                        for (String nodeViewersClassName : nodeViewersClassNames) {
+                            ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class
+                                    .forName(nodeViewersClassName)
+                                    .newInstance();
+                            nodeViewers.add(viewer);
+                        }
+                        DefaultListModel model = new DefaultListModel();
+                        for (ZooInspectorNodeViewer viewer : nodeViewers) {
+                            model.addElement(viewer);
+                        }
+                        viewersList.setModel(model);
+                        panel.revalidate();
+                        panel.repaint();
+                    } catch (Exception ex) {
+                        LoggerFactory
+                                .getLogger()
+                                .error(
+                                        "Error loading node veiwer configuration from file.",
+                                        ex);
+                        JOptionPane.showMessageDialog(
+                                ZooInspectorNodeViewersDialog.this,
+                                "Error loading node veiwer configuration from file: "
+                                        + ex.getMessage(), "Error",
+                                JOptionPane.ERROR_MESSAGE);
+                    }
+                }
+            }
+        });
+        setDefaultsButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                int answer = JOptionPane
+                        .showConfirmDialog(
+                                ZooInspectorNodeViewersDialog.this,
+                                "Are you sure you want to save this configuration as the default?",
+                                "Confirm Set Defaults",
+                                JOptionPane.YES_NO_OPTION,
+                                JOptionPane.WARNING_MESSAGE);
+                if (answer == JOptionPane.YES_OPTION) {
+                    DefaultListModel listModel = (DefaultListModel) viewersList
+                            .getModel();
+                    List<String> nodeViewersClassNames = new ArrayList<String>();
+                    Object[] modelContents = listModel.toArray();
+                    for (Object o : modelContents) {
+                        nodeViewersClassNames.add(((ZooInspectorNodeViewer) o)
+                                .getClass().getCanonicalName());
+                    }
+                    try {
+                        manager
+                                .setDefaultNodeViewerConfiguration(nodeViewersClassNames);
+                    } catch (IOException ex) {
+                        LoggerFactory
+                                .getLogger()
+                                .error(
+                                        "Error setting default node veiwer configuration.",
+                                        ex);
+                        JOptionPane.showMessageDialog(
+                                ZooInspectorNodeViewersDialog.this,
+                                "Error setting default node veiwer configuration: "
+                                        + ex.getMessage(), "Error",
+                                JOptionPane.ERROR_MESSAGE);
+                    }
+                }
+            }
+        });
+
+        JPanel buttonsPanel = new JPanel();
+        buttonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
+        JButton okButton = new JButton("OK");
+        okButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                ZooInspectorNodeViewersDialog.this.dispose();
+                DefaultListModel listModel = (DefaultListModel) viewersList
+                        .getModel();
+                newViewers.clear();
+                Object[] modelContents = listModel.toArray();
+                for (Object o : modelContents) {
+                    newViewers.add((ZooInspectorNodeViewer) o);
+                }
+                currentViewers.clear();
+                currentViewers.addAll(newViewers);
+                for (NodeViewersChangeListener listener : listeners) {
+                    listener.nodeViewersChanged(currentViewers);
+                }
+            }
+        });
+        buttonsPanel.add(okButton);
+        JButton cancelButton = new JButton("Cancel");
+        cancelButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                ZooInspectorNodeViewersDialog.this.dispose();
+            }
+        });
+        buttonsPanel.add(cancelButton);
+        this.add(panel, BorderLayout.CENTER);
+        this.add(buttonsPanel, BorderLayout.SOUTH);
+        this.pack();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event
+     * .ListSelectionEvent)
+     */
+    public void valueChanged(ListSelectionEvent e) {
+        int index = viewersList.getSelectedIndex();
+        if (index == -1) {
+            removeButton.setEnabled(false);
+            upButton.setEnabled(false);
+            downButton.setEnabled(false);
+        } else {
+            removeButton.setEnabled(true);
+            if (index == 0) {
+                upButton.setEnabled(false);
+            } else {
+                upButton.setEnabled(true);
+            }
+            if (index == ((DefaultListModel) viewersList.getModel()).getSize()) {
+                downButton.setEnabled(false);
+            } else {
+                downButton.setEnabled(true);
+            }
+        }
+    }
+}

+ 140 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java

@@ -0,0 +1,140 @@
+/**
+ * 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.zookeeper.inspector.gui;
+
+import java.awt.BorderLayout;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.TreePath;
+
+import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer;
+import org.apache.zookeeper.inspector.manager.ZooInspectorManager;
+import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager;
+
+/**
+ * This is the {@link JPanel} which contains the {@link ZooInspectorNodeViewer}s
+ */
+public class ZooInspectorNodeViewersPanel extends JPanel implements
+        TreeSelectionListener, ChangeListener {
+
+    private final List<ZooInspectorNodeViewer> nodeVeiwers = new ArrayList<ZooInspectorNodeViewer>();
+    private final List<Boolean> needsReload = new ArrayList<Boolean>();
+    private final JTabbedPane tabbedPane;
+    private final List<String> selectedNodes = new ArrayList<String>();
+    private final ZooInspectorNodeManager zooInspectorManager;
+
+    /**
+     * @param zooInspectorManager
+     *            - the {@link ZooInspectorManager} for the application
+     * @param nodeVeiwers
+     *            - the {@link ZooInspectorNodeViewer}s to show
+     */
+    public ZooInspectorNodeViewersPanel(
+            ZooInspectorNodeManager zooInspectorManager,
+            List<ZooInspectorNodeViewer> nodeVeiwers) {
+        this.zooInspectorManager = zooInspectorManager;
+        this.setLayout(new BorderLayout());
+        tabbedPane = new JTabbedPane(JTabbedPane.TOP,
+                JTabbedPane.WRAP_TAB_LAYOUT);
+        setNodeViewers(nodeVeiwers);
+        tabbedPane.addChangeListener(this);
+        this.add(tabbedPane, BorderLayout.CENTER);
+        reloadSelectedViewer();
+    }
+
+    /**
+     * @param nodeViewers
+     *            - the {@link ZooInspectorNodeViewer}s to show
+     */
+    public void setNodeViewers(List<ZooInspectorNodeViewer> nodeViewers) {
+        this.nodeVeiwers.clear();
+        this.nodeVeiwers.addAll(nodeViewers);
+        needsReload.clear();
+        tabbedPane.removeAll();
+        for (ZooInspectorNodeViewer nodeViewer : nodeVeiwers) {
+            nodeViewer.setZooInspectorManager(zooInspectorManager);
+            needsReload.add(true);
+            tabbedPane.add(nodeViewer.getTitle(), nodeViewer);
+        }
+        this.revalidate();
+        this.repaint();
+    }
+
+    private void reloadSelectedViewer() {
+        int index = this.tabbedPane.getSelectedIndex();
+        if (index != -1 && this.needsReload.get(index)) {
+            ZooInspectorNodeViewer viewer = this.nodeVeiwers.get(index);
+            viewer.nodeSelectionChanged(selectedNodes);
+            this.needsReload.set(index, false);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event
+     * .TreeSelectionEvent)
+     */
+    public void valueChanged(TreeSelectionEvent e) {
+        TreePath[] paths = e.getPaths();
+        selectedNodes.clear();
+        for (TreePath path : paths) {
+            boolean appended = false;
+            StringBuilder sb = new StringBuilder();
+            Object[] pathArray = path.getPath();
+            for (Object o : pathArray) {
+                if (o != null) {
+                    String nodeName = o.toString();
+                    if (nodeName != null) {
+                        if (nodeName.length() > 0) {
+                            appended = true;
+                            sb.append("/"); //$NON-NLS-1$
+                            sb.append(o.toString());
+                        }
+                    }
+                }
+            }
+            if (appended) {
+                selectedNodes.add(sb.toString());
+            }
+        }
+        for (int i = 0; i < needsReload.size(); i++) {
+            this.needsReload.set(i, true);
+        }
+        reloadSelectedViewer();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent
+     * )
+     */
+    public void stateChanged(ChangeEvent e) {
+        reloadSelectedViewer();
+    }
+}

+ 361 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java

@@ -0,0 +1,361 @@
+/**
+ * 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.zookeeper.inspector.gui;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JToolBar;
+import javax.swing.SwingWorker;
+
+import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer;
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+import org.apache.zookeeper.inspector.manager.ZooInspectorManager;
+
+/**
+ * The parent {@link JPanel} for the whole application
+ */
+public class ZooInspectorPanel extends JPanel implements
+        NodeViewersChangeListener {
+    private final JButton refreshButton;
+    private final JButton disconnectButton;
+    private final JButton connectButton;
+    private final ZooInspectorNodeViewersPanel nodeViewersPanel;
+    private final ZooInspectorTreeViewer treeViewer;
+    private final ZooInspectorManager zooInspectorManager;
+    private final JButton addNodeButton;
+    private final JButton deleteNodeButton;
+    private final JButton nodeViewersButton;
+    private final JButton aboutButton;
+    private final List<NodeViewersChangeListener> listeners = new ArrayList<NodeViewersChangeListener>();
+    {
+        listeners.add(this);
+    }
+
+    /**
+     * @param zooInspectorManager
+     *            - the {@link ZooInspectorManager} for the application
+     */
+    public ZooInspectorPanel(final ZooInspectorManager zooInspectorManager) {
+        this.zooInspectorManager = zooInspectorManager;
+        final ArrayList<ZooInspectorNodeViewer> nodeViewers = new ArrayList<ZooInspectorNodeViewer>();
+        try {
+            List<String> defaultNodeViewersClassNames = this.zooInspectorManager
+                    .getDefaultNodeViewerConfiguration();
+            for (String className : defaultNodeViewersClassNames) {
+                nodeViewers.add((ZooInspectorNodeViewer) Class.forName(
+                        className).newInstance());
+            }
+        } catch (Exception ex) {
+            LoggerFactory.getLogger().error(
+                    "Error loading default node viewers.", ex);
+            JOptionPane.showMessageDialog(ZooInspectorPanel.this,
+                    "Error loading default node viewers: " + ex.getMessage(),
+                    "Error", JOptionPane.ERROR_MESSAGE);
+        }
+        nodeViewersPanel = new ZooInspectorNodeViewersPanel(
+                zooInspectorManager, nodeViewers);
+        treeViewer = new ZooInspectorTreeViewer(zooInspectorManager,
+                nodeViewersPanel);
+        this.setLayout(new BorderLayout());
+        JToolBar toolbar = new JToolBar();
+        toolbar.setFloatable(false);
+        connectButton = new JButton(ZooInspectorIconResources.getConnectIcon());
+        disconnectButton = new JButton(ZooInspectorIconResources
+                .getDisconnectIcon());
+        refreshButton = new JButton(ZooInspectorIconResources.getRefreshIcon());
+        addNodeButton = new JButton(ZooInspectorIconResources.getAddNodeIcon());
+        deleteNodeButton = new JButton(ZooInspectorIconResources
+                .getDeleteNodeIcon());
+        nodeViewersButton = new JButton(ZooInspectorIconResources
+                .getChangeNodeViewersIcon());
+        aboutButton = new JButton(ZooInspectorIconResources
+                .getInformationIcon());
+        toolbar.add(connectButton);
+        toolbar.add(disconnectButton);
+        toolbar.add(refreshButton);
+        toolbar.add(addNodeButton);
+        toolbar.add(deleteNodeButton);
+        toolbar.add(nodeViewersButton);
+        toolbar.add(aboutButton);
+        aboutButton.setEnabled(true);
+        connectButton.setEnabled(true);
+        disconnectButton.setEnabled(false);
+        refreshButton.setEnabled(false);
+        addNodeButton.setEnabled(false);
+        deleteNodeButton.setEnabled(false);
+        nodeViewersButton.setEnabled(true);
+        nodeViewersButton.setToolTipText("Change Node Viewers");
+        aboutButton.setToolTipText("About ZooInspector");
+        connectButton.setToolTipText("Connect");
+        disconnectButton.setToolTipText("Disconnect");
+        refreshButton.setToolTipText("Refresh");
+        addNodeButton.setToolTipText("Add Node");
+        deleteNodeButton.setToolTipText("Delete Node");
+        connectButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                ZooInspectorConnectionPropertiesDialog zicpd = new ZooInspectorConnectionPropertiesDialog(
+                        zooInspectorManager.getLastConnectionProps(),
+                        zooInspectorManager.getConnectionPropertiesTemplate(),
+                        ZooInspectorPanel.this);
+                zicpd.setVisible(true);
+            }
+        });
+        disconnectButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                disconnect();
+            }
+        });
+        refreshButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                treeViewer.refreshView();
+            }
+        });
+        addNodeButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                final List<String> selectedNodes = treeViewer
+                        .getSelectedNodes();
+                if (selectedNodes.size() == 1) {
+                    final String nodeName = JOptionPane.showInputDialog(
+                            ZooInspectorPanel.this,
+                            "Please Enter a name for the new node",
+                            "Create Node", JOptionPane.INFORMATION_MESSAGE);
+                    if (nodeName != null && nodeName.length() > 0) {
+                        SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
+
+                            @Override
+                            protected Boolean doInBackground() throws Exception {
+                                return ZooInspectorPanel.this.zooInspectorManager
+                                        .createNode(selectedNodes.get(0),
+                                                nodeName);
+                            }
+
+                            @Override
+                            protected void done() {
+                                treeViewer.refreshView();
+                            }
+                        };
+                        worker.execute();
+                    }
+                } else {
+                    JOptionPane.showMessageDialog(ZooInspectorPanel.this,
+                            "Please select 1 parent node for the new node.");
+                }
+            }
+        });
+        deleteNodeButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                final List<String> selectedNodes = treeViewer
+                        .getSelectedNodes();
+                if (selectedNodes.size() == 0) {
+                    JOptionPane.showMessageDialog(ZooInspectorPanel.this,
+                            "Please select at least 1 node to be deleted");
+                } else {
+                    int answer = JOptionPane.showConfirmDialog(
+                            ZooInspectorPanel.this,
+                            "Are you sure you want to delete the selected nodes?"
+                                    + "(This action cannot be reverted)",
+                            "Confirm Delete", JOptionPane.YES_NO_OPTION,
+                            JOptionPane.WARNING_MESSAGE);
+                    if (answer == JOptionPane.YES_OPTION) {
+                        SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
+
+                            @Override
+                            protected Boolean doInBackground() throws Exception {
+                                for (String nodePath : selectedNodes) {
+                                    ZooInspectorPanel.this.zooInspectorManager
+                                            .deleteNode(nodePath);
+                                }
+                                return true;
+                            }
+
+                            @Override
+                            protected void done() {
+                                treeViewer.refreshView();
+                            }
+                        };
+                        worker.execute();
+                    }
+                }
+            }
+        });
+        nodeViewersButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                ZooInspectorNodeViewersDialog nvd = new ZooInspectorNodeViewersDialog(
+                        JOptionPane.getRootFrame(), nodeViewers, listeners,
+                        zooInspectorManager);
+                nvd.setVisible(true);
+            }
+        });
+        aboutButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                ZooInspectorAboutDialog zicpd = new ZooInspectorAboutDialog(
+                        JOptionPane.getRootFrame());
+                zicpd.setVisible(true);
+            }
+        });
+        JScrollPane treeScroller = new JScrollPane(treeViewer);
+        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
+                treeScroller, nodeViewersPanel);
+        splitPane.setResizeWeight(0.25);
+        this.add(splitPane, BorderLayout.CENTER);
+        this.add(toolbar, BorderLayout.NORTH);
+    }
+
+    /**
+     * @param connectionProps
+     *            the {@link Properties} for connecting to the zookeeper
+     *            instance
+     */
+    public void connect(final Properties connectionProps) {
+        SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
+
+            @Override
+            protected Boolean doInBackground() throws Exception {
+                zooInspectorManager.setLastConnectionProps(connectionProps);
+                return zooInspectorManager.connect(connectionProps);
+            }
+
+            @Override
+            protected void done() {
+                try {
+                    if (get()) {
+                        treeViewer.refreshView();
+                        connectButton.setEnabled(false);
+                        disconnectButton.setEnabled(true);
+                        refreshButton.setEnabled(true);
+                        addNodeButton.setEnabled(true);
+                        deleteNodeButton.setEnabled(true);
+                    } else {
+                        JOptionPane.showMessageDialog(ZooInspectorPanel.this,
+                                "Unable to connect to zookeeper", "Error",
+                                JOptionPane.ERROR_MESSAGE);
+                    }
+                } catch (InterruptedException e) {
+                    LoggerFactory
+                            .getLogger()
+                            .error(
+                                    "Error occurred while connecting to ZooKeeper server",
+                                    e);
+                } catch (ExecutionException e) {
+                    LoggerFactory
+                            .getLogger()
+                            .error(
+                                    "Error occurred while connecting to ZooKeeper server",
+                                    e);
+                }
+            }
+
+        };
+        worker.execute();
+    }
+
+    /**
+	 * 
+	 */
+    public void disconnect() {
+        disconnect(false);
+    }
+
+    /**
+     * @param wait
+     *            - set this to true if the method should only return once the
+     *            application has successfully disconnected
+     */
+    public void disconnect(boolean wait) {
+        SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
+
+            @Override
+            protected Boolean doInBackground() throws Exception {
+                return ZooInspectorPanel.this.zooInspectorManager.disconnect();
+            }
+
+            @Override
+            protected void done() {
+                try {
+                    if (get()) {
+                        treeViewer.clearView();
+                        connectButton.setEnabled(true);
+                        disconnectButton.setEnabled(false);
+                        refreshButton.setEnabled(false);
+                        addNodeButton.setEnabled(false);
+                        deleteNodeButton.setEnabled(false);
+                    }
+                } catch (InterruptedException e) {
+                    LoggerFactory
+                            .getLogger()
+                            .error(
+                                    "Error occurred while disconnecting from ZooKeeper server",
+                                    e);
+                } catch (ExecutionException e) {
+                    LoggerFactory
+                            .getLogger()
+                            .error(
+                                    "Error occurred while disconnecting from ZooKeeper server",
+                                    e);
+                }
+            }
+
+        };
+        worker.execute();
+        if (wait) {
+            while (!worker.isDone()) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    LoggerFactory
+                            .getLogger()
+                            .error(
+                                    "Error occurred while disconnecting from ZooKeeper server",
+                                    e);
+                }
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.gui.NodeViewersChangeListener#
+     * nodeViewersChanged(java.util.List)
+     */
+    public void nodeViewersChanged(List<ZooInspectorNodeViewer> newViewers) {
+        this.nodeViewersPanel.setNodeViewers(newViewers);
+    }
+
+    /**
+     * @param connectionProps
+     * @throws IOException
+     */
+    public void setdefaultConnectionProps(Properties connectionProps)
+            throws IOException {
+        this.zooInspectorManager.saveDefaultConnectionFile(connectionProps);
+    }
+}

+ 362 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java

@@ -0,0 +1,362 @@
+/**
+ * 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.zookeeper.inspector.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.SwingWorker;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import org.apache.zookeeper.inspector.manager.NodeListener;
+import org.apache.zookeeper.inspector.manager.ZooInspectorManager;
+
+import com.nitido.utils.toaster.Toaster;
+
+/**
+ * A {@link JPanel} for showing the tree view of all the nodes in the zookeeper
+ * instance
+ */
+public class ZooInspectorTreeViewer extends JPanel implements NodeListener {
+    private final ZooInspectorManager zooInspectorManager;
+    private final JTree tree;
+    private final Toaster toasterManager;
+
+    /**
+     * @param zooInspectorManager
+     *            - the {@link ZooInspectorManager} for the application
+     * @param listener
+     *            - the {@link TreeSelectionListener} to listen for changes in
+     *            the selected node on the node tree
+     */
+    public ZooInspectorTreeViewer(
+            final ZooInspectorManager zooInspectorManager,
+            TreeSelectionListener listener) {
+        this.zooInspectorManager = zooInspectorManager;
+        this.setLayout(new BorderLayout());
+        final JPopupMenu popupMenu = new JPopupMenu();
+        final JMenuItem addNotify = new JMenuItem("Add Change Notification");
+        this.toasterManager = new Toaster();
+        this.toasterManager.setBorderColor(Color.BLACK);
+        this.toasterManager.setMessageColor(Color.BLACK);
+        this.toasterManager.setToasterColor(Color.WHITE);
+        addNotify.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                List<String> selectedNodes = getSelectedNodes();
+                zooInspectorManager.addWatchers(selectedNodes,
+                        ZooInspectorTreeViewer.this);
+            }
+        });
+        final JMenuItem removeNotify = new JMenuItem(
+                "Remove Change Notification");
+        removeNotify.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                List<String> selectedNodes = getSelectedNodes();
+                zooInspectorManager.removeWatchers(selectedNodes);
+            }
+        });
+        tree = new JTree(new DefaultMutableTreeNode());
+        tree.setCellRenderer(new ZooInspectorTreeCellRenderer());
+        tree.setEditable(false);
+        tree.getSelectionModel().addTreeSelectionListener(listener);
+        tree.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3) {
+                    // TODO only show add if a selected node isn't being
+                    // watched, and only show remove if a selected node is being
+                    // watched
+                    popupMenu.removeAll();
+                    popupMenu.add(addNotify);
+                    popupMenu.add(removeNotify);
+                    popupMenu.show(ZooInspectorTreeViewer.this, e.getX(), e
+                            .getY());
+                }
+            }
+        });
+        this.add(tree, BorderLayout.CENTER);
+    }
+
+    /**
+     * Refresh the tree view
+     */
+    public void refreshView() {
+        final Set<TreePath> expandedNodes = new LinkedHashSet<TreePath>();
+        int rowCount = tree.getRowCount();
+        for (int i = 0; i < rowCount; i++) {
+            TreePath path = tree.getPathForRow(i);
+            if (tree.isExpanded(path)) {
+                expandedNodes.add(path);
+            }
+        }
+        final TreePath[] selectedNodes = tree.getSelectionPaths();
+        SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
+
+            @Override
+            protected Boolean doInBackground() throws Exception {
+                tree.setModel(new DefaultTreeModel(new ZooInspectorTreeNode(
+                        "/", null)));
+                return true;
+            }
+
+            @Override
+            protected void done() {
+                for (TreePath path : expandedNodes) {
+                    tree.expandPath(path);
+                }
+                tree.getSelectionModel().setSelectionPaths(selectedNodes);
+            }
+        };
+        worker.execute();
+    }
+
+    /**
+     * clear the tree view of all nodes
+     */
+    public void clearView() {
+        tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode()));
+    }
+
+    /**
+     * @author Colin
+     * 
+     */
+    private static class ZooInspectorTreeCellRenderer extends
+            DefaultTreeCellRenderer {
+        public ZooInspectorTreeCellRenderer() {
+            setLeafIcon(ZooInspectorIconResources.getTreeLeafIcon());
+            setOpenIcon(ZooInspectorIconResources.getTreeOpenIcon());
+            setClosedIcon(ZooInspectorIconResources.getTreeClosedIcon());
+        }
+    }
+
+    /**
+     * @author Colin
+     * 
+     */
+    private class ZooInspectorTreeNode implements TreeNode {
+        private final String nodePath;
+        private final String nodeName;
+        private final ZooInspectorTreeNode parent;
+
+        public ZooInspectorTreeNode(String nodePath, ZooInspectorTreeNode parent) {
+            this.parent = parent;
+            this.nodePath = nodePath;
+            int index = nodePath.lastIndexOf("/");
+            if (index == -1) {
+                throw new IllegalArgumentException("Invalid node path"
+                        + nodePath);
+            }
+            this.nodeName = nodePath.substring(index + 1);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see javax.swing.tree.TreeNode#children()
+         */
+        public Enumeration<TreeNode> children() {
+            List<String> children = zooInspectorManager
+                    .getChildren(this.nodePath);
+            Collections.sort(children);
+            List<TreeNode> returnChildren = new ArrayList<TreeNode>();
+            for (String child : children) {
+                returnChildren.add(new ZooInspectorTreeNode((this.nodePath
+                        .equals("/") ? "" : this.nodePath)
+                        + "/" + child, this));
+            }
+            return Collections.enumeration(returnChildren);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see javax.swing.tree.TreeNode#getAllowsChildren()
+         */
+        public boolean getAllowsChildren() {
+            return zooInspectorManager.isAllowsChildren(this.nodePath);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see javax.swing.tree.TreeNode#getChildAt(int)
+         */
+        public TreeNode getChildAt(int childIndex) {
+            String child = zooInspectorManager.getNodeChild(this.nodePath,
+                    childIndex);
+            if (child != null) {
+                return new ZooInspectorTreeNode((this.nodePath.equals("/") ? ""
+                        : this.nodePath)
+                        + "/" + child, this);
+            }
+            return null;
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see javax.swing.tree.TreeNode#getChildCount()
+         */
+        public int getChildCount() {
+            return zooInspectorManager.getNumChildren(this.nodePath);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see javax.swing.tree.TreeNode#getIndex(javax.swing.tree.TreeNode)
+         */
+        public int getIndex(TreeNode node) {
+            return zooInspectorManager.getNodeIndex(this.nodePath);
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see javax.swing.tree.TreeNode#getParent()
+         */
+        public TreeNode getParent() {
+            return this.parent;
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see javax.swing.tree.TreeNode#isLeaf()
+         */
+        public boolean isLeaf() {
+            return !zooInspectorManager.hasChildren(this.nodePath);
+        }
+
+        @Override
+        public String toString() {
+            return this.nodeName;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + getOuterType().hashCode();
+            result = prime * result
+                    + ((nodePath == null) ? 0 : nodePath.hashCode());
+            result = prime * result
+                    + ((parent == null) ? 0 : parent.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            ZooInspectorTreeNode other = (ZooInspectorTreeNode) obj;
+            if (!getOuterType().equals(other.getOuterType()))
+                return false;
+            if (nodePath == null) {
+                if (other.nodePath != null)
+                    return false;
+            } else if (!nodePath.equals(other.nodePath))
+                return false;
+            if (parent == null) {
+                if (other.parent != null)
+                    return false;
+            } else if (!parent.equals(other.parent))
+                return false;
+            return true;
+        }
+
+        private ZooInspectorTreeViewer getOuterType() {
+            return ZooInspectorTreeViewer.this;
+        }
+
+    }
+
+    /**
+     * @return {@link List} of the currently selected nodes
+     */
+    public List<String> getSelectedNodes() {
+        TreePath[] paths = tree.getSelectionPaths();
+        List<String> selectedNodes = new ArrayList<String>();
+        if (paths != null) {
+            for (TreePath path : paths) {
+                StringBuilder sb = new StringBuilder();
+                Object[] pathArray = path.getPath();
+                for (Object o : pathArray) {
+                    String nodeName = o.toString();
+                    if (nodeName.length() > 0) {
+                        sb.append("/");
+                        sb.append(o.toString());
+                    }
+                }
+                selectedNodes.add(sb.toString());
+            }
+        }
+        return selectedNodes;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.NodeListener#processEvent(java
+     * .lang.String, java.lang.String, java.util.Map)
+     */
+    public void processEvent(String nodePath, String eventType,
+            Map<String, String> eventInfo) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Node: ");
+        sb.append(nodePath);
+        sb.append("\nEvent: ");
+        sb.append(eventType);
+        if (eventInfo != null) {
+            for (Map.Entry<String, String> entry : eventInfo.entrySet()) {
+                sb.append("\n");
+                sb.append(entry.getKey());
+                sb.append(": ");
+                sb.append(entry.getValue());
+            }
+        }
+        this.toasterManager.showToaster(ZooInspectorIconResources
+                .getInformationIcon(), sb.toString());
+    }
+}

+ 21 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html

@@ -0,0 +1,21 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>ZooInspector v0.1</title>
+</head>
+<body>
+<p>ZooInspector was developed by Colin Goodheart-Smithe and is
+available under the Apache Software Licence v2.0.</p>
+<p>The Icons used were sourced from the Eclipse project (<a
+	href="http://www.eclipse.org">http://www.eclipse.org</a>) and licensed
+under the Eclipse Public Licence v1.0. [<a
+	href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/org/documents/epl-v10.php</a>]
+</p>
+<p>ZooKeeper is available from <a
+	href="http://hadoop.apache.org/zookeeper/">http://hadoop.apache.org/zookeeper/</a>
+and is licensed under an Apache Software Licence v2.0</p>
+<p>The ApacheSoftware Licence v2.0 can be found at <a
+	href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a></p>
+</body>
+</html>

+ 187 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java

@@ -0,0 +1,187 @@
+/**
+ * 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.zookeeper.inspector.gui.nodeviewer;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.BorderFactory;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.SwingWorker;
+
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager;
+
+/**
+ * A node viewer for displaying the ACLs currently applied to the selected node
+ */
+public class NodeViewerACL extends ZooInspectorNodeViewer {
+    private ZooInspectorNodeManager zooInspectorManager;
+    private final JPanel aclDataPanel;
+    private String selectedNode;
+
+    /**
+	 * 
+	 */
+    public NodeViewerACL() {
+        this.setLayout(new BorderLayout());
+        this.aclDataPanel = new JPanel();
+        this.aclDataPanel.setBackground(Color.WHITE);
+        JScrollPane scroller = new JScrollPane(this.aclDataPanel);
+        this.add(scroller, BorderLayout.CENTER);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * getTitle()
+     */
+    @Override
+    public String getTitle() {
+        return "Node ACLs";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * nodeSelectionChanged(java.util.Set)
+     */
+    @Override
+    public void nodeSelectionChanged(List<String> selectedNodes) {
+        this.aclDataPanel.removeAll();
+        if (selectedNodes.size() > 0) {
+            this.selectedNode = selectedNodes.get(0);
+            SwingWorker<List<Map<String, String>>, Void> worker = new SwingWorker<List<Map<String, String>>, Void>() {
+
+                @Override
+                protected List<Map<String, String>> doInBackground()
+                        throws Exception {
+                    return NodeViewerACL.this.zooInspectorManager
+                            .getACLs(NodeViewerACL.this.selectedNode);
+                }
+
+                @Override
+                protected void done() {
+                    List<Map<String, String>> acls = null;
+                    try {
+                        acls = get();
+                    } catch (InterruptedException e) {
+                        acls = new ArrayList<Map<String, String>>();
+                        LoggerFactory.getLogger().error(
+                                "Error retrieving ACL Information for node: "
+                                        + NodeViewerACL.this.selectedNode, e);
+                    } catch (ExecutionException e) {
+                        acls = new ArrayList<Map<String, String>>();
+                        LoggerFactory.getLogger().error(
+                                "Error retrieving ACL Information for node: "
+                                        + NodeViewerACL.this.selectedNode, e);
+                    }
+                    aclDataPanel.setLayout(new GridBagLayout());
+                    int j = 0;
+                    for (Map<String, String> data : acls) {
+                        int rowPos = 2 * j + 1;
+                        JPanel aclPanel = new JPanel();
+                        aclPanel.setBorder(BorderFactory
+                                .createLineBorder(Color.BLACK));
+                        aclPanel.setBackground(Color.WHITE);
+                        aclPanel.setLayout(new GridBagLayout());
+                        int i = 0;
+                        for (Map.Entry<String, String> entry : data.entrySet()) {
+                            int rowPosACL = 2 * i + 1;
+                            JLabel label = new JLabel(entry.getKey());
+                            JTextField text = new JTextField(entry.getValue());
+                            text.setEditable(false);
+                            GridBagConstraints c1 = new GridBagConstraints();
+                            c1.gridx = 1;
+                            c1.gridy = rowPosACL;
+                            c1.gridwidth = 1;
+                            c1.gridheight = 1;
+                            c1.weightx = 0;
+                            c1.weighty = 0;
+                            c1.anchor = GridBagConstraints.NORTHWEST;
+                            c1.fill = GridBagConstraints.BOTH;
+                            c1.insets = new Insets(5, 5, 5, 5);
+                            c1.ipadx = 0;
+                            c1.ipady = 0;
+                            aclPanel.add(label, c1);
+                            GridBagConstraints c2 = new GridBagConstraints();
+                            c2.gridx = 3;
+                            c2.gridy = rowPosACL;
+                            c2.gridwidth = 1;
+                            c2.gridheight = 1;
+                            c2.weightx = 0;
+                            c2.weighty = 0;
+                            c2.anchor = GridBagConstraints.NORTHWEST;
+                            c2.fill = GridBagConstraints.BOTH;
+                            c2.insets = new Insets(5, 5, 5, 5);
+                            c2.ipadx = 0;
+                            c2.ipady = 0;
+                            aclPanel.add(text, c2);
+                            i++;
+                        }
+                        GridBagConstraints c = new GridBagConstraints();
+                        c.gridx = 1;
+                        c.gridy = rowPos;
+                        c.gridwidth = 1;
+                        c.gridheight = 1;
+                        c.weightx = 1;
+                        c.weighty = 1;
+                        c.anchor = GridBagConstraints.NORTHWEST;
+                        c.fill = GridBagConstraints.NONE;
+                        c.insets = new Insets(5, 5, 5, 5);
+                        c.ipadx = 0;
+                        c.ipady = 0;
+                        aclDataPanel.add(aclPanel, c);
+                    }
+                    NodeViewerACL.this.aclDataPanel.revalidate();
+                    NodeViewerACL.this.aclDataPanel.repaint();
+                }
+            };
+            worker.execute();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * setZooInspectorManager
+     * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager)
+     */
+    @Override
+    public void setZooInspectorManager(
+            ZooInspectorNodeManager zooInspectorManager) {
+        this.zooInspectorManager = zooInspectorManager;
+    }
+
+}

+ 146 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java

@@ -0,0 +1,146 @@
+/**
+ * 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.zookeeper.inspector.gui.nodeviewer;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.JToolBar;
+import javax.swing.SwingWorker;
+
+import org.apache.zookeeper.inspector.gui.ZooInspectorIconResources;
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager;
+
+/**
+ * A node viewer for displaying the data for the currently selected node
+ */
+public class NodeViewerData extends ZooInspectorNodeViewer {
+    private ZooInspectorNodeManager zooInspectorManager;
+    private final JTextPane dataArea;
+    private final JToolBar toolbar;
+    private String selectedNode;
+
+    /**
+	 * 
+	 */
+    public NodeViewerData() {
+        this.setLayout(new BorderLayout());
+        this.dataArea = new JTextPane();
+        this.toolbar = new JToolBar();
+        this.toolbar.setFloatable(false);
+        JScrollPane scroller = new JScrollPane(this.dataArea);
+        scroller
+                .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+        this.add(scroller, BorderLayout.CENTER);
+        this.add(this.toolbar, BorderLayout.NORTH);
+        JButton saveButton = new JButton(ZooInspectorIconResources
+                .getSaveIcon());
+        saveButton.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                if (selectedNode != null) {
+                    if (JOptionPane.showConfirmDialog(NodeViewerData.this,
+                            "Are you sure you want to save this node?"
+                                    + " (this action cannot be reverted)",
+                            "Confirm Save", JOptionPane.YES_NO_OPTION,
+                            JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) {
+                        zooInspectorManager.setData(selectedNode, dataArea
+                                .getText());
+                    }
+                }
+            }
+        });
+        this.toolbar.add(saveButton);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * getTitle()
+     */
+    @Override
+    public String getTitle() {
+        return "Node Data";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * nodeSelectionChanged(java.util.Set)
+     */
+    @Override
+    public void nodeSelectionChanged(List<String> selectedNodes) {
+        if (selectedNodes.size() > 0) {
+            this.selectedNode = selectedNodes.get(0);
+            SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
+
+                @Override
+                protected String doInBackground() throws Exception {
+                    return NodeViewerData.this.zooInspectorManager
+                            .getData(NodeViewerData.this.selectedNode);
+                }
+
+                @Override
+                protected void done() {
+                    String data = "";
+                    try {
+                        data = get();
+                    } catch (InterruptedException e) {
+                        LoggerFactory.getLogger().error(
+                                "Error retrieving data for node: "
+                                        + NodeViewerData.this.selectedNode, e);
+                    } catch (ExecutionException e) {
+                        LoggerFactory.getLogger().error(
+                                "Error retrieving data for node: "
+                                        + NodeViewerData.this.selectedNode, e);
+                    }
+                    NodeViewerData.this.dataArea.setText(data);
+                }
+            };
+            worker.execute();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * setZooInspectorManager
+     * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager)
+     */
+    @Override
+    public void setZooInspectorManager(
+            ZooInspectorNodeManager zooInspectorManager) {
+        this.zooInspectorManager = zooInspectorManager;
+    }
+
+}

+ 186 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java

@@ -0,0 +1,186 @@
+/**
+ * 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.zookeeper.inspector.gui.nodeviewer;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.SwingWorker;
+
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager;
+
+/**
+ * A node viewer for displaying the meta data for the currently selected node.
+ * The meta data is essentially the information from the {@link Stat} for the
+ * node
+ */
+public class NodeViewerMetaData extends ZooInspectorNodeViewer {
+    private ZooInspectorNodeManager zooInspectorManager;
+    private final JPanel metaDataPanel;
+    private String selectedNode;
+
+    /**
+	 * 
+	 */
+    public NodeViewerMetaData() {
+        this.setLayout(new BorderLayout());
+        this.metaDataPanel = new JPanel();
+        this.metaDataPanel.setBackground(Color.WHITE);
+        JScrollPane scroller = new JScrollPane(this.metaDataPanel);
+        this.add(scroller, BorderLayout.CENTER);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * getTitle()
+     */
+    @Override
+    public String getTitle() {
+        return "Node Metadata";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * nodeSelectionChanged(java.util.Set)
+     */
+    @Override
+    public void nodeSelectionChanged(List<String> selectedNodes) {
+        this.metaDataPanel.removeAll();
+        if (selectedNodes.size() > 0) {
+            this.selectedNode = selectedNodes.get(0);
+            SwingWorker<Map<String, String>, Void> worker = new SwingWorker<Map<String, String>, Void>() {
+
+                @Override
+                protected Map<String, String> doInBackground() throws Exception {
+                    return NodeViewerMetaData.this.zooInspectorManager
+                            .getNodeMeta(NodeViewerMetaData.this.selectedNode);
+                }
+
+                @Override
+                protected void done() {
+                    Map<String, String> data = null;
+                    try {
+                        data = get();
+                    } catch (InterruptedException e) {
+                        data = new HashMap<String, String>();
+                        LoggerFactory.getLogger().error(
+                                "Error retrieving meta data for node: "
+                                        + NodeViewerMetaData.this.selectedNode,
+                                e);
+                    } catch (ExecutionException e) {
+                        data = new HashMap<String, String>();
+                        LoggerFactory.getLogger().error(
+                                "Error retrieving meta data for node: "
+                                        + NodeViewerMetaData.this.selectedNode,
+                                e);
+                    }
+                    NodeViewerMetaData.this.metaDataPanel
+                            .setLayout(new GridBagLayout());
+                    JPanel infoPanel = new JPanel();
+                    infoPanel.setBackground(Color.WHITE);
+                    infoPanel.setLayout(new GridBagLayout());
+                    int i = 0;
+                    int rowPos = 0;
+                    for (Map.Entry<String, String> entry : data.entrySet()) {
+                        rowPos = 2 * i + 1;
+                        JLabel label = new JLabel(entry.getKey());
+                        JTextField text = new JTextField(entry.getValue());
+                        text.setEditable(false);
+                        GridBagConstraints c1 = new GridBagConstraints();
+                        c1.gridx = 0;
+                        c1.gridy = rowPos;
+                        c1.gridwidth = 1;
+                        c1.gridheight = 1;
+                        c1.weightx = 0;
+                        c1.weighty = 0;
+                        c1.anchor = GridBagConstraints.WEST;
+                        c1.fill = GridBagConstraints.HORIZONTAL;
+                        c1.insets = new Insets(5, 5, 5, 5);
+                        c1.ipadx = 0;
+                        c1.ipady = 0;
+                        infoPanel.add(label, c1);
+                        GridBagConstraints c2 = new GridBagConstraints();
+                        c2.gridx = 2;
+                        c2.gridy = rowPos;
+                        c2.gridwidth = 1;
+                        c2.gridheight = 1;
+                        c2.weightx = 0;
+                        c2.weighty = 0;
+                        c2.anchor = GridBagConstraints.WEST;
+                        c2.fill = GridBagConstraints.HORIZONTAL;
+                        c2.insets = new Insets(5, 5, 5, 5);
+                        c2.ipadx = 0;
+                        c2.ipady = 0;
+                        infoPanel.add(text, c2);
+                        i++;
+                    }
+                    GridBagConstraints c = new GridBagConstraints();
+                    c.gridx = 1;
+                    c.gridy = rowPos;
+                    c.gridwidth = 1;
+                    c.gridheight = 1;
+                    c.weightx = 1;
+                    c.weighty = 1;
+                    c.anchor = GridBagConstraints.NORTHWEST;
+                    c.fill = GridBagConstraints.NONE;
+                    c.insets = new Insets(5, 5, 5, 5);
+                    c.ipadx = 0;
+                    c.ipady = 0;
+                    NodeViewerMetaData.this.metaDataPanel.add(infoPanel, c);
+                    NodeViewerMetaData.this.metaDataPanel.revalidate();
+                    NodeViewerMetaData.this.metaDataPanel.repaint();
+                }
+            };
+            worker.execute();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer#
+     * setZooInspectorManager
+     * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager)
+     */
+    @Override
+    public void setZooInspectorManager(
+            ZooInspectorNodeManager zooInspectorManager) {
+        this.zooInspectorManager = zooInspectorManager;
+    }
+
+}

+ 138 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java

@@ -0,0 +1,138 @@
+/**
+ * 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.zookeeper.inspector.gui.nodeviewer;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.util.List;
+
+import javax.swing.JPanel;
+
+import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager;
+
+/**
+ * A {@link JPanel} for displaying information about the currently selected
+ * node(s)
+ */
+public abstract class ZooInspectorNodeViewer extends JPanel implements
+        Transferable {
+    /**
+     * The {@link DataFlavor} used for DnD in the node viewer configuration
+     * dialog
+     */
+    public static final DataFlavor nodeViewerDataFlavor = new DataFlavor(
+            ZooInspectorNodeViewer.class, "nodeviewer");
+
+    /**
+     * @param zooInspectorManager
+     */
+    public abstract void setZooInspectorManager(
+            ZooInspectorNodeManager zooInspectorManager);
+
+    /**
+     * Called whenever the selected nodes in the tree view changes.
+     * 
+     * @param selectedNodes
+     *            - the nodes currently selected in the tree view
+     * 
+     */
+    public abstract void nodeSelectionChanged(List<String> selectedNodes);
+
+    /**
+     * @return the title of the node viewer. this will be shown on the tab for
+     *         this node viewer.
+     */
+    public abstract String getTitle();
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer
+     * .DataFlavor)
+     */
+    public Object getTransferData(DataFlavor flavor)
+            throws UnsupportedFlavorException, IOException {
+        if (flavor.equals(nodeViewerDataFlavor)) {
+            return this.getClass().getCanonicalName();
+        } else {
+            return null;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.awt.datatransfer.Transferable#getTransferDataFlavors()
+     */
+    public DataFlavor[] getTransferDataFlavors() {
+        return new DataFlavor[] { nodeViewerDataFlavor };
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seejava.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt.
+     * datatransfer.DataFlavor)
+     */
+    public boolean isDataFlavorSupported(DataFlavor flavor) {
+        return flavor.equals(nodeViewerDataFlavor);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((getTitle() == null) ? 0 : getTitle().hashCode());
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ZooInspectorNodeViewer other = (ZooInspectorNodeViewer) obj;
+        if (getClass().getCanonicalName() != other.getClass()
+                .getCanonicalName()) {
+            return false;
+        }
+        if (getTitle() == null) {
+            if (other.getTitle() != null)
+                return false;
+        } else if (!getTitle().equals(other.getTitle()))
+            return false;
+        return true;
+    }
+}

+ 38 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java

@@ -0,0 +1,38 @@
+/**
+ * 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.zookeeper.inspector.logger;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Provides a {@link Logger} for use across the entire application
+ * 
+ */
+public class LoggerFactory
+{
+	private static final Logger logger = Logger.getLogger("org.apache.zookeeper.inspector"); //$NON-NLS-1$
+
+	/**
+	 * @return {@link Logger} for ZooInspector
+	 */
+	public static Logger getLogger()
+	{
+		return logger;
+	}
+
+}

+ 37 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java

@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.inspector.manager;
+
+import java.util.Map;
+
+/**
+ * A Listener for Events on zookeeper nodes
+ */
+public interface NodeListener {
+    /**
+     * @param nodePath
+     *            - the path of the node
+     * @param eventType
+     *            - the event type
+     * @param eventInfo
+     *            - a {@link Map} containing any other information about this
+     *            event
+     */
+    public void processEvent(String nodePath, String eventType,
+            Map<String, String> eventInfo);
+}

+ 120 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java

@@ -0,0 +1,120 @@
+/**
+ * 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.zookeeper.inspector.manager;
+
+/**
+ * A utility class for storing a pair of objects
+ * 
+ * @param <K>
+ * @param <V>
+ */
+public class Pair<K, V> {
+    private K key;
+    private V value;
+
+    /**
+     * @param key
+     * @param value
+     */
+    public Pair(K key, V value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    /**
+	 * 
+	 */
+    public Pair() {
+        // Do Nothing
+    }
+
+    /**
+     * @return key
+     */
+    public K getKey() {
+        return key;
+    }
+
+    /**
+     * @param key
+     */
+    public void setKey(K key) {
+        this.key = key;
+    }
+
+    /**
+     * @return value
+     */
+    public V getValue() {
+        return value;
+    }
+
+    /**
+     * @param value
+     */
+    public void setValue(V value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return "Pair [" + key + ", " + value + "]";
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((key == null) ? 0 : key.hashCode());
+        result = prime * result + ((value == null) ? 0 : value.hashCode());
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Pair<?, ?> other = (Pair<?, ?>) obj;
+        if (key == null) {
+            if (other.key != null)
+                return false;
+        } else if (!key.equals(other.key))
+            return false;
+        if (value == null) {
+            if (other.value != null)
+                return false;
+        } else if (!value.equals(other.value))
+            return false;
+        return true;
+    }
+
+}

+ 139 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java

@@ -0,0 +1,139 @@
+/**
+ * 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.zookeeper.inspector.manager;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.swing.JComboBox;
+import javax.swing.JTextField;
+
+/**
+ * A Manager for all interactions between the application and the Zookeeper
+ * instance
+ */
+public interface ZooInspectorManager extends ZooInspectorNodeManager,
+        ZooInspectorNodeTreeManager {
+
+    /**
+     * @param connectionProps
+     * @return true if successfully connected
+     */
+    public boolean connect(Properties connectionProps);
+
+    /**
+     * @return true if successfully disconnected
+     */
+    public boolean disconnect();
+
+    /**
+     * @return a {@link Pair} containing the following:
+     *         <ul>
+     *         <li>a {@link Map} of property keys to list of possible values. If
+     *         the list size is 1 the value is taken to be the default value for
+     *         a {@link JTextField}. If the list size is greater than 1, the
+     *         values are taken to be the possible options to show in a
+     *         {@link JComboBox} with the first selected as default.</li>
+     *         <li>a {@link Map} of property keys to the label to show on the UI
+     *         </li>
+     *         <ul>
+     * 
+     */
+    public Pair<Map<String, List<String>>, Map<String, String>> getConnectionPropertiesTemplate();
+
+    /**
+     * @param selectedNodes
+     *            - the nodes to add the watcher to
+     * @param nodeListener
+     *            - the node listener for this watcher
+     */
+    public void addWatchers(Collection<String> selectedNodes,
+            NodeListener nodeListener);
+
+    /**
+     * @param selectedNodes
+     *            - the nodes to remove the watchers from
+     */
+    public void removeWatchers(Collection<String> selectedNodes);
+
+    /**
+     * @param selectedFile
+     *            - the file to load which contains the node viewers
+     *            configuration
+     * @return nodeViewers - the class names of the node viewers from the
+     *         configuration
+     * @throws IOException
+     *             - if the configuration file cannot be loaded
+     */
+    public List<String> loadNodeViewersFile(File selectedFile)
+            throws IOException;
+
+    /**
+     * @param selectedFile
+     *            - the file to save the configuration to
+     * @param nodeViewersClassNames
+     *            - the class names of the node viewers
+     * @throws IOException
+     *             - if the configuration file cannot be saved
+     */
+    public void saveNodeViewersFile(File selectedFile,
+            List<String> nodeViewersClassNames) throws IOException;
+
+    /**
+     * @param nodeViewersClassNames
+     *            - the class names of the node viewers
+     * @throws IOException
+     *             - if the default configuration file cannot be loaded
+     */
+    public void setDefaultNodeViewerConfiguration(
+            List<String> nodeViewersClassNames) throws IOException;
+
+    /**
+     * @return nodeViewers - the class names of the node viewers from the
+     *         configuration
+     * @throws IOException
+     *             - if the default configuration file cannot be loaded
+     */
+    List<String> getDefaultNodeViewerConfiguration() throws IOException;
+
+    /**
+     * @param connectionProps
+     *            - the connection properties last used to connect to the
+     *            zookeeeper instance
+     */
+    public void setLastConnectionProps(Properties connectionProps);
+
+    /**
+     * @return last connection Properties - the connection properties last used
+     *         to connect to the zookeeeper instance
+     */
+    public Properties getLastConnectionProps();
+
+    /**
+     * @param props
+     *            - the properties to use as the default connection settings
+     * @throws IOException
+     *             - if the default configuration file cannot be saved
+     */
+    public void saveDefaultConnectionFile(Properties props) throws IOException;
+
+}

+ 852 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java

@@ -0,0 +1,852 @@
+/**
+ * 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.zookeeper.inspector.manager;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.Watcher.Event.EventType;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooDefs.Perms;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager;
+import org.apache.zookeeper.inspector.encryption.DataEncryptionManager;
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+import org.apache.zookeeper.retry.ZooKeeperRetry;
+
+/**
+ * A default implementation of {@link ZooInspectorManager} for connecting to
+ * zookeeper instances
+ */
+public class ZooInspectorManagerImpl implements ZooInspectorManager {
+    private static final String A_VERSION = "ACL Version";
+    private static final String C_TIME = "Creation Time";
+    private static final String C_VERSION = "Children Version";
+    private static final String CZXID = "Creation ID";
+    private static final String DATA_LENGTH = "Data Length";
+    private static final String EPHEMERAL_OWNER = "Ephemeral Owner";
+    private static final String M_TIME = "Last Modified Time";
+    private static final String MZXID = "Modified ID";
+    private static final String NUM_CHILDREN = "Number of Children";
+    private static final String PZXID = "Node ID";
+    private static final String VERSION = "Data Version";
+    private static final String ACL_PERMS = "Permissions";
+    private static final String ACL_SCHEME = "Scheme";
+    private static final String ACL_ID = "Id";
+    private static final String SESSION_STATE = "Session State";
+    private static final String SESSION_ID = "Session ID";
+    /**
+     * The key used for the connect string in the connection properties file
+     */
+    public static final String CONNECT_STRING = "hosts";
+    /**
+     * The key used for the session timeout in the connection properties file
+     */
+    public static final String SESSION_TIMEOUT = "timeout";
+    /**
+     * The key used for the data encryption manager in the connection properties
+     * file
+     */
+    public static final String DATA_ENCRYPTION_MANAGER = "encryptionManager";
+
+    private static final File defaultNodeViewersFile = new File(
+            "./config/defaultNodeVeiwers.cfg");
+    private static final File defaultConnectionFile = new File(
+            "./config/defaultConnectionSettings.cfg");
+
+    private DataEncryptionManager encryptionManager;
+    private String connectString;
+    private int sessionTimeout;
+    private ZooKeeper zooKeeper;
+    private final Map<String, NodeWatcher> watchers = new HashMap<String, NodeWatcher>();
+    protected boolean connected = true;
+    private Properties lastConnectionProps;
+    private String defaultEncryptionManager;
+    private String defaultTimeout;
+    private String defaultHosts;
+
+    /**
+     * @throws IOException
+     *             - thrown if the default connection settings cannot be loaded
+     * 
+     */
+    public ZooInspectorManagerImpl() throws IOException {
+        loadDefaultConnectionFile();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorManager#connect(java
+     * .util.Properties)
+     */
+    public boolean connect(Properties connectionProps) {
+        try {
+            if (this.zooKeeper == null) {
+                String connectString = connectionProps
+                        .getProperty(CONNECT_STRING);
+                String sessionTimeout = connectionProps
+                        .getProperty(SESSION_TIMEOUT);
+                String encryptionManager = connectionProps
+                        .getProperty(DATA_ENCRYPTION_MANAGER);
+                if (connectString == null || sessionTimeout == null) {
+                    throw new IllegalArgumentException(
+                            "Both connect string and session timeout are required.");
+                }
+                if (encryptionManager == null) {
+                    this.encryptionManager = new BasicDataEncryptionManager();
+                } else {
+                    Class<?> clazz = Class.forName(encryptionManager);
+
+                    if (Arrays.asList(clazz.getInterfaces()).contains(
+                            DataEncryptionManager.class)) {
+                        this.encryptionManager = (DataEncryptionManager) Class
+                                .forName(encryptionManager).newInstance();
+                    } else {
+                        throw new IllegalArgumentException(
+                                "Data encryption manager must implement DataEncryptionManager interface");
+                    }
+                }
+                this.connectString = connectString;
+                this.sessionTimeout = Integer.valueOf(sessionTimeout);
+                this.zooKeeper = new ZooKeeperRetry(connectString, Integer
+                        .valueOf(sessionTimeout), new Watcher() {
+
+                    public void process(WatchedEvent event) {
+                        if (event.getState() == KeeperState.Expired) {
+                            connected = false;
+                        }
+                    }
+                });
+                ((ZooKeeperRetry) this.zooKeeper).setRetryLimit(10);
+                connected = ((ZooKeeperRetry) this.zooKeeper).testConnection();
+                return connected;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        connected = false;
+        return connected;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorManager#disconnect()
+     */
+    public boolean disconnect() {
+        try {
+            if (this.zooKeeper != null) {
+                this.zooKeeper.close();
+                this.zooKeeper = null;
+                connected = false;
+                removeWatchers(this.watchers.keySet());
+                return true;
+            }
+        } catch (Exception e) {
+            LoggerFactory.getLogger().error(
+                    "Error occurred while disconnecting from ZooKeeper server",
+                    e);
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
+     * getChildren(java.lang.String)
+     */
+    public List<String> getChildren(String nodePath) {
+        if (connected) {
+            try {
+
+                return zooKeeper.getChildren(nodePath, false);
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred retrieving children of node: "
+                                + nodePath, e);
+            }
+        }
+        return null;
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getData
+     * (java.lang.String)
+     */
+    public String getData(String nodePath) {
+        if (connected) {
+            try {
+                if (nodePath.length() == 0) {
+                    nodePath = "/";
+                }
+                Stat s = zooKeeper.exists(nodePath, false);
+                if (s != null) {
+                    return this.encryptionManager.decryptData(zooKeeper
+                            .getData(nodePath, false, s));
+                }
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred getting data for node: " + nodePath, e);
+            }
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
+     * getNodeChild(java.lang.String, int)
+     */
+    public String getNodeChild(String nodePath, int childIndex) {
+        if (connected) {
+            try {
+                Stat s = zooKeeper.exists(nodePath, false);
+                if (s != null) {
+                    return this.zooKeeper.getChildren(nodePath, false).get(
+                            childIndex);
+                }
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred retrieving child " + childIndex
+                                + " of node: " + nodePath, e);
+            }
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
+     * getNodeIndex(java.lang.String)
+     */
+    public int getNodeIndex(String nodePath) {
+        if (connected) {
+            int index = nodePath.lastIndexOf("/");
+            if (index == -1
+                    || (!nodePath.equals("/") && nodePath.charAt(nodePath
+                            .length() - 1) == '/')) {
+                throw new IllegalArgumentException("Invalid node path: "
+                        + nodePath);
+            }
+            String parentPath = nodePath.substring(0, index);
+            String child = nodePath.substring(index + 1);
+            if (parentPath != null && parentPath.length() > 0) {
+                List<String> children = this.getChildren(parentPath);
+                if (children != null) {
+                    return children.indexOf(child);
+                }
+            }
+        }
+        return -1;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getACLs
+     * (java.lang.String)
+     */
+    public List<Map<String, String>> getACLs(String nodePath) {
+        List<Map<String, String>> returnACLs = new ArrayList<Map<String, String>>();
+        if (connected) {
+            try {
+                if (nodePath.length() == 0) {
+                    nodePath = "/";
+                }
+                Stat s = zooKeeper.exists(nodePath, false);
+                if (s != null) {
+                    List<ACL> acls = zooKeeper.getACL(nodePath, s);
+                    for (ACL acl : acls) {
+                        Map<String, String> aclMap = new LinkedHashMap<String, String>();
+                        aclMap.put(ACL_SCHEME, acl.getId().getScheme());
+                        aclMap.put(ACL_ID, acl.getId().getId());
+                        StringBuilder sb = new StringBuilder();
+                        int perms = acl.getPerms();
+                        boolean addedPerm = false;
+                        if ((perms & Perms.READ) == Perms.READ) {
+                            sb.append("Read");
+                            addedPerm = true;
+                        }
+                        if (addedPerm) {
+                            sb.append(", ");
+                        }
+                        if ((perms & Perms.WRITE) == Perms.WRITE) {
+                            sb.append("Write");
+                            addedPerm = true;
+                        }
+                        if (addedPerm) {
+                            sb.append(", ");
+                        }
+                        if ((perms & Perms.CREATE) == Perms.CREATE) {
+                            sb.append("Create");
+                            addedPerm = true;
+                        }
+                        if (addedPerm) {
+                            sb.append(", ");
+                        }
+                        if ((perms & Perms.DELETE) == Perms.DELETE) {
+                            sb.append("Delete");
+                            addedPerm = true;
+                        }
+                        if (addedPerm) {
+                            sb.append(", ");
+                        }
+                        if ((perms & Perms.ADMIN) == Perms.ADMIN) {
+                            sb.append("Admin");
+                            addedPerm = true;
+                        }
+                        aclMap.put(ACL_PERMS, sb.toString());
+                        returnACLs.add(aclMap);
+                    }
+                }
+            } catch (InterruptedException e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred retrieving ACLs of node: " + nodePath,
+                        e);
+            } catch (KeeperException e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred retrieving ACLs of node: " + nodePath,
+                        e);
+            }
+        }
+        return returnACLs;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
+     * getNodeMeta(java.lang.String)
+     */
+    public Map<String, String> getNodeMeta(String nodePath) {
+        Map<String, String> nodeMeta = new LinkedHashMap<String, String>();
+        if (connected) {
+            try {
+                if (nodePath.length() == 0) {
+                    nodePath = "/";
+                }
+                Stat s = zooKeeper.exists(nodePath, false);
+                if (s != null) {
+                    nodeMeta.put(A_VERSION, String.valueOf(s.getAversion()));
+                    nodeMeta.put(C_TIME, String.valueOf(s.getCtime()));
+                    nodeMeta.put(C_VERSION, String.valueOf(s.getCversion()));
+                    nodeMeta.put(CZXID, String.valueOf(s.getCzxid()));
+                    nodeMeta
+                            .put(DATA_LENGTH, String.valueOf(s.getDataLength()));
+                    nodeMeta.put(EPHEMERAL_OWNER, String.valueOf(s
+                            .getEphemeralOwner()));
+                    nodeMeta.put(M_TIME, String.valueOf(s.getMtime()));
+                    nodeMeta.put(MZXID, String.valueOf(s.getMzxid()));
+                    nodeMeta.put(NUM_CHILDREN, String.valueOf(s
+                            .getNumChildren()));
+                    nodeMeta.put(PZXID, String.valueOf(s.getPzxid()));
+                    nodeMeta.put(VERSION, String.valueOf(s.getVersion()));
+                }
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred retrieving meta data for node: "
+                                + nodePath, e);
+            }
+        }
+        return nodeMeta;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
+     * getNumChildren(java.lang.String)
+     */
+    public int getNumChildren(String nodePath) {
+        if (connected) {
+            try {
+                Stat s = zooKeeper.exists(nodePath, false);
+                if (s != null) {
+                    return s.getNumChildren();
+                }
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred getting the number of children of node: "
+                                + nodePath, e);
+            }
+        }
+        return -1;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
+     * hasChildren(java.lang.String)
+     */
+    public boolean hasChildren(String nodePath) {
+        return getNumChildren(nodePath) > 0;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
+     * isAllowsChildren(java.lang.String)
+     */
+    public boolean isAllowsChildren(String nodePath) {
+        if (connected) {
+            try {
+                Stat s = zooKeeper.exists(nodePath, false);
+                if (s != null) {
+                    return s.getEphemeralOwner() == 0;
+                }
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred determining whether node is allowed children: "
+                                + nodePath, e);
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
+     * getSessionMeta()
+     */
+    public Map<String, String> getSessionMeta() {
+        Map<String, String> sessionMeta = new LinkedHashMap<String, String>();
+        try {
+            if (zooKeeper != null) {
+
+                sessionMeta.put(SESSION_ID, String.valueOf(zooKeeper
+                        .getSessionId()));
+                sessionMeta.put(SESSION_STATE, String.valueOf(zooKeeper
+                        .getState().toString()));
+                sessionMeta.put(CONNECT_STRING, this.connectString);
+                sessionMeta.put(SESSION_TIMEOUT, String
+                        .valueOf(this.sessionTimeout));
+            }
+        } catch (Exception e) {
+            LoggerFactory.getLogger().error(
+                    "Error occurred retrieving session meta data.", e);
+        }
+        return sessionMeta;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#createNode
+     * (java.lang.String, java.lang.String)
+     */
+    public boolean createNode(String parent, String nodeName) {
+        if (connected) {
+            try {
+                String[] nodeElements = nodeName.split("/");
+                for (String nodeElement : nodeElements) {
+                    String node = parent + "/" + nodeElement;
+                    Stat s = zooKeeper.exists(node, false);
+                    if (s == null) {
+                        zooKeeper.create(node, this.encryptionManager
+                                .encryptData(null), Ids.OPEN_ACL_UNSAFE,
+                                CreateMode.PERSISTENT);
+                        parent = node;
+                    }
+                }
+                return true;
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred creating node: " + parent + "/"
+                                + nodeName, e);
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#deleteNode
+     * (java.lang.String)
+     */
+    public boolean deleteNode(String nodePath) {
+        if (connected) {
+            try {
+                Stat s = zooKeeper.exists(nodePath, false);
+                if (s != null) {
+                    List<String> children = zooKeeper.getChildren(nodePath,
+                            false);
+                    for (String child : children) {
+                        String node = nodePath + "/" + child;
+                        deleteNode(node);
+                    }
+                    zooKeeper.delete(nodePath, -1);
+                }
+                return true;
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred deleting node: " + nodePath, e);
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager#setData
+     * (java.lang.String, java.lang.String)
+     */
+    public boolean setData(String nodePath, String data) {
+        if (connected) {
+            try {
+                zooKeeper.setData(nodePath, this.encryptionManager
+                        .encryptData(data), -1);
+                return true;
+            } catch (Exception e) {
+                LoggerFactory.getLogger().error(
+                        "Error occurred setting data for node: " + nodePath, e);
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
+     * getConnectionPropertiesTemplate()
+     */
+    public Pair<Map<String, List<String>>, Map<String, String>> getConnectionPropertiesTemplate() {
+        Map<String, List<String>> template = new LinkedHashMap<String, List<String>>();
+        template.put(CONNECT_STRING, Arrays
+                .asList(new String[] { defaultHosts }));
+        template.put(SESSION_TIMEOUT, Arrays
+                .asList(new String[] { defaultTimeout }));
+        template.put(DATA_ENCRYPTION_MANAGER, Arrays
+                .asList(new String[] { defaultEncryptionManager }));
+        Map<String, String> labels = new LinkedHashMap<String, String>();
+        labels.put(CONNECT_STRING, "Connect String");
+        labels.put(SESSION_TIMEOUT, "Session Timeout");
+        labels.put(DATA_ENCRYPTION_MANAGER, "Data Encryption Manager");
+        return new Pair<Map<String, List<String>>, Map<String, String>>(
+                template, labels);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorManager#addWatchers
+     * (java.util.Collection,
+     * org.apache.zookeeper.inspector.manager.NodeListener)
+     */
+    public void addWatchers(Collection<String> selectedNodes,
+            NodeListener nodeListener) {
+        // add watcher for each node and add node to collection of
+        // watched nodes
+        if (connected) {
+            for (String node : selectedNodes) {
+                if (!watchers.containsKey(node)) {
+                    try {
+                        watchers.put(node, new NodeWatcher(node, nodeListener,
+                                zooKeeper));
+                    } catch (Exception e) {
+                        LoggerFactory.getLogger().error(
+                                "Error occured adding node watcher for node: "
+                                        + node, e);
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.zookeeper.inspector.manager.ZooInspectorManager#removeWatchers
+     * (java.util.Collection)
+     */
+    public void removeWatchers(Collection<String> selectedNodes) {
+        // remove watcher for each node and remove node from
+        // collection of watched nodes
+        if (connected) {
+            for (String node : selectedNodes) {
+                if (watchers.containsKey(node)) {
+                    NodeWatcher watcher = watchers.remove(node);
+                    if (watcher != null) {
+                        watcher.stop();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * A Watcher which will re-add itself every time an event is fired
+     * 
+     */
+    public class NodeWatcher implements Watcher {
+
+        private final String nodePath;
+        private final NodeListener nodeListener;
+        private final ZooKeeper zookeeper;
+        private boolean closed = false;
+
+        /**
+         * @param nodePath
+         *            - the path to the node to watch
+         * @param nodeListener
+         *            the {@link NodeListener} for this node
+         * @param zookeeper
+         *            - a {@link ZooKeeper} to use to access zookeeper
+         * @throws InterruptedException
+         * @throws KeeperException
+         */
+        public NodeWatcher(String nodePath, NodeListener nodeListener,
+                ZooKeeper zookeeper) throws KeeperException,
+                InterruptedException {
+            this.nodePath = nodePath;
+            this.nodeListener = nodeListener;
+            this.zookeeper = zookeeper;
+            Stat s = zooKeeper.exists(nodePath, this);
+            if (s != null) {
+                zookeeper.getChildren(nodePath, this);
+            }
+        }
+
+        public void process(WatchedEvent event) {
+            if (!closed) {
+                try {
+                    if (event.getType() != EventType.NodeDeleted) {
+
+                        Stat s = zooKeeper.exists(nodePath, this);
+                        if (s != null) {
+                            zookeeper.getChildren(nodePath, this);
+                        }
+                    }
+                } catch (Exception e) {
+                    LoggerFactory.getLogger().error(
+                            "Error occured re-adding node watcherfor node "
+                                    + nodePath, e);
+                }
+                nodeListener.processEvent(event.getPath(), event.getType()
+                        .name(), null);
+            }
+        }
+
+        /**
+		 * 
+		 */
+        public void stop() {
+            this.closed = true;
+        }
+
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
+     * loadNodeViewersFile(java.io.File)
+     */
+    public List<String> loadNodeViewersFile(File selectedFile)
+            throws IOException {
+        List<String> result = new ArrayList<String>();
+        if (defaultNodeViewersFile.exists()) {
+            FileReader reader = new FileReader(selectedFile);
+            try {
+                BufferedReader buff = new BufferedReader(reader);
+                try {
+                    while (buff.ready()) {
+                        String line = buff.readLine();
+                        if (line != null && line.length() > 0) {
+                            result.add(line);
+                        }
+                    }
+                } finally {
+                    buff.close();
+                }
+            } finally {
+                reader.close();
+            }
+        }
+        return result;
+    }
+
+    private void loadDefaultConnectionFile() throws IOException {
+        if (defaultConnectionFile.exists()) {
+            Properties props = new Properties();
+
+            FileReader reader = new FileReader(defaultConnectionFile);
+            try {
+                props.load(reader);
+            } finally {
+                reader.close();
+            }
+            defaultEncryptionManager = props
+                    .getProperty(DATA_ENCRYPTION_MANAGER) == null ? "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager"
+                    : props.getProperty(DATA_ENCRYPTION_MANAGER);
+            defaultTimeout = props.getProperty(SESSION_TIMEOUT) == null ? "5000"
+                    : props.getProperty(SESSION_TIMEOUT);
+            defaultHosts = props.getProperty(CONNECT_STRING) == null ? "localhost:2181"
+                    : props.getProperty(CONNECT_STRING);
+        } else {
+            defaultEncryptionManager = "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager";
+            defaultTimeout = "5000";
+            defaultHosts = "localhost:2181";
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
+     * saveDefaultConnectionFile(java.util.Properties)
+     */
+    public void saveDefaultConnectionFile(Properties props) throws IOException {
+        File defaultDir = defaultConnectionFile.getParentFile();
+        if (!defaultDir.exists()) {
+            if (!defaultDir.mkdirs()) {
+                throw new IOException(
+                        "Failed to create configuration directory: "
+                                + defaultDir.getAbsolutePath());
+            }
+        }
+        if (!defaultConnectionFile.exists()) {
+            if (!defaultConnectionFile.createNewFile()) {
+                throw new IOException(
+                        "Failed to create default connection file: "
+                                + defaultConnectionFile.getAbsolutePath());
+            }
+        }
+        FileWriter writer = new FileWriter(defaultConnectionFile);
+        try {
+            props.store(writer, "Default connection for ZooInspector");
+        } finally {
+            writer.close();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
+     * saveNodeViewersFile(java.io.File, java.util.List)
+     */
+    public void saveNodeViewersFile(File selectedFile,
+            List<String> nodeViewersClassNames) throws IOException {
+        if (!selectedFile.exists()) {
+            if (!selectedFile.createNewFile()) {
+                throw new IOException(
+                        "Failed to create node viewers configuration file: "
+                                + selectedFile.getAbsolutePath());
+            }
+        }
+        FileWriter writer = new FileWriter(selectedFile);
+        try {
+            BufferedWriter buff = new BufferedWriter(writer);
+            try {
+                for (String nodeViewersClassName : nodeViewersClassNames) {
+                    buff.append(nodeViewersClassName);
+                    buff.append("\n");
+                }
+            } finally {
+                buff.flush();
+                buff.close();
+            }
+        } finally {
+            writer.close();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
+     * setDefaultNodeViewerConfiguration(java.io.File, java.util.List)
+     */
+    public void setDefaultNodeViewerConfiguration(
+            List<String> nodeViewersClassNames) throws IOException {
+        File defaultDir = defaultNodeViewersFile.getParentFile();
+        if (!defaultDir.exists()) {
+            if (!defaultDir.mkdirs()) {
+                throw new IOException(
+                        "Failed to create configuration directory: "
+                                + defaultDir.getAbsolutePath());
+            }
+        }
+        saveNodeViewersFile(defaultNodeViewersFile, nodeViewersClassNames);
+    }
+
+    public List<String> getDefaultNodeViewerConfiguration() throws IOException {
+        return loadNodeViewersFile(defaultNodeViewersFile);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
+     * getLastConnectionProps()
+     */
+    public Properties getLastConnectionProps() {
+        return this.lastConnectionProps;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
+     * setLastConnectionProps(java.util.Properties)
+     */
+    public void setLastConnectionProps(Properties connectionProps) {
+        this.lastConnectionProps = connectionProps;
+    }
+}

+ 33 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeManager.java

@@ -0,0 +1,33 @@
+/**
+ * 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.zookeeper.inspector.manager;
+
+/**
+ * A Manager for all interactions between the application and the nodes in a
+ * Zookeeper instance
+ * */
+public interface ZooInspectorNodeManager extends ZooInspectorReadOnlyManager {
+    /**
+     * @param nodePath
+     *            - the path to the node on which to set the data
+     * @param data
+     *            - the data to set on the this node
+     * @return true if the data for the node was successfully updated
+     */
+    public boolean setData(String nodePath, String data);
+}

+ 43 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeTreeManager.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.zookeeper.inspector.manager;
+
+/**
+ * A Manager for all interactions between the application and the node tree in a
+ * Zookeeper instance
+ */
+public interface ZooInspectorNodeTreeManager extends
+        ZooInspectorReadOnlyManager {
+
+    /**
+     * @param parent
+     *            - the parent node path for the node to add
+     * @param nodeName
+     *            - the name of the new node
+     * @return true if the node was successfully created
+     */
+    public abstract boolean createNode(String parent, String nodeName);
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return true if the node was successfully deleted
+     */
+    public abstract boolean deleteNode(String nodePath);
+
+}

+ 99 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorReadOnlyManager.java

@@ -0,0 +1,99 @@
+/**
+ * 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.zookeeper.inspector.manager;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A Manager for all read only interactions between the application and a node
+ * in a Zookeeper instance
+ */
+public interface ZooInspectorReadOnlyManager {
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return the data for the node
+     */
+    public abstract String getData(String nodePath);
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return the metaData for the node
+     */
+    public abstract Map<String, String> getNodeMeta(String nodePath);
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return the ACLs set on the node
+     */
+    public abstract List<Map<String, String>> getACLs(String nodePath);
+
+    /**
+     * @return the metaData for the current session
+     */
+    public abstract Map<String, String> getSessionMeta();
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return true if the node has children
+     */
+    public abstract boolean hasChildren(String nodePath);
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return the index of the node within its siblings
+     */
+    public abstract int getNodeIndex(String nodePath);
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return the number of children of the node
+     */
+    public abstract int getNumChildren(String nodePath);
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @param childIndex
+     *            - the index to the node in the list of node children
+     * @return the path to the node for the child of the nodePath at childIndex
+     */
+    public abstract String getNodeChild(String nodePath, int childIndex);
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return true if the node allows children nodes
+     */
+    public abstract boolean isAllowsChildren(String nodePath);
+
+    /**
+     * @param nodePath
+     *            - the path to the node to delete
+     * @return a {@link List} of the children of the node
+     */
+    public abstract List<String> getChildren(String nodePath);
+
+}

+ 288 - 0
src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java

@@ -0,0 +1,288 @@
+/**
+ * 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.zookeeper.retry;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.inspector.logger.LoggerFactory;
+
+/**
+ * A Class which extends {@link ZooKeeper} and will automatically retry calls to
+ * zookeeper if a {@link KeeperException.ConnectionLossException} occurs
+ */
+public class ZooKeeperRetry extends ZooKeeper {
+
+    private boolean closed = false;
+    private final Watcher watcher;
+    private int limit = -1;
+
+    /**
+     * @param connectString
+     * @param sessionTimeout
+     * @param watcher
+     * @throws IOException
+     */
+    public ZooKeeperRetry(String connectString, int sessionTimeout,
+            Watcher watcher) throws IOException {
+        super(connectString, sessionTimeout, watcher);
+        this.watcher = watcher;
+    }
+
+    /**
+     * @param connectString
+     * @param sessionTimeout
+     * @param watcher
+     * @param sessionId
+     * @param sessionPasswd
+     * @throws IOException
+     */
+    public ZooKeeperRetry(String connectString, int sessionTimeout,
+            Watcher watcher, long sessionId, byte[] sessionPasswd)
+            throws IOException {
+        super(connectString, sessionTimeout, watcher, sessionId, sessionPasswd);
+        this.watcher = watcher;
+    }
+
+    @Override
+    public synchronized void close() throws InterruptedException {
+        this.closed = true;
+        super.close();
+    }
+
+    @Override
+    public String create(String path, byte[] data, List<ACL> acl,
+            CreateMode createMode) throws KeeperException, InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.create(path, data, acl, createMode);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+                if (exists(path, false) != null) {
+                    return path;
+                }
+            } catch (KeeperException.NodeExistsException e) {
+                return path;
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return null;
+    }
+
+    @Override
+    public void delete(String path, int version) throws InterruptedException,
+            KeeperException {
+        int count = 0;
+        do {
+            try {
+                super.delete(path, version);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+                if (exists(path, false) == null) {
+                    return;
+                }
+            } catch (KeeperException.NoNodeException e) {
+                break;
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+    }
+
+    @Override
+    public Stat exists(String path, boolean watch) throws KeeperException,
+            InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.exists(path, watch ? watcher : null);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return null;
+    }
+
+    @Override
+    public Stat exists(String path, Watcher watcher) throws KeeperException,
+            InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.exists(path, watcher);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return null;
+    }
+
+    @Override
+    public List<ACL> getACL(String path, Stat stat) throws KeeperException,
+            InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.getACL(path, stat);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return null;
+    }
+
+    @Override
+    public List<String> getChildren(String path, boolean watch)
+            throws KeeperException, InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.getChildren(path, watch ? watcher : null);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return new ArrayList<String>();
+    }
+
+    @Override
+    public List<String> getChildren(String path, Watcher watcher)
+            throws KeeperException, InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.getChildren(path, watcher);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return new ArrayList<String>();
+    }
+
+    @Override
+    public byte[] getData(String path, boolean watch, Stat stat)
+            throws KeeperException, InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.getData(path, watch ? watcher : null, stat);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return null;
+    }
+
+    @Override
+    public byte[] getData(String path, Watcher watcher, Stat stat)
+            throws KeeperException, InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.getData(path, watcher, stat);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return null;
+    }
+
+    @Override
+    public Stat setACL(String path, List<ACL> acl, int version)
+            throws KeeperException, InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.setACL(path, acl, version);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+                Stat s = exists(path, false);
+                if (s != null) {
+                    if (getACL(path, s).equals(acl)) {
+                        return s;
+                    }
+                } else {
+                    return null;
+                }
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return null;
+    }
+
+    @Override
+    public Stat setData(String path, byte[] data, int version)
+            throws KeeperException, InterruptedException {
+        int count = 0;
+        do {
+            try {
+                return super.setData(path, data, version);
+            } catch (KeeperException.ConnectionLossException e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+                Stat s = exists(path, false);
+                if (s != null) {
+                    if (getData(path, false, s) == data) {
+                        return s;
+                    }
+                } else {
+                    return null;
+                }
+            }
+        } while (!closed && (limit == -1 || count++ < limit));
+        return null;
+    }
+
+    /**
+     * @param limit
+     */
+    public void setRetryLimit(int limit) {
+        this.limit = limit;
+    }
+
+    /**
+     * @return true if successfully connected to zookeeper
+     */
+    public boolean testConnection() {
+        int count = 0;
+        do {
+            try {
+                return super.exists("/", null) != null;
+            } catch (Exception e) {
+                LoggerFactory.getLogger().warn(
+                        "ZooKeeper connection lost.  Trying to reconnect.");
+            }
+        } while (count++ < 5);
+        return false;
+    }
+
+}

+ 18 - 0
src/contrib/zooinspector/zooInspector-dev.sh

@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# 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.
+         
+java -cp ../../../build/contrib/ZooInspector/zookeeper-dev-ZooInspector.jar:../../../build/lib/log4j-1.2.15.jar:lib/zookeeper-3.3.0.jar:lib/jtoaster-1.0.4.jar:lib org.apache.zookeeper.inspector.ZooInspector

+ 18 - 0
src/contrib/zooinspector/zooInspector.cmd

@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# 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.
+
+java -cp zookeeper-dev-ZooInspector.jar;lib/log4j-1.2.15.jar;lib/zookeeper-3.3.0.jar;lib/jToaster-1.0.4.jar;lib org.apache.zookeeper.inspector.ZooInspector

+ 18 - 0
src/contrib/zooinspector/zooInspector.sh

@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# 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.
+
+java -cp lib/New.jar:zookeeper-dev-ZooInspector.jar:lib/log4j-1.2.15.jar:;lib/zookeeper-3.3.0.jar:lib/jToaster-1.0.4.jar:lib org.apache.zookeeper.inspector.ZooInspector