zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an...@apache.org
Subject [22/44] zookeeper git commit: ZOOKEEPER-3030: MAVEN MIGRATION 3.4 - Step 1.3 - move contrib directories
Date Tue, 07 Aug 2018 09:43:08 GMT
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py
new file mode 100644
index 0000000..eccc881
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py
@@ -0,0 +1,16 @@
+#  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.
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py
new file mode 100644
index 0000000..6b1f178
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py
@@ -0,0 +1,29 @@
+#  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.
+
+from django import forms
+from django.forms.widgets import Textarea, HiddenInput
+
+class CreateZNodeForm(forms.Form):
+  name = forms.CharField(max_length=64)
+  data = forms.CharField(required=False, widget=Textarea)
+  sequence = forms.BooleanField(required=False)
+
+class EditZNodeForm(forms.Form):
+  data = forms.CharField(required=False, widget=Textarea)
+  version = forms.IntegerField(required=False, widget=HiddenInput)
+  
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py
new file mode 100644
index 0000000..a46696b
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py
@@ -0,0 +1,17 @@
+#  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.
+
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py
new file mode 100644
index 0000000..e4874a1
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py
@@ -0,0 +1,230 @@
+
+# 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.
+
+import urllib2
+import urllib
+import simplejson
+
+from contextlib import contextmanager
+
+class RequestWithMethod(urllib2.Request):
+    """ Request class that know how to set the method name """
+    def __init__(self, *args, **kwargs):
+        urllib2.Request.__init__(self, *args, **kwargs)
+        self._method = None
+
+    def get_method(self):
+        return self._method or \
+            urllib2.Request.get_method(self)
+
+    def set_method(self, method):
+        self._method = method
+
+class ZooKeeper(object):
+
+    class Error(Exception): pass
+
+    class NotFound(Error): pass
+
+    class ZNodeExists(Error): pass
+
+    class InvalidSession(Error): pass
+
+    class WrongVersion(Error): pass
+
+    def __init__(self, uri = 'http://localhost:9998'):
+        self._base = uri
+        self._session = None
+
+    def start_session(self, expire=5, id=None):
+        """ Create a session and return the ID """
+        if id is None:
+            url = "%s/sessions/v1/?op=create&expire=%d" % (self._base, expire)
+            self._session = self._do_post(url)['id']
+        else:
+            self._session = id
+        return self._session
+
+    def close_session(self):
+        """ Close the session on the server """
+        if self._session is not None:
+            url = '%s/sessions/v1/%s' % (self._base, self._session)
+            self._do_delete(url)
+            self._session = None
+
+    def heartbeat(self):
+        """ Send a heartbeat request. This is needed in order to keep a session alive """
+        if self._session is not None:
+            url = '%s/sessions/v1/%s' % (self._base, self._session)
+            self._do_put(url, '')
+
+    @contextmanager
+    def session(self, *args, **kwargs):
+        """ Session handling using a context manager """
+        yield self.start_session(*args, **kwargs)
+        self.close_session()
+
+    def get(self, path):
+        """ Get a node """
+        url = "%s/znodes/v1%s" % (self._base, path)
+        return self._do_get(url)
+
+    def get_children(self, path):
+        """ Get all the children for a given path. This function creates a generator """
+        for child_path in self.get_children_paths(path, uris=True):
+            try:
+                yield self._do_get(child_path)
+            except ZooKeeper.NotFound:
+                continue
+
+    def get_children_paths(self, path, uris=False):
+        """ Get the paths for children nodes """
+        url = "%s/znodes/v1%s?view=children" % (self._base, path)
+        resp = self._do_get(url)
+        for child in resp.get('children', []):
+            yield child if not uris else resp['child_uri_template']\
+              .replace('{child}', urllib2.quote(child))
+       
+    def create(self, path, data=None, sequence=False, ephemeral=False):
+        """ Create a new node. By default this call creates a persistent znode.
+
+        You can also create an ephemeral or a sequential znode.
+        """
+        ri = path.rindex('/')
+        head, name = path[:ri+1], path[ri+1:]
+        if head != '/': head = head[:-1]
+
+        flags = {
+            'null': 'true' if data is None else 'false',
+            'ephemeral': 'true' if ephemeral else 'false',
+            'sequence': 'true' if sequence else 'false'
+        }
+        if ephemeral:
+            if self._session:
+                flags['session'] = self._session
+            else:
+                raise ZooKeeper.Error, 'You need a session '\
+                    'to create an ephemeral node'
+        flags = urllib.urlencode(flags)
+
+        url = "%s/znodes/v1%s?op=create&name=%s&%s" % \
+            (self._base, head, name, flags)
+
+        return self._do_post(url, data)
+
+    def set(self, path, data=None, version=-1, null=False):
+        """ Set the value of node """
+        url = "%s/znodes/v1%s?%s" % (self._base, path, \
+            urllib.urlencode({
+                'version': version,
+                'null': 'true' if null else 'false'
+        }))
+        return self._do_put(url, data)
+
+    def delete(self, path, version=-1):
+        """ Delete a znode """
+        if type(path) is list:
+            map(lambda el: self.delete(el, version), path)
+            return
+
+        url = '%s/znodes/v1%s?%s' % (self._base, path, \
+            urllib.urlencode({
+                'version':version
+        }))
+        try:
+            return self._do_delete(url)
+        except urllib2.HTTPError, e:
+            if e.code == 412:
+                raise ZooKeeper.WrongVersion(path)
+            elif e.code == 404:
+                raise ZooKeeper.NotFound(path)
+            raise
+
+    def recursive_delete(self, path):
+        """ Delete all the nodes from the tree """
+        for child in self.get_children_paths(path):
+            fp = ("%s/%s" % (path, child)).replace('//', '/')
+            self.recursive_delete(fp)
+        self.delete(path)
+
+    def exists(self, path):
+        """ Do a znode exists """
+        try:
+            self.get(path)
+            return True
+        except ZooKeeper.NotFound:
+            return False
+
+    def _do_get(self, uri):
+        """ Send a GET request and convert errors to exceptions """
+        try:
+            req = urllib2.urlopen(uri)
+            resp = simplejson.load(req)
+
+            if 'Error' in resp:
+               raise ZooKeeper.Error(resp['Error'])
+
+            return resp
+        except urllib2.HTTPError, e:
+            if e.code == 404:
+                raise ZooKeeper.NotFound(uri)
+            raise
+
+    def _do_post(self, uri, data=None):
+        """ Send a POST request and convert errors to exceptions """
+        try:
+            req = urllib2.Request(uri, {})
+            req.add_header('Content-Type', 'application/octet-stream')
+            if data is not None:
+                req.add_data(data)
+
+            resp = simplejson.load(urllib2.urlopen(req))
+            if 'Error' in resp:
+                raise ZooKeeper.Error(resp['Error'])
+            return resp
+
+        except urllib2.HTTPError, e:
+            if e.code == 201:
+                return True
+            elif e.code == 409:
+                raise ZooKeeper.ZNodeExists(uri)
+            elif e.code == 401:
+                raise ZooKeeper.InvalidSession(uri)
+            raise
+
+    def _do_delete(self, uri):
+        """ Send a DELETE request """
+        req = RequestWithMethod(uri)
+        req.set_method('DELETE')
+        req.add_header('Content-Type', 'application/octet-stream')
+        return urllib2.urlopen(req).read()
+
+    def _do_put(self, uri, data):
+        """ Send a PUT request """
+        try:
+            req = RequestWithMethod(uri)
+            req.set_method('PUT')
+            req.add_header('Content-Type', 'application/octet-stream')
+            if data is not None:
+                req.add_data(data)
+
+            return urllib2.urlopen(req).read()
+        except urllib2.HTTPError, e:
+            if e.code == 412: # precondition failed
+                raise ZooKeeper.WrongVersion(uri)
+            raise
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py
new file mode 100644
index 0000000..844c695
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py
@@ -0,0 +1,30 @@
+#  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.
+
+DJANGO_APPS = [ "zkui" ]
+NICE_NAME = "ZooKeeper Browser"
+REQUIRES_HADOOP = False
+
+CLUSTERS = [{
+        'nice_name': 'Default',
+        'hostport': 'localhost:2181,localhost:2182,localhost:2183',
+        'rest_gateway': 'http://localhost:9998'
+    }
+]
+
+DEPENDER_PACKAGE_YMLS = [
+    "src/zkui/static/js/package.yml",
+]

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png
new file mode 100644
index 0000000..1da4a29
Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png differ

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png
new file mode 100644
index 0000000..cb40df3
Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png differ

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/bootstrap.js
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/bootstrap.js b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/bootstrap.js
new file mode 100644
index 0000000..8e3fbfb
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/bootstrap.js
@@ -0,0 +1,32 @@
+// 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.
+CCS.Desktop.register({
+	Zkui : {
+		name : 'ZooKeeper Browser',
+		css : '/zkui/static/css/zkui.css',
+		require: [ 'Zkui' ],
+		launch: function(path, options){
+			return new Zkui(path || '/zkui/', options);
+		},
+		menu: {
+			id: 'ccs-zkui-menu',
+			img: {
+				src: '/zkui/static/art/zkui.png'
+			}
+		},
+		help: '/help/zkui/'
+	}
+});

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/zkui.css
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/zkui.css b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/zkui.css
new file mode 100644
index 0000000..c49f392
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/zkui.css
@@ -0,0 +1,56 @@
+/*
+ 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.
+*/
+
+.zkui img.zkui_icon {
+	width: 55px;
+	height: 55px;
+	position: absolute;
+	top: 27px;
+	left: 3px;
+}
+
+div.zkui .left_col li {
+    margin: 5px 0px;
+    font-size: 16px;
+    background-color: white;
+    color: black;
+    padding: 2px 1px 1px 5px;
+    -moz-border-radius: 3px;
+    -webkit-border-radius: 3px;
+    border: solid black 1px;
+}
+
+div.zkui .left_col li:hover {
+    background-color: lightBlue;
+    color: white;
+}
+
+div.zkui .left_col li a {
+    color: black;
+    display: block;
+}
+
+div.zkui .left_col li a:hover {
+    text-decoration: none;
+}
+
+div.zkui .createZnodeForm td,
+div.zkui .editZnodeForm td {
+  padding: 5px;
+}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/index.html
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/index.html b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/index.html
new file mode 100644
index 0000000..355c8cd
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/index.html
@@ -0,0 +1,26 @@
+<!--
+   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.
+-->
+
+<h1>ZooKeeper Browser</h1>
+
+
+<p>ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services</p>
+
+<h2>About</h2>
+
+<p>The ZooKeeper Browser application allows you to see how the cluster nodes are working and also allows you to do CRUD operations on the znode hierarchy.</p>
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js
new file mode 100644
index 0000000..c8bf383
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js
@@ -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.
+
+/*
+---
+
+script: Zkui.js
+
+description: Defines Zkui; a Hue application that extends CCS.JBrowser.
+
+authors:
+- Unknown
+
+requires:
+- ccs-shared/CCS.JBrowser
+
+provides: [Zkui]
+
+...
+*/
+ART.Sheet.define('window.art.browser.zkui', {
+	'min-width': 620
+});
+
+var Zkui = new Class({
+
+	Extends: CCS.JBrowser,
+
+	options: {
+		className: 'art browser logo_header zkui'
+	},
+
+	initialize: function(path, options){
+		this.parent(path || '/zkui/', options);
+	}
+
+});

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml
new file mode 100644
index 0000000..c2c07ad
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml
@@ -0,0 +1,19 @@
+# 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.
+copyright: Apache License v2.0
+version: 0.1
+description: ZooKeeper Browser
+name: ZooKeeper Browser
+sources: [Source/Zkui/Zkui.js]

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py
new file mode 100644
index 0000000..48f35dd
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py
@@ -0,0 +1,170 @@
+#  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.
+
+import socket
+import re
+
+from StringIO import StringIO
+
+class Session(object):
+
+  class BrokenLine(Exception): pass
+
+  def __init__(self, session):
+    m = re.search('/(\d+\.\d+\.\d+\.\d+):(\d+)\[(\d+)\]\((.*)\)', session)
+    if m:
+        self.host = m.group(1)
+        self.port = m.group(2)
+        self.interest_ops = m.group(3)
+        for d in m.group(4).split(","):
+            k,v = d.split("=")
+            self.__dict__[k] = v
+    else:
+        raise Session.BrokenLine() 
+
+class ZooKeeperStats(object):
+
+    def __init__(self, host='localhost', port='2181', timeout=1):
+        self._address = (host, int(port))
+        self._timeout = timeout
+
+    def get_stats(self):
+        """ Get ZooKeeper server stats as a map """
+        data = self._send_cmd('mntr')
+        if data:
+            return self._parse(data)
+        else:
+            data = self._send_cmd('stat')
+            return self._parse_stat(data)
+
+    def get_clients(self):
+      """ Get ZooKeeper server clients """
+      clients = []
+
+      stat = self._send_cmd('stat')
+      if not stat:
+        return clients
+
+      sio = StringIO(stat)
+
+      #skip two lines
+      sio.readline()
+      sio.readline()
+
+      for line in sio:
+        if not line.strip():
+          break
+        try:
+          clients.append(Session(line.strip()))
+        except Session.BrokenLine:
+          continue
+
+      return clients
+
+    def _create_socket(self):
+        return socket.socket()
+
+    def _send_cmd(self, cmd):
+        """ Send a 4letter word command to the server """
+        s = self._create_socket()
+        s.settimeout(self._timeout)
+
+        s.connect(self._address)
+        s.send(cmd)
+
+        data = s.recv(2048)
+        s.close()
+
+        return data
+
+    def _parse(self, data):
+        """ Parse the output from the 'mntr' 4letter word command """
+        h = StringIO(data)
+        
+        result = {}
+        for line in h.readlines():
+            try:
+                key, value = self._parse_line(line)
+                result[key] = value
+            except ValueError:
+                pass # ignore broken lines
+
+        return result
+
+    def _parse_stat(self, data):
+        """ Parse the output from the 'stat' 4letter word command """
+        h = StringIO(data)
+
+        result = {}
+        
+        version = h.readline()
+        if version:
+            result['zk_version'] = version[version.index(':')+1:].strip()
+
+        # skip all lines until we find the empty one
+        while h.readline().strip(): pass
+
+        for line in h.readlines():
+            m = re.match('Latency min/avg/max: (\d+)/(\d+)/(\d+)', line)
+            if m is not None:
+                result['zk_min_latency'] = int(m.group(1))
+                result['zk_avg_latency'] = int(m.group(2))
+                result['zk_max_latency'] = int(m.group(3))
+                continue
+
+            m = re.match('Received: (\d+)', line)
+            if m is not None:
+                result['zk_packets_received'] = int(m.group(1))
+                continue
+
+            m = re.match('Sent: (\d+)', line)
+            if m is not None:
+                result['zk_packets_sent'] = int(m.group(1))
+                continue
+
+            m = re.match('Outstanding: (\d+)', line)
+            if m is not None:
+                result['zk_outstanding_requests'] = int(m.group(1))
+                continue
+
+            m = re.match('Mode: (.*)', line)
+            if m is not None:
+                result['zk_server_state'] = m.group(1)
+                continue
+
+            m = re.match('Node count: (\d+)', line)
+            if m is not None:
+                result['zk_znode_count'] = int(m.group(1))
+                continue
+
+        return result 
+
+    def _parse_line(self, line):
+        try:
+            key, value = map(str.strip, line.split('\t'))
+        except ValueError:
+            raise ValueError('Found invalid line: %s' % line)
+
+        if not key:
+            raise ValueError('The key is mandatory and should not be empty')
+
+        try:
+            value = int(value)
+        except (TypeError, ValueError):
+            pass
+
+        return key, value
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako
new file mode 100644
index 0000000..2bee9a7
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako
@@ -0,0 +1,51 @@
+<%!
+#  Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+%>
+
+<%namespace name="shared" file="shared_components.mako" />
+
+${shared.header("ZooKeeper Browser > Clients > %s:%s" % (host, port))}
+
+<h1>${host}:${port} :: client connections</h1>
+<br />
+
+% if clients:
+  <table data-filters="HtmlTable"> 
+  <thead>
+    <tr>
+      <th>Host</th>
+      <th>Port</th>
+      <th>Interest Ops</th>
+      <th>Queued</th>
+      <th>Received</th>
+      <th>Sent</th>
+  </thead>
+  % for client in clients:
+    <tr>
+      <td>${client.host}</td>
+      <td>${client.port}</td>
+      <td>${client.interest_ops}</td>
+      <td>${client.queued}</td>
+      <td>${client.recved}</td>
+      <td>${client.sent}</td>
+    </tr>
+  % endfor
+  </table>
+% endif
+
+${shared.footer()}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako
new file mode 100644
index 0000000..2a8b8cc
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako
@@ -0,0 +1,34 @@
+<%!
+#  Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+%>
+<%namespace name="shared" file="shared_components.mako" />
+
+${shared.header("ZooKeeper Browser > Create Znode")}
+
+<h2>Create New Znode :: ${path}</h2>
+<hr /><br />
+
+<form class="createZnodeForm" action="" method="POST">
+<table align="center">
+  ${form.as_table()|n}
+<tr><td colspan="2" align="right">
+  <button type="submit">Create</button>
+</td></tr>
+</table>
+</form>
+
+${shared.footer()}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako
new file mode 100644
index 0000000..997bd07
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako
@@ -0,0 +1,34 @@
+<%!
+#  Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+%>
+<%namespace name="shared" file="shared_components.mako" />
+
+${shared.header("ZooKeeper Browser > Edit Znode > %s" % path)}
+
+<h2>Edit Znode Data :: ${path}</h2>
+<hr /><br />
+
+<form class="editZnodeForm" action="" method="POST">
+<table align="center">
+  ${form.as_table()|n}
+<tr><td colspan="2" align="right">
+  <button type="submit">Save</button>
+</td></tr>
+</table>
+</form>
+
+${shared.footer()}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako
new file mode 100644
index 0000000..567919d
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako
@@ -0,0 +1,54 @@
+<%!
+#  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.
+%>
+<%namespace name="shared" file="shared_components.mako" />
+
+${shared.header("ZooKeeper Browser")}
+
+<h2>Overview</h2>
+
+<br />
+
+% for i, c in enumerate(overview):
+  <h3> ${i+1}. <a href="${url('zkui.views.view', id=i)}">${c['nice_name']} Cluster Overview</a></h3><br />
+
+  <table data-filters="HtmlTable">
+  <thead>
+    <tr>
+      <th>Node</th>
+      <th>Role</th>
+      <th>Avg Latency</th>
+      <th>Watch Count</th>
+      <th>Version</th>
+    </tr>
+  </thead>
+  % for host, stats in c['stats'].items():
+    <tr>
+      <td>${host}</td>
+      <td>${stats.get('zk_server_state', '')}</td>
+      <td>${stats.get('zk_avg_latency', '')}</td>
+      <td>${stats.get('zk_watch_count', '')}</td>
+      <td>${stats.get('zk_version', '')}</td>
+    </tr>
+  % endfor
+  </table>
+
+  <br /><br />
+% endfor 
+
+${shared.footer()}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako
new file mode 100644
index 0000000..f9a4589
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako
@@ -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.
+%>
+
+<%!
+import datetime
+from django.template.defaultfilters import urlencode, escape
+from zkui import settings
+%>
+
+<%def name="header(title='ZooKeeper Browser', toolbar=True)">
+  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+  <html>
+    <head>
+      <title>${title}</title>
+    </head>
+    <body>
+      % if toolbar:
+      <div class="toolbar">
+        <a href="${url('zkui.views.index')}"><img src="/zkui/static/art/zkui.png" class="zkui_icon"/></a>
+      </div>
+      % endif
+
+    <div data-filters="SplitView">
+    <div class="left_col jframe_padded" style="width:150px;">
+        <ul>
+          <li><a href="${url("zkui.views.index")}">Overview</a></li>
+        </ul>
+        <br />
+
+        <h2>Clusters</h2>
+        <ul>
+            % for id, c in enumerate(settings.CLUSTERS):
+                <li><a href="${url("zkui.views.view", id=id)}">
+                    ${c['nice_name']}</a></li>
+            % endfor
+        </ul>
+    </div>
+
+    <div class="right_col jframe_padded">
+</%def>
+
+<%def name="info_button(url, text)">
+  <a data-filters="ArtButton" href="${url}" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px;" data-icon-styles="{'width': 14, 'height': 14}">${text}</a>
+</%def>
+
+<%def name="footer()">
+        </div>
+    </div>
+    </body>
+  </html>
+</%def>

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako
new file mode 100644
index 0000000..c74c202
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako
@@ -0,0 +1,75 @@
+<%!
+#  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.
+%>
+<%namespace name="shared" file="shared_components.mako" />
+
+${shared.header("ZooKeeper Browser > Tree > %s > %s" % (cluster['nice_name'], path))}
+
+<h1>${cluster['nice_name'].lower()} :: ${path}</h1>
+<br />
+
+<table data-filters="HtmlTable">
+  <thead>
+  <th colspan="2">Children</th>
+  </thead>
+  % for child in children:
+    <tr><td width="100%">
+      <a href="${url('zkui.views.tree', id=cluster['id'], \
+          path=("%s/%s" % (path, child)).replace('//', '/'))}">
+      ${child}</a>
+    </td><td>
+      <a title="Delete ${child}" class="delete frame_tip confirm_and_post" alt="Are you sure you want to delete ${child}?" href="${url('zkui.views.delete', id=cluster['id'], \
+          path=("%s/%s" % (path, child)).replace('//', '/'))}">Delete</a>
+    </td></tr>
+  % endfor
+</table>
+<br />
+<span style="float: right">
+  ${shared.info_button(url('zkui.views.create', id=cluster['id'], path=path), 'Create New')}
+</span>
+
+<div style="clear: both"></div>
+
+<h2>data :: base64 :: length :: ${znode.get('dataLength', 0)}</h2>
+<br />
+
+<textarea name="data64" style="width: 100%;" rows="5" readonly="readonly">${znode.get('data64', '')}</textarea>
+<div style="clear: both"></div>
+<span style="float: right">
+  ${shared.info_button(url('zkui.views.edit_as_base64', id=cluster['id'], path=path), 'Edit as Base64')}
+  ${shared.info_button(url('zkui.views.edit_as_text', id=cluster['id'], path=path), 'Edit as Text')}
+</span>
+<div style="clear: both"></div>
+<br />
+
+<h2>stat information</h2>
+<br />
+
+<table data-filters="HtmlTable">
+  <thead><tr><th>Key</th>
+    <th width="80%">Value</th></tr></thead>
+  % for key in ('pzxid', 'ctime', 'aversion', 'mzxid', \
+      'ephemeralOwner', 'version', 'mtime', 'cversion', 'czxid'):
+    <tr><td>${key}</td><td>${znode[key]}</td></tr> 
+  % endfor
+</table>
+
+<br />
+<a target="_blank" href="http://zookeeper.apache.org/docs/current/zookeeperProgrammers.html#sc_zkStatStructure">Details on stat information.</a>
+
+${shared.footer()}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako
new file mode 100644
index 0000000..e046afc
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako
@@ -0,0 +1,128 @@
+<%!
+#  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.
+%>
+<%namespace name="shared" file="shared_components.mako" />
+
+${shared.header("ZooKeeper Browser > %s" % cluster['nice_name'])}
+
+<%def name="show_stats(stats)">
+    <thead>
+      <tr><th>Key</th>
+      <th width="100%">Value</th></tr>
+    </thead>
+
+    <tr><td>Version</td>
+      <td>${stats.get('zk_version')}</td>
+    </tr>
+
+    <tr><td>Latency</td><td>
+      Min: ${stats.get('zk_min_latency', '')}
+      Avg: ${stats.get('zk_avg_latency', '')}
+      Max: ${stats.get('zk_max_latency', '')}
+    </td></tr>
+
+    <tr><td>Packets</td>
+      <td>Sent: ${stats.get('zk_packets_sent', '')}
+      Received: ${stats.get('zk_packets_received', '')}
+      </td>
+    </tr>
+
+    <tr><td>Outstanding Requests</td>
+      <td>${stats.get('zk_outstanding_requests', '')}</td>
+    </tr>
+
+    <tr><td>Watch Count</td>
+      <td>${stats.get('zk_watch_count', '')}</td>
+    </tr>
+
+    <tr><td>Open FD Count</td>
+      <td>${stats.get('zk_open_file_descriptor_count', '')}</td>
+    </tr>
+
+    <tr><td>Max FD Count</td>
+      <td>${stats.get('zk_max_file_descriptor_count', '')}</td>
+    </tr>
+
+</%def> 
+
+<h2> ${cluster['nice_name']} Cluster Overview </h2>
+
+${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Znode Hierarchy')}
+
+<br /><br />
+
+% if leader:
+<h2>General</h2>
+
+<table data-filters="HtmlTable">
+  <thead>
+    <tr><th>Key</th><th width="100%">Value</th></tr>
+  </thead>
+
+  <tr><td>ZNode Count</td>
+    <td>${leader.get('zk_znode_count', '')}</td></tr>
+
+  <tr><td>Ephemerals Count</td>
+    <td>${leader.get('zk_ephemerals_count', '')}</td></tr>
+
+  <tr><td>Approximate Data Size</td>
+    <td>${leader.get('zk_approximate_data_size', '')} bytes</td></tr>
+
+</table>
+<br /><br />
+% endif
+
+% if leader:
+  <h2>node :: ${leader['host']} :: leader</h2>
+
+  ${shared.info_button(url('zkui.views.clients', host=leader['host']), 'View Client Connections')}
+
+  <br /><br />
+  <table data-filters="HtmlTable">
+    ${show_stats(leader)}
+    
+    <tr><td>Followers</td>
+      <td>${leader.get('zk_followers', '')}</td>
+    </tr>
+
+    <tr><td>Synced Followers</td>
+      <td>${leader.get('zk_synced_followers', '')}</td>
+    </tr>
+
+    <tr><td>Pending Syncs</td>
+      <td>${leader.get('zk_pending_syncs', '')}</td>
+    </tr>
+  
+  </table>
+<br /><br />
+% endif
+
+% for stats in followers:
+  <h2>node :: ${stats['host']} :: follower</h2>
+  <br />
+
+  ${shared.info_button(url('zkui.views.clients', host=stats['host']), 'View Client Connections')}
+
+  <br /><br />
+  <table data-filters="HtmlTable">
+    ${show_stats(stats)}
+  </table>
+  <br /><br />
+% endfor
+
+${shared.footer()}
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py
new file mode 100644
index 0000000..f795f7e
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py
@@ -0,0 +1,28 @@
+#  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.
+
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('zkui',
+  url(r'^$', 'views.index'),
+  url(r'view/(?P<id>\d+)$', 'views.view'),
+  url(r'clients/(?P<host>.+)$', 'views.clients'),
+  url(r'tree/(?P<id>\d+)(?P<path>.+)$', 'views.tree'),
+  url(r'create/(?P<id>\d+)(?P<path>.*)$', 'views.create'),
+  url(r'delete/(?P<id>\d+)(?P<path>.*)$', 'views.delete'),
+  url(r'edit/base64/(?P<id>\d+)(?P<path>.*)$', 'views.edit_as_base64'),
+  url(r'edit/text/(?P<id>\d+)(?P<path>.*)$', 'views.edit_as_text')
+)

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py
new file mode 100644
index 0000000..fb01317
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py
@@ -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.
+
+from zkui import settings
+
+from django.http import Http404
+
+def get_cluster_or_404(id):
+  try:
+    id = int(id)
+    if not (0 <= id < len(settings.CLUSTERS)):
+      raise ValueError, 'Undefined cluster id.'
+  except (TypeError, ValueError):
+    raise Http404()
+
+  cluster = settings.CLUSTERS[id]
+  cluster['id'] = id
+
+  return cluster
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py
new file mode 100644
index 0000000..64d926b
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py
@@ -0,0 +1,165 @@
+#  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.
+
+from desktop.lib.django_util import render
+from django.http import Http404
+
+from zkui import settings
+from zkui.stats import ZooKeeperStats
+from zkui.rest import ZooKeeper
+from zkui.utils import get_cluster_or_404
+from zkui.forms import CreateZNodeForm, EditZNodeForm
+
+def _get_global_overview():
+  overview = []
+  for c in settings.CLUSTERS:
+    overview.append(_get_overview(c))
+  return overview
+
+def _get_overview(cluster):
+  stats = {}
+  for s in cluster['hostport'].split(','):
+    host, port = map(str.strip, s.split(':'))
+
+    zks = ZooKeeperStats(host, port)
+    stats[s] = zks.get_stats() or {}
+
+  cluster['stats'] = stats
+  return cluster
+
+def _group_stats_by_role(cluster):
+  leader, followers = None, []
+  for host, stats in cluster['stats'].items():
+    stats['host'] = host
+
+    if stats.get('zk_server_state') == 'leader':
+      leader = stats
+
+    elif stats.get('zk_server_state') == 'follower':
+      followers.append(stats) 
+
+  return leader, followers           
+ 
+def index(request):
+  overview = _get_global_overview()  
+  return render('index.mako', request, 
+    dict(overview=overview))
+
+def view(request, id):
+  cluster = get_cluster_or_404(id)
+
+  cluster = _get_overview(cluster)
+  leader, followers = _group_stats_by_role(cluster)
+
+  return render('view.mako', request, 
+    dict(cluster=cluster, leader=leader, followers=followers))
+
+def clients(request, host):
+  parts = host.split(':')  
+  if len(parts) != 2:
+    raise Http404
+
+  host, port = parts
+  zks = ZooKeeperStats(host, port)
+  clients = zks.get_clients()
+
+  return render('clients.mako', request,
+    dict(host=host, port=port, clients=clients))
+
+def tree(request, id, path):
+  cluster = get_cluster_or_404(id)
+  zk = ZooKeeper(cluster['rest_gateway'])
+
+  znode = zk.get(path)
+  children = sorted(zk.get_children_paths(path))
+  
+  return render('tree.mako', request,
+    dict(cluster=cluster, path=path, \
+      znode=znode, children=children))
+
+def delete(request, id, path):
+  cluster = get_cluster_or_404(id)
+  if request.method == 'POST':
+    zk = ZooKeeper(cluster['rest_gateway'])
+    try:
+      zk.recursive_delete(path)
+    except ZooKeeper.NotFound:
+      pass
+
+  return tree(request, id, path[:path.rindex('/')] or '/')
+
+def create(request, id, path):
+  cluster = get_cluster_or_404(id)
+
+  if request.method == 'POST':
+    form = CreateZNodeForm(request.POST)
+    if form.is_valid():
+      zk = ZooKeeper(cluster['rest_gateway'])
+
+      full_path = ("%s/%s" % (path, form.cleaned_data['name']))\
+        .replace('//', '/')
+
+      zk.create(full_path, \
+        form.cleaned_data['data'], \
+        sequence = form.cleaned_data['sequence'])
+      return tree(request, id, path)
+  else:
+    form = CreateZNodeForm()
+
+  return render('create.mako', request, 
+    dict(path=path, form=form))
+
+def edit_as_base64(request, id, path):
+  cluster = get_cluster_or_404(id)
+  zk = ZooKeeper(cluster['rest_gateway'])
+  node = zk.get(path)
+
+  if request.method == 'POST':
+    form = EditZNodeForm(request.POST)
+    if form.is_valid():
+      # TODO is valid base64 string?
+      data = form.cleaned_data['data'].decode('base64')
+      zk.set(path, data, form.cleaned_data['version'])
+
+    return tree(request, id, path)
+  else:
+    form = EditZNodeForm(dict(\
+      data=node.get('data64', ''), 
+      version=node.get('version', '-1')))
+
+  return render('edit.mako', request,
+    dict(path=path, form=form))
+
+def edit_as_text(request, id, path):
+  cluster = get_cluster_or_404(id)
+  zk = ZooKeeper(cluster['rest_gateway'])
+  node = zk.get(path)
+
+  if request.method == 'POST':
+    form = EditZNodeForm(request.POST)
+    if form.is_valid():
+      zk.set(path, form.cleaned_data['data'])
+
+    return tree(request, id, path)
+  else:
+    form = EditZNodeForm(dict(data=node.get('data64', '')\
+      .decode('base64').strip(), 
+      version=node.get('version', '-1')))
+
+  return render('edit.mako', request,
+    dict(path=path, form=form))
+
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py
new file mode 100644
index 0000000..ba44e26
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py
@@ -0,0 +1,23 @@
+#  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.
+
+from desktop.lib.windmill_util import logged_in_client
+
+def test_zkui():
+  """ launches the default view for zkui """
+  client = logged_in_client()
+  client.click(id='ccs-zkui-menu')
+  client.waits.forElement(classname='CCS-ZKUI', timeout='2000')  

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt b/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt
new file mode 100644
index 0000000..8ccaa1c
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt
@@ -0,0 +1,70 @@
+LogGraph README
+
+1 - About
+LogGraph is an application for viewing and filtering zookeeper logs. It can handle transaction logs and message logs. 
+
+2 - Compiling
+
+Run "ant jar" in src/contrib/loggraph/. This will download all dependencies and compile all the loggraph code.
+
+Once compilation has finished, you can run it the the loggraph.sh script in zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources.
+This will start and embedded web server on your machine.
+Navigate to http://localhost:8182/graph/main.html
+
+3 - Usage
+LogGraph presents the user with 4 views, 
+ 
+  a) Simple log view
+     This view simply displays the log text. This isn't very useful without filters (see "Filtering the logs").
+
+  b) Server view
+     The server view shows the interactions between the different servers in an ensemble. The X axis represents time. 
+        * Exceptions show up as red dots. Hovering your mouse over them will give you more details of the exception
+	* The colour of the line represents the election state of the server. 
+	   - orange means LOOKING for leader
+	   - dark green means the server is the leader
+	   - light green means the server is following a leader
+	   - yellow means there isn't enough information to determine the state of the server. 
+	* The gray arrows denote election messages between servers. Pink dashed arrows are messages that were sent but never delivered.
+
+  c) Session view
+     The session view shows the lifetime of sessions on a server. Use the time filter to narrow down the view. Any more than about 2000 events will take a long time to view in your browser. 
+     The X axis represents time. Each line is a session. The black dots represent events on the session. You can click on the black dots for more details of the event.
+
+  d) Stats view
+     There is currently only one statistics view, Transactions/minute. Suggestions for other statistic views are very welcome.
+
+4 - Filtering the logs
+The logs can be filtered in 2 ways, by time and by content. 
+
+To filter by time simply move the slider to the desired start time. The time window specifies how many milliseconds after and including the start time will be displayed.
+
+Content filtering uses a adhoc filtering language, using prefix notation. The language looks somewhat similar to lisp. A statement in the language takes the form (op arg arg ....). A statement resolves to a boolean value. Statements can be nested. 
+
+4.1 - Filter arguments
+An argument can be a number, a string or a symbol. A number is any argument which starts with -, + or 0 to 9. If the number starts with 0x it is interpretted as hexidecimal. Otherwise it is interpretted as decimal. If the argument begins with a double-quote, (") it is interpretted as a string. Anything else is interpretted as a symbol.
+
+4.2 - Filter symbols
+The possible filter symbols are: 
+
+client-id : number, the session id of the client who initiated a transaction.
+cxid : number, the cxid of a transaction
+zxid : number, the zxid of a transaction
+operation : string, the operation being performed, for example "setData", "createSession", "closeSession", "error", "create"
+
+4.3 - Filter operations
+The possible filter operations are:
+
+or : logical or, takes 1 or more arguments which must be other statements.
+and : logical and, takes 1 or more arguments which must be other statements.
+not : logical not, takes 1 argument which must be another statement.
+xor : exclusive or, takes 1 or more arguments which must be other statements.
+= : equals, takes 1 or more arguments, which must all be equal to each other to return true.
+> : greater than, takes 1 or more arguments, to return true the 1st argument must be greater than the 2nd argument which must be greater than the 3rd argument and so on... 
+< : less than, takes 1 or more arguments, to return true the 1st argument must be less than the 2nd argument which must be less than the 3rd argument and so on... 
+
+4.3 - Filter examples
+Give me all the setData operations with session id 0xdeadbeef or 0xcafeb33r but not with zxid 0x12341234 ->
+
+(and (= operation "setData") (or (= client-id 0xdeadbeef) (= client-id 0xcafeb33r)) (not (= zxid 0x12341234)))
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml b/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml
new file mode 100644
index 0000000..11143e7
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+
+<!--
+   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="loggraph" default="jar">
+
+  <import file="../build-contrib.xml"/>
+  
+  <target name="init" depends="check-contrib,zookeeperbuildcontrib.init" unless="skip.contrib">
+    <echo message="contrib: ${name}"/>
+    <mkdir dir="${build.dir}"/>
+    <antcall target="init-contrib"/>
+  </target>
+
+  <target name="compile" depends="init,ivy-retrieve,zookeeperbuildcontrib.compile" unless="skip.contrib">
+  </target>
+
+  <target name="setjarname">
+    <property name="jarname" value="${build.dir}/zookeeper-${version}-${name}.jar"/>
+  </target>
+
+  <target name="jar" depends="setjarname,compile"  >
+    <jar destfile="${jarname}">
+      <fileset file="${zk.root}/LICENSE.txt" />
+      <fileset dir="${build.classes}" />
+      <fileset dir="src/main/webapp"/>
+      <manifest>
+        <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.graph"/>
+        <attribute name="Implementation-Version" value="${revision}"/>
+        <attribute name="Implementation-Vendor" value="The Apache Software Foundation"/>
+      </manifest>
+    </jar>
+  </target>
+  
+  <target name="test">
+    <echo message="No test target defined for this package" />
+  </target>
+  
+
+  <target name="package" depends="compile, zookeeperbuildcontrib.package" unless="skip.contrib">
+    <echo message="contrib: ${name}"/>
+    
+    <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/main"/>
+    </copy>
+
+  </target>
+
+</project>

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml b/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml
new file mode 100644
index 0000000..d6fa9d6
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml
@@ -0,0 +1,44 @@
+<!--
+   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 ZooKeeper" url="http://zookeeper.apache.org"/>
+    <description>ZooKeeper Graphing</description>
+  </info>
+
+  <configurations defaultconfmapping="default">
+    <conf name="default"/>
+    <conf name="test"/>
+  </configurations>
+
+  <dependencies>
+    <dependency org="org.slf4j" name="slf4j-api" rev="1.6.1"/>
+    <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.6.1" transitive="false"/>
+  
+    <!-- transitive false turns off dependency checking, log4j deps seem borked -->
+    <dependency org="log4j" name="log4j" rev="1.2.15" transitive="false"/>
+    <dependency org="org.eclipse.jetty" name="jetty-server" rev="7.0.1.v20091125" />
+    <dependency org="org.eclipse.jetty" name="jetty-servlet" rev="7.0.1.v20091125" />
+    <dependency org="com.googlecode.json-simple" name="json-simple" rev="1.1" />
+  </dependencies>
+
+</ivy-module>

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java
new file mode 100644
index 0000000..c0912fa
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java
@@ -0,0 +1,22 @@
+/**
+ * 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.graph;
+
+public class FilterException extends Exception {
+    public FilterException(String s) { super(s); }
+};

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java
new file mode 100644
index 0000000..ee73283
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java
@@ -0,0 +1,75 @@
+/**
+ * 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.graph;
+
+import java.util.ArrayList;
+import org.apache.zookeeper.graph.filterops.*;
+
+public abstract class FilterOp {
+    protected ArrayList<FilterOp> subOps;
+    protected ArrayList<Arg> args;
+
+    public enum ArgType {
+	STRING, NUMBER, SYMBOL
+    }
+
+    public FilterOp() {
+	subOps = new ArrayList<FilterOp>();
+	args = new ArrayList<Arg>();
+    }
+
+    public static FilterOp newOp(String op) throws FilterException {
+	if (op.equals("or")) 
+	    return new OrOp();
+	if (op.equals("and"))
+	    return new AndOp();
+	if (op.equals("not"))
+	    return new NotOp();
+	if (op.equals("xor"))
+	    return new XorOp();
+	if (op.equals("="))
+	    return new EqualsOp();
+	if (op.equals("<"))
+	    return new LessThanOp();
+	if (op.equals(">")) 
+	    return new GreaterThanOp();
+
+	throw new FilterException("Invalid operation '"+op+"'");
+    }
+
+    public void addSubOp(FilterOp op) {
+	subOps.add(op);
+    }
+    
+    public void addArg(Arg arg) {
+	args.add(arg); 
+    }
+
+    public abstract boolean matches(LogEntry entry) throws FilterException;
+    
+    public String toString() {
+	String op = "(" + getClass().getName();
+	for (FilterOp f :  subOps) {
+	    op += " " + f;
+	}
+	for (Arg a : args) {
+	    op += " " + a;
+	}
+	return op + ")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java
new file mode 100644
index 0000000..cf12e3a
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java
@@ -0,0 +1,131 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph;
+
+import java.io.PushbackReader;
+import java.io.StringReader;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.zookeeper.graph.filterops.*;
+
+public class FilterParser {
+    private PushbackReader reader;
+
+    public FilterParser(String s) {
+	reader = new PushbackReader(new StringReader(s));
+    }
+
+    private String readUntilSpace() throws IOException {
+	StringBuffer buffer = new StringBuffer();
+
+	int c = reader.read();
+	while (!Character.isWhitespace(c) && c != ')' && c != '(') {
+	    buffer.append((char)c);
+	    c = reader.read();
+	    if (c == -1) {
+		break;
+	    }
+	}	
+	reader.unread(c);
+
+	return buffer.toString().trim();
+    }
+
+    private StringArg readStringArg() throws IOException, FilterException {
+	int c = reader.read();
+	int last = 0;
+	if (c != '"') {
+	    throw new FilterException("Check the parser, trying to read a string that doesn't begin with quotes");
+	}
+	StringBuffer buffer = new StringBuffer();
+	while (reader.ready()) {
+	    last = c;
+	    c = reader.read();
+	    if (c == -1) {
+		break;
+	    }
+	    
+	    if (c == '"' && last != '\\') {
+		return new StringArg(buffer.toString());
+	    } else {
+		buffer.append((char)c);
+	    }
+	}
+	throw new FilterException("Unterminated string");
+    }
+
+    private NumberArg readNumberArg() throws IOException, FilterException {
+	String strval = readUntilSpace();
+	
+	try {
+	    if (strval.startsWith("0x")) {
+		return new NumberArg(Long.valueOf(strval.substring(2), 16));
+	    } else {
+		return new NumberArg(Long.valueOf(strval));
+	    }
+	} catch (NumberFormatException e) {
+	    throw new FilterException("Not a number [" + strval + "]\n" + e);
+	}
+    }
+
+    private SymbolArg readSymbolArg() throws IOException, FilterException {
+	return new SymbolArg(readUntilSpace());
+    }
+
+    public FilterOp parse() throws IOException, FilterException {
+	int c = reader.read();
+	if (c != '(') {
+	    throw new FilterException("Invalid format");
+	}
+
+	String opstr = readUntilSpace();
+	FilterOp op = FilterOp.newOp(opstr);
+
+	while (reader.ready()) {
+	    c = reader.read();
+	    if (c == -1) {
+		break;
+	    }
+	    if (c == '(') {
+		reader.unread(c);
+		op.addSubOp(parse());
+	    } else if (c == ')') {
+		return op;
+	    } else if (c == '"') {
+		reader.unread(c);
+		op.addArg(readStringArg());
+	    } else if (Character.isDigit(c) || c == '-' || c == '+') {
+		reader.unread(c);
+		op.addArg(readNumberArg());
+	    } else if (Character.isJavaIdentifierStart(c)) {
+		reader.unread(c);
+		op.addArg(readSymbolArg());
+	    }
+	}
+	throw new FilterException("Incomplete filter");
+    }
+
+    public static void main(String[] args) throws IOException, FilterException {
+	if (args.length == 1) {
+	    System.out.println(new FilterParser(args[0]).parse());
+	} else {
+	    System.out.println(new FilterParser("(or (and (= session foobar) (= session barfoo)) (= session sdfs))").parse());
+	}
+    }
+};

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java
new file mode 100644
index 0000000..afaf3a1
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java
@@ -0,0 +1,223 @@
+/**
+ * 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.graph;
+
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+import java.io.Writer;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+public class JsonGenerator {
+    private JSONObject root;
+    private HashSet<Integer> servers;
+
+    private class Message {
+	private int from;
+	private int to;
+	private long zxid;
+
+	public Message(int from, int to, long zxid) {
+	    this.from = from;
+	    this.to = to;
+	    this.zxid = zxid;
+	}
+	
+	public boolean equals(Message m) {
+	    return (m.from == this.from 
+		    && m.to == this.to
+		    && m.zxid == this.zxid);
+	}
+    };
+
+    public JSONObject txnEntry(TransactionEntry e) {
+	JSONObject event = new JSONObject();
+
+	event.put("time", Long.toString(e.getTimestamp()));
+	event.put("client", Long.toHexString(e.getClientId()));
+	event.put("cxid", Long.toHexString(e.getCxid()));
+	event.put("zxid", Long.toHexString(e.getZxid()));
+	event.put("op", e.getOp());
+	event.put("extra", e.getExtra());
+	event.put("type", "transaction");
+
+	return event;
+    }
+
+    /**
+       Assumes entries are sorted by timestamp.
+     */
+    public JsonGenerator(LogIterator iter) {
+	servers = new HashSet<Integer>();
+
+	Pattern stateChangeP = Pattern.compile("- (LOOKING|FOLLOWING|LEADING)");
+	Pattern newElectionP = Pattern.compile("New election. My id =  (\\d+), Proposed zxid = (\\d+)");
+	Pattern receivedProposalP = Pattern.compile("Notification: (\\d+) \\(n.leader\\), (\\d+) \\(n.zxid\\), (\\d+) \\(n.round\\), .+ \\(n.state\\), (\\d+) \\(n.sid\\), .+ \\(my state\\)");
+	Pattern exceptionP = Pattern.compile("xception");
+	
+	root = new JSONObject();
+	Matcher m = null;
+	JSONArray events = new JSONArray();
+	root.put("events", events);
+	
+	long starttime = Long.MAX_VALUE;
+	long endtime = 0;
+
+	int leader = 0;
+	long curEpoch = 0;
+	boolean newEpoch = false;
+
+	while (iter.hasNext()) {
+	    LogEntry ent = iter.next();
+	    
+	    if (ent.getTimestamp() < starttime) {
+		starttime = ent.getTimestamp();
+	    }
+	    if (ent.getTimestamp() > endtime) {
+		endtime = ent.getTimestamp();
+	    }
+	    
+	    if (ent.getType() == LogEntry.Type.TXN) {
+		events.add(txnEntry((TransactionEntry)ent));
+	    } else {
+		Log4JEntry e = (Log4JEntry)ent;
+		servers.add(e.getNode());
+		
+		if ((m = stateChangeP.matcher(e.getEntry())).find()) {
+		    JSONObject stateChange = new JSONObject();
+		    stateChange.put("type", "stateChange");
+		    stateChange.put("time", e.getTimestamp());
+		    stateChange.put("server", e.getNode());
+		    stateChange.put("state", m.group(1));
+		    events.add(stateChange);
+		    
+		    if (m.group(1).equals("LEADING")) {
+			leader = e.getNode();
+		    }
+		} else if ((m = newElectionP.matcher(e.getEntry())).find()) {
+		    Iterator<Integer> iterator = servers.iterator();
+		    long zxid = Long.valueOf(m.group(2));
+		    int count = (int)zxid;// & 0xFFFFFFFFL;
+		    int epoch = (int)Long.rotateRight(zxid, 32);// >> 32;
+		    
+		    if (leader != 0 && epoch > curEpoch) {
+			JSONObject stateChange = new JSONObject();
+			stateChange.put("type", "stateChange");
+			stateChange.put("time", e.getTimestamp());
+			stateChange.put("server", leader);
+			stateChange.put("state", "INIT");
+			events.add(stateChange);
+			leader = 0;
+		    }
+		    
+		    while (iterator.hasNext()) {
+			int dst = iterator.next();
+			if (dst != e.getNode()) {
+			    JSONObject msg = new JSONObject();
+			    msg.put("type", "postmessage");
+			    msg.put("src", e.getNode());
+			    msg.put("dst", dst);
+			    msg.put("time", e.getTimestamp());
+			    msg.put("zxid", m.group(2));
+			    msg.put("count", count);
+			    msg.put("epoch", epoch);
+			    
+			    events.add(msg);
+			}
+		    }
+		} else if ((m = receivedProposalP.matcher(e.getEntry())).find()) {
+		    // Pattern.compile("Notification: \\d+, (\\d+), (\\d+), \\d+, [^,]*, [^,]*, (\\d+)");//, LOOKING, LOOKING, 2
+		    int src = Integer.valueOf(m.group(4));
+		    long zxid = Long.valueOf(m.group(2));
+		    int dst = e.getNode();
+		    long epoch2 = Long.valueOf(m.group(3));
+		    
+		    int count = (int)zxid;// & 0xFFFFFFFFL;
+		    int epoch = (int)Long.rotateRight(zxid, 32);// >> 32;
+		    
+		    if (leader != 0 && epoch > curEpoch) {
+			JSONObject stateChange = new JSONObject();
+			stateChange.put("type", "stateChange");
+			stateChange.put("time", e.getTimestamp());
+			stateChange.put("server", leader);
+			stateChange.put("state", "INIT");
+			events.add(stateChange);
+			leader = 0;
+		    }
+		    
+		    if (src != dst) {
+			JSONObject msg = new JSONObject();
+			msg.put("type", "delivermessage");
+			msg.put("src", src);
+			msg.put("dst", dst);
+			msg.put("time", e.getTimestamp());
+			msg.put("zxid", zxid);
+			msg.put("epoch", epoch);
+			msg.put("count", count);
+			msg.put("epoch2", epoch2);
+			
+			events.add(msg);
+		    }
+		} else if ((m = exceptionP.matcher(e.getEntry())).find()) {
+		    JSONObject ex = new JSONObject();
+		    ex.put("type", "exception");
+		    ex.put("server", e.getNode());
+		    ex.put("time", e.getTimestamp());
+		    ex.put("text", e.getEntry());
+		    events.add(ex);
+		} 
+	    }
+	    JSONObject ex = new JSONObject();
+	    ex.put("type", "text");
+	    ex.put("time", ent.getTimestamp());
+	    String txt = ent.toString();
+	    ex.put("text", txt);
+	    events.add(ex);
+	}
+	//	System.out.println("pending messages: "+pendingMessages.size());
+	root.put("starttime", starttime);
+	root.put("endtime", endtime);
+
+	JSONArray serversarray = new JSONArray();
+	root.put("servers", serversarray);
+	
+	Iterator<Integer> iterator = servers.iterator();
+	while (iterator.hasNext()) {
+	    serversarray.add(iterator.next());
+	}
+    }
+
+    public String toString() {
+	return JSONValue.toJSONString(root);
+    }
+
+    public static void main(String[] args) throws Exception {
+	MergedLogSource src = new MergedLogSource(args);
+	LogIterator iter = src.iterator();
+	System.out.println(new JsonGenerator(iter));
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java
new file mode 100644
index 0000000..0edc146
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java
@@ -0,0 +1,40 @@
+/**
+ * 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.graph;
+
+public class Log4JEntry extends LogEntry {
+    public Log4JEntry(long timestamp, int node, String entry) { 
+	super(timestamp);
+	setAttribute("log-text", entry);
+	setAttribute("node", new Integer(node));
+    }
+
+    public String getEntry() {
+	return (String) getAttribute("log-text");
+    }
+
+    public String toString() {
+	return "" + getTimestamp() + "::::" + getNode() + "::::"  + getEntry();
+    }
+
+    public int getNode() {
+	return (Integer) getAttribute("node");
+    }
+
+    public Type getType() { return LogEntry.Type.LOG4J; }
+}


Mime
View raw message