You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by ma...@apache.org on 2009/06/25 00:59:34 UTC

svn commit: r788205 [1/2] - in /hadoop/zookeeper/trunk: ./ docs/ src/c/src/ src/c/tests/ src/docs/src/documentation/content/xdocs/ src/java/main/org/apache/zookeeper/ src/java/test/org/apache/zookeeper/test/

Author: mahadev
Date: Wed Jun 24 22:59:34 2009
New Revision: 788205

URL: http://svn.apache.org/viewvc?rev=788205&view=rev
Log:
ZOOKEEPER-237. Add a Chroot request (phunt and mahadev)

Added:
    hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ChrootAsyncTest.java
    hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ChrootClientTest.java
    hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ChrootTest.java
Modified:
    hadoop/zookeeper/trunk/CHANGES.txt
    hadoop/zookeeper/trunk/docs/zookeeperAdmin.html
    hadoop/zookeeper/trunk/docs/zookeeperAdmin.pdf
    hadoop/zookeeper/trunk/docs/zookeeperProgrammers.html
    hadoop/zookeeper/trunk/docs/zookeeperProgrammers.pdf
    hadoop/zookeeper/trunk/docs/zookeeperStarted.html
    hadoop/zookeeper/trunk/docs/zookeeperStarted.pdf
    hadoop/zookeeper/trunk/src/c/src/zk_adaptor.h
    hadoop/zookeeper/trunk/src/c/src/zk_hashtable.c
    hadoop/zookeeper/trunk/src/c/src/zookeeper.c
    hadoop/zookeeper/trunk/src/c/tests/TestClient.cc
    hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
    hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
    hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml
    hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxn.java
    hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java
    hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ClientBase.java
    hadoop/zookeeper/trunk/src/java/test/org/apache/zookeeper/test/ClientTest.java

Modified: hadoop/zookeeper/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/CHANGES.txt?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/CHANGES.txt (original)
+++ hadoop/zookeeper/trunk/CHANGES.txt Wed Jun 24 22:59:34 2009
@@ -251,6 +251,8 @@
 
   ZOOKEEPER-395. Python bindings. (henry robinson via mahadev)
 
+  ZOOKEEPER-237. Add a Chroot request (phunt and mahadev)
+
 Release 3.1.0 - 2009-02-06
   
 Non-backward compatible changes:

Modified: hadoop/zookeeper/trunk/docs/zookeeperAdmin.html
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/docs/zookeeperAdmin.html?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/docs/zookeeperAdmin.html (original)
+++ hadoop/zookeeper/trunk/docs/zookeeperAdmin.html Wed Jun 24 22:59:34 2009
@@ -1450,8 +1450,11 @@
 <a name="N104A0"></a><a name="sc_bestPractices"></a>
 <h3 class="h4">Best Practices</h3>
 <p>For best results, take note of the following list of good
-      Zookeeper practices. <em>[tbd...]</em>
-</p>
+      Zookeeper practices:</p>
+<p>For multi-tennant installations see the <a href="zookeeperProgrammers.html#ch_zkSessions">section</a>
+      detailing ZooKeeper "chroot" support, this can be very useful
+      when deploying many applications/services interfacing to a
+      single ZooKeeper cluster.</p>
 </div>
 
 <p align="right">

Modified: hadoop/zookeeper/trunk/docs/zookeeperAdmin.pdf
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/docs/zookeeperAdmin.pdf?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
Binary files - no diff available.

Modified: hadoop/zookeeper/trunk/docs/zookeeperProgrammers.html
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/docs/zookeeperProgrammers.html?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/docs/zookeeperProgrammers.html (original)
+++ hadoop/zookeeper/trunk/docs/zookeeperProgrammers.html Wed Jun 24 22:59:34 2009
@@ -758,7 +758,7 @@
 <h2 class="h3">ZooKeeper Sessions</h2>
 <div class="section">
 <p>To create a client session the application code must provide
-    a string containing a comma separated list of host:port pairs,
+    a connection string containing a comma separated list of host:port pairs,
     each corresponding to a ZooKeeper server (e.g. "127.0.0.1:4545" or
     "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"). The ZooKeeper
     client library will pick an arbitrary server and try to connect to
@@ -766,6 +766,23 @@
     disconnected from the server for any reason, the client will
     automatically try the next server in the list, until a connection
     is (re-)established.</p>
+<p> 
+<strong>Added in 3.2.0</strong>: An
+    optional "chroot" suffix may also be appended to the connection
+    string. This will run the client commands while interpreting all
+    paths relative to this root (similar to the unix chroot
+    command). If used the example would look like:
+    "127.0.0.1:4545/app/a" or
+    "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a" where the
+    client would be rooted at "/app/a" and all paths would be relative
+    to this root - ie getting/setting/etc...  "/foo/bar" would result
+    in operations being run on "/app/a/foo/bar" (from the server
+    perspective). This feature is particularly useful in multi-tenant
+    environments where each user of a particular ZooKeeper service
+    could be rooted differently. This makes re-use much simpler as
+    each user can code his/her application as if it were rooted at
+    "/", while actual location (say /app/a) could be determined at
+    deployment time.</p>
 <p>When a client gets a handle to the ZooKeeper service,
     ZooKeeper creates a ZooKeeper session, represented as a 64-bit
     number, that it assigns to the client. If the client connects to a
@@ -825,7 +842,7 @@
 </div>
 
   
-<a name="N101C9"></a><a name="ch_zkWatches"></a>
+<a name="N101CF"></a><a name="ch_zkWatches"></a>
 <h2 class="h3">ZooKeeper Watches</h2>
 <div class="section">
 <p>All of the read operations in ZooKeeper - <strong>getData()</strong>, <strong>getChildren()</strong>, and <strong>exists()</strong> - have the option of setting a watch as a
@@ -908,7 +925,7 @@
     general this all occurs transparently. There is one case where a watch
     may be missed: a watch for the existance of a znode not yet created will
     be missed if the znode is created and deleted while disconnected.</p>
-<a name="N101FF"></a><a name="sc_WatchGuarantees"></a>
+<a name="N10205"></a><a name="sc_WatchGuarantees"></a>
 <h3 class="h4">What ZooKeeper Guarantees about Watches</h3>
 <p>With regard to watches, ZooKeeper maintains these
       guarantees:</p>
@@ -943,7 +960,7 @@
 </li>
       
 </ul>
-<a name="N10224"></a><a name="sc_WatchRememberThese"></a>
+<a name="N1022A"></a><a name="sc_WatchRememberThese"></a>
 <h3 class="h4">Things to Remember about Watches</h3>
 <ul>
         
@@ -1002,7 +1019,7 @@
 </div>
 
   
-<a name="N10250"></a><a name="sc_ZooKeeperAccessControl"></a>
+<a name="N10256"></a><a name="sc_ZooKeeperAccessControl"></a>
 <h2 class="h3">ZooKeeper access control using ACLs</h2>
 <div class="section">
 <p>ZooKeeper uses ACLs to control access to its znodes (the
@@ -1037,7 +1054,7 @@
     example, the pair <em>(ip:19.22.0.0/16, READ)</em>
     gives the <em>READ</em> permission to any clients with
     an IP address that starts with 19.22.</p>
-<a name="N10283"></a><a name="sc_ACLPermissions"></a>
+<a name="N10289"></a><a name="sc_ACLPermissions"></a>
 <h3 class="h4">ACL Permissions</h3>
 <p>ZooKeeper supports the following permissions:</p>
 <ul>
@@ -1093,7 +1110,7 @@
       node, but nothing more. (The problem is, if you want to call
       zoo_exists() on a node that doesn't exist, there is no
       permission to check.)</p>
-<a name="N102D9"></a><a name="sc_BuiltinACLSchemes"></a>
+<a name="N102DF"></a><a name="sc_BuiltinACLSchemes"></a>
 <h4>Builtin ACL Schemes</h4>
 <p>ZooKeeeper has the following built in schemes:</p>
 <ul>
@@ -1142,7 +1159,7 @@
 
       
 </ul>
-<a name="N1031D"></a><a name="ZooKeeper+C+client+API"></a>
+<a name="N10323"></a><a name="ZooKeeper+C+client+API"></a>
 <h4>ZooKeeper C client API</h4>
 <p>The following constants are provided by the ZooKeeper C
       library:</p>
@@ -1364,7 +1381,7 @@
 </div>
 
   
-<a name="N10434"></a><a name="sc_ZooKeeperPluggableAuthentication"></a>
+<a name="N1043A"></a><a name="sc_ZooKeeperPluggableAuthentication"></a>
 <h2 class="h3">Pluggable ZooKeeper authentication</h2>
 <div class="section">
 <p>ZooKeeper runs in a variety of different environments with
@@ -1450,7 +1467,7 @@
 </div>
       
   
-<a name="N104A0"></a><a name="ch_zkGuarantees"></a>
+<a name="N104A6"></a><a name="ch_zkGuarantees"></a>
 <h2 class="h3">Consistency Guarantees</h2>
 <div class="section">
 <p>ZooKeeper is a high performance, scalable service. Both reads and
@@ -1576,12 +1593,12 @@
 </div>
 
   
-<a name="N10507"></a><a name="ch_bindings"></a>
+<a name="N1050D"></a><a name="ch_bindings"></a>
 <h2 class="h3">Bindings</h2>
 <div class="section">
 <p>The ZooKeeper client libraries come in two languages: Java and C.
     The following sections describe these.</p>
-<a name="N10510"></a><a name="Java+Binding"></a>
+<a name="N10516"></a><a name="Java+Binding"></a>
 <h3 class="h4">Java Binding</h3>
 <p>There are two packages that make up the ZooKeeper Java binding:
       <strong>org.apache.zookeeper</strong> and <strong>org.apache.zookeeper.data</strong>. The rest of the
@@ -1648,7 +1665,7 @@
       (SESSION_EXPIRED and AUTH_FAILED), the ZooKeeper object becomes invalid.
       On a close, the two threads shut down and any further access on zookeeper
       handle is undefined behavior and should be avoided. </p>
-<a name="N10559"></a><a name="C+Binding"></a>
+<a name="N1055F"></a><a name="C+Binding"></a>
 <h3 class="h4">C Binding</h3>
 <p>The C binding has a single-threaded and multi-threaded library.
       The multi-threaded library is easiest to use and is most similar to the
@@ -1665,7 +1682,7 @@
       (i.e. FreeBSD 4.x). In all other cases, application developers should
       link with zookeeper_mt, as it includes support for both Sync and Async
       API.</p>
-<a name="N10568"></a><a name="Installation"></a>
+<a name="N1056E"></a><a name="Installation"></a>
 <h4>Installation</h4>
 <p>If you're building the client from a check-out from the Apache
         repository, follow the steps outlined below. If you're building from a
@@ -1796,7 +1813,7 @@
 </li>
         
 </ol>
-<a name="N10611"></a><a name="Using+the+C+Client"></a>
+<a name="N10617"></a><a name="Using+the+C+Client"></a>
 <h4>Using the C Client</h4>
 <p>You can test your client by running a ZooKeeper server (see
         instructions on the project wiki page on how to run it) and connecting
@@ -1854,7 +1871,7 @@
 </div>
 
    
-<a name="N10657"></a><a name="ch_guideToZkOperations"></a>
+<a name="N1065D"></a><a name="ch_guideToZkOperations"></a>
 <h2 class="h3">Building Blocks: A Guide to ZooKeeper Operations</h2>
 <div class="section">
 <p>This section surveys all the operations a developer can perform
@@ -1872,28 +1889,28 @@
 </li>
     
 </ul>
-<a name="N1066B"></a><a name="sc_errorsZk"></a>
+<a name="N10671"></a><a name="sc_errorsZk"></a>
 <h3 class="h4">Handling Errors</h3>
 <p>Both the Java and C client bindings may report errors. The Java client binding does so by throwing KeeperException, calling code() on the exception will return the specific error code. The C client binding returns an error code as defined in the enum ZOO_ERRORS. API callbacks indicate result code for both language bindings. See the API documentation (javadoc for Java, doxygen for C) for full details on the possible errors and their meaning.</p>
-<a name="N10675"></a><a name="sc_connectingToZk"></a>
+<a name="N1067B"></a><a name="sc_connectingToZk"></a>
 <h3 class="h4">Connecting to ZooKeeper</h3>
 <p></p>
-<a name="N1067E"></a><a name="sc_readOps"></a>
+<a name="N10684"></a><a name="sc_readOps"></a>
 <h3 class="h4">Read Operations</h3>
 <p></p>
-<a name="N10687"></a><a name="sc_writeOps"></a>
+<a name="N1068D"></a><a name="sc_writeOps"></a>
 <h3 class="h4">Write Operations</h3>
 <p></p>
-<a name="N10690"></a><a name="sc_handlingWatches"></a>
+<a name="N10696"></a><a name="sc_handlingWatches"></a>
 <h3 class="h4">Handling Watches</h3>
 <p></p>
-<a name="N10699"></a><a name="sc_miscOps"></a>
+<a name="N1069F"></a><a name="sc_miscOps"></a>
 <h3 class="h4">Miscelleaneous ZooKeeper Operations</h3>
 <p></p>
 </div>
 
   
-<a name="N106A3"></a><a name="ch_programStructureWithExample"></a>
+<a name="N106A9"></a><a name="ch_programStructureWithExample"></a>
 <h2 class="h3">Program Structure, with Simple Example</h2>
 <div class="section">
 <p>
@@ -1902,7 +1919,7 @@
 </div>
 
   
-<a name="N106AE"></a><a name="ch_gotchas"></a>
+<a name="N106B4"></a><a name="ch_gotchas"></a>
 <h2 class="h3">Gotchas: Common Problems and Troubleshooting</h2>
 <div class="section">
 <p>So now you know ZooKeeper. It's fast, simple, your application

Modified: hadoop/zookeeper/trunk/docs/zookeeperProgrammers.pdf
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/docs/zookeeperProgrammers.pdf?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
Binary files - no diff available.

Modified: hadoop/zookeeper/trunk/docs/zookeeperStarted.html
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/docs/zookeeperStarted.html?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/docs/zookeeperStarted.html (original)
+++ hadoop/zookeeper/trunk/docs/zookeeperStarted.html Wed Jun 24 22:59:34 2009
@@ -518,7 +518,7 @@
 <p>ZooKeeper has a Java bindings and C bindings. They are
       functionally equivalent. The C bindings exist in two variants: single
       threaded and multi-threaded. These differ only in how the messaging loop
-      is done. For more information, see the <a href="zookeeperProgrammers.html#ch_programStructureWithExample.html">Programming
+      is done. For more information, see the <a href="zookeeperProgrammers.html#ch_programStructureWithExample">Programming
       Examples in the ZooKeeper Programmer's Guide</a> for
       sample code using of the different APIs.</p>
 <a name="N1013E"></a><a name="sc_RunningReplicatedZooKeeper"></a>

Modified: hadoop/zookeeper/trunk/docs/zookeeperStarted.pdf
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/docs/zookeeperStarted.pdf?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
Binary files - no diff available.

Modified: hadoop/zookeeper/trunk/src/c/src/zk_adaptor.h
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/c/src/zk_adaptor.h?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/c/src/zk_adaptor.h (original)
+++ hadoop/zookeeper/trunk/src/c/src/zk_adaptor.h Wed Jun 24 22:59:34 2009
@@ -208,6 +208,8 @@
     zk_hashtable* active_node_watchers;   
     zk_hashtable* active_exist_watchers;
     zk_hashtable* active_child_watchers;
+    /** used for chroot path at the client side **/
+    char *chroot;
 };
 
 
@@ -222,7 +224,8 @@
 int process_async(int outstanding_sync);
 void process_completions(zhandle_t *zh);
 int flush_send_queue(zhandle_t*zh, int timeout);
-
+char* sub_string(zhandle_t *zh, const char* server_path);
+void free_duplicate_path(char* free_path, const char* path);
 void zoo_lock_auth(zhandle_t *zh);
 void zoo_unlock_auth(zhandle_t *zh);
 

Modified: hadoop/zookeeper/trunk/src/c/src/zk_hashtable.c
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/c/src/zk_hashtable.c?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/c/src/zk_hashtable.c (original)
+++ hadoop/zookeeper/trunk/src/c/src/zk_hashtable.c Wed Jun 24 22:59:34 2009
@@ -266,10 +266,12 @@
 static void do_foreach_watcher(watcher_object_t* wo,zhandle_t* zh,
         const char* path,int type,int state)
 {
+    char *client_path = sub_string(zh, path);
     while(wo!=0){
-        wo->watcher(zh,type,state,path,wo->context);
+        wo->watcher(zh,type,state,client_path,wo->context);
         wo=wo->next;
     }    
+    free_duplicate_path(client_path, path);
 }
 
 watcher_object_list_t *collectWatchers(zhandle_t *zh,int type, char *path)

Modified: hadoop/zookeeper/trunk/src/c/src/zookeeper.c
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/c/src/zookeeper.c?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/c/src/zookeeper.c (original)
+++ hadoop/zookeeper/trunk/src/c/src/zookeeper.c Wed Jun 24 22:59:34 2009
@@ -186,6 +186,7 @@
 static __attribute__((unused)) void print_completion_queue(zhandle_t *zh);
 
 static void *SYNCHRONOUS_MARKER = (void*)&SYNCHRONOUS_MARKER;
+static int isValidPath(const char* path, const int flags);
 
 const void *zoo_get_context(zhandle_t *zh) 
 {
@@ -372,6 +373,12 @@
         free(zh->addrs);
         zh->addrs = NULL;
     }
+
+    if (zh->chroot != 0) {
+        free(zh->chroot);
+        zh->chroot = NULL;
+    }
+    
     free_auth_info(&zh->auth_h);
     destroy_zk_hashtable(zh->active_node_watchers);
     destroy_zk_hashtable(zh->active_exist_watchers);
@@ -413,7 +420,7 @@
         zh->addrs = 0;
     }
     if (!hosts) {
-        LOG_ERROR(("out of memory"));
+         LOG_ERROR(("out of memory"));
         errno=ENOMEM;
         return ZSYSTEMERROR;
     }
@@ -607,6 +614,7 @@
 
     int errnosave;
     zhandle_t *zh = calloc(1, sizeof(*zh));
+    char *index_chroot;
     if (!zh) {
         return 0;
     }
@@ -624,7 +632,28 @@
         errno=EINVAL;
         goto abort;
     }
-    zh->hostname = strdup(host);
+    //parse the host to get the chroot if 
+    //available
+    index_chroot = strchr(host, '/');
+    if (index_chroot) {
+        zh->chroot = strdup(index_chroot);
+        // if chroot is just / set it to null
+        if (strlen(zh->chroot) == 1) {
+            zh->chroot = NULL;
+        }
+        // cannot use strndup so allocate and strcpy
+        zh->hostname = (char *) malloc(index_chroot - host + 1);
+        zh->hostname = strncpy(zh->hostname, host, (index_chroot - host));
+        //strncpy does not null terminate
+        *(zh->hostname + (index_chroot - host) +1) = '\0';
+        
+    } else {
+        zh->chroot = NULL;
+        zh->hostname = strdup(host);
+    }
+    if (zh->chroot && !isValidPath(zh->chroot, 0)) { 
+        goto abort;
+    }
     if (zh->hostname == 0) {
         goto abort;
     }
@@ -661,6 +690,54 @@
     return 0;
 }
 
+/**
+ * deallocated the free_path only its beeen allocated
+ * and not equal to path
+ */
+void free_duplicate_path(char *free_path, const char* path) {
+    if (free_path != path) {
+        free(free_path);
+    }
+}
+
+/**
+  prepend the chroot path if available else return the path
+*/
+static char* prepend_string(zhandle_t *zh, const char* client_path) {
+    char *ret_str;
+    if (zh->chroot == NULL) 
+        return (char *) client_path;
+    // handle the chroot itself, client_path = "/"
+    if (strlen(client_path) == 1) { 
+        return strdup(zh->chroot);
+    } 
+    ret_str = (char *) malloc(strlen(zh->chroot) + strlen(client_path) + 1);
+    strcpy(ret_str, zh->chroot);
+    return strcat(ret_str, client_path);
+}
+    
+/**
+   strip off the chroot string from the server path
+   if there is one else return the exact path
+ */
+char* sub_string(zhandle_t *zh, const char* server_path) {
+    char *ret_str;
+    if (zh->chroot == NULL)
+        return (char *) server_path;
+    if (strncmp(server_path, zh->chroot, strlen(zh->chroot) != 0)) {
+        LOG_ERROR(("server path %s does not include chroot path %s", 
+                   server_path, zh->chroot));
+        return NULL;
+    }
+    if (strlen(server_path) == strlen(zh->chroot)) {
+        //return "/"
+        ret_str = strdup("/");
+        return ret_str;
+    }
+    ret_str = strdup(server_path + strlen(zh->chroot));
+    return ret_str;
+} 
+
 static buffer_list_t *allocate_buffer(char *buff, int len)
 {
     buffer_list_t *buffer = calloc(1, sizeof(*buffer));
@@ -1679,7 +1756,6 @@
             deserialize_WatcherEvent(ia, "event", &evt);
             type = evt.type;
             path = evt.path;
-
             /* We are doing a notification, so there is no pending request */
             completion_list_t *c = 
                 create_completion_entry(WATCHER_EVENT_XID,-1,0,0,0);
@@ -1847,8 +1923,7 @@
     if (process_async(zh->outstanding_sync)) {
         process_completions(zh);
     }
-    return api_epilog(zh,ZOK);
-}
+    return api_epilog(zh,ZOK);}
 
 int zoo_state(zhandle_t *zh)
 {
@@ -2093,24 +2168,29 @@
         data_completion_t dc, const void *data)
 {
     struct oarchive *oa; 
+    char *server_path = prepend_string(zh, path);
     struct RequestHeader h = { .xid = get_xid(), .type = GETDATA_OP};
-    struct GetDataRequest req = { (char*)path, watcher!=0 };
+    struct GetDataRequest req =  { (char*)server_path, watcher!=0 };
     int rc;
-    
-    if (zh==0 || !isValidPath(path, 0))
+            
+    if (zh==0 || !isValidPath(server_path, 0)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa=create_buffer_oarchive();
     rc = serialize_RequestHeader(oa, "header", &h);
     rc = rc < 0 ? rc : serialize_GetDataRequest(oa, "req", &req);
     enter_critical(zh);
     rc = rc < 0 ? rc : add_data_completion(zh, h.xid, dc, data,
-        create_watcher_registration(path,data_result_checker,watcher,watcherCtx));
+        create_watcher_registration(server_path,data_result_checker,watcher,watcherCtx));
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
-
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
     
@@ -2128,13 +2208,19 @@
     struct RequestHeader h = { .xid = get_xid(), .type = SETDATA_OP};
     struct SetDataRequest req;
     int rc;
+    char *server_path;
+    server_path = prepend_string(zh, path);
     
-    if (zh==0 || !isValidPath(path, 0))
+    if (zh==0 || !isValidPath(server_path, 0)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa = create_buffer_oarchive();
-    req.path = (char*)path;
+    req.path = (char*)server_path;
     req.data.buff = (char*)buffer;
     req.data.len = buflen;
     req.version = version;
@@ -2145,6 +2231,7 @@
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
 
@@ -2163,13 +2250,19 @@
     struct RequestHeader h = { .xid = get_xid(), .type = CREATE_OP };
     struct CreateRequest req;
     int rc;
+    char *server_path;
     
-    if (zh==0 || !isValidPath(path, flags))
+    server_path = prepend_string(zh, path);
+    if (zh==0 || !isValidPath(server_path, flags)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa = create_buffer_oarchive();
-    req.path = (char*)path;
+    req.path = (char*)server_path;
     req.flags = flags;
     req.data.buff = (char*)value;
     req.data.len = valuelen;
@@ -2186,6 +2279,7 @@
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
     
@@ -2203,13 +2297,19 @@
     struct RequestHeader h = { .xid = get_xid(), .type = DELETE_OP};
     struct DeleteRequest req;
     int rc;
+    char *server_path;
     
-    if (zh==0 || !isValidPath(path, 0))
+    server_path = prepend_string(zh, path);
+    if (zh==0 || !isValidPath(server_path, 0)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa = create_buffer_oarchive();
-    req.path = (char*)path;
+    req.path = (char*)server_path;
     req.version = version;
     rc = serialize_RequestHeader(oa, "header", &h);
     rc = rc < 0 ? rc : serialize_DeleteRequest(oa, "req", &req);
@@ -2218,6 +2318,7 @@
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
 
@@ -2240,23 +2341,29 @@
 {
     struct oarchive *oa;
     struct RequestHeader h = { .xid = get_xid(), .type = EXISTS_OP };
-    struct ExistsRequest req = {(char*)path, watcher!=0 };
+    char *server_path = prepend_string(zh, path);
+    struct ExistsRequest req  = {(char*)server_path, watcher!=0 }; 
     int rc;
     
-    if (zh==0 || !isValidPath(path, 0))
+    if (zh==0 || !isValidPath(server_path, 0)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa = create_buffer_oarchive();
     rc = serialize_RequestHeader(oa, "header", &h);
     rc = rc < 0 ? rc : serialize_ExistsRequest(oa, "req", &req);
     enter_critical(zh);
     rc = rc < 0 ? rc : add_stat_completion(zh, h.xid, completion, data,
-        create_watcher_registration(path,exists_result_checker,
+        create_watcher_registration(server_path,exists_result_checker,
                 watcher,watcherCtx));
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
 
@@ -2279,22 +2386,28 @@
 {
     struct oarchive *oa;
     struct RequestHeader h = { .xid = get_xid(), .type = GETCHILDREN_OP};
-    struct GetChildrenRequest req={(char*)path, watcher!=0 };
+    char * server_path = prepend_string(zh, path);
+    struct GetChildrenRequest req = {(char*)server_path, watcher!=0 }; 
     int rc;
-    
-    if (zh==0 || !isValidPath(path, 0))
+        
+    if (zh==0 || !isValidPath(server_path, 0)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa = create_buffer_oarchive();
     rc = serialize_RequestHeader(oa, "header", &h);
     rc = rc < 0 ? rc : serialize_GetChildrenRequest(oa, "req", &req);
     enter_critical(zh);
     rc = rc < 0 ? rc : add_strings_completion(zh, h.xid, dc, data,
-            create_watcher_registration(path,child_result_checker,watcher,watcherCtx));
+            create_watcher_registration(server_path,child_result_checker,watcher,watcherCtx));
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
 
@@ -2312,13 +2425,19 @@
     struct RequestHeader h = { .xid = get_xid(), .type = SYNC_OP};
     struct SyncRequest req;
     int rc;
-
-    if (zh==0 || !isValidPath(path, 0))
+    char *server_path;
+    
+    server_path = prepend_string(zh, path);
+    if (zh==0 || !isValidPath(server_path, 0)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa = create_buffer_oarchive();
-    req.path = (char*)path;
+    req.path = (char*)server_path;
     rc = serialize_RequestHeader(oa, "header", &h);
     rc = rc < 0 ? rc : serialize_SyncRequest(oa, "req", &req);
     enter_critical(zh);
@@ -2326,6 +2445,7 @@
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
 
@@ -2344,13 +2464,19 @@
     struct RequestHeader h = { .xid = get_xid(), .type = GETACL_OP};
     struct GetACLRequest req;
     int rc;
+    char *server_path;
     
-    if (zh==0 || !isValidPath(path, 0))
+    server_path = prepend_string(zh, path);
+    if (zh==0 || !isValidPath(server_path, 0)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa = create_buffer_oarchive();
-    req.path = (char*)path;
+    req.path = (char*)server_path;
     rc = serialize_RequestHeader(oa, "header", &h);
     rc = rc < 0 ? rc : serialize_GetACLRequest(oa, "req", &req);
     enter_critical(zh);
@@ -2358,6 +2484,7 @@
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
 
@@ -2375,13 +2502,19 @@
     struct RequestHeader h = { .xid = get_xid(), .type = SETACL_OP};
     struct SetACLRequest req;
     int rc;
-    
-    if (zh==0 || !isValidPath(path, 0))
+    char *server_path;
+
+    server_path = prepend_string(zh, path);
+    if (zh==0 || !isValidPath(server_path, 0)) {
+        free_duplicate_path(server_path, path);
         return ZBADARGUMENTS;
-    if (is_unrecoverable(zh))
+    }
+    if (is_unrecoverable(zh)) {
+        free_duplicate_path(server_path, path);
         return ZINVALIDSTATE;
+    }
     oa = create_buffer_oarchive();
-    req.path = (char*)path;
+    req.path = (char*)server_path;
     req.acl = *acl;
     req.version = version;
     rc = serialize_RequestHeader(oa, "header", &h);
@@ -2391,6 +2524,7 @@
     rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
             get_buffer_len(oa));
     leave_critical(zh);
+    free_duplicate_path(server_path, path);
     /* We queued the buffer, so don't free it */
     close_buffer_oarchive(&oa, 0);
 

Modified: hadoop/zookeeper/trunk/src/c/tests/TestClient.cc
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/c/tests/TestClient.cc?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/c/tests/TestClient.cc (original)
+++ hadoop/zookeeper/trunk/src/c/tests/TestClient.cc Wed Jun 24 22:59:34 2009
@@ -164,6 +164,7 @@
     CPPUNIT_TEST(testPathValidation);
     CPPUNIT_TEST(testPing);
     CPPUNIT_TEST(testAcl);
+    CPPUNIT_TEST(testChroot);
     CPPUNIT_TEST(testAuth);
     CPPUNIT_TEST(testWatcherAutoResetWithGlobal);
     CPPUNIT_TEST(testWatcherAutoResetWithLocal);
@@ -191,7 +192,7 @@
     const char *getHostPorts() {
         return hostPorts;
     }
-
+    
     zhandle_t *createClient(watchctx_t *ctx) {
         zhandle_t *zk = zookeeper_init(hostPorts, watcher, 10000, 0,
                                        ctx, 0);
@@ -200,6 +201,14 @@
         return zk;
     }
     
+    zhandle_t *createchClient(watchctx_t *ctx) {
+        zhandle_t *zk = zookeeper_init(hp_chroot, watcher, 10000, 0,
+                                       ctx, 0);
+        ctx->zh = zk;
+        sleep(1);
+        return zk;
+    }
+        
 public:
 
 
@@ -273,6 +282,7 @@
     
     static zhandle_t *async_zk;
     static volatile int count;
+    static char* hp_chroot;
     
     static void statCompletion(int rc, const struct Stat *stat, const void *data) {
         int tmp = (int) (long) data;
@@ -293,6 +303,35 @@
         }
     }
     
+    static void create_completion_fn(int rc, const char* value, const void *data) {
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        count++;
+    }
+    
+    static void waitForCreateCompletion(int seconds) {
+        time_t expires = time(0) + seconds;
+        while(count == 0 && time(0) < expires) {
+            sleep(1);
+        }
+        count--;
+    }
+
+    static void watcher_chroot_fn(zhandle_t *zh, int type,
+                                    int state, const char *path,void *watcherCtx) {
+        // check for path
+        char *client_path = (char *) watcherCtx;
+        CPPUNIT_ASSERT(strcmp(client_path, path) == 0);
+        count ++;
+    }
+    
+    static void waitForChrootWatch(int seconds) {
+        time_t expires = time(0) + seconds;
+        while (count == 0 && time(0) < expires) {
+            sleep(1);
+        }
+        count--;
+    }
+
     static void waitForVoidCompletion(int seconds) {
         time_t expires = time(0) + seconds;
         while(count == 0 && time(0) < expires) {
@@ -522,7 +561,93 @@
         verifyCreateOk("/f/.f/f", zk);
         verifyCreateOk("/f/f./f", zk);
     }
-
+    
+    void testChroot() {
+        // the c client async callbacks do 
+        // not callback with the path, so 
+        // we dont need to test taht for now
+        // we should fix that though soon!
+        watchctx_t ctx, ctx_ch;
+        zhandle_t *zk, *zk_ch;
+        char buf[60];
+        int rc, len;
+        struct Stat stat;
+        const char* data = "garbage";
+        const char* retStr = "/chroot"; 
+        const char* root= "/";
+        hp_chroot = "127.0.0.1:22181/test/mahadev";
+        zk_ch = createchClient(&ctx_ch);
+        CPPUNIT_ASSERT(zk_ch != NULL);
+        zk = createClient(&ctx);
+        rc = zoo_create(zk, "/test", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        rc = zoo_create(zk, "/test/mahadev", data, 7, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        // try an exists with /
+        len = 60;
+        rc = zoo_get(zk_ch, "/", 0, buf, &len, &stat);
+        CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
+        //check if the data is the same
+        CPPUNIT_ASSERT(strncmp(buf, data, 7) == 0);
+        //check for watches 
+        rc = zoo_wexists(zk_ch, "/chroot", watcher_chroot_fn, (void *) retStr, &stat);
+        //now check if we can do create/delete/get/sets/acls/getChildren and others 
+        //check create
+        rc = zoo_create(zk_ch, "/chroot", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0,0);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        waitForChrootWatch(3);
+        CPPUNIT_ASSERT(count == 0);
+        rc = zoo_create(zk_ch, "/chroot/child", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        rc = zoo_exists(zk, "/test/mahadev/chroot/child", 0, &stat);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        
+        rc = zoo_delete(zk_ch, "/chroot/child", -1);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        rc = zoo_exists(zk, "/test/mahadev/chroot/child", 0, &stat);
+        CPPUNIT_ASSERT_EQUAL((int) ZNONODE, rc);
+        rc = zoo_wget(zk_ch, "/chroot", watcher_chroot_fn, (char*) retStr,
+                      buf, &len, &stat);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        rc = zoo_set(zk_ch, "/chroot",buf, 3, -1);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        waitForChrootWatch(3);
+        CPPUNIT_ASSERT(count == 0);
+        // check for getchildren
+        struct String_vector children;
+        rc = zoo_get_children(zk_ch, "/", 0, &children);
+        CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
+        CPPUNIT_ASSERT_EQUAL((int)1, children.count);
+        //check if te child if chroot
+        CPPUNIT_ASSERT(strcmp((retStr+1), children.data[0]) == 0);
+        // check for get/set acl
+        struct ACL_vector acl;
+        rc = zoo_get_acl(zk_ch, "/", &acl, &stat);
+        CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
+        CPPUNIT_ASSERT_EQUAL((int)1, acl.count);
+        CPPUNIT_ASSERT_EQUAL(ZOO_PERM_ALL, acl.data->perms);
+        // set acl
+        rc = zoo_set_acl(zk_ch, "/chroot", -1,  &ZOO_READ_ACL_UNSAFE);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        // see if you add children
+        rc = zoo_create(zk_ch, "/chroot/child1", "",0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0);
+        CPPUNIT_ASSERT_EQUAL((int)ZNOAUTH, rc);
+        //add wget children test
+        rc = zoo_wget_children(zk_ch, "/", watcher_chroot_fn, (char*) root, &children);
+        CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
+        
+        //now create a node
+        rc = zoo_create(zk_ch, "/child2", "",0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0);
+        CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
+        waitForChrootWatch(3);
+        CPPUNIT_ASSERT(count == 0);
+        //check for one async call just to make sure
+        rc = zoo_acreate(zk_ch, "/child3", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 
+                         create_completion_fn, 0);
+        waitForCreateCompletion(3);
+        CPPUNIT_ASSERT(count == 0);
+    }
+        
     void testAsyncWatcherAutoReset()
     {
         watchctx_t ctx;
@@ -747,4 +872,5 @@
 volatile int Zookeeper_simpleSystem::count;
 zhandle_t *Zookeeper_simpleSystem::async_zk;
 const char Zookeeper_simpleSystem::hostPorts[] = "127.0.0.1:22181";
+char* Zookeeper_simpleSystem::hp_chroot;
 CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_simpleSystem);

Modified: hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml (original)
+++ hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml Wed Jun 24 22:59:34 2009
@@ -1139,7 +1139,15 @@
       <title>Best Practices</title>
 
       <para>For best results, take note of the following list of good
-      Zookeeper practices. <emphasis>[tbd...]</emphasis></para>
+      Zookeeper practices:</para>
+
+
+      <para>For multi-tennant installations see the <ulink
+      url="zookeeperProgrammers.html#ch_zkSessions">section</ulink>
+      detailing ZooKeeper "chroot" support, this can be very useful
+      when deploying many applications/services interfacing to a
+      single ZooKeeper cluster.</para>
+
     </section>
   </section>
 </article>

Modified: hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml (original)
+++ hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml Wed Jun 24 22:59:34 2009
@@ -359,7 +359,7 @@
     <title>ZooKeeper Sessions</title>
 
     <para>To create a client session the application code must provide
-    a string containing a comma separated list of host:port pairs,
+    a connection string containing a comma separated list of host:port pairs,
     each corresponding to a ZooKeeper server (e.g. "127.0.0.1:4545" or
     "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"). The ZooKeeper
     client library will pick an arbitrary server and try to connect to
@@ -368,6 +368,23 @@
     automatically try the next server in the list, until a connection
     is (re-)established.</para>
 
+    <para> <emphasis role="bold">Added in 3.2.0</emphasis>: An
+    optional "chroot" suffix may also be appended to the connection
+    string. This will run the client commands while interpreting all
+    paths relative to this root (similar to the unix chroot
+    command). If used the example would look like:
+    "127.0.0.1:4545/app/a" or
+    "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a" where the
+    client would be rooted at "/app/a" and all paths would be relative
+    to this root - ie getting/setting/etc...  "/foo/bar" would result
+    in operations being run on "/app/a/foo/bar" (from the server
+    perspective). This feature is particularly useful in multi-tenant
+    environments where each user of a particular ZooKeeper service
+    could be rooted differently. This makes re-use much simpler as
+    each user can code his/her application as if it were rooted at
+    "/", while actual location (say /app/a) could be determined at
+    deployment time.</para>
+
     <para>When a client gets a handle to the ZooKeeper service,
     ZooKeeper creates a ZooKeeper session, represented as a 64-bit
     number, that it assigns to the client. If the client connects to a

Modified: hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml (original)
+++ hadoop/zookeeper/trunk/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml Wed Jun 24 22:59:34 2009
@@ -328,7 +328,7 @@
       functionally equivalent. The C bindings exist in two variants: single
       threaded and multi-threaded. These differ only in how the messaging loop
       is done. For more information, see the <ulink
-      url="zookeeperProgrammers.html#ch_programStructureWithExample.html">Programming
+      url="zookeeperProgrammers.html#ch_programStructureWithExample">Programming
       Examples in the ZooKeeper Programmer's Guide</ulink> for
       sample code using of the different APIs.</para>
     </section>

Modified: hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxn.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxn.java?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxn.java (original)
+++ hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ClientCnxn.java Wed Jun 24 22:59:34 2009
@@ -50,6 +50,7 @@
 import org.apache.zookeeper.ZooDefs.OpCode;
 import org.apache.zookeeper.ZooKeeper.States;
 import org.apache.zookeeper.ZooKeeper.WatchRegistration;
+import org.apache.zookeeper.common.PathUtils;
 import org.apache.zookeeper.proto.AuthPacket;
 import org.apache.zookeeper.proto.ConnectRequest;
 import org.apache.zookeeper.proto.ConnectResponse;
@@ -133,6 +134,8 @@
 
     private byte sessionPasswd[] = new byte[16];
 
+    final String chrootPath;
+
     final SendThread sendThread;
 
     final EventThread eventThread;
@@ -176,7 +179,10 @@
 
         ByteBuffer bb;
 
-        String path;
+        /** Client's view of the path (may differ due to chroot) **/
+        String clientPath;
+        /** Servers's view of the path (may differ due to chroot) **/
+        String serverPath;
 
         ReplyHeader replyHeader;
 
@@ -226,7 +232,8 @@
         public String toString() {
             StringBuffer sb = new StringBuffer();
 
-            sb.append("path:" + path);
+            sb.append("clientPath:" + clientPath);
+            sb.append(" serverPath:" + serverPath);
             sb.append(" finished:" + finished);
 
             sb.append(" header:: " + header);
@@ -285,6 +292,23 @@
         this.watcher = watcher;
         this.sessionId = sessionId;
         this.sessionPasswd = sessionPasswd;
+
+        // parse out chroot, if any
+        int off = hosts.indexOf('/');
+        if (off >= 0) {
+            String chrootPath = hosts.substring(off);
+            // ignore "/" chroot spec, same as null
+            if (chrootPath.length() == 1) {
+                this.chrootPath = null;
+            } else {
+                PathUtils.validatePath(chrootPath);
+                this.chrootPath = chrootPath;
+            }
+            hosts = hosts.substring(0,  off);
+        } else {
+            this.chrootPath = null;
+        }
+
         String hostsList[] = hosts.split(",");
         for (String host : hostsList) {
             int port = 2181;
@@ -406,7 +430,7 @@
                         } else {
                             Packet p = (Packet) event;
                             int rc = 0;
-                            String path = p.path;
+                            String clientPath = p.clientPath;
                             if (p.replyHeader.getErr() != 0) {
                                 rc = p.replyHeader.getErr();
                             }
@@ -418,62 +442,65 @@
                                 StatCallback cb = (StatCallback) p.cb;
                                 if (rc == 0) {
                                     if (p.response instanceof ExistsResponse) {
-                                        cb.processResult(rc, path, p.ctx,
+                                        cb.processResult(rc, clientPath, p.ctx,
                                                 ((ExistsResponse) p.response)
                                                         .getStat());
                                     } else if (p.response instanceof SetDataResponse) {
-                                        cb.processResult(rc, path, p.ctx,
+                                        cb.processResult(rc, clientPath, p.ctx,
                                                 ((SetDataResponse) p.response)
                                                         .getStat());
                                     } else if (p.response instanceof SetACLResponse) {
-                                        cb.processResult(rc, path, p.ctx,
+                                        cb.processResult(rc, clientPath, p.ctx,
                                                 ((SetACLResponse) p.response)
                                                         .getStat());
                                     }
                                 } else {
-                                    cb.processResult(rc, path, p.ctx, null);
+                                    cb.processResult(rc, clientPath, p.ctx, null);
                                 }
                             } else if (p.response instanceof GetDataResponse) {
                                 DataCallback cb = (DataCallback) p.cb;
                                 GetDataResponse rsp = (GetDataResponse) p.response;
                                 if (rc == 0) {
-                                    cb.processResult(rc, path, p.ctx, rsp
+                                    cb.processResult(rc, clientPath, p.ctx, rsp
                                             .getData(), rsp.getStat());
                                 } else {
-                                    cb.processResult(rc, path, p.ctx, null,
+                                    cb.processResult(rc, clientPath, p.ctx, null,
                                             null);
                                 }
                             } else if (p.response instanceof GetACLResponse) {
                                 ACLCallback cb = (ACLCallback) p.cb;
                                 GetACLResponse rsp = (GetACLResponse) p.response;
                                 if (rc == 0) {
-                                    cb.processResult(rc, path, p.ctx, rsp
+                                    cb.processResult(rc, clientPath, p.ctx, rsp
                                             .getAcl(), rsp.getStat());
                                 } else {
-                                    cb.processResult(rc, path, p.ctx, null,
+                                    cb.processResult(rc, clientPath, p.ctx, null,
                                             null);
                                 }
                             } else if (p.response instanceof GetChildrenResponse) {
                                 ChildrenCallback cb = (ChildrenCallback) p.cb;
                                 GetChildrenResponse rsp = (GetChildrenResponse) p.response;
                                 if (rc == 0) {
-                                    cb.processResult(rc, path, p.ctx, rsp
+                                    cb.processResult(rc, clientPath, p.ctx, rsp
                                             .getChildren());
                                 } else {
-                                    cb.processResult(rc, path, p.ctx, null);
+                                    cb.processResult(rc, clientPath, p.ctx, null);
                                 }
                             } else if (p.response instanceof CreateResponse) {
                                 StringCallback cb = (StringCallback) p.cb;
                                 CreateResponse rsp = (CreateResponse) p.response;
                                 if (rc == 0) {
-                                    cb.processResult(rc, path, p.ctx, rsp
-                                            .getPath());
+                                    cb.processResult(rc, clientPath, p.ctx,
+                                            (chrootPath == null
+                                                    ? rsp.getPath()
+                                                    : rsp.getPath()
+                                              .substring(chrootPath.length())));
                                 } else {
-                                    cb.processResult(rc, path, p.ctx, null);
+                                    cb.processResult(rc, clientPath, p.ctx, null);
                                 }
                             } else if (p.cb instanceof VoidCallback) {
                                 VoidCallback cb = (VoidCallback) p.cb;
-                                cb.processResult(rc, path, p.ctx);
+                                cb.processResult(rc, clientPath, p.ctx);
                             }
                         }
                     } catch (Throwable t) {
@@ -601,6 +628,13 @@
                     + Long.toHexString(sessionId));
                 WatcherEvent event = new WatcherEvent();
                 event.deserialize(bbia, "response");
+
+                // convert from a server path to a client path
+                if (chrootPath != null) {
+                    String serverPath = event.getPath();
+                    event.setPath(serverPath.substring(chrootPath.length()));
+                }
+
                 WatchedEvent we = new WatchedEvent(event);
                 if (LOG.isDebugEnabled()) {
                     LOG.debug("Got " + we + " for sessionid 0x"
@@ -791,7 +825,7 @@
         private void sendPing() {
             lastPingSentNs = System.nanoTime();
             RequestHeader h = new RequestHeader(-2, OpCode.ping);
-            queuePacket(h, null, null, null, null, null, null, null);
+            queuePacket(h, null, null, null, null, null, null, null, null);
         }
 
         int lastConnectIndex = -1;
@@ -1059,7 +1093,7 @@
             throws InterruptedException {
         ReplyHeader r = new ReplyHeader();
         Packet packet = queuePacket(h, r, request, response, null, null, null,
-                    watchRegistration);
+                    null, watchRegistration);
         synchronized (packet) {
             while (!packet.finished) {
                 packet.wait();
@@ -1069,8 +1103,9 @@
     }
 
     Packet queuePacket(RequestHeader h, ReplyHeader r, Record request,
-            Record response, AsyncCallback cb, String path, Object ctx,
-            WatchRegistration watchRegistration) {
+            Record response, AsyncCallback cb, String clientPath,
+            String serverPath, Object ctx, WatchRegistration watchRegistration)
+    {
         Packet packet = null;
         synchronized (outgoingQueue) {
             if (h.getType() != OpCode.ping && h.getType() != OpCode.auth) {
@@ -1080,7 +1115,8 @@
                     watchRegistration);
             packet.cb = cb;
             packet.ctx = ctx;
-            packet.path = path;
+            packet.clientPath = clientPath;
+            packet.serverPath = serverPath;
             if (!zooKeeper.state.isAlive()) {
                 conLossPacket(packet);
             } else {
@@ -1098,7 +1134,7 @@
         if (zooKeeper.state == States.CONNECTED) {
             queuePacket(new RequestHeader(-4, OpCode.auth), null,
                     new AuthPacket(0, scheme, auth), null, null, null, null,
-                    null);
+                    null, null);
         }
     }
 }

Modified: hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java?rev=788205&r1=788204&r2=788205&view=diff
==============================================================================
--- hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java (original)
+++ hadoop/zookeeper/trunk/src/java/main/org/apache/zookeeper/ZooKeeper.java Wed Jun 24 22:59:34 2009
@@ -148,7 +148,9 @@
          * @see org.apache.zookeeper.ClientWatchManager#materialize(Event.KeeperState, Event.EventType, java.lang.String)
          */
         public Set<Watcher> materialize(Watcher.Event.KeeperState state,
-                                        Watcher.Event.EventType type, String path) {
+                                        Watcher.Event.EventType type,
+                                        String clientPath)
+        {
             Set<Watcher> result = new HashSet<Watcher>();
 
             switch (type) {
@@ -183,36 +185,36 @@
             case NodeDataChanged:
             case NodeCreated:
                 synchronized (dataWatches) {
-                    addTo(dataWatches.remove(path), result);
+                    addTo(dataWatches.remove(clientPath), result);
                 }
                 synchronized (existWatches) {
-                    addTo(existWatches.remove(path), result);
+                    addTo(existWatches.remove(clientPath), result);
                 }
                 break;
             case NodeChildrenChanged:
                 synchronized (childWatches) {
-                    addTo(childWatches.remove(path), result);
+                    addTo(childWatches.remove(clientPath), result);
                 }
                 break;
             case NodeDeleted:
                 synchronized (dataWatches) {
-                    addTo(dataWatches.remove(path), result);
+                    addTo(dataWatches.remove(clientPath), result);
                 }
                 // XXX This shouldn't be needed, but just in case
                 synchronized (existWatches) {
-                    Set<Watcher> list = existWatches.remove(path);
+                    Set<Watcher> list = existWatches.remove(clientPath);
                     if (list != null) {
-                        addTo(existWatches.remove(path), result);
+                        addTo(existWatches.remove(clientPath), result);
                         LOG.warn("We are triggering an exists watch for delete! Shouldn't happen!");
                     }
                 }
                 synchronized (childWatches) {
-                    addTo(childWatches.remove(path), result);
+                    addTo(childWatches.remove(clientPath), result);
                 }
                 break;
             default:
                 String msg = "Unhandled watch event type " + type
-                    + " with state " + state + " on path " + path;
+                    + " with state " + state + " on path " + clientPath;
                 LOG.error(msg);
                 throw new RuntimeException(msg);
             }
@@ -226,11 +228,11 @@
      */
     abstract class WatchRegistration {
         private Watcher watcher;
-        private String path;
-        public WatchRegistration(Watcher watcher, String path)
+        private String clientPath;
+        public WatchRegistration(Watcher watcher, String clientPath)
         {
             this.watcher = watcher;
-            this.path = path;
+            this.clientPath = clientPath;
         }
 
         abstract protected Map<String, Set<Watcher>> getWatches(int rc);
@@ -244,10 +246,10 @@
             if (shouldAddWatch(rc)) {
                 Map<String, Set<Watcher>> watches = getWatches(rc);
                 synchronized(watches) {
-                    Set<Watcher> watchers = watches.get(path);
+                    Set<Watcher> watchers = watches.get(clientPath);
                     if (watchers == null) {
                         watchers = new HashSet<Watcher>();
-                        watches.put(path, watchers);
+                        watches.put(clientPath, watchers);
                     }
                     watchers.add(watcher);
                 }
@@ -268,8 +270,8 @@
      * even in the case where NONODE result code is returned.
      */
     class ExistsWatchRegistration extends WatchRegistration {
-        public ExistsWatchRegistration(Watcher watcher, String path) {
-            super(watcher, path);
+        public ExistsWatchRegistration(Watcher watcher, String clientPath) {
+            super(watcher, clientPath);
         }
 
         @Override
@@ -284,8 +286,8 @@
     }
 
     class DataWatchRegistration extends WatchRegistration {
-        public DataWatchRegistration(Watcher watcher, String path) {
-            super(watcher, path);
+        public DataWatchRegistration(Watcher watcher, String clientPath) {
+            super(watcher, clientPath);
         }
 
         @Override
@@ -295,8 +297,8 @@
     }
 
     class ChildWatchRegistration extends WatchRegistration {
-        public ChildWatchRegistration(Watcher watcher, String path) {
-            super(watcher, path);
+        public ChildWatchRegistration(Watcher watcher, String clientPath) {
+            super(watcher, clientPath);
         }
 
         @Override
@@ -318,17 +320,27 @@
     protected final ClientCnxn cnxn;
 
     /**
-     * To create a client(ZooKeeper) object, the application needs to pass a
-     * string containing a comma separated list of host:port pairs, each 
-     * corresponding to a ZooKeeper server.
+     * To create a ZooKeeper client object, the application needs to pass a
+     * connection string containing a comma separated list of host:port pairs,
+     * each corresponding to a ZooKeeper server.
      * <p>
      * The client object will pick an arbitrary server and try to connect to it.
      * If failed, it will try the next one in the list, until a connection is
      * established, or all the servers have been tried.
+     * <p>
+     * Added in 3.2.0: An optional "chroot" suffix may also be appended to the
+     * connection string. This will run the client commands while interpreting
+     * all paths relative to this root (similar to the unix chroot command).
      *
-     * @param host
+     * @param connectString
      *            comma separated host:port pairs, each corresponding to a zk
      *            server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
+     *            If the optional chroot suffix is used the example would look
+     *            like: "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a"
+     *            where the client would be rooted at "/app/a" and all paths
+     *            would be relative to this root - ie getting/setting/etc...
+     *            "/foo/bar" would result in operations being run on
+     *            "/app/a/foo/bar" (from the server perspective).
      * @param sessionTimeout
      *            session timeout in milliseconds
      * @param watcher
@@ -336,35 +348,47 @@
      *            also be notified for node events
      *
      * @throws IOException in cases of network failure
+     * @throws IllegalArgumentException if an invalid chroot path is specified
      */
-    public ZooKeeper(String host, int sessionTimeout, Watcher watcher)
-            throws IOException {
-        LOG.info("Initiating client connection, host=" + host
+    public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)
+        throws IOException
+    {
+        LOG.info("Initiating client connection, connectString=" + connectString
                 + " sessionTimeout=" + sessionTimeout + " watcher=" + watcher);
 
         watchManager.defaultWatcher = watcher;
-        cnxn = new ClientCnxn(host, sessionTimeout, this, watchManager);
+        cnxn = new ClientCnxn(connectString, sessionTimeout, this, watchManager);
         cnxn.start();
     }
 
     /**
-     * To create a client(ZooKeeper) object, the application needs to pass a
-     * string containing a comma separated list of host:port pairs, each
-     * corresponding to a ZooKeeper server.
+     * To create a ZooKeeper client object, the application needs to pass a
+     * connection string containing a comma separated list of host:port pairs,
+     * each corresponding to a ZooKeeper server.
      * <p>
      * The client object will pick an arbitrary server and try to connect to it.
      * If failed, it will try the next one in the list, until a connection is
      * established, or all the servers have been tried.
      * <p>
+     * Added in 3.2.0: An optional "chroot" suffix may also be appended to the
+     * connection string. This will run the client commands while interpreting
+     * all paths relative to this root (similar to the unix chroot command).
+     * <p>
      * Use {@link #getSessionId} and {@link #getSessionPasswd} on an established
      * client connection, these values must be passed as sessionId and
      * sessionPasswd respectively if reconnecting. Otherwise, if not
      * reconnecting, use the other constructor which does not require these
      * parameters.
      *
-     * @param host
+     * @param connectString
      *            comma separated host:port pairs, each corresponding to a zk
      *            server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
+     *            If the optional chroot suffix is used the example would look
+     *            like: "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a"
+     *            where the client would be rooted at "/app/a" and all paths
+     *            would be relative to this root - ie getting/setting/etc...
+     *            "/foo/bar" would result in operations being run on
+     *            "/app/a/foo/bar" (from the server perspective).
      * @param sessionTimeout
      *            session timeout in milliseconds
      * @param watcher
@@ -376,10 +400,13 @@
      *            password for this session
      *
      * @throws IOException in cases of network failure
+     * @throws IllegalArgumentException if an invalid chroot path is specified
      */
-    public ZooKeeper(String host, int sessionTimeout, Watcher watcher,
-            long sessionId, byte[] sessionPasswd) throws IOException {
-        LOG.info("Initiating client connection, host=" + host
+    public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,
+            long sessionId, byte[] sessionPasswd)
+        throws IOException
+    {
+        LOG.info("Initiating client connection, connectString=" + connectString
                 + " sessionTimeout=" + sessionTimeout
                 + " watcher=" + watcher
                 + " sessionId=" + sessionId
@@ -387,7 +414,7 @@
                 + (sessionPasswd == null ? "<null>" : "<hidden>"));
 
         watchManager.defaultWatcher = watcher;
-        cnxn = new ClientCnxn(host, sessionTimeout, this, watchManager,
+        cnxn = new ClientCnxn(connectString, sessionTimeout, this, watchManager,
                 sessionId, sessionPasswd);
         cnxn.start();
     }
@@ -429,9 +456,6 @@
      * their parents) will be triggered.
      *
      * @throws InterruptedException
-     *
-     * @throws IOException
-     * @throws InterruptedException
      */
     public synchronized void close() throws InterruptedException {
         LOG.info("Closing session: 0x" + Long.toHexString(getSessionId()));
@@ -446,6 +470,25 @@
     }
 
     /**
+     * Prepend the chroot to the client path (if present). The expectation of
+     * this function is that the client path has been validated before this
+     * function is called
+     * @param clientPath path to the node
+     * @return server view of the path (chroot prepended to client path)
+     */
+    private String prependChroot(String clientPath) {
+        if (cnxn.chrootPath != null) {
+            // handle clientPath = "/"
+            if (clientPath.length() == 1) {
+                return cnxn.chrootPath;
+            }
+            return cnxn.chrootPath + clientPath;
+        } else {
+            return clientPath;
+        }
+    }
+    
+    /**
      * Create a node with the given path. The node data will be the given data,
      * and node acl will be the given acl.
      * <p>
@@ -495,16 +538,18 @@
      *                and/or sequential
      * @return the actual path of the created node
      * @throws KeeperException if the server returns a non-zero error code
-     * @throws org.apache.zookeeper.KeeperException.InvalidACLException if the ACL is invalid
+     * @throws KeeperException.InvalidACLException if the ACL is invalid
      * @throws InterruptedException if the transaction is interrupted
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public String create(String path, byte data[], List<ACL> acl,
+    public String create(final String path, byte data[], List<ACL> acl,
             CreateMode createMode)
         throws KeeperException, InterruptedException
     {
-        // also handle the case where server will append name suffix
-        PathUtils.validatePath(path, createMode.isSequential());
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath, createMode.isSequential());
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.create);
@@ -512,7 +557,7 @@
         CreateResponse response = new CreateResponse();
         request.setData(data);
         request.setFlags(createMode.toFlag());
-        request.setPath(path);
+        request.setPath(serverPath);
         if (acl != null && acl.size() == 0) {
             throw new KeeperException.InvalidACLException();
         }
@@ -520,9 +565,13 @@
         ReplyHeader r = cnxn.submitRequest(h, request, response, null);
         if (r.getErr() != 0) {
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
-                    path);
+                    clientPath);
+        }
+        if (cnxn.chrootPath == null) {
+            return response.getPath();
+        } else {
+            return response.getPath().substring(cnxn.chrootPath.length());
         }
-        return response.getPath();
     }
 
     /**
@@ -532,11 +581,13 @@
      * @see #create(String, byte[], List, CreateMode)
      */
 
-    public void create(String path, byte data[], List<ACL> acl,
+    public void create(final String path, byte data[], List<ACL> acl,
             CreateMode createMode,  StringCallback cb, Object ctx)
     {
-        // also handle the case where server will append name suffix
-        PathUtils.validatePath(path, createMode.isSequential());
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath, createMode.isSequential());
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.create);
@@ -545,9 +596,10 @@
         ReplyHeader r = new ReplyHeader();
         request.setData(data);
         request.setFlags(createMode.toFlag());
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setAcl(acl);
-        cnxn.queuePacket(h, r, request, response, cb, path, ctx, null);
+        cnxn.queuePacket(h, r, request, response, cb, clientPath,
+                serverPath, ctx, null);
     }
 
     /**
@@ -573,22 +625,38 @@
      * @param version
      *                the expected node version.
      * @throws InterruptedException IF the server transaction is interrupted
-     * @throws KeeperException If the server signals an error with a non-zero return code.
+     * @throws KeeperException If the server signals an error with a non-zero
+     *   return code.
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public void delete(String path, int version) throws
-            InterruptedException, KeeperException {
-        PathUtils.validatePath(path);
+    public void delete(final String path, int version)
+        throws InterruptedException, KeeperException
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+        
+        final String serverPath;
+
+        // maintain semantics even in chroot case
+        // specifically - root cannot be deleted
+        // I think this makes sense even in chroot case.
+        if (clientPath.equals("/")) {
+            // a bit of a hack, but delete(/) will never succeed and ensures
+            // that the same semantics are maintained
+            serverPath = clientPath;
+        } else {
+            serverPath = prependChroot(clientPath);
+        }
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.delete);
         DeleteRequest request = new DeleteRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setVersion(version);
         ReplyHeader r = cnxn.submitRequest(h, request, null, null);
         if (r.getErr() != 0) {
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
-                    path);
+                    clientPath);
         }
     }
 
@@ -598,15 +666,32 @@
      *
      * @see #delete(String, int)
      */
-    public void delete(String path, int version, VoidCallback cb, Object ctx) {
-        PathUtils.validatePath(path);
+    public void delete(final String path, int version, VoidCallback cb,
+            Object ctx)
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        final String serverPath;
+
+        // maintain semantics even in chroot case
+        // specifically - root cannot be deleted
+        // I think this makes sense even in chroot case.
+        if (clientPath.equals("/")) {
+            // a bit of a hack, but delete(/) will never succeed and ensures
+            // that the same semantics are maintained
+            serverPath = clientPath;
+        } else {
+            serverPath = prependChroot(clientPath);
+        }
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.delete);
         DeleteRequest request = new DeleteRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setVersion(version);
-        cnxn.queuePacket(h, new ReplyHeader(), request, null, cb, path, ctx, null);
+        cnxn.queuePacket(h, new ReplyHeader(), request, null, cb, clientPath,
+                serverPath, ctx, null);
     }
 
     /**
@@ -626,28 +711,33 @@
      * @throws InterruptedException If the server transaction is interrupted.
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public Stat exists(String path, Watcher watcher) throws KeeperException,
-        InterruptedException
+    public Stat exists(final String path, Watcher watcher)
+        throws KeeperException, InterruptedException
     {
-        PathUtils.validatePath(path);
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        // the watch contains the un-chroot path
+        WatchRegistration wcb = null;
+        if (watcher != null) {
+            wcb = new ExistsWatchRegistration(watcher, clientPath);
+        }
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.exists);
         ExistsRequest request = new ExistsRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setWatch(watcher != null);
         SetDataResponse response = new SetDataResponse();
-        WatchRegistration wcb = null;
-        if (watcher != null) {
-            wcb = new ExistsWatchRegistration(watcher, path);
-        }
         ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);
         if (r.getErr() != 0) {
             if (r.getErr() == KeeperException.Code.NONODE.intValue()) {
                 return null;
             }
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
-                    path);
+                    clientPath);
         }
 
         return response.getStat().getCzxid() == -1 ? null : response.getStat();
@@ -683,23 +773,28 @@
      *
      * @see #exists(String, boolean)
      */
-    public void exists(String path, Watcher watcher, StatCallback cb,
-            Object ctx)
+    public void exists(final String path, Watcher watcher,
+            StatCallback cb, Object ctx)
     {
-        PathUtils.validatePath(path);
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        // the watch contains the un-chroot path
+        WatchRegistration wcb = null;
+        if (watcher != null) {
+            wcb = new ExistsWatchRegistration(watcher, clientPath);
+        }
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.exists);
         ExistsRequest request = new ExistsRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setWatch(watcher != null);
         SetDataResponse response = new SetDataResponse();
-        WatchRegistration wcb = null;
-        if (watcher != null) {
-            wcb = new ExistsWatchRegistration(watcher, path);
-        }
-        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, path,
-                        ctx, wcb);
+        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,
+                clientPath, serverPath, ctx, wcb);
     }
 
     /**
@@ -731,24 +826,30 @@
      * @throws InterruptedException If the server transaction is interrupted.
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public byte[] getData(String path, Watcher watcher, Stat stat)
-            throws KeeperException, InterruptedException {
-        PathUtils.validatePath(path);
+    public byte[] getData(final String path, Watcher watcher, Stat stat)
+        throws KeeperException, InterruptedException
+     {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        // the watch contains the un-chroot path
+        WatchRegistration wcb = null;
+        if (watcher != null) {
+            wcb = new DataWatchRegistration(watcher, clientPath);
+        }
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.getData);
         GetDataRequest request = new GetDataRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setWatch(watcher != null);
         GetDataResponse response = new GetDataResponse();
-        WatchRegistration wcb = null;
-        if (watcher != null) {
-            wcb = new DataWatchRegistration(watcher, path);
-        }
         ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);
         if (r.getErr() != 0) {
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
-                    path);
+                    clientPath);
         }
         if (stat != null) {
             DataTree.copyStat(response.getStat(), stat);
@@ -785,21 +886,28 @@
      *
      * @see #getData(String, Watcher, Stat)
      */
-    public void getData(String path, Watcher watcher, DataCallback cb, Object ctx) {
-        PathUtils.validatePath(path);
+    public void getData(final String path, Watcher watcher,
+            DataCallback cb, Object ctx)
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        // the watch contains the un-chroot path
+        WatchRegistration wcb = null;
+        if (watcher != null) {
+            wcb = new DataWatchRegistration(watcher, clientPath);
+        }
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.getData);
         GetDataRequest request = new GetDataRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setWatch(watcher != null);
         GetDataResponse response = new GetDataResponse();
-        WatchRegistration wcb = null;
-        if (watcher != null) {
-            wcb = new DataWatchRegistration(watcher, path);
-        }
-        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, path,
-                        ctx, wcb);
+        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,
+                clientPath, serverPath, ctx, wcb);
     }
 
     /**
@@ -840,21 +948,25 @@
      * @throws KeeperException If the server signals an error with a non-zero error code.
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public Stat setData(String path, byte data[], int version)
-            throws KeeperException, InterruptedException {
-        PathUtils.validatePath(path);
+    public Stat setData(final String path, byte data[], int version)
+        throws KeeperException, InterruptedException
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.setData);
         SetDataRequest request = new SetDataRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setData(data);
         request.setVersion(version);
         SetDataResponse response = new SetDataResponse();
         ReplyHeader r = cnxn.submitRequest(h, request, response, null);
         if (r.getErr() != 0) {
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
-                    path);
+                    clientPath);
         }
         return response.getStat();
     }
@@ -865,20 +977,23 @@
      *
      * @see #setData(String, byte[], int)
      */
-    public void setData(String path, byte data[], int version, StatCallback cb,
-            Object ctx) {
-        PathUtils.validatePath(path);
+    public void setData(final String path, byte data[], int version,
+            StatCallback cb, Object ctx)
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.setData);
         SetDataRequest request = new SetDataRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setData(data);
         request.setVersion(version);
         SetDataResponse response = new SetDataResponse();
-        cnxn
-                .queuePacket(h, new ReplyHeader(), request, response, cb, path,
-                        ctx, null);
+        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,
+                clientPath, serverPath, ctx, null);
     }
 
     /**
@@ -896,19 +1011,23 @@
      * @throws KeeperException If the server signals an error with a non-zero error code.
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public List<ACL> getACL(String path, Stat stat)
-            throws KeeperException, InterruptedException {
-        PathUtils.validatePath(path);
+    public List<ACL> getACL(final String path, Stat stat)
+        throws KeeperException, InterruptedException
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.getACL);
         GetACLRequest request = new GetACLRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         GetACLResponse response = new GetACLResponse();
         ReplyHeader r = cnxn.submitRequest(h, request, response, null);
         if (r.getErr() != 0) {
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
-                    path);
+                    clientPath);
         }
         DataTree.copyStat(response.getStat(), stat);
         return response.getAcl();
@@ -920,17 +1039,21 @@
      *
      * @see #getACL(String, Stat)
      */
-    public void getACL(String path, Stat stat, ACLCallback cb, Object ctx) {
-        PathUtils.validatePath(path);
+    public void getACL(final String path, Stat stat, ACLCallback cb,
+            Object ctx)
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.getACL);
         GetACLRequest request = new GetACLRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         GetACLResponse response = new GetACLResponse();
-        cnxn
-                .queuePacket(h, new ReplyHeader(), request, response, cb, path,
-                        ctx, null);
+        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,
+                clientPath, serverPath, ctx, null);
     }
 
     /**
@@ -953,14 +1076,18 @@
      * @throws org.apache.zookeeper.KeeperException.InvalidACLException If the acl is invalide.
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public Stat setACL(String path, List<ACL> acl, int version)
-            throws KeeperException, InterruptedException {
-        PathUtils.validatePath(path);
+    public Stat setACL(final String path, List<ACL> acl, int version)
+        throws KeeperException, InterruptedException
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.setACL);
         SetACLRequest request = new SetACLRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         if (acl != null && acl.size() == 0) {
             throw new KeeperException.InvalidACLException();
         }
@@ -970,7 +1097,7 @@
         ReplyHeader r = cnxn.submitRequest(h, request, response, null);
         if (r.getErr() != 0) {
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
-                    path);
+                    clientPath);
         }
         return response.getStat();
     }
@@ -981,20 +1108,23 @@
      *
      * @see #setACL(String, List, int)
      */
-    public void setACL(String path, List<ACL> acl, int version,
-            StatCallback cb, Object ctx) {
-        PathUtils.validatePath(path);
+    public void setACL(final String path, List<ACL> acl, int version,
+            StatCallback cb, Object ctx)
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.setACL);
         SetACLRequest request = new SetACLRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setAcl(acl);
         request.setVersion(version);
         SetACLResponse response = new SetACLResponse();
-        cnxn
-                .queuePacket(h, new ReplyHeader(), request, response, cb, path,
-                        ctx, null);
+        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,
+                clientPath, serverPath, ctx, null);
     }
 
     /**
@@ -1018,24 +1148,30 @@
      * @throws KeeperException If the server signals an error with a non-zero error code.
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public List<String> getChildren(String path, Watcher watcher)
-            throws KeeperException, InterruptedException {
-        PathUtils.validatePath(path);
+    public List<String> getChildren(final String path, Watcher watcher)
+        throws KeeperException, InterruptedException
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        // the watch contains the un-chroot path
+        WatchRegistration wcb = null;
+        if (watcher != null) {
+            wcb = new ChildWatchRegistration(watcher, clientPath);
+        }
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.getChildren);
         GetChildrenRequest request = new GetChildrenRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setWatch(watcher != null);
         GetChildrenResponse response = new GetChildrenResponse();
-        WatchRegistration wcb = null;
-        if (watcher != null) {
-            wcb = new ChildWatchRegistration(watcher, path);
-        }
         ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);
         if (r.getErr() != 0) {
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
-                    path);
+                    clientPath);
         }
         return response.getChildren();
     }
@@ -1071,22 +1207,28 @@
      *
      * @see #getChildren(String, Watcher)
      */
-    public void getChildren(String path, Watcher watcher, ChildrenCallback cb,
-            Object ctx) {
-        PathUtils.validatePath(path);
+    public void getChildren(final String path, Watcher watcher,
+            ChildrenCallback cb, Object ctx)
+    {
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        // the watch contains the un-chroot path
+        WatchRegistration wcb = null;
+        if (watcher != null) {
+            wcb = new ChildWatchRegistration(watcher, clientPath);
+        }
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.getChildren);
         GetChildrenRequest request = new GetChildrenRequest();
-        request.setPath(path);
+        request.setPath(serverPath);
         request.setWatch(watcher != null);
         GetChildrenResponse response = new GetChildrenResponse();
-        WatchRegistration wcb = null;
-        if (watcher != null) {
-            wcb = new ChildWatchRegistration(watcher, path);
-        }
-        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, path,
-                        ctx, wcb);
+        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,
+                clientPath, serverPath, ctx, wcb);
     }
 
     /**
@@ -1107,16 +1249,19 @@
      * @param ctx context to be provided to the callback
      * @throws IllegalArgumentException if an invalid path is specified
      */
-    public void sync(String path, VoidCallback cb, Object ctx){
-        PathUtils.validatePath(path);
+    public void sync(final String path, VoidCallback cb, Object ctx){
+        final String clientPath = path;
+        PathUtils.validatePath(clientPath);
+
+        final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
         h.setType(ZooDefs.OpCode.sync);
         SyncRequest request = new SyncRequest();
         SyncResponse response = new SyncResponse();
-        request.setPath(path);
-        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, path, ctx,
-                null);
+        request.setPath(serverPath);
+        cnxn.queuePacket(h, new ReplyHeader(), request, response, cb,
+                clientPath, serverPath, ctx, null);
     }
 
     public States getState() {