zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an...@apache.org
Subject [17/45] zookeeper git commit: ZOOKEEPER-3030: MAVEN MIGRATION - Step 1.3 - move contrib directories
Date Mon, 06 Aug 2018 12:14:26 GMT
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt b/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt
new file mode 100644
index 0000000..8c5f701
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt
@@ -0,0 +1,355 @@
+# 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.
+
+A REST HTTP gateway for ZooKeeper
+=================================
+
+Specification Version: 2
+
+ZooKeeper is meant to enable distributed coordination and also store
+system configuration and other relatively small amounts of information
+that must be stored in a persistent and consistent manner. The
+information stored in ZooKeeper is meant to be highly available to a
+large number of nodes in a distributed-computing cluster.
+
+ZooKeeper offers a client-side library that supports rich semantics
+that include strict ordering guarantees on operations, the creation of
+ephemeral znodes, and the ability to watch for changes to state.
+However, where clients need simple "CRUD" (create, read, update,
+delete) operations, the ZooKeeper libraries can be cumbersome, both to
+the programmers who have to use them (who are increasingly used to
+REST-style APIs), and to the operators who have to deploy and update
+them (for whom deploying and updating client libraries can be very
+painful).
+
+It turns out that most languages comes with client libraries for HTTP
+that are easy and familiar to program against, and deployed as part of
+the language runtime. Thus, for simple CRUD clients, an HTTP gateway
+would be a less cumbersome interface than the ZooKeeper library.
+
+This document describes a gatway for using HTTP to interact with a
+ZooKeeper repository.
+
+Binding ZooKeeper to HTTP
+-------------------------
+
+Encoding
+--------
+
+UTF-8 unless otherwise noted
+
+Paths
+-----
+
+A ZooKeeper paths are mapped to IRIs and URIs as follows. ZK paths
+are converted to IRIs by simply percent-encoding any characters in the
+ZK path that are not allowed in IRI paths. ZK paths are converted to
+URIs by mapping them first to IRIs, then converting to URIs in the
+standard way.
+
+Going from URIs and IRIs is the reverse of the above but for one
+difference: any "." and ".." segments in an IRI or URI must be folded
+before conversion. (Fortunately, ZK does not allow "." and ".."
+segments in its paths.)
+
+ZK and IRIs recommend the same practices when it comes to Unicode
+normalization: ultimately, normalization is left to application
+designers, but both recommend that application designers use NFC as a
+best practice.
+
+Root
+----
+
+The following examples assume that the ZooKeeper znode heirarchy is
+bound to the root of the HTTP servers namespace. This may not be the
+case in practice however, the gateway may bind to some prefix, for
+example the URL for accessing /a/b/c may be:
+
+  http://localhost/zookeeper/znodes/v1/a/b/c
+
+This is perfectly valid. Users of the REST service should be aware of
+this fact and code their clients to support any root (in this case
+"/zookeeper" on the server localhost).
+
+
+Basics: GET, PUT, HEAD, and DELETE
+----------------------------------
+
+HTTP's GET, PUT, HEAD, and DELETE operations map naturally to
+ZooKeeper's "get," "set," "exists," and "delete" operations.
+
+ZooKeeper znodes have a version number that changes each time the
+znode's value is updated. This number is returned by "get," "set," and
+"exists" operations. The "set" and "delete" operations optionally take
+a version number. If one is supplied, then "set" or "delete" will fail
+if the current version of the znode doesn't match the version-number
+supplied in the call. This mechanism supports atomic read-modify-write
+cycles. Set/delete requests may include an optional parameter
+"version" which defaults to no version check.
+
+
+Getting ZooKeeper children
+--------------------------
+
+We overload the GET method to return the children of a ZooKeeper. In
+particular, the GET method takes an optional parameter "view" which
+could be set to one of type values, either "data" or "children". The
+default is "data". Thus, to get the children of a znode named
+"/a/b/c", then the GET request should start:
+
+  GET /znodes/v1/a/b/c?view=children HTTP/1.1
+
+If the requested view is "data", then the data of a znode is returned
+as described in the previous section. If the requested view is
+"children", then a list of children is returned in either an XML
+document, or in a JSON object. (The default is JSON, but this can be
+controlled changed by setting the Accept header.)
+
+
+Creating a ZooKeeper session
+----------------------------
+
+In order to be able to create ephemeral nodes you first need to start
+a new session.
+
+  POST /sessions/v1?op=create&expire=<SECONDS> HTTP/1.1
+
+If the session creation is successful, then a 201 code will be returned.
+
+A session is just an UUID that you can pass around as a parameter and
+the REST server will foward your request on the attached persistent 
+connection.
+
+Keeping a session alive
+-----------------------
+
+To keep a session alive you must send hearbeat requests:
+
+  PUT /sessions/v1/<SESSION-UUID> HTTP/1.1
+
+Closing a ZooKeeper session
+---------------------------
+
+You can close a connection by sending a DELETE request.
+
+  DELETE /sessions/v1/<SESSION-UUID> HTTP/1.1
+
+If you don't close a session it will automatically expire after
+the amount of time you specified on creation. 
+
+Creating a ZooKeeper znode
+--------------------------
+
+We use the POST method to create a ZooKeeper znode. For example, to
+create a znode named "c" under a parent named "/a/b", then the POST
+request should start:
+
+  POST /znodes/v1/a/b?op=create&name=c HTTP/1.1
+
+If the creation is successful, then a 201 code will be returned. If
+it fails, then a number of different codes might be returned
+(documented in a later subsection).
+
+ZooKeeper's create operation has a flag that tells the server to
+append a sequence-number to the client-supplied znode-name in order to
+make the znode-name unique. If you set this flag and ask to create a
+znode named "/a/b/c", and a znode named "/a/b" already exists, then
+"create" will create a znode named "/a/b/c-#" instead, where "#" is and
+integer required to generate a unique name in for format %10d.
+
+To obtain this behavior, an additional "sequence=true" parameter
+should be added to the parameters of the POST. (Note that "sequence"
+is an optional parameter, that defaults to "false"; this default may
+be provided explicitly if desired.)
+
+On success the actual path of the created znode will be returned.
+
+If you want to create an ephemeral node you need to specify an
+additional "ephemeral=true" parameter. (Note that "ephemeral" is an optional
+parameter, that defaults to "false")
+
+(Note: ZooKeeper also allows the client to set ACLs for the
+newly-created znode. This feature is not currently supported by the
+HTTP gateway to ZooKeeper.)
+
+
+Content types and negotiation
+-----------------------------
+
+ZooKeeper REST gateway implementations may support three content-types
+for request and response messages:
+
+* application/octet-stream
+
+  HEAD   - returns nothing (note below: status = 204)
+  GET    - returns the znode data as an octet-stream
+  PUT    - send binary data, returns nothing
+  POST   - send binary data, returns the name of the znode
+  DELETE - returns nothing
+
+  For PUT and HEAD some other content-type (i.e. JSON or XML) must be
+  used to access the Stat information of a znode.
+
+* application/json, application/javascript & application/xml
+
+  HEAD   - returns nothing
+  GET    - returns a STAT or CHILD structure
+  PUT    - send binary data, returns a STAT structure (sans data field)
+  POST   - send binary data, returns a PATH structure
+  DELETE - returns nothing
+
+  (structures defined below)
+
+  Results returning DATA may include an optional "dataformat"
+  parameter which has two possible values; base64 (default) or
+  utf8. This allows the caller to control the format of returned data
+  and may simplify usage -- for example cat'ing results to the command
+  line with something like curl, or accessing a url through a browser.
+  Care should be exercised however, if utf8 is used on non character
+  data errors may result.
+
+  "application/javascript" requests may include an optional "callback"
+  parameter. The response is wrapped in a callback method of your
+  choice. e.g. appending &callback=foo to your request will result in
+  a response body of: foo(...). Callbacks may only contain
+  alphanumeric characters and underscores.
+
+PATH
+  path : string
+  uri: string
+
+  path is the full path to the znode as seen by ZooKeeper
+
+  uri is the full URI of the znode as seen by the REST server, does not
+  include any query parameters (i.e. it's the path to the REST resource)
+
+SESSION
+  id : string UUID
+  uri : string
+
+CHILD
+  PATH
+  child_uri_template: string
+  children : [ string* ]
+
+  The children list of strings contains only the name of the child
+  znodes, not the full path.
+
+  child_uri_template is a template for URI of child znodes as seen by the
+  REST server. e.g. "http://localhost:9998/znodes/v1/foo/{child}", where
+  foo is the parent node, and {child} can be substituted with the name
+  of each child in the children array in order to access that resource.
+  This template is provided to simplify child access.
+    
+STAT
+  PATH
+  encoding : value of "base64" or "utf8"
+  data     : base64 or utf8 encoded string
+  stat :
+    czxid          : number
+    mzxid          : number
+    ctime          : number
+    mtime          : number
+    version        : number
+    cversion       : number
+    aversion       : number
+    ephemeralOwner : number
+    datalength     : number
+    numChildren    : number
+    pzxid          : number
+
+
+Error Codes
+-----------
+
+The ZooKeeper gateway uses HTTP response codes as follows:
+
+   * 200 (Success) - ZOK for "get" "set" "delete", "yes" case of "exists" (json/xml)
+   * 201 (Created) - ZOK for "create"
+   * 204 (No Content) - ZOK for "yes" case of "exists" (octet)
+   * 400 (Bad Request) - ZINVALIDACL, ZBADARGUMENTS, version param not a number
+   * 401 (Unauthorized) - ZAUTHFAILED
+   * 404 (Not Found) - ZOK for "no" case of "exists;" ZNONODE for "get," "set," and "delete"
+   * 409 (Conflict) - ZNODEEXISTS, ZNONODE for "create," ZNOTEMPTY, 
+   * 412 (Precondition Failed) - ZBADVERSION
+   * 415 (Unsupported Media Type) - if content-type of PUT or POST is not "application/octet-stream"
+   * 500 (Internal Server Error) - Failure in gateway code
+   * 501 (Not Implemented) - HTTP method other than GET, PUT, HEAD, DELETE
+   * 502 (Bad Gateway) - All other ZooKeeper error codes
+   * 503 (Service Unavailable) - ZSESSIONEXPIRED, ZCONNECTIONLOSS, (gateway will try to reestablish the connection, but will not hold the request waiting...)
+   * 504 (Gateway Timeout) - ZOPERATIONTIMEOUT, or ZooKeeper does not return in a timely manner
+
+Note that these are the codes used by the HTTP-to-Gateway software
+itself. Depending on how this software is configured into a Web
+server, the resulting Web Server might behave differently, e.g., it
+might do redirection, check other headers, etc.
+
+Error Messages
+--------------
+
+Error messages are returned to the caller, format is dependent on the
+format requested in the call. 
+
+* application/octet-stream
+
+  A string containing the error message. It should include the request
+  and information detailing the reason for the error.
+
+* application/json
+
+  { "request":"GET /a/b/c", "message":"Node doesn't exist" }
+
+* application/xml
+
+<?xml version="1.0" encoding="UTF-8"?>
+<error>
+  <request>GET /a/b/c</request>
+  <message>Node doesn't exist</message>
+</error>
+
+
+Binding ZooKeeper to an HTTP server
+-----------------------------------
+
+It might be sage to assume that everyone is happy to run an Apache
+server, and thus write a "mod_zookeeper" for Apache that works only
+for the Apache Web Server. However, different operational
+environments prefer different Web Servers, and it would be nice to
+support more than one Web server.
+
+Issues:
+
+   * Configuration.
+
+   * Defining a root: Need to provide a URL alias and associate it
+     with a server. Need to be able to map different aliases to
+     different servers (implemented via multiple ZK connections).
+
+   * Sharing connection across multiple processes.
+
+   * Asynchronous.
+
+   * Adaptors.
+
+   * Code re-use.
+
+
+Authentication -- TBD, not currently supported
+
+...the config file should contain authentication material for the gateway
+
+...the config file should contain an ACL list to be passed along to "create"
+
+...would we ever want to authenticate each request to ZooKeeper?...

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/build.xml
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/build.xml b/zookeeper-contrib/zookeeper-contrib-rest/build.xml
new file mode 100644
index 0000000..5097182
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/build.xml
@@ -0,0 +1,189 @@
+<?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="rest" default="jar">
+  <import file="../build-contrib.xml"/>
+
+	<property name="test.build.dir" value="${build.test}" />
+    <property name="test.src.dir" value="src/test/java"/>
+    <property name="test.log.dir" value="${test.build.dir}/logs" />
+    <property name="test.data.dir" value="${test.build.dir}/data" />
+    <property name="test.data.upgrade.dir" value="${test.data.dir}/upgrade" />
+    <property name="test.tmp.dir" value="${test.build.dir}/tmp" />
+    <property name="test.output" value="no" />
+    <property name="test.timeout" value="900000" />
+    <property name="test.junit.output.format" value="plain" />
+    <property name="test.junit.fork.mode" value="perTest" />
+    <property name="test.junit.printsummary" value="yes" />
+    <property name="test.junit.haltonfailure" value="no" />
+    <property name="test.junit.maxmem" value="512m" />
+
+    <!-- ====================================================== -->
+    <!-- Macro definitions                                      -->
+    <!-- ====================================================== -->
+    <macrodef name="macro_tar" description="Worker Macro for tar">
+      <attribute name="param.destfile"/>
+      <element name="param.listofitems"/>
+      <sequential>
+        <tar compression="gzip" longfile="gnu"
+             destfile="@{param.destfile}">
+          <param.listofitems/>
+        </tar>
+      </sequential>
+    </macrodef>
+
+  <target name="package" depends="jar" unless="skip.contrib">
+    <echo message="contrib: ${name}"/>
+    <mkdir dir="${dist.dir}${package.share}/contrib/${name}"/>
+    <copy todir="${dist.dir}${package.share}/contrib/${name}">
+      <fileset dir="${build.dir}">
+        <include name="zookeeper-${version}-${name}.jar" />
+      </fileset>
+    </copy>
+    <copy todir="${dist.dir}${package.share}/contrib/${name}/lib">
+      <fileset dir="${build.dir}/lib" />
+    </copy>
+    <copy todir="${dist.dir}${package.share}/contrib/${name}/conf">
+      <fileset dir="conf" />
+    </copy>
+  </target>
+
+  <target name="setjarname">
+    <property name="jarname"
+              value="${build.dir}/zookeeper-${version}-${name}.jar"/>
+  </target>
+
+  <target name="compile" depends="ivy-retrieve,zookeeperbuildcontrib.compile"/>
+
+  <target name="test"
+          depends="compile-test,test-init,test-category,test-start,junit.run,test-stop" />
+
+	<target name="compile-test" depends="ivy-retrieve-test,compile">
+  		<property name="target.jdk" value="${ant.java.version}" />	
+		<property name="src.test.local" location="${basedir}/src/test" />
+		<mkdir dir="${build.test}"/>
+		<javac srcdir="${src.test.local}" 
+			destdir="${build.test}" 
+			target="${target.jdk}" 
+			debug="on" encoding="${build.encoding}">
+			<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-start">
+      <exec executable="${test.src.dir}/zkServer.sh">
+        <arg value="startClean"/>
+      </exec>
+    </target>
+
+    <target name="test-stop">
+      <exec executable="${test.src.dir}/zkServer.sh">
+        <arg value="stop"/>
+      </exec>
+    </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}" />
+          </classpath>
+	  <classpath>
+	    <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="jar" depends="checkMainCompiled, setjarname, compile">
+    <echo message="contrib: ${name}"/>
+    <jar jarfile="${jarname}">
+      <fileset file="${zk.root}/LICENSE.txt" />
+      <fileset dir="${build.classes}"/>
+      <fileset dir="${build.test}"/>
+    </jar>
+  </target>
+
+  <target name="run" depends="jar">
+    <echo message="contrib: ${name}"/>
+    <java classname="org.apache.zookeeper.server.jersey.RestMain" fork="true">
+      <classpath>
+        <pathelement path="${jarname}" />
+        <fileset dir="${build.dir}/lib" includes="*.jar"/>
+        <fileset dir="${zk.root}/build" includes="zookeeper-*.jar"/>
+        <pathelement path="${zk.root}/src/contrib/${name}/conf" />
+        <fileset dir="${zk.root}/src/java/lib">
+          <include name="**/*.jar" />
+        </fileset>
+      </classpath>
+    </java>
+  </target>
+
+  <target name="tar" depends="clean, jar">
+    <echo message="building tar.gz: ${name}" />
+    <macro_tar param.destfile="${build.dir}/zookeeper-${version}-${name}.tar.gz">
+      <param.listofitems>
+        <tarfileset dir="${build.dir}/lib" prefix="lib" includes="**/*.jar" />
+        <tarfileset file="${build.dir}/zookeeper-*-rest.jar" />
+        <tarfileset dir="${zk.root}/build" includes="zookeeper-*.jar" prefix="lib" />
+        <tarfileset dir="${zk.root}/src/contrib/${name}/conf" prefix="conf" />
+        <tarfileset dir="${zk.root}/src/java/lib" prefix="lib" includes="**/*.jar" />
+        <tarfileset file="${zk.root}/src/contrib/${name}/rest.sh" />
+      </param.listofitems>
+    </macro_tar>
+  </target>
+
+</project>
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/README
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/README b/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/README
new file mode 100644
index 0000000..085810a
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/README
@@ -0,0 +1,8 @@
+
+In order to generate .jks (java keystore files) you need to use keytool.
+
+The password for the existing .jks is "123456" (without quotes).
+
+Some tutorials:
+ - http://www.mobilefish.com/tutorials/java/java_quickguide_keytool.html
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.cer
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.cer b/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.cer
new file mode 100644
index 0000000..13e5aab
Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.cer differ

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.jks
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.jks b/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.jks
new file mode 100644
index 0000000..539e8be
Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.jks differ

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/conf/log4j.properties
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/conf/log4j.properties b/zookeeper-contrib/zookeeper-contrib-rest/conf/log4j.properties
new file mode 100644
index 0000000..21ba7e4
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/conf/log4j.properties
@@ -0,0 +1,68 @@
+#
+# 
+# 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.
+# 
+#
+
+#
+# ZooKeeper Logging Configuration
+#
+
+# Format is "<default threshold> (, <appender>)+
+
+# DEFAULT: console appender only
+log4j.rootLogger=INFO, CONSOLE
+
+# Example with rolling log file
+#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
+
+# Example with rolling log file and tracing
+#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
+
+#
+# Log INFO level and above messages to the console
+#
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Threshold=INFO
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
+
+#
+# Add ROLLINGFILE to rootLogger to get log file output
+#    Log DEBUG level and above messages to a log file
+log4j.appender.ROLLINGFILE=org.apache.log4j.ConsoleAppender
+log4j.appender.ROLLINGFILE.Threshold=DEBUG
+log4j.appender.ROLLINGFILE.File=bookkeeper.log
+log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
+
+# Max log file size of 10MB
+log4j.appender.ROLLINGFILE.MaxFileSize=10MB
+# uncomment the next line to limit number of backup files
+#log4j.appender.ROLLINGFILE.MaxBackupIndex=10
+
+#
+# Add TRACEFILE to rootLogger to get log file output
+#    Log DEBUG level and above messages to a log file
+log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
+log4j.appender.TRACEFILE.Threshold=TRACE
+log4j.appender.TRACEFILE.File=bookkeeper_trace.log
+
+log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
+### Notice we are including log4j's NDC here (%x)
+log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/conf/rest.properties
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/conf/rest.properties b/zookeeper-contrib/zookeeper-contrib-rest/conf/rest.properties
new file mode 100644
index 0000000..f0abb45
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/conf/rest.properties
@@ -0,0 +1,70 @@
+#
+# 
+# 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.
+# 
+#
+
+#
+# ZooKeeper REST Gateway Configuration file
+#
+
+rest.port = 9998
+
+#
+# Endpoint definition
+#
+
+# plain configuration <context-path>;<host-port>
+rest.endpoint.1 = /;localhost:2181,localhost:2182
+
+# ... or chrooted to /zookeeper
+# rest.endpoint.1 = /;localhost:2181,localhost:2182/zookeeper
+
+# HTTP Basic authentication for this endpoint
+# rest.endpoint.1.http.auth = root:root1
+
+# create -e /a data digest:'demo:ojnHEyje6F33LLzGVzg+yatf4Fc=':cdrwa
+# any session on this endpoint will use authentication
+# rest.endpoint.1.zk.digest = demo:test
+
+# you can easily generate the ACL using Python:
+# import sha; sha.sha('demo:test').digest().encode('base64').strip()
+
+#
+# ... you can define as many endpoints as you wish
+#
+
+# rest.endpoint.2 = /restricted;localhost:2181
+# rest.endpoint.2.http.auth = admin:pass
+
+# rest.endpoint.3 = /cluster1;localhost:2181,localhost:2182
+# ** you should configure one end-point for each ZooKeeper cluster
+# etc.
+
+# Global HTTP Basic Authentication 
+# You should also enable HTTPS-only access
+# The authentication credentials are sent as plain text
+
+# rest.http.auth = guest:guest1
+
+# Uncomment the lines bellow to allow https-only access
+
+# rest.ssl = true
+# rest.ssl.jks = keys/rest.jks
+# rest.ssl.jks.pass = 123456
+ 
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/ivy.xml
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/ivy.xml b/zookeeper-contrib/zookeeper-contrib-rest/ivy.xml
new file mode 100644
index 0000000..5ed8a9e
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/ivy.xml
@@ -0,0 +1,48 @@
+<!--
+   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 REST</description>
+  </info>
+
+  <configurations defaultconfmapping="default">
+    <conf name="default"/>
+    <conf name="test"/>
+  </configurations>
+
+  <dependencies>
+    <dependency org="org.slf4j" name="slf4j-api" rev="1.7.5"/>
+    <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.5" transitive="false"/>
+  
+    <!-- transitive false turns off dependency checking, log4j deps seem borked -->
+    <dependency org="log4j" name="log4j" rev="1.2.17" transitive="false"/>
+    <dependency org="asm" name="asm" rev="3.1" />
+    <dependency org="com.sun.grizzly" name="grizzly-servlet-webserver" rev="1.9.8" />
+    <dependency org="com.sun.jersey" name="jersey-server" rev="1.1.5.1" />
+    <dependency org="com.sun.jersey" name="jersey-json" rev="1.1.5.1" />
+
+    <dependency org="junit" name="junit" rev="4.12" conf="test->default"/>
+    <dependency org="com.sun.jersey" name="jersey-client" rev="1.1.5.1" conf="test->default"/>
+  </dependencies>
+
+</ivy-module>

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/rest.sh
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/rest.sh b/zookeeper-contrib/zookeeper-contrib-rest/rest.sh
new file mode 100644
index 0000000..daa8198
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/rest.sh
@@ -0,0 +1,90 @@
+#!/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.
+
+#
+# If this scripted is run out of /usr/bin or some other system bin directory
+# it should be linked to and not copied. Things like java jar files are found
+# relative to the canonical path of this script.
+#
+
+# Only follow symlinks if readlink supports it
+if readlink -f "$0" > /dev/null 2>&1
+then
+  ZKREST=`readlink -f "$0"`
+else
+  ZKREST="$0"
+fi
+ZKREST_HOME=`dirname "$ZKREST"`
+
+if $cygwin
+then
+    # cygwin has a "kill" in the shell itself, gets confused
+    KILL=/bin/kill
+else
+    KILL=kill
+fi
+
+if [ -z $ZKREST_PIDFILE ]
+    then ZKREST_PIDFILE=$ZKREST_HOME/server.pid
+fi
+
+ZKREST_MAIN=org.apache.zookeeper.server.jersey.RestMain
+
+ZKREST_CONF=$ZKREST_HOME/conf
+ZKREST_LOG=$ZKREST_HOME/zkrest.log
+
+CLASSPATH="$ZKREST_CONF:$CLASSPATH"
+
+for i in "$ZKREST_HOME"/lib/*.jar
+do
+    CLASSPATH="$i:$CLASSPATH"
+done
+
+for i in "$ZKREST_HOME"/zookeeper-*.jar
+do
+    CLASSPATH="$i:$CLASSPATH"
+done
+
+case $1 in
+start)
+    echo  "Starting ZooKeeper REST Gateway ... "
+    java  -cp "$CLASSPATH" $JVMFLAGS $ZKREST_MAIN >$ZKREST_LOG 2>&1 &
+    /bin/echo -n $! > "$ZKREST_PIDFILE"
+    echo STARTED
+    ;;
+stop)
+    echo "Stopping ZooKeeper REST Gateway ... "
+    if [ ! -f "$ZKREST_PIDFILE" ]
+    then
+    echo "error: could not find file $ZKREST_PIDFILE"
+    exit 1
+    else
+    $KILL -9 $(cat "$ZKREST_PIDFILE")
+    rm "$ZKREST_PIDFILE"
+    echo STOPPED
+    fi
+    ;;
+restart)
+    shift
+    "$0" stop ${@}
+    sleep 3
+    "$0" start ${@}
+    ;;
+*)
+    echo "Usage: $0 {start|stop|restart}" >&2
+
+esac

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java
new file mode 100644
index 0000000..954ad04
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java
@@ -0,0 +1,151 @@
+/**
+ * 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.server.jersey;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.server.jersey.cfg.Credentials;
+import org.apache.zookeeper.server.jersey.cfg.Endpoint;
+import org.apache.zookeeper.server.jersey.cfg.RestCfg;
+import org.apache.zookeeper.server.jersey.filters.HTTPBasicAuth;
+
+import com.sun.grizzly.SSLConfig;
+import com.sun.grizzly.http.embed.GrizzlyWebServer;
+import com.sun.grizzly.http.servlet.ServletAdapter;
+import com.sun.jersey.spi.container.servlet.ServletContainer;
+
+/**
+ * Demonstration of how to run the REST service using Grizzly
+ */
+public class RestMain {
+
+   private static Logger LOG = LoggerFactory.getLogger(RestMain.class);
+
+   private GrizzlyWebServer gws;
+   private RestCfg cfg;
+
+   public RestMain(RestCfg cfg) {
+       this.cfg = cfg;
+   }
+
+   public void start() throws IOException {
+       System.out.println("Starting grizzly ...");
+
+       boolean useSSL = cfg.useSSL();
+       gws = new GrizzlyWebServer(cfg.getPort(), "/tmp/23cxv45345/2131xc2/", useSSL);
+       // BUG: Grizzly needs a doc root if you are going to register multiple adapters
+
+       for (Endpoint e : cfg.getEndpoints()) {
+           ZooKeeperService.mapContext(e.getContext(), e);
+           gws.addGrizzlyAdapter(createJerseyAdapter(e), new String[] { e
+                   .getContext() });
+       }
+       
+       if (useSSL) {
+           System.out.println("Starting SSL ...");
+           String jks = cfg.getJKS("keys/rest.jks");
+           String jksPassword = cfg.getJKSPassword();
+
+           SSLConfig sslConfig = new SSLConfig();
+           URL resource = getClass().getClassLoader().getResource(jks);
+           if (resource == null) {
+               LOG.error("Unable to find the keystore file: " + jks);
+               System.exit(2);
+           }
+           try {
+               sslConfig.setKeyStoreFile(new File(resource.toURI())
+                       .getAbsolutePath());
+           } catch (URISyntaxException e1) {
+               LOG.error("Unable to load keystore: " + jks, e1);
+               System.exit(2);
+           }
+           sslConfig.setKeyStorePass(jksPassword);
+           gws.setSSLConfig(sslConfig);
+       }
+
+       gws.start();
+   }
+
+   public void stop() {
+       gws.stop();
+       ZooKeeperService.closeAll();
+   }
+
+   private ServletAdapter createJerseyAdapter(Endpoint e) {
+       ServletAdapter jersey = new ServletAdapter();
+
+       jersey.setServletInstance(new ServletContainer());
+       jersey.addInitParameter("com.sun.jersey.config.property.packages",
+               "org.apache.zookeeper.server.jersey.resources");
+       jersey.setContextPath(e.getContext());
+
+       Credentials c = Credentials.join(e.getCredentials(), cfg
+               .getCredentials());
+       if (!c.isEmpty()) {
+           jersey.addFilter(new HTTPBasicAuth(c), e.getContext()
+                   + "-basic-auth", null);
+       }
+
+       return jersey;
+   }
+
+   /**
+    * The entry point for starting the server
+    * 
+    */
+   public static void main(String[] args) throws Exception {
+       RestCfg cfg = new RestCfg("rest.properties");
+
+       final RestMain main = new RestMain(cfg);
+       main.start();
+
+       Runtime.getRuntime().addShutdownHook(new Thread() {
+           @Override
+           public void run() {
+               main.stop();
+               System.out.println("Got exit request. Bye.");
+           }
+       });
+
+       printEndpoints(cfg);
+       System.out.println("Server started.");
+   }
+
+   private static void printEndpoints(RestCfg cfg) {
+       int port = cfg.getPort();
+
+       for (Endpoint e : cfg.getEndpoints()) {
+
+           String context = e.getContext();
+           if (context.charAt(context.length() - 1) != '/') {
+               context += "/";
+           }
+
+           System.out.println(String.format(
+                   "Started %s - WADL: http://localhost:%d%sapplication.wadl",
+                   context, port, context));
+       }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java
new file mode 100644
index 0000000..21d27a9
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java
@@ -0,0 +1,242 @@
+/**
+ * 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.server.jersey;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.server.jersey.cfg.Endpoint;
+
+/**
+ * Singleton which provides JAX-RS resources access to the ZooKeeper client.
+ * There's a single session for each base uri (so usually just one).
+ */
+public class ZooKeeperService {
+
+   private static Logger LOG = LoggerFactory.getLogger(ZooKeeperService.class);
+
+   /** Map base uri to ZooKeeper host:port parameters */
+   private static Map<String, Endpoint> contextMap = new HashMap<String, Endpoint>();
+
+   /** Map base uri to ZooKeeper session */
+   private static Map<String, ZooKeeper> zkMap = new HashMap<String, ZooKeeper>();
+
+   /** Session timers */
+   private static Map<String, SessionTimerTask> zkSessionTimers = new HashMap<String, SessionTimerTask>();
+   private static Timer timer = new Timer();
+
+   /** Track the status of the ZooKeeper session */
+   private static class MyWatcher implements Watcher {
+       final String contextPath;
+
+       /** Separate watcher for each base uri */
+       public MyWatcher(String contextPath) {
+           this.contextPath = contextPath;
+       }
+
+       /**
+        * Track state - in particular watch for expiration. if it happens for
+        * re-creation of the ZK client session
+        */
+       synchronized public void process(WatchedEvent event) {
+           if (event.getState() == KeeperState.Expired) {
+               close(contextPath);
+           }
+       }
+   }
+
+   /** ZooKeeper session timer */
+   private static class SessionTimerTask extends TimerTask {
+
+       private int delay;
+       private String contextPath, session;
+       private Timer timer;
+
+       public SessionTimerTask(int delayInSeconds, String session,
+               String contextPath, Timer timer) {
+           delay = delayInSeconds * 1000; // convert to milliseconds
+           this.contextPath = contextPath;
+           this.session = session;
+           this.timer = timer;
+           reset();
+       }
+
+       public SessionTimerTask(SessionTimerTask t) {
+           this(t.delay / 1000, t.session, t.contextPath, t.timer);
+       }
+
+       @Override
+       public void run() {
+           if (LOG.isInfoEnabled()) {
+               LOG.info(String.format("Session '%s' expired after "
+                       + "'%d' milliseconds.", session, delay));
+           }
+           ZooKeeperService.close(contextPath, session);
+       }
+
+       public void reset() {
+           timer.schedule(this, delay);
+       }
+
+   }
+
+   /**
+    * Specify ZooKeeper host:port for a particular context path. The host:port
+    * string is passed to the ZK client, so this can be formatted with more
+    * than a single host:port pair.
+    */
+   synchronized public static void mapContext(String contextPath, Endpoint e) {
+       contextMap.put(contextPath, e);
+   }
+
+   /**
+    * Reset timer for a session
+    */
+   synchronized public static void resetTimer(String contextPath,
+           String session) {
+       if (session != null) {
+           String uri = concat(contextPath, session);
+
+           SessionTimerTask t = zkSessionTimers.remove(uri);
+           t.cancel();
+
+           zkSessionTimers.put(uri, new SessionTimerTask(t));
+       }
+   }
+
+   /**
+    * Close the ZooKeeper session and remove it from the internal maps
+    */
+   public static void close(String contextPath) {
+       close(contextPath, null);
+   }
+
+   /**
+    * Close the ZooKeeper session and remove it
+    */
+   synchronized public static void close(String contextPath, String session) {
+       String uri = concat(contextPath, session);
+
+       TimerTask t = zkSessionTimers.remove(uri);
+       if (t != null) {
+           t.cancel();
+       }
+
+       ZooKeeper zk = zkMap.remove(uri);
+       if (zk == null) {
+           return;
+       }
+       try {
+           zk.close();
+       } catch (InterruptedException e) {
+           LOG.error("Interrupted while closing ZooKeeper connection.", e);
+       }
+   }
+
+   /**
+    * Close all the ZooKeeper sessions and remove them from the internal maps
+    */
+   synchronized public static void closeAll() {
+       Set<String> sessions = new TreeSet<String>(zkMap.keySet());
+       for (String key : sessions) {
+           close(key);
+       }
+   }
+
+   /**
+    * Is there an active connection for this session?
+    */
+   synchronized public static boolean isConnected(String contextPath,
+           String session) {
+       return zkMap.containsKey(concat(contextPath, session));
+   }
+
+   /**
+    * Return a ZooKeeper client not tied to a specific session.
+    */
+   public static ZooKeeper getClient(String contextPath) throws IOException {
+       return getClient(contextPath, null);
+   }
+
+   /**
+    * Return a ZooKeeper client for a session with a default expire time
+    * 
+    * @throws IOException
+    */
+   public static ZooKeeper getClient(String contextPath, String session)
+           throws IOException {
+       return getClient(contextPath, session, 5);
+   }
+
+   /**
+    * Return a ZooKeeper client which may or may not be connected, but it will
+    * not be expired. This method can be called multiple times, the same object
+    * will be returned except in the case where the session expires (at which
+    * point a new session will be returned)
+    */
+   synchronized public static ZooKeeper getClient(String contextPath,
+           String session, int expireTime) throws IOException {
+       final String connectionId = concat(contextPath, session);
+
+       ZooKeeper zk = zkMap.get(connectionId);
+       if (zk == null) {
+
+           if (LOG.isInfoEnabled()) {
+               LOG.info(String.format("creating new "
+                       + "connection for : '%s'", connectionId));
+           }
+           Endpoint e = contextMap.get(contextPath);
+           zk = new ZooKeeper(e.getHostPort(), 30000, new MyWatcher(
+                   connectionId));
+           
+           for (Map.Entry<String, String> p : e.getZooKeeperAuthInfo().entrySet()) {
+               zk.addAuthInfo("digest", String.format("%s:%s", p.getKey(),
+                       p.getValue()).getBytes());
+           }
+           
+           zkMap.put(connectionId, zk);
+
+           // a session should automatically expire after an amount of time
+           if (session != null) {
+               zkSessionTimers.put(connectionId, new SessionTimerTask(
+                       expireTime, session, contextPath, timer));
+           }
+       }
+       return zk;
+   }
+
+   private static String concat(String contextPath, String session) {
+       if (session != null) {
+           return String.format("%s@%s", contextPath, session);
+       }
+       return contextPath;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java
new file mode 100644
index 0000000..0730be5
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.server.jersey.cfg;
+
+import java.util.HashMap;
+
+public class Credentials extends HashMap<String, String> {
+
+   public static Credentials join(Credentials a, Credentials b) {
+       Credentials result = new Credentials();
+       result.putAll(a);
+       result.putAll(b);
+       return result;
+   }
+   
+   public Credentials() {
+       super();
+   }
+   
+   public Credentials(String credentials) {
+       super();
+       
+       if (!credentials.trim().equals("")) {
+           String[] parts = credentials.split(",");
+           for(String p : parts) {
+               String[] userPass = p.split(":");
+               put(userPass[0], userPass[1]);
+           }
+       }
+   }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java
new file mode 100644
index 0000000..2a62782
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java
@@ -0,0 +1,72 @@
+/**
+ * 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.server.jersey.cfg;
+
+public class Endpoint {
+
+   private String context;
+   private HostPortSet hostPort;
+   private Credentials credentials;
+   private Credentials zookeeperAuth;
+
+   public Endpoint(String context, String hostPortList) {
+       this.context = context;
+       this.hostPort = new HostPortSet(hostPortList);
+   }
+
+   public String getContext() {
+       return context;
+   }
+
+   public String getHostPort() {
+       return hostPort.toString();
+   }
+
+   public Credentials getCredentials() {
+       return credentials;
+   }
+   
+   public void setCredentials(String c) {
+       this.credentials = new Credentials(c);
+   }
+   
+   public void setZooKeeperAuthInfo(String digest) {
+       zookeeperAuth = new Credentials(digest);
+   }
+   
+   public final Credentials getZooKeeperAuthInfo() {
+       return zookeeperAuth;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+       Endpoint e = (Endpoint) o;
+       return context.equals(e.context);
+   }
+
+   @Override
+   public int hashCode() {
+       return context.hashCode();
+   }
+
+   @Override
+   public String toString() {
+       return String.format("<Endpoint %s %s>", context, hostPort.toString());
+   }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java
new file mode 100644
index 0000000..51a1bdd
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.server.jersey.cfg;
+
+public class HostPort {
+
+   private String host;
+   private int port;
+   
+   public HostPort(String hostPort) {
+       String[] parts = hostPort.split(":");
+       host = parts[0];
+       port = Integer.parseInt(parts[1]);
+   }
+
+   public String getHost() {
+       return host;
+   }
+
+   public int getPort() {
+       return port;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+       HostPort p = (HostPort) o;
+       return host.equals(p.host) && port == p.port;
+   }
+   
+   @Override
+   public int hashCode() {
+       return String.format("%s:%d", host, port).hashCode();
+   }
+   
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java
new file mode 100644
index 0000000..301a565
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.server.jersey.cfg;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class HostPortSet {
+
+   private Set<HostPort> hostPortSet = new HashSet<HostPort>();
+   private String original;
+   
+   public HostPortSet(String hostPortList) {
+       original = hostPortList;
+       
+       int chrootStart = hostPortList.indexOf('/');
+       String hostPortPairs;
+       if (chrootStart != -1) {
+           hostPortPairs = hostPortList.substring(0, chrootStart);
+       } else {
+           hostPortPairs = hostPortList;
+       }
+       
+       String[] parts = hostPortPairs.split(",");
+       for(String p : parts) {
+           hostPortSet.add(new HostPort(p));
+       }
+   }
+   
+   @Override
+   public String toString() {
+       return original;
+   }
+   
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java
new file mode 100644
index 0000000..93dd632
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java
@@ -0,0 +1,110 @@
+/**
+ * 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.server.jersey.cfg;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+public class RestCfg {
+
+   private Properties cfg = new Properties();
+
+   private Set<Endpoint> endpoints = new HashSet<Endpoint>();
+   private Credentials credentials = new Credentials();
+
+   public RestCfg(String resource) throws IOException {
+       this(RestCfg.class.getClassLoader().getResourceAsStream(resource));
+   }
+
+   public RestCfg(InputStream io) throws IOException {
+     try {
+       cfg.load(io);
+       extractEndpoints();
+       extractCredentials();
+     } finally {
+       io.close();
+     }
+   }
+
+   private void extractCredentials() {
+       if (cfg.containsKey("rest.http.auth")) {
+           credentials = new Credentials(cfg.getProperty("rest.http.auth", ""));
+       }
+   }
+
+   private void extractEndpoints() {
+       int count = 1;
+       while (true) {
+           String e = cfg.getProperty(
+                   String.format("rest.endpoint.%d", count), null);
+           if (e == null) {
+               break;
+           }
+
+           String[] parts = e.split(";");
+           if (parts.length != 2) {
+               count++;
+               continue;
+           }
+           Endpoint point = new Endpoint(parts[0], parts[1]);
+           
+           String c = cfg.getProperty(String.format(
+                   "rest.endpoint.%d.http.auth", count), "");
+           point.setCredentials(c);
+           
+           String digest = cfg.getProperty(String.format(
+                   "rest.endpoint.%d.zk.digest", count), "");
+           point.setZooKeeperAuthInfo(digest);
+
+           endpoints.add(point);
+           count++;
+       }
+   }
+
+   public int getPort() {
+       return Integer.parseInt(cfg.getProperty("rest.port", "9998"));
+   }
+
+   public boolean useSSL() {
+       return Boolean.valueOf(cfg.getProperty("rest.ssl", "false"));
+   }
+
+   public final Set<Endpoint> getEndpoints() {
+       return endpoints;
+   }
+
+   public final Credentials getCredentials() {
+       return credentials;
+   }
+
+   public String getJKS() {
+       return cfg.getProperty("rest.ssl.jks");
+   }
+
+   public String getJKS(String def) {
+       return cfg.getProperty("rest.ssl.jks", def);
+   }
+
+   public String getJKSPassword() {
+       return cfg.getProperty("rest.ssl.jks.pass");
+   }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java
new file mode 100644
index 0000000..49640b5
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java
@@ -0,0 +1,87 @@
+/**
+ * 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.server.jersey.filters;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.zookeeper.server.jersey.cfg.Credentials;
+
+import com.sun.jersey.core.util.Base64;
+
+public class HTTPBasicAuth implements Filter {
+
+    private Credentials credentials;
+
+    public HTTPBasicAuth(Credentials c) {
+       credentials = c;
+    }
+
+    @Override
+    public void doFilter(ServletRequest req0, ServletResponse resp0,
+            FilterChain chain) throws IOException, ServletException {
+
+        HttpServletRequest request = (HttpServletRequest) req0;
+        HttpServletResponse response = (HttpServletResponse) resp0;
+
+        String authorization = request.getHeader("Authorization");
+        if (authorization != null) {
+            String c[] = parseAuthorization(authorization);
+            if (c != null && credentials.containsKey(c[0])
+                    && credentials.get(c[0]).equals(c[1])) {
+                chain.doFilter(request, response);
+                return;
+            }
+        }
+
+        response.setHeader("WWW-Authenticate", "Basic realm=\"Restricted\"");
+        response.sendError(401);
+    }
+
+    private String[] parseAuthorization(String authorization) {
+        String parts[] = authorization.split(" ");
+        if (parts.length == 2 && parts[0].equalsIgnoreCase("Basic")) {
+            String userPass = Base64.base64Decode(parts[1]);
+
+            int p = userPass.indexOf(":");
+            if (p != -1) {
+                return new String[] { userPass.substring(0, p),
+                        userPass.substring(p + 1) };
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void init(FilterConfig arg0) throws ServletException {
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.java
new file mode 100644
index 0000000..b3fad55
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.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.server.jersey.jaxb;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+/**
+ * Represents the CHILD using JAXB.
+ * Special JSON version is required to get proper formatting in both
+ * JSON and XML output. See details in ZNodeResource.
+ */
+@XmlRootElement(name="child")
+public class ZChildren {
+    public String path;
+    public String uri;
+
+    public String child_uri_template;
+    @XmlElementWrapper(name="children")
+    @XmlElement(name="child")
+    public List<String> children;
+
+    public ZChildren() {
+        // needed by jersey
+        children = new ArrayList<String>();
+    }
+
+    public ZChildren(String path, String uri, String child_uri_template,
+            List<String> children)
+    {
+        this.path = path;
+        this.uri = uri;
+        this.child_uri_template = child_uri_template;
+        if (children != null) {
+            this.children = children;
+        } else {
+            this.children = new ArrayList<String>();
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return path.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ZChildren)) {
+            return false;
+        }
+        ZChildren o = (ZChildren) obj;
+        return path.equals(o.path) && children.equals(o.children);
+    }
+
+    @Override
+    public String toString() {
+        return "ZChildren(" + path + "," + children + ")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java
new file mode 100644
index 0000000..0dcece0
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java
@@ -0,0 +1,76 @@
+/**
+ * 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.server.jersey.jaxb;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+/**
+ * Represents the CHILD using JAXB.
+ * Special JSON version is required to get proper formatting in both
+ * JSON and XML output. See details in ZNodeResource.
+ */
+@XmlRootElement(name="child")
+public class ZChildrenJSON {
+    public String path;
+    public String uri;
+
+    public String child_uri_template;
+    public List<String> children;
+
+    public ZChildrenJSON() {
+        // needed by jersey
+        children = new ArrayList<String>();
+    }
+
+    public ZChildrenJSON(String path, String uri, String child_uri_template,
+            List<String> children)
+    {
+        this.path = path;
+        this.uri = uri;
+        this.child_uri_template = child_uri_template;
+        if (children != null) {
+            this.children = children;
+        } else {
+            this.children = new ArrayList<String>();
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return path.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ZChildrenJSON)) {
+            return false;
+        }
+        ZChildrenJSON o = (ZChildrenJSON) obj;
+        return path.equals(o.path) && children.equals(o.children);
+    }
+
+    @Override
+    public String toString() {
+        return "ZChildrenJSON(" + path + "," + children + ")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java
new file mode 100644
index 0000000..e976ee0
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java
@@ -0,0 +1,41 @@
+/**
+ * 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.server.jersey.jaxb;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+/**
+ * Represents an ERROR using JAXB.
+ */
+@XmlRootElement(name="error")
+public class ZError {
+    public String request;
+    public String message;
+
+    public ZError(){
+        // needed by jersey
+    }
+
+    public ZError(String request, String message) {
+        this.request = request;
+        this.message = message;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java
new file mode 100644
index 0000000..4d83717
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java
@@ -0,0 +1,63 @@
+/**
+ * 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.server.jersey.jaxb;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+/**
+ * Represents a PATH using JAXB.
+ */
+@XmlRootElement(name="path")
+public class ZPath {
+    public String path;
+    public String uri;
+
+    public ZPath(){
+        // needed by jersey
+    }
+
+    public ZPath(String path) {
+        this(path, null);
+    }
+
+    public ZPath(String path, String uri) {
+        this.path = path;
+        this.uri = uri;
+    }
+
+    @Override
+    public int hashCode() {
+        return path.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ZPath)) {
+            return false;
+        }
+        ZPath o = (ZPath) obj;
+        return path.equals(o.path);
+    }
+
+    @Override
+    public String toString() {
+        return "ZPath(" + path + ")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java
new file mode 100644
index 0000000..06ca9e5
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java
@@ -0,0 +1,55 @@
+/**
+ * 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.server.jersey.jaxb;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name="session")
+public class ZSession {
+    public String id;
+    public String uri;
+    
+    public ZSession() {
+        // needed by jersey
+    }
+    
+    public ZSession(String id, String uri) {
+        this.id = id;
+        this.uri = uri;
+    }
+    
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if(!(obj instanceof ZSession)) {
+            return false;
+        }
+        ZSession s = (ZSession) obj;
+        return id.equals(s.id);
+    }
+    
+    @Override
+    public String toString() {
+        return "ZSession(" + id +")";   
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java
new file mode 100644
index 0000000..af70d18
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java
@@ -0,0 +1,106 @@
+/**
+ * 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.server.jersey.jaxb;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+/**
+ * Represents a STAT using JAXB.
+ */
+@XmlRootElement(name="stat")
+public class ZStat {
+    public String path;
+    public String uri;
+    public byte[] data64;
+    public String dataUtf8;
+
+    public long czxid;
+    public long mzxid;
+    public long ctime;
+    public long mtime;
+    public int version;
+    public int cversion;
+    public int aversion;
+    public long ephemeralOwner;
+    public int dataLength;
+    public int numChildren;
+    public long pzxid;
+
+
+    public ZStat(){
+        // needed by jersey
+    }
+
+    public ZStat(String path, byte[] data64, String dataUtf8)
+    {
+        this.path = path;
+        this.data64 = data64;
+        this.dataUtf8 = dataUtf8;
+    }
+
+    public ZStat(String path, String uri, byte[] data64, String dataUtf8,
+            long czxid, long mzxid, long ctime, long mtime, int version,
+            int cversion, int aversion, long ephemeralOwner, int dataLength,
+            int numChildren, long pzxid)
+    {
+        this.path = path;
+        this.uri = uri;
+        this.data64 = data64;
+        this.dataUtf8 = dataUtf8;
+
+        this.czxid = czxid;
+        this.mzxid = mzxid;
+        this.ctime = ctime;
+        this.mtime = mtime;
+        this.version = version;
+        this.cversion = cversion;
+        this.aversion = aversion;
+        this.ephemeralOwner = ephemeralOwner;
+        this.dataLength = dataLength;
+        this.numChildren = numChildren;
+        this.pzxid = pzxid;
+    }
+
+    @Override
+    public int hashCode() {
+        return path.hashCode();
+    }
+
+    /**
+     * This method considers two ZStats equal if their path, encoding, and
+     * data match. It does not compare the ZooKeeper
+     * org.apache.zookeeper.data.Stat class fields.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ZStat)) {
+            return false;
+        }
+        ZStat o = (ZStat) obj;
+        return toString().equals(o.toString());
+    }
+
+    @Override
+    public String toString() {
+        return "ZStat(" + path + "," + "b64["
+            + (data64 == null ? null : new String(data64)) + "],"
+            + dataUtf8 + ")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java
new file mode 100644
index 0000000..0893586
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java
@@ -0,0 +1,72 @@
+/**
+ * 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.server.jersey.resources;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+import javax.xml.bind.JAXBContext;
+
+import org.apache.zookeeper.server.jersey.jaxb.ZChildrenJSON;
+import org.apache.zookeeper.server.jersey.jaxb.ZPath;
+import org.apache.zookeeper.server.jersey.jaxb.ZStat;
+
+import com.sun.jersey.api.json.JSONConfiguration;
+import com.sun.jersey.api.json.JSONJAXBContext;
+
+/**
+ * Tell Jersey how to resolve JSON formatting. Specifically detail the
+ * fields which are arrays and which are numbers (not strings).
+ */
+@Provider
+@SuppressWarnings("unchecked")
+public final class JAXBContextResolver implements ContextResolver<JAXBContext> {
+    private final JAXBContext context;
+
+    private final Set<Class> typesSet;
+
+    public JAXBContextResolver() throws Exception {
+        Class[] typesArr =
+            new Class[]{ZPath.class, ZStat.class, ZChildrenJSON.class};
+        typesSet = new HashSet<Class>(Arrays.asList(typesArr));
+        context = new JSONJAXBContext(
+                JSONConfiguration.mapped()
+                    .arrays("children")
+                    .nonStrings("czxid")
+                    .nonStrings("mzxid")
+                    .nonStrings("ctime")
+                    .nonStrings("mtime")
+                    .nonStrings("version")
+                    .nonStrings("cversion")
+                    .nonStrings("aversion")
+                    .nonStrings("ephemeralOwner")
+                    .nonStrings("dataLength")
+                    .nonStrings("numChildren")
+                    .nonStrings("pzxid")
+                    .build(),
+                typesArr);
+    }
+
+    public JAXBContext getContext(Class<?> objectType) {
+        return (typesSet.contains(objectType)) ? context : null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java
new file mode 100644
index 0000000..fdfc27b
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java
@@ -0,0 +1,86 @@
+/**
+ * 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.server.jersey.resources;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.server.jersey.jaxb.ZError;
+
+
+/**
+ * Map KeeperException to HTTP status codes
+ */
+@Provider
+public class KeeperExceptionMapper implements ExceptionMapper<KeeperException> {
+    private UriInfo ui;
+
+    public KeeperExceptionMapper(@Context UriInfo ui) {
+        this.ui = ui;
+    }
+
+    public Response toResponse(KeeperException e) {
+        Response.Status status;
+        String message;
+
+        String path = e.getPath();
+
+        switch(e.code()) {
+        case AUTHFAILED:
+            status = Response.Status.UNAUTHORIZED;
+            message = path + " not authorized";
+            break;
+        case BADARGUMENTS:
+            status = Response.Status.BAD_REQUEST;
+            message = path + " bad arguments";
+            break;
+        case BADVERSION:
+            status = Response.Status.PRECONDITION_FAILED;
+            message = path + " bad version";
+            break;
+        case INVALIDACL:
+            status = Response.Status.BAD_REQUEST;
+            message = path + " invalid acl";
+            break;
+        case NODEEXISTS:
+            status = Response.Status.CONFLICT;
+            message = path + " already exists";
+            break;
+        case NONODE:
+            status = Response.Status.NOT_FOUND;
+            message = path + " not found";
+            break;
+        case NOTEMPTY:
+            status = Response.Status.CONFLICT;
+            message = path + " not empty";
+            break;
+        default:
+            status = Response.Status.fromStatusCode(502); // bad gateway
+            message = "Error processing request for " + path
+                + " : " + e.getMessage();
+        }
+
+        return Response.status(status).entity(
+                new ZError(ui.getRequestUri().toString(), message)).build();
+    }
+}


Mime
View raw message