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();
+ }
+}
|