zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an...@apache.org
Subject [15/44] zookeeper git commit: ZOOKEEPER-3030: MAVEN MIGRATION 3.4 - Step 1.3 - move contrib directories
Date Tue, 07 Aug 2018 09:43:01 GMT
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.java
new file mode 100644
index 0000000..46f33bb
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.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.resources;
+
+import javax.ws.rs.WebApplicationException;
+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.server.jersey.jaxb.ZError;
+
+/**
+ * Map RuntimeException to HTTP status codes
+ */
+@Provider
+public class RuntimeExceptionMapper
+    implements ExceptionMapper<RuntimeException>
+{
+    private UriInfo ui;
+
+    public RuntimeExceptionMapper(@Context UriInfo ui) {
+        this.ui = ui;
+    }
+
+    public Response toResponse(RuntimeException e) {
+        // don't try to handle jersey exceptions ourselves
+        if (e instanceof WebApplicationException) { 
+            WebApplicationException ie =(WebApplicationException) e; 
+            return ie.getResponse(); 
+        } 
+
+        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
+                new ZError(ui.getRequestUri().toString(),
+                        "Error processing request due to " + e
+                        )).build();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java
new file mode 100644
index 0000000..0744604
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java
@@ -0,0 +1,135 @@
+/**
+ * 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.io.IOException;
+import java.net.URI;
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.server.jersey.ZooKeeperService;
+import org.apache.zookeeper.server.jersey.jaxb.ZError;
+import org.apache.zookeeper.server.jersey.jaxb.ZSession;
+
+import com.sun.jersey.api.json.JSONWithPadding;
+
+@Path("sessions/v1/{session: .*}")
+public class SessionsResource {
+
+    private static Logger LOG = LoggerFactory.getLogger(SessionsResource.class);
+
+    private String contextPath;
+
+    public SessionsResource(@Context HttpServletRequest request) {
+        contextPath = request.getContextPath();
+        if (contextPath.equals("")) {
+            contextPath = "/";
+        }
+    }
+
+    @PUT
+    @Produces( { MediaType.APPLICATION_JSON, "application/javascript",
+            MediaType.APPLICATION_XML })
+    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+    public Response keepAliveSession(@PathParam("session") String session,
+            @Context UriInfo ui, byte[] data) {
+
+        if (!ZooKeeperService.isConnected(contextPath, session)) {
+            throwNotFound(session, ui);
+        }
+
+        ZooKeeperService.resetTimer(contextPath, session);
+        return Response.status(Response.Status.OK).build();
+    }
+
+    @POST
+    @Produces( { MediaType.APPLICATION_JSON, "application/javascript",
+            MediaType.APPLICATION_XML })
+    public Response createSession(@QueryParam("op") String op,
+            @DefaultValue("5") @QueryParam("expire") String expire,
+            @Context UriInfo ui) {
+        if (!op.equals("create")) {
+            throw new WebApplicationException(Response.status(
+                    Response.Status.BAD_REQUEST).entity(
+                    new ZError(ui.getRequestUri().toString(), "")).build());
+        }
+
+        int expireInSeconds;
+        try {
+            expireInSeconds = Integer.parseInt(expire);
+        } catch (NumberFormatException e) {
+            throw new WebApplicationException(Response.status(
+                    Response.Status.BAD_REQUEST).build());
+        }
+
+        String uuid = UUID.randomUUID().toString();
+        while (ZooKeeperService.isConnected(contextPath, uuid)) {
+            uuid = UUID.randomUUID().toString();
+        }
+
+        // establish the connection to the ZooKeeper cluster
+        try {
+            ZooKeeperService.getClient(contextPath, uuid, expireInSeconds);
+        } catch (IOException e) {
+            LOG.error("Failed while trying to create a new session", e);
+
+            throw new WebApplicationException(Response.status(
+                    Response.Status.INTERNAL_SERVER_ERROR).build());
+        }
+
+        URI uri = ui.getAbsolutePathBuilder().path(uuid).build();
+        return Response.created(uri).entity(
+                new JSONWithPadding(new ZSession(uuid, uri.toString())))
+                .build();
+    }
+
+    @DELETE
+    @Produces( { MediaType.APPLICATION_JSON, "application/javascript",
+            MediaType.APPLICATION_XML, MediaType.APPLICATION_OCTET_STREAM })
+    public void deleteSession(@PathParam("session") String session,
+            @Context UriInfo ui) {
+        ZooKeeperService.close(contextPath, session);
+    }
+
+    private static void throwNotFound(String session, UriInfo ui)
+            throws WebApplicationException {
+        throw new WebApplicationException(Response.status(
+                Response.Status.NOT_FOUND).entity(
+                new ZError(ui.getRequestUri().toString(), session
+                        + " not found")).build());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.java
new file mode 100644
index 0000000..706ab89
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.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.resources;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.zookeeper.server.jersey.jaxb.ZError;
+
+/**
+ * Tell Jersey how to format an octet response error message.
+ */
+@Produces(MediaType.APPLICATION_OCTET_STREAM)
+@Provider
+public class ZErrorWriter implements MessageBodyWriter<ZError> {
+
+    public long getSize(ZError t, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType)  {
+        return -1;
+    }
+
+    public boolean isWriteable(Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return ZError.class.isAssignableFrom(type);
+    }
+
+    public void writeTo(ZError t, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders,
+            OutputStream os)
+        throws IOException, WebApplicationException
+    {
+        PrintStream p = new PrintStream(os);
+        p.print("Request " + t.request + " failed due to " + t.message);
+        p.flush();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java
new file mode 100644
index 0000000..77371ea
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java
@@ -0,0 +1,412 @@
+/**
+ * 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.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.server.jersey.ZooKeeperService;
+import org.apache.zookeeper.server.jersey.jaxb.ZChildren;
+import org.apache.zookeeper.server.jersey.jaxb.ZChildrenJSON;
+import org.apache.zookeeper.server.jersey.jaxb.ZError;
+import org.apache.zookeeper.server.jersey.jaxb.ZPath;
+import org.apache.zookeeper.server.jersey.jaxb.ZStat;
+
+import com.sun.jersey.api.json.JSONWithPadding;
+
+/**
+ * Version 1 implementation of the ZooKeeper REST specification.
+ */
+// TODO test octet fully
+@Path("znodes/v1{path: /.*}")
+public class ZNodeResource {
+    private final ZooKeeper zk;
+
+    public ZNodeResource(@DefaultValue("") @QueryParam("session") String session,
+            @Context UriInfo ui,
+            @Context HttpServletRequest request
+            )
+            throws IOException {
+
+        String contextPath = request.getContextPath();
+        if (contextPath.equals("")) {
+            contextPath = "/";
+        }
+        if (session.equals("")) {
+            session = null;
+        } else if (!ZooKeeperService.isConnected(contextPath, session)) {
+            throw new WebApplicationException(Response.status(
+                    Response.Status.UNAUTHORIZED).build());
+        }
+        zk = ZooKeeperService.getClient(contextPath, session);
+    }
+
+    private void ensurePathNotNull(String path) {
+        if (path == null) {
+            throw new IllegalArgumentException("Invalid path \"" + path + "\"");
+        }
+    }
+
+    @HEAD
+    @Produces( { MediaType.APPLICATION_JSON, "application/javascript",
+            MediaType.APPLICATION_XML })
+    public Response existsZNode(@PathParam("path") String path,
+            @Context UriInfo ui) throws InterruptedException, KeeperException {
+        Stat stat = zk.exists(path, false);
+        if (stat == null) {
+            throwNotFound(path, ui);
+        }
+        return Response.status(Response.Status.OK).build();
+    }
+
+    @HEAD
+    @Produces( { MediaType.APPLICATION_OCTET_STREAM })
+    public Response existsZNodeAsOctet(@PathParam("path") String path,
+            @Context UriInfo ui) throws InterruptedException, KeeperException {
+        Stat stat = zk.exists(path, false);
+        if (stat == null) {
+            throwNotFound(path, ui);
+        }
+        return Response.status(Response.Status.NO_CONTENT).build();
+    }
+
+    /*
+     * getZNodeList and getZNodeListJSON are bogus - but necessary.
+     * Unfortunately Jersey 1.0.3 is unable to render both xml and json properly
+     * in the case where a object contains a list/array. It's impossible to get
+     * it to render properly for both. As a result we need to split into two
+     * jaxb classes.
+     */
+
+    @GET
+    @Produces( { MediaType.APPLICATION_JSON, "application/javascript" })
+    public Response getZNodeListJSON(
+            @PathParam("path") String path,
+            @QueryParam("callback") String callback,
+            @DefaultValue("data") @QueryParam("view") String view,
+            @DefaultValue("base64") @QueryParam("dataformat") String dataformat,
+            @Context UriInfo ui) throws InterruptedException, KeeperException {
+        return getZNodeList(true, path, callback, view, dataformat, ui);
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_XML)
+    public Response getZNodeList(
+            @PathParam("path") String path,
+            @QueryParam("callback") String callback,
+            @DefaultValue("data") @QueryParam("view") String view,
+            @DefaultValue("base64") @QueryParam("dataformat") String dataformat,
+            @Context UriInfo ui) throws InterruptedException, KeeperException {
+        return getZNodeList(false, path, callback, view, dataformat, ui);
+    }
+
+    private Response getZNodeList(boolean json, String path, String callback,
+            String view, String dataformat, UriInfo ui)
+            throws InterruptedException, KeeperException {
+        ensurePathNotNull(path);
+
+        if (view.equals("children")) {
+            List<String> children = new ArrayList<String>();
+            for (String child : zk.getChildren(path, false)) {
+                children.add(child);
+            }
+
+            Object child;
+            String childTemplate = ui.getAbsolutePath().toString();
+            if (!childTemplate.endsWith("/")) {
+                childTemplate += "/";
+            }
+            childTemplate += "{child}";
+            if (json) {
+                child = new ZChildrenJSON(path,
+                        ui.getAbsolutePath().toString(), childTemplate,
+                        children);
+            } else {
+                child = new ZChildren(path, ui.getAbsolutePath().toString(),
+                        childTemplate, children);
+            }
+            return Response.status(Response.Status.OK).entity(
+                    new JSONWithPadding(child, callback)).build();
+        } else {
+            Stat stat = new Stat();
+            byte[] data = zk.getData(path, false, stat);
+
+            byte[] data64;
+            String dataUtf8;
+            if (data == null) {
+                data64 = null;
+                dataUtf8 = null;
+            } else if (!dataformat.equals("utf8")) {
+                data64 = data;
+                dataUtf8 = null;
+            } else {
+                data64 = null;
+                dataUtf8 = new String(data);
+            }
+            ZStat zstat = new ZStat(path, ui.getAbsolutePath().toString(),
+                    data64, dataUtf8, stat.getCzxid(), stat.getMzxid(), stat
+                            .getCtime(), stat.getMtime(), stat.getVersion(),
+                    stat.getCversion(), stat.getAversion(), stat
+                            .getEphemeralOwner(), stat.getDataLength(), stat
+                            .getNumChildren(), stat.getPzxid());
+
+            return Response.status(Response.Status.OK).entity(
+                    new JSONWithPadding(zstat, callback)).build();
+        }
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_OCTET_STREAM)
+    public Response getZNodeListAsOctet(@PathParam("path") String path)
+            throws InterruptedException, KeeperException {
+        ensurePathNotNull(path);
+
+        Stat stat = new Stat();
+        byte[] data = zk.getData(path, false, stat);
+
+        if (data == null) {
+            return Response.status(Response.Status.NO_CONTENT).build();
+        } else {
+            return Response.status(Response.Status.OK).entity(data).build();
+        }
+    }
+
+    @PUT
+    @Produces( { MediaType.APPLICATION_JSON, "application/javascript",
+            MediaType.APPLICATION_XML })
+    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+    public Response setZNode(
+            @PathParam("path") String path,
+            @QueryParam("callback") String callback,
+            @DefaultValue("-1") @QueryParam("version") String versionParam,
+            @DefaultValue("base64") @QueryParam("dataformat") String dataformat,
+            @DefaultValue("false") @QueryParam("null") String setNull,
+            @Context UriInfo ui, byte[] data) throws InterruptedException,
+            KeeperException {
+        ensurePathNotNull(path);
+
+        int version;
+        try {
+            version = Integer.parseInt(versionParam);
+        } catch (NumberFormatException e) {
+            throw new WebApplicationException(Response.status(
+                    Response.Status.BAD_REQUEST).entity(
+                    new ZError(ui.getRequestUri().toString(), path
+                            + " bad version " + versionParam)).build());
+        }
+
+        if (setNull.equals("true")) {
+            data = null;
+        }
+
+        Stat stat = zk.setData(path, data, version);
+
+        ZStat zstat = new ZStat(path, ui.getAbsolutePath().toString(), null,
+                null, stat.getCzxid(), stat.getMzxid(), stat.getCtime(), stat
+                        .getMtime(), stat.getVersion(), stat.getCversion(),
+                stat.getAversion(), stat.getEphemeralOwner(), stat
+                        .getDataLength(), stat.getNumChildren(), stat
+                        .getPzxid());
+
+        return Response.status(Response.Status.OK).entity(
+                new JSONWithPadding(zstat, callback)).build();
+    }
+
+    @PUT
+    @Produces(MediaType.APPLICATION_OCTET_STREAM)
+    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+    public void setZNodeAsOctet(@PathParam("path") String path,
+            @DefaultValue("-1") @QueryParam("version") String versionParam,
+            @DefaultValue("false") @QueryParam("null") String setNull,
+            @Context UriInfo ui, byte[] data) throws InterruptedException,
+            KeeperException {
+        ensurePathNotNull(path);
+
+        int version;
+        try {
+            version = Integer.parseInt(versionParam);
+        } catch (NumberFormatException e) {
+            throw new WebApplicationException(Response.status(
+                    Response.Status.BAD_REQUEST).entity(
+                    new ZError(ui.getRequestUri().toString(), path
+                            + " bad version " + versionParam)).build());
+        }
+
+        if (setNull.equals("true")) {
+            data = null;
+        }
+
+        zk.setData(path, data, version);
+    }
+
+    @POST
+    @Produces( { MediaType.APPLICATION_JSON, "application/javascript",
+            MediaType.APPLICATION_XML })
+    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+    public Response createZNode(
+            @PathParam("path") String path,
+            @QueryParam("callback") String callback,
+            @DefaultValue("create") @QueryParam("op") String op,
+            @QueryParam("name") String name,
+            @DefaultValue("base64") @QueryParam("dataformat") String dataformat,
+            @DefaultValue("false") @QueryParam("null") String setNull,
+            @DefaultValue("false") @QueryParam("sequence") String sequence,
+            @DefaultValue("false") @QueryParam("ephemeral") String ephemeral,
+            @Context UriInfo ui, byte[] data) throws InterruptedException,
+            KeeperException {
+        ensurePathNotNull(path);
+
+        if (path.equals("/")) {
+            path += name;
+        } else {
+            path += "/" + name;
+        }
+
+        if (!op.equals("create")) {
+            throw new WebApplicationException(Response.status(
+                    Response.Status.BAD_REQUEST).entity(
+                    new ZError(ui.getRequestUri().toString(), path
+                            + " bad operaton " + op)).build());
+        }
+
+        if (setNull.equals("true")) {
+            data = null;
+        }
+
+        CreateMode createMode;
+        if (sequence.equals("true")) {
+            if (ephemeral.equals("false")) {
+                createMode = CreateMode.PERSISTENT_SEQUENTIAL;
+            } else {
+                createMode = CreateMode.EPHEMERAL_SEQUENTIAL;
+            }
+        } else if (ephemeral.equals("false")) {
+            createMode = CreateMode.PERSISTENT;
+        } else {
+            createMode = CreateMode.EPHEMERAL;
+        }
+
+        String newPath = zk.create(path, data, Ids.OPEN_ACL_UNSAFE, createMode);
+
+        URI uri = ui.getAbsolutePathBuilder().path(newPath).build();
+
+        return Response.created(uri).entity(
+                new JSONWithPadding(new ZPath(newPath, ui.getAbsolutePath()
+                        .toString()))).build();
+    }
+
+    @POST
+    @Produces(MediaType.APPLICATION_OCTET_STREAM)
+    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+    public Response createZNodeAsOctet(@PathParam("path") String path,
+            @DefaultValue("create") @QueryParam("op") String op,
+            @QueryParam("name") String name,
+            @DefaultValue("false") @QueryParam("null") String setNull,
+            @DefaultValue("false") @QueryParam("sequence") String sequence,
+            @Context UriInfo ui, byte[] data) throws InterruptedException,
+            KeeperException {
+        ensurePathNotNull(path);
+
+        if (path.equals("/")) {
+            path += name;
+        } else {
+            path += "/" + name;
+        }
+
+        if (!op.equals("create")) {
+            throw new WebApplicationException(Response.status(
+                    Response.Status.BAD_REQUEST).entity(
+                    new ZError(ui.getRequestUri().toString(), path
+                            + " bad operaton " + op)).build());
+        }
+
+        if (setNull.equals("true")) {
+            data = null;
+        }
+
+        CreateMode createMode;
+        if (sequence.equals("true")) {
+            createMode = CreateMode.PERSISTENT_SEQUENTIAL;
+        } else {
+            createMode = CreateMode.PERSISTENT;
+        }
+
+        String newPath = zk.create(path, data, Ids.OPEN_ACL_UNSAFE, createMode);
+
+        URI uri = ui.getAbsolutePathBuilder().path(newPath).build();
+
+        return Response.created(uri).entity(
+                new ZPath(newPath, ui.getAbsolutePath().toString())).build();
+    }
+
+    @DELETE
+    @Produces( { MediaType.APPLICATION_JSON, "application/javascript",
+            MediaType.APPLICATION_XML, MediaType.APPLICATION_OCTET_STREAM })
+    public void deleteZNode(@PathParam("path") String path,
+            @DefaultValue("-1") @QueryParam("version") String versionParam,
+            @Context UriInfo ui) throws InterruptedException, KeeperException {
+        ensurePathNotNull(path);
+
+        int version;
+        try {
+            version = Integer.parseInt(versionParam);
+        } catch (NumberFormatException e) {
+            throw new WebApplicationException(Response.status(
+                    Response.Status.BAD_REQUEST).entity(
+                    new ZError(ui.getRequestUri().toString(), path
+                            + " bad version " + versionParam)).build());
+        }
+
+        zk.delete(path, version);
+    }
+
+    private static void throwNotFound(String path, UriInfo ui)
+            throws WebApplicationException {
+        throw new WebApplicationException(Response.status(
+                Response.Status.NOT_FOUND).entity(
+                new ZError(ui.getRequestUri().toString(), path + " not found"))
+                .build());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/python/README.txt
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/python/README.txt b/zookeeper-contrib/zookeeper-contrib-rest/src/python/README.txt
new file mode 100644
index 0000000..acc8ffb
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/python/README.txt
@@ -0,0 +1,9 @@
+Some basic python scripts which use the REST interface:
+
+zkrest.py -- basic REST ZooKeeper client
+demo_master_election.py -- shows how to implement master election
+demo_queue.py -- basic queue
+zk_dump_tree.py -- dumps the nodes & data of a znode hierarchy
+
+Generally these scripts require:
+  * simplejson

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_master_election.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_master_election.py b/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_master_election.py
new file mode 100644
index 0000000..c0317c7
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_master_election.py
@@ -0,0 +1,90 @@
+#! /usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+import threading
+import time
+
+from zkrest import ZooKeeper
+
+class Agent(threading.Thread):
+    """ A basic agent that wants to become a master and exit """
+
+    root = '/election'
+
+    def __init__(self, id):
+        super(Agent, self).__init__()
+        self.zk = ZooKeeper()
+        self.id = id
+
+    def run(self):
+        print 'Starting #%s' % self.id
+        with self.zk.session(expire=5):
+
+            # signal agent presence
+            r = self.zk.create("%s/agent-" % self.root, 
+                sequence=True, ephemeral=True)
+            self.me = r['path']
+
+            while True:
+                children = sorted([el['path'] \
+                    for el in self.zk.get_children(self.root)])
+                master, previous = children[0], None
+                try:
+                    index = children.index(self.me)
+                    if index != 0:
+                        previous = children[index-1]
+                except ValueError:
+                    break
+
+                if previous is None:
+                    self.do_master_work()
+                    # and don't forget to send heartbeat messages
+                    break
+                else:
+                    # do slave work in another thread
+                    pass
+               
+                # wait for the previous agent or current master to exit / finish
+                while self.zk.exists(previous) or self.zk.exists(master):
+                    time.sleep(0.5)
+                    self.zk.heartbeat()
+
+                # TODO signal the slave thread to exit and wait for it
+                # and rerun the election loop
+
+    def do_master_work(self):
+        print "#%s: I'm the master: %s" % (self.id, self.me) 
+            
+def main():
+    zk = ZooKeeper()
+
+    # create the root node used for master election
+    if not zk.exists('/election'):
+        zk.create('/election')
+
+    print 'Starting 10 agents ...'
+    agents = [Agent(id) for id in range(0,15)]
+
+    map(Agent.start, agents)
+    map(Agent.join, agents)
+
+    zk.delete('/election')    
+
+if __name__ == '__main__':
+    sys.exit(main())

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_queue.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_queue.py b/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_queue.py
new file mode 100644
index 0000000..9ca4c64
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_queue.py
@@ -0,0 +1,99 @@
+#! /usr/bin/env python
+
+# 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.
+
+
+# This is a simple message queue built on top of ZooKeeper. In order
+# to be used in production it needs better error handling but it's 
+# still useful as a proof-of-concept. 
+
+# Why use ZooKeeper as a queue? Highly available by design and has
+# great performance.
+
+import sys
+import threading
+import time
+
+from zkrest import ZooKeeper
+
+class Queue(object):
+    def __init__(self, root, zk):
+        self.root = root
+
+        self.zk = zk
+
+    def put(self, data):
+        self.zk.create("%s/el-" % self.root, str(data), sequence=True, ephemeral=True)
+
+        # creating ephemeral nodes for easy cleanup
+        # in a real world scenario you should create
+        # normal sequential znodes
+
+    def fetch(self):
+        """ Pull an element from the queue
+
+        This function is not blocking if the queue is empty, it will
+        just return None.
+        """
+        children = sorted(self.zk.get_children(self.root), \
+            lambda a, b: cmp(a['path'], b['path']))
+
+        if not children:
+            return None
+
+        try:
+            first = children[0]
+            self.zk.delete(first['path'], version=first['version'])
+            if 'data64' not in first:
+                return ''
+            else:
+                return first['data64'].decode('base64')
+
+        except (ZooKeeper.WrongVersion, ZooKeeper.NotFound):
+            # someone changed the znode between the get and delete
+            # this should not happen
+            # in practice you should retry the fetch
+            raise
+        
+
+def main():
+    zk = ZooKeeper()
+    zk.start_session(expire=60)
+
+    if not zk.exists('/queue'):
+        zk.create('/queue')
+    q = Queue('/queue', zk)
+
+    print 'Pushing to queue 1 ... 5'
+    map(q.put, [1,2,3,4,5])
+
+    print 'Extracting ...'
+    while True:
+        el = q.fetch()
+        if el is None:
+            break
+        print el    
+
+    zk.close_session()
+    zk.delete('/queue')
+
+    print 'Done.'
+   
+
+if __name__ == '__main__':
+    sys.exit(main())
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/python/test.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/python/test.py b/zookeeper-contrib/zookeeper-contrib-rest/src/python/test.py
new file mode 100644
index 0000000..363747a
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/python/test.py
@@ -0,0 +1,163 @@
+#! /usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import unittest
+
+from zkrest import ZooKeeper
+
+class ZooKeeperREST_TestCase(unittest.TestCase):
+    
+    BASE_URI = 'http://localhost:9998'
+
+    def setUp(self):
+        self.zk = ZooKeeper(self.BASE_URI)
+
+    def tearDown(self):
+        try:
+            self.zk.delete('/test')
+        except ZooKeeper.NotFound:
+            pass
+
+    def test_get_root_node(self):
+        assert self.zk.get('/') is not None
+
+    def test_get_node_not_found(self):
+        self.assertRaises(ZooKeeper.NotFound, \
+            self.zk.get, '/dummy-node')
+
+    def test_exists_node(self):
+        assert self.zk.exists('/zookeeper') is True
+
+    def test_get_children(self):
+        assert any([child['path'] == '/zookeeper/quota' \
+            for child in self.zk.get_children('/zookeeper')])
+            
+    def test_create_znode(self):
+        try:
+            self.zk.create('/test')
+        except ZooKeeper.ZNodeExists:
+            pass # it's ok if already exists
+        assert self.zk.exists('/test') is True
+
+    def test_create_hierarchy(self):
+        try:
+            self.zk.delete(['/a/b', '/a'])
+        except ZooKeeper.NotFound:
+            pass
+
+        self.zk.create('/a')
+        self.zk.create('/a/b')
+
+        self.zk.delete(['/a/b', '/a'])
+
+    def test_create_with_data(self):
+        self.zk.create('/test', 'some-data')
+
+        zn = self.zk.get('/test')
+        self.assertEqual(zn.get('data64', None), \
+            'some-data'.encode('base64').strip())
+
+    def test_delete_znode(self):
+        self.zk.create('/test')
+
+        self.zk.delete('/test')
+        assert not self.zk.exists('/test')
+
+    def test_delete_older_version(self):
+        self.zk.create('/test')
+
+        zn = self.zk.get('/test')
+        # do one more modification in order to increase the version number
+        self.zk.set('/test', 'dummy-data')
+
+        self.assertRaises(ZooKeeper.WrongVersion, \
+            self.zk.delete, '/test', version=zn['version'])
+
+    def test_delete_raise_not_found(self):
+        self.zk.create('/test')
+
+        zn = self.zk.get('/test')
+        self.zk.delete('/test')
+ 
+        self.assertRaises(ZooKeeper.NotFound, \
+            self.zk.delete, '/test', version=zn['version'])
+
+    def test_set(self):
+        self.zk.create('/test')
+
+        self.zk.set('/test', 'dummy')
+
+        self.assertEqual(self.zk.get('/test')['data64'], \
+            'dummy'.encode('base64').strip())
+
+    def test_set_with_older_version(self):
+        if not self.zk.exists('/test'):
+            self.zk.create('/test', 'random-data')
+
+        zn = self.zk.get('/test')
+        self.zk.set('/test', 'new-data')
+        self.assertRaises(ZooKeeper.WrongVersion, self.zk.set, \
+            '/test', 'older-version', version=zn['version'])
+
+    def test_set_null(self):
+        if not self.zk.exists('/test'):
+            self.zk.create('/test', 'random-data')
+        self.zk.set('/test', 'data')
+        assert 'data64' in self.zk.get('/test')
+
+        self.zk.set('/test', null=True)
+        assert 'data64' not in self.zk.get('/test')
+
+    def test_create_ephemeral_node(self):
+        with self.zk.session():
+            if self.zk.exists('/ephemeral-test'):
+                self.zk.delete('/ephemeral-test')
+
+            self.zk.create('/ephemeral-test', ephemeral=True)
+            zn = self.zk.get('/ephemeral-test')
+
+            assert zn['ephemeralOwner'] != 0
+
+    def test_create_session(self):
+        with self.zk.session() as sid:
+            self.assertEqual(len(sid), 36) # UUID
+
+    def test_session_invalidation(self):
+        self.zk.start_session(expire=1)
+        self.zk.create('/ephemeral-test', ephemeral=True)
+
+        # keep the session alive by sending heartbeat requests
+        for _ in range(1,2):
+            self.zk.heartbeat()
+            time.sleep(0.9)
+
+        time.sleep(2) # wait for the session to expire
+        self.assertRaises(ZooKeeper.InvalidSession, \
+            self.zk.create, '/ephemeral-test', ephemeral=True)
+
+    def test_presence_signaling(self):
+        with self.zk.session(expire=1):
+            self.zk.create('/i-am-online', ephemeral=True)
+            assert self.zk.exists('/i-am-online')
+        assert not self.zk.exists('/i-am-online')
+
+
+if __name__ == '__main__':
+    unittest.main()
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/python/zk_dump_tree.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/python/zk_dump_tree.py b/zookeeper-contrib/zookeeper-contrib-rest/src/python/zk_dump_tree.py
new file mode 100755
index 0000000..517d23b
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/python/zk_dump_tree.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import getopt
+import sys
+import simplejson
+import urllib2
+from base64 import b64decode
+
+printdata = False
+fullpath = False
+
+def dump_node(url, depth):
+    """Dump the node, then dump children recursively
+    
+    Arguments:
+    - `url`:
+    - `depth`:
+    """
+    req = urllib2.urlopen(url)
+    resp = simplejson.load(req)
+    if 'Error' in resp:
+      raise resp['Error']
+
+    if fullpath:
+      name = resp['path']
+    else:
+      name = '/' + resp['path'].split('/')[-1]
+
+    data64 = resp.get('data64')
+    dataUtf8 = resp.get('dataUtf8')
+    if data64 and printdata:
+      data = b64decode(data64)
+      print '%(indent)s%(name)s = b64(%(data64)s) str(%(data)s)' % \
+          {'indent':' '*2*depth, 'name':name, 'data64':data64, 'data':data}
+    elif dataUtf8 and printdata:
+      print '%(indent)s%(name)s = %(data)s' % \
+          {'indent':' '*2*depth, 'name':name, 'data':dataUtf8}
+    else:
+      print '%(indent)s%(name)s' % {'indent':' '*2*depth, 'name':name}
+
+    req = urllib2.urlopen(resp['uri'] + '?view=children')
+    resp = simplejson.load(req)
+
+    for child in resp.get('children', []):
+        dump_node(resp['child_uri_template']
+                  .replace("{child}", urllib2.quote(child)),
+                  depth + 1)
+
+def zk_dump_tree(url, root):
+    """Dump the tree starting at the roota
+    
+    Arguments:
+    - `root`:
+    """
+    dump_node(url + '/znodes/v1' + root, 0)
+
+def usage():
+    """Usage
+    """
+    print 'Usage: zk_dump_tree.py [-h|--help -u|--url=url -d|--data -f|--fullpath -r|--root=root]'
+    print '  where url is the url of the rest server, data is whether to'
+    print '  to include node data on output, root is the znode root'
+    print '  fullpath prints the full node path (useful for copy/paste)'
+
+if __name__ == '__main__':
+    try:
+        opts, args = getopt.getopt(sys.argv[1:],
+            "hu:dfr:", ["help", "url=", "data", "fullpath", "root="])
+    except getopt.GetoptError, err:
+        # print help information and exit:
+        print str(err) # will print something like "option -a not recognized"
+        usage()
+        sys.exit(2)
+    url ='http://localhost:9998'
+    root = '/'
+    for o, a in opts:
+        if o in ("-d", "--data"):
+            printdata = True
+        elif o in ("-h", "--help"):
+            usage()
+            sys.exit()
+        elif o in ("-u", "--url"):
+            url = a
+        elif o in ("-r", "--root"):
+            root = a
+        elif o in ("-f", "--fullpath"):
+            fullpath = True
+        else:
+            assert False, "unhandled option"
+    
+    print 'Accessing REST server at ' + url
+    zk_dump_tree(url, root)

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/python/zkrest.py
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/python/zkrest.py b/zookeeper-contrib/zookeeper-contrib-rest/src/python/zkrest.py
new file mode 100644
index 0000000..c009d5d
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/python/zkrest.py
@@ -0,0 +1,218 @@
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import urllib2
+import urllib
+import simplejson
+
+from contextlib import contextmanager
+
+class RequestWithMethod(urllib2.Request):
+    """ Request class that know how to set the method name """
+    def __init__(self, *args, **kwargs):
+        urllib2.Request.__init__(self, *args, **kwargs)
+        self._method = None
+
+    def get_method(self):
+        return self._method or \
+            urllib2.Request.get_method(self)
+
+    def set_method(self, method):
+        self._method = method
+
+class ZooKeeper(object):
+
+    class Error(Exception): pass
+
+    class NotFound(Error): pass
+
+    class ZNodeExists(Error): pass
+
+    class InvalidSession(Error): pass
+
+    class WrongVersion(Error): pass
+
+    def __init__(self, uri = 'http://localhost:9998'):
+        self._base = uri
+        self._session = None
+
+    def start_session(self, expire=5, id=None):
+        """ Create a session and return the ID """
+        if id is None:
+            url = "%s/sessions/v1/?op=create&expire=%d" % (self._base, expire)
+            self._session = self._do_post(url)['id']
+        else:
+            self._session = id
+        return self._session
+
+    def close_session(self):
+        """ Close the session on the server """
+        if self._session is not None:
+            url = '%s/sessions/v1/%s' % (self._base, self._session)
+            self._do_delete(url)
+            self._session = None
+
+    def heartbeat(self):
+        """ Send a heartbeat request. This is needed in order to keep a session alive """
+        if self._session is not None:
+            url = '%s/sessions/v1/%s' % (self._base, self._session)
+            self._do_put(url, '')
+
+    @contextmanager
+    def session(self, *args, **kwargs):
+        """ Session handling using a context manager """
+        yield self.start_session(*args, **kwargs)
+        self.close_session()
+
+    def get(self, path):
+        """ Get a node """
+        url = "%s/znodes/v1%s" % (self._base, path)
+        return self._do_get(url)
+
+    def get_children(self, path):
+        """ Get all the children for a given path. This function creates a generator """
+        url = "%s/znodes/v1%s?view=children" % (self._base, path)
+        resp = self._do_get(url)
+        for child in resp.get('children', []):
+            try:
+                yield self._do_get(resp['child_uri_template']\
+                    .replace('{child}', urllib2.quote(child)))
+            except ZooKeeper.NotFound:
+                continue
+
+    def create(self, path, data=None, sequence=False, ephemeral=False):
+        """ Create a new node. By default this call creates a persistent znode.
+
+        You can also create an ephemeral or a sequential znode.
+        """
+        ri = path.rindex('/')
+        head, name = path[:ri+1], path[ri+1:]
+        if head != '/': head = head[:-1]
+
+        flags = {
+            'null': 'true' if data is None else 'false',
+            'ephemeral': 'true' if ephemeral else 'false',
+            'sequence': 'true' if sequence else 'false'
+        }
+        if ephemeral:
+            if self._session:
+                flags['session'] = self._session
+            else:
+                raise ZooKeeper.Error, 'You need a session '\
+                    'to create an ephemeral node'
+        flags = urllib.urlencode(flags)
+
+        url = "%s/znodes/v1%s?op=create&name=%s&%s" % \
+            (self._base, head, name, flags)
+
+        return self._do_post(url, data)
+
+    def set(self, path, data=None, version=-1, null=False):
+        """ Set the value of node """
+        url = "%s/znodes/v1%s?%s" % (self._base, path, \
+            urllib.urlencode({
+                'version': version,
+                'null': 'true' if null else 'false'
+        }))
+        return self._do_put(url, data)
+
+    def delete(self, path, version=-1):
+        """ Delete a znode """
+        if type(path) is list:
+            map(lambda el: self.delete(el, version), path)
+            return
+
+        url = '%s/znodes/v1%s?%s' % (self._base, path, \
+            urllib.urlencode({
+                'version':version
+        }))
+        try:
+            return self._do_delete(url)
+        except urllib2.HTTPError, e:
+            if e.code == 412:
+                raise ZooKeeper.WrongVersion(path)
+            elif e.code == 404:
+                raise ZooKeeper.NotFound(path)
+            raise
+
+    def exists(self, path):
+        """ Do a znode exists """
+        try:
+            self.get(path)
+            return True
+        except ZooKeeper.NotFound:
+            return False
+
+    def _do_get(self, uri):
+        """ Send a GET request and convert errors to exceptions """
+        try:
+            req = urllib2.urlopen(uri)
+            resp = simplejson.load(req)
+
+            if 'Error' in resp:
+               raise ZooKeeper.Error(resp['Error'])
+
+            return resp
+        except urllib2.HTTPError, e:
+            if e.code == 404:
+                raise ZooKeeper.NotFound(uri)
+            raise
+
+    def _do_post(self, uri, data=None):
+        """ Send a POST request and convert errors to exceptions """
+        try:
+            req = urllib2.Request(uri, {})
+            req.add_header('Content-Type', 'application/octet-stream')
+            if data is not None:
+                req.add_data(data)
+
+            resp = simplejson.load(urllib2.urlopen(req))
+            if 'Error' in resp:
+                raise ZooKeeper.Error(resp['Error'])
+            return resp
+
+        except urllib2.HTTPError, e:
+            if e.code == 201:
+                return True
+            elif e.code == 409:
+                raise ZooKeeper.ZNodeExists(uri)
+            elif e.code == 401:
+                raise ZooKeeper.InvalidSession(uri)
+            raise
+
+    def _do_delete(self, uri):
+        """ Send a DELETE request """
+        req = RequestWithMethod(uri)
+        req.set_method('DELETE')
+        req.add_header('Content-Type', 'application/octet-stream')
+        return urllib2.urlopen(req).read()
+
+    def _do_put(self, uri, data):
+        """ Send a PUT request """
+        try:
+            req = RequestWithMethod(uri)
+            req.set_method('PUT')
+            req.add_header('Content-Type', 'application/octet-stream')
+            if data is not None:
+                req.add_data(data)
+
+            return urllib2.urlopen(req).read()
+        except urllib2.HTTPError, e:
+            if e.code == 412: # precondition failed
+                raise ZooKeeper.WrongVersion(uri)
+            raise
+

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/Base.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/Base.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/Base.java
new file mode 100644
index 0000000..2d5f51a
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/Base.java
@@ -0,0 +1,94 @@
+/**
+ * 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.ByteArrayInputStream;
+
+import junit.framework.TestCase;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.server.jersey.SetTest.MyWatcher;
+import org.apache.zookeeper.server.jersey.cfg.RestCfg;
+import org.junit.After;
+import org.junit.Before;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.WebResource;
+
+/**
+ * Test stand-alone server.
+ * 
+ */
+public class Base extends TestCase {
+   protected static final Logger LOG = LoggerFactory.getLogger(Base.class);
+
+   protected static final String CONTEXT_PATH = "/zk";
+   protected static final int GRIZZLY_PORT = 10104;
+   protected static final String BASEURI = String.format(
+           "http://localhost:%d%s", GRIZZLY_PORT, CONTEXT_PATH);
+   protected static final String ZKHOSTPORT = "localhost:22182";
+   protected Client client;
+   protected WebResource znodesr, sessionsr;
+
+   protected ZooKeeper zk;
+
+   private RestMain rest;
+
+   @Before
+   public void setUp() throws Exception {
+       super.setUp();
+
+       RestCfg cfg = new RestCfg(new ByteArrayInputStream(String.format(
+               "rest.port=%s\n" + 
+               "rest.endpoint.1=%s;%s\n",
+               GRIZZLY_PORT, CONTEXT_PATH, ZKHOSTPORT).getBytes()));
+
+       rest = new RestMain(cfg);
+       rest.start();
+
+       zk = new ZooKeeper(ZKHOSTPORT, 30000, new MyWatcher());
+
+       client = Client.create();
+       znodesr = client.resource(BASEURI).path("znodes/v1");
+       sessionsr = client.resource(BASEURI).path("sessions/v1/");
+   }
+
+   @After
+   public void tearDown() throws Exception {
+       super.tearDown();
+
+       client.destroy();
+       zk.close();
+       rest.stop();
+   }
+
+   protected static String createBaseZNode() throws Exception {
+       ZooKeeper zk = new ZooKeeper(ZKHOSTPORT, 30000, new MyWatcher());
+
+       String baseZnode = zk.create("/test-", null, Ids.OPEN_ACL_UNSAFE,
+               CreateMode.PERSISTENT_SEQUENTIAL);
+       zk.close();
+
+       return baseZnode;
+   }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/CreateTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/CreateTest.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/CreateTest.java
new file mode 100644
index 0000000..49c3349
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/CreateTest.java
@@ -0,0 +1,163 @@
+/**
+ * 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.util.Arrays;
+import java.util.Collection;
+
+import javax.ws.rs.core.MediaType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.server.jersey.jaxb.ZPath;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.WebResource.Builder;
+
+
+/**
+ * Test stand-alone server.
+ *
+ */
+@RunWith(Parameterized.class)
+public class CreateTest extends Base {
+    protected static final Logger LOG = LoggerFactory.getLogger(CreateTest.class);
+
+    private String accept;
+    private String path;
+    private String name;
+    private String encoding;
+    private ClientResponse.Status expectedStatus;
+    private ZPath expectedPath;
+    private byte[] data;
+    private boolean sequence;
+
+    public static class MyWatcher implements Watcher {
+        public void process(WatchedEvent event) {
+            // FIXME ignore for now
+        }
+    }
+
+    @Parameters
+    public static Collection<Object[]> data() throws Exception {
+        String baseZnode = Base.createBaseZNode();
+
+        return Arrays.asList(new Object[][] {
+          {MediaType.APPLICATION_JSON,
+              baseZnode, "foo bar", "utf8",
+              ClientResponse.Status.CREATED,
+              new ZPath(baseZnode + "/foo bar"), null,
+              false },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-t1", "utf8",
+              ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-t1"),
+              null, false },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-t1", "utf8",
+              ClientResponse.Status.CONFLICT, null, null, false },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-t2", "utf8",
+              ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-t2"),
+              "".getBytes(), false },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-t2", "utf8",
+              ClientResponse.Status.CONFLICT, null, null, false },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-t3", "utf8",
+              ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-t3"),
+              "foo".getBytes(), false },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-t3", "utf8",
+              ClientResponse.Status.CONFLICT, null, null, false },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-t4", "base64",
+              ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-t4"),
+              "foo".getBytes(), false },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-", "utf8",
+              ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-"), null,
+              true },
+          {MediaType.APPLICATION_JSON, baseZnode, "c-", "utf8",
+              ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-"), null,
+              true }
+          });
+    }
+
+    public CreateTest(String accept, String path, String name, String encoding,
+            ClientResponse.Status status, ZPath expectedPath, byte[] data,
+            boolean sequence)
+    {
+        this.accept = accept;
+        this.path = path;
+        this.name = name;
+        this.encoding = encoding;
+        this.expectedStatus = status;
+        this.expectedPath = expectedPath;
+        this.data = data;
+        this.sequence = sequence;
+    }
+
+    @Test
+    public void testCreate() throws Exception {
+        LOG.info("STARTING " + getName());
+
+        WebResource wr = znodesr.path(path).queryParam("dataformat", encoding)
+            .queryParam("name", name);
+        if (data == null) {
+            wr = wr.queryParam("null", "true");
+        }
+        if (sequence) {
+            wr = wr.queryParam("sequence", "true");
+        }
+
+        Builder builder = wr.accept(accept);
+
+        ClientResponse cr;
+        if (data == null) {
+            cr = builder.post(ClientResponse.class);
+        } else {
+            cr = builder.post(ClientResponse.class, data);
+        }
+        assertEquals(expectedStatus, cr.getClientResponseStatus());
+
+        if (expectedPath == null) {
+            return;
+        }
+
+        ZPath zpath = cr.getEntity(ZPath.class);
+        if (sequence) {
+            assertTrue(zpath.path.startsWith(expectedPath.path));
+            assertTrue(zpath.uri.startsWith(znodesr.path(path).toString()));
+        } else {
+            assertEquals(expectedPath, zpath);
+            assertEquals(znodesr.path(path).toString(), zpath.uri);
+        }
+
+        // use out-of-band method to verify
+        byte[] data = zk.getData(zpath.path, false, new Stat());
+        if (data == null && this.data == null) {
+            return;
+        } else if (data == null || this.data == null) {
+            assertEquals(data, this.data);
+        } else {
+            assertTrue(new String(data) + " == " + new String(this.data),
+                    Arrays.equals(data, this.data));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/DeleteTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/DeleteTest.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/DeleteTest.java
new file mode 100644
index 0000000..052239d
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/DeleteTest.java
@@ -0,0 +1,95 @@
+/**
+ * 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.util.Arrays;
+import java.util.Collection;
+
+import javax.ws.rs.core.MediaType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.sun.jersey.api.client.ClientResponse;
+
+
+/**
+ * Test stand-alone server.
+ *
+ */
+@RunWith(Parameterized.class)
+public class DeleteTest extends Base {
+    protected static final Logger LOG = LoggerFactory.getLogger(DeleteTest.class);
+
+    private String zpath;
+    private ClientResponse.Status expectedStatus;
+
+    public static class MyWatcher implements Watcher {
+        public void process(WatchedEvent event) {
+            // FIXME ignore for now
+        }
+    }
+
+    @Parameters
+    public static Collection<Object[]> data() throws Exception {
+        String baseZnode = Base.createBaseZNode();
+
+        return Arrays.asList(new Object[][] {
+          {baseZnode, baseZnode, ClientResponse.Status.NO_CONTENT },
+          {baseZnode, baseZnode, ClientResponse.Status.NO_CONTENT }
+        });
+    }
+
+    public DeleteTest(String path, String zpath, ClientResponse.Status status) {
+        this.zpath = zpath;
+        this.expectedStatus = status;
+    }
+
+    public void verify(String type) throws Exception {
+        if (expectedStatus != ClientResponse.Status.NOT_FOUND) {
+            zpath = zk.create(zpath, null, Ids.OPEN_ACL_UNSAFE,
+                    CreateMode.PERSISTENT_SEQUENTIAL);
+        }
+
+        ClientResponse cr = znodesr.path(zpath).accept(type).type(type)
+            .delete(ClientResponse.class);
+        assertEquals(expectedStatus, cr.getClientResponseStatus());
+
+        // use out-of-band method to verify
+        Stat stat = zk.exists(zpath, false);
+        assertNull(stat);
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        LOG.info("STARTING " + getName());
+        verify(MediaType.APPLICATION_OCTET_STREAM);
+        verify(MediaType.APPLICATION_JSON);
+        verify(MediaType.APPLICATION_XML);
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/ExistsTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/ExistsTest.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/ExistsTest.java
new file mode 100644
index 0000000..696ea95
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/ExistsTest.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;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.ws.rs.core.MediaType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.sun.jersey.api.client.ClientResponse;
+
+
+/**
+ * Test stand-alone server.
+ *
+ */
+@RunWith(Parameterized.class)
+public class ExistsTest extends Base {
+    protected static final Logger LOG = LoggerFactory.getLogger(ExistsTest.class);
+
+    private String path;
+    private ClientResponse.Status expectedStatus;
+
+    @Parameters
+    public static Collection<Object[]> data() throws Exception {
+        String baseZnode = Base.createBaseZNode();
+
+     return Arrays.asList(new Object[][] {
+      {baseZnode, ClientResponse.Status.OK },
+      {baseZnode + "dkdk38383", ClientResponse.Status.NOT_FOUND }
+     });
+    }
+
+    public ExistsTest(String path, ClientResponse.Status status) {
+        this.path = path;
+        this.expectedStatus = status;
+    }
+
+    private void verify(String type) {
+        ClientResponse cr = znodesr.path(path).accept(type).type(type).head();
+        if (type.equals(MediaType.APPLICATION_OCTET_STREAM)
+                && expectedStatus == ClientResponse.Status.OK) {
+            assertEquals(ClientResponse.Status.NO_CONTENT,
+                    cr.getClientResponseStatus());
+        } else {
+            assertEquals(expectedStatus, cr.getClientResponseStatus());
+        }
+    }
+
+    @Test
+    public void testExists() throws Exception {
+        LOG.info("STARTING " + getName());
+        verify(MediaType.APPLICATION_OCTET_STREAM);
+        verify(MediaType.APPLICATION_JSON);
+        verify(MediaType.APPLICATION_XML);
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/GetChildrenTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/GetChildrenTest.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/GetChildrenTest.java
new file mode 100644
index 0000000..a046692
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/GetChildrenTest.java
@@ -0,0 +1,139 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.server.jersey;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.server.jersey.jaxb.ZChildren;
+import org.apache.zookeeper.server.jersey.jaxb.ZChildrenJSON;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.sun.jersey.api.client.ClientResponse;
+
+
+/**
+ * Test stand-alone server.
+ *
+ */
+@RunWith(Parameterized.class)
+public class GetChildrenTest extends Base {
+    protected static final Logger LOG = LoggerFactory.getLogger(GetChildrenTest.class);
+
+    private String accept;
+    private String path;
+    private ClientResponse.Status expectedStatus;
+    private String expectedPath;
+    private List<String> expectedChildren;
+
+    @Parameters
+    public static Collection<Object[]> data() throws Exception {
+        String baseZnode = Base.createBaseZNode();
+        String baseZnode2 = Base.createBaseZNode();
+        String baseZnode3 = Base.createBaseZNode();
+        String baseZnode4 = Base.createBaseZNode();
+        String baseZnode5 = Base.createBaseZNode();
+        String baseZnode6 = Base.createBaseZNode();
+
+        return Arrays.asList(new Object[][] {
+          {MediaType.APPLICATION_JSON, baseZnode + "abddkdkd",
+              ClientResponse.Status.NOT_FOUND, null, null },
+          {MediaType.APPLICATION_XML, baseZnode + "abddkdkd",
+              ClientResponse.Status.NOT_FOUND, null, null },
+          {MediaType.APPLICATION_JSON, baseZnode, ClientResponse.Status.OK,
+              baseZnode, Arrays.asList(new String[] {}) },
+          {MediaType.APPLICATION_XML, baseZnode, ClientResponse.Status.OK,
+              baseZnode, Arrays.asList(new String[] {}) },
+          {MediaType.APPLICATION_JSON, baseZnode, ClientResponse.Status.OK,
+              baseZnode, Arrays.asList(new String[] {"c1"}) },
+          {MediaType.APPLICATION_XML, baseZnode4, ClientResponse.Status.OK,
+              baseZnode4, Arrays.asList(new String[] {"c1"}) },
+          {MediaType.APPLICATION_JSON, baseZnode2, ClientResponse.Status.OK,
+              baseZnode2, Arrays.asList(new String[] {"c1", "c2"}) },
+          {MediaType.APPLICATION_XML, baseZnode5, ClientResponse.Status.OK,
+              baseZnode5, Arrays.asList(new String[] {"c1", "c2"}) },
+          {MediaType.APPLICATION_JSON, baseZnode3, ClientResponse.Status.OK,
+              baseZnode3, Arrays.asList(new String[] {"c1", "c2", "c3", "c4"}) },
+          {MediaType.APPLICATION_XML, baseZnode6, ClientResponse.Status.OK,
+              baseZnode6, Arrays.asList(new String[] {"c1", "c2", "c3", "c4"}) }
+
+          });
+    }
+
+    public GetChildrenTest(String accept, String path, ClientResponse.Status status,
+            String expectedPath, List<String> expectedChildren)
+    {
+        this.accept = accept;
+        this.path = path;
+        this.expectedStatus = status;
+        this.expectedPath = expectedPath;
+        this.expectedChildren = expectedChildren;
+    }
+
+    @Test
+    public void testGetChildren() throws Exception {
+        LOG.info("STARTING " + getName());
+
+        if (expectedChildren != null) {
+            for(String child : expectedChildren) {
+                zk.create(expectedPath + "/" + child, null,
+                        Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+            }
+        }
+
+        ClientResponse cr = znodesr.path(path).queryParam("view", "children")
+            .accept(accept).get(ClientResponse.class);
+        assertEquals(expectedStatus, cr.getClientResponseStatus());
+
+        if (expectedChildren == null) {
+            return;
+        }
+
+        if (accept.equals(MediaType.APPLICATION_JSON)) {
+            ZChildrenJSON zchildren = cr.getEntity(ZChildrenJSON.class);
+            Collections.sort(expectedChildren);
+            Collections.sort(zchildren.children);
+            assertEquals(expectedChildren, zchildren.children);
+            assertEquals(znodesr.path(path).toString(), zchildren.uri);
+            assertEquals(znodesr.path(path).toString() + "/{child}",
+                    zchildren.child_uri_template);
+        } else if (accept.equals(MediaType.APPLICATION_XML)) {
+            ZChildren zchildren = cr.getEntity(ZChildren.class);
+            Collections.sort(expectedChildren);
+            Collections.sort(zchildren.children);
+            assertEquals(expectedChildren, zchildren.children);
+            assertEquals(znodesr.path(path).toString(), zchildren.uri);
+            assertEquals(znodesr.path(path).toString() + "/{child}",
+                    zchildren.child_uri_template);
+        } else {
+            fail("unknown accept type");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/GetTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/GetTest.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/GetTest.java
new file mode 100644
index 0000000..f00946e
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/GetTest.java
@@ -0,0 +1,123 @@
+/**
+ * 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.util.Arrays;
+import java.util.Collection;
+
+import javax.ws.rs.core.MediaType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.server.jersey.jaxb.ZStat;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.sun.jersey.api.client.ClientResponse;
+
+
+/**
+ * Test stand-alone server.
+ *
+ */
+@RunWith(Parameterized.class)
+public class GetTest extends Base {
+    protected static final Logger LOG = LoggerFactory.getLogger(GetTest.class);
+
+    private String accept;
+    private String path;
+    private String encoding;
+    private ClientResponse.Status expectedStatus;
+    private ZStat expectedStat;
+
+    @Parameters
+    public static Collection<Object[]> data() throws Exception {
+        String baseZnode = Base.createBaseZNode();
+
+     return Arrays.asList(new Object[][] {
+      {MediaType.APPLICATION_JSON, baseZnode, "utf8",
+          ClientResponse.Status.OK, new ZStat(baseZnode, null, null) },
+      {MediaType.APPLICATION_JSON, baseZnode, "utf8",
+          ClientResponse.Status.OK, new ZStat(baseZnode, null, "") },
+      {MediaType.APPLICATION_JSON, baseZnode, "utf8",
+          ClientResponse.Status.OK, new ZStat(baseZnode, null, "foo") },
+      {MediaType.APPLICATION_JSON, baseZnode, "base64",
+          ClientResponse.Status.OK, new ZStat(baseZnode, null, null) },
+      {MediaType.APPLICATION_JSON, baseZnode, "base64",
+          ClientResponse.Status.OK, new ZStat(baseZnode, "".getBytes(), null) },
+      {MediaType.APPLICATION_JSON, baseZnode, "base64",
+          ClientResponse.Status.OK, new ZStat(baseZnode, "".getBytes(), null) },
+      {MediaType.APPLICATION_JSON, baseZnode, "base64",
+              ClientResponse.Status.OK, new ZStat(baseZnode, "foo".getBytes(), null) },
+      {MediaType.APPLICATION_JSON, baseZnode + "abaddkdk", "utf8",
+                      ClientResponse.Status.NOT_FOUND, null },
+      {MediaType.APPLICATION_JSON, baseZnode + "abaddkdk", "base64",
+              ClientResponse.Status.NOT_FOUND, null },
+
+      {MediaType.APPLICATION_XML, baseZnode, "utf8",
+                  ClientResponse.Status.OK, new ZStat(baseZnode, null, "foo") },
+      {MediaType.APPLICATION_XML, baseZnode, "base64",
+                      ClientResponse.Status.OK,
+                      new ZStat(baseZnode, "foo".getBytes(), null) },
+      {MediaType.APPLICATION_XML, baseZnode + "abaddkdk", "utf8",
+                      ClientResponse.Status.NOT_FOUND, null },
+      {MediaType.APPLICATION_XML, baseZnode + "abaddkdk", "base64",
+              ClientResponse.Status.NOT_FOUND, null }
+
+     });
+    }
+
+    public GetTest(String accept, String path, String encoding,
+            ClientResponse.Status status, ZStat stat)
+    {
+        this.accept = accept;
+        this.path = path;
+        this.encoding = encoding;
+        this.expectedStatus = status;
+        this.expectedStat = stat;
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        LOG.info("STARTING " + getName());
+
+        if (expectedStat != null) {
+            if (expectedStat.data64 != null || expectedStat.dataUtf8 == null) {
+                zk.setData(expectedStat.path, expectedStat.data64, -1);
+            } else {
+                zk.setData(expectedStat.path,
+                        expectedStat.dataUtf8.getBytes(), -1);
+            }
+        }
+
+        ClientResponse cr = znodesr.path(path).queryParam("dataformat", encoding)
+            .accept(accept).get(ClientResponse.class);
+        assertEquals(expectedStatus, cr.getClientResponseStatus());
+
+        if (expectedStat == null) {
+            return;
+        }
+
+        ZStat zstat = cr.getEntity(ZStat.class);
+        assertEquals(expectedStat, zstat);
+        assertEquals(znodesr.path(path).toString(), zstat.uri);
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/RestTestSuite.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/RestTestSuite.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/RestTestSuite.java
new file mode 100644
index 0000000..fc69caf
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/RestTestSuite.java
@@ -0,0 +1,42 @@
+/**
+ * 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 org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({WadlTest.class, GetTest.class, GetChildrenTest.class,
+    CreateTest.class, SetTest.class, ExistsTest.class, DeleteTest.class })
+public class RestTestSuite {
+
+    @BeforeClass
+    public static void setUp() {
+        // suite setup
+    }
+
+    @AfterClass
+    public static void tearDown() {
+        // suite setup
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/RootTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/RootTest.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/RootTest.java
new file mode 100644
index 0000000..a4f9b9e
--- /dev/null
+++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/org/apache/zookeeper/server/jersey/RootTest.java
@@ -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.
+ */
+
+package org.apache.zookeeper.server.jersey;
+
+import java.util.Arrays;
+
+import javax.ws.rs.core.MediaType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.server.jersey.jaxb.ZPath;
+import org.junit.Test;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.WebResource.Builder;
+
+
+/**
+ * Test stand-alone server.
+ *
+ */
+public class RootTest extends Base {
+    protected static final Logger LOG = LoggerFactory.getLogger(RootTest.class);
+
+    @Test
+    public void testCreate() throws Exception {
+        LOG.info("STARTING " + getName());
+        
+        String path = "/";
+        String name = "roottest-create";
+        byte[] data = "foo".getBytes();
+
+        WebResource wr = znodesr.path(path).queryParam("dataformat", "utf8")
+            .queryParam("name", name);
+        Builder builder = wr.accept(MediaType.APPLICATION_JSON);
+
+        ClientResponse cr;
+        cr = builder.post(ClientResponse.class, data);
+        assertEquals(ClientResponse.Status.CREATED, cr.getClientResponseStatus());
+
+        ZPath zpath = cr.getEntity(ZPath.class);
+        assertEquals(new ZPath(path + name), zpath);
+        assertEquals(znodesr.path(path).toString(), zpath.uri);
+
+        // use out-of-band method to verify
+        byte[] rdata = zk.getData(zpath.path, false, new Stat());
+        assertTrue(new String(rdata) + " == " + new String(data),
+                Arrays.equals(rdata, data));
+    }
+}


Mime
View raw message