Merge 'CURATOR-217' into CURATOR-3.0
diff --git a/curator-client/pom.xml b/curator-client/pom.xml
index 0f3955b..7bb437c 100644
--- a/curator-client/pom.xml
+++ b/curator-client/pom.xml
@@ -46,6 +46,16 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.apache.zookeeper</groupId>
+            <artifactId>zookeeper</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
@@ -61,5 +71,17 @@
             <artifactId>curator-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/curator-client/src/main/java/org/apache/curator/RetryLoop.java b/curator-client/src/main/java/org/apache/curator/RetryLoop.java
index 6b66e82..065ebef 100644
--- a/curator-client/src/main/java/org/apache/curator/RetryLoop.java
+++ b/curator-client/src/main/java/org/apache/curator/RetryLoop.java
@@ -150,7 +150,8 @@
         return (rc == KeeperException.Code.CONNECTIONLOSS.intValue()) ||
             (rc == KeeperException.Code.OPERATIONTIMEOUT.intValue()) ||
             (rc == KeeperException.Code.SESSIONMOVED.intValue()) ||
-            (rc == KeeperException.Code.SESSIONEXPIRED.intValue());
+            (rc == KeeperException.Code.SESSIONEXPIRED.intValue()) ||
+            (rc == KeeperException.Code.NEWCONFIGNOQUORUM.intValue());
     }
 
     /**
diff --git a/curator-client/src/main/java/org/apache/curator/ensemble/EnsembleListener.java b/curator-client/src/main/java/org/apache/curator/ensemble/EnsembleListener.java
new file mode 100644
index 0000000..8f963cd
--- /dev/null
+++ b/curator-client/src/main/java/org/apache/curator/ensemble/EnsembleListener.java
@@ -0,0 +1,24 @@
+/**
+ * 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.curator.ensemble;
+
+public interface EnsembleListener {
+
+    void connectionStringUpdated(String connectionString);
+}
diff --git a/curator-client/src/main/java/org/apache/curator/ensemble/dynamic/DynamicEnsembleProvider.java b/curator-client/src/main/java/org/apache/curator/ensemble/dynamic/DynamicEnsembleProvider.java
new file mode 100644
index 0000000..70b755f
--- /dev/null
+++ b/curator-client/src/main/java/org/apache/curator/ensemble/dynamic/DynamicEnsembleProvider.java
@@ -0,0 +1,61 @@
+/**
+ * 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.curator.ensemble.dynamic;
+
+import com.google.common.base.Preconditions;
+import org.apache.curator.ensemble.EnsembleListener;
+import org.apache.curator.ensemble.EnsembleProvider;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class DynamicEnsembleProvider implements EnsembleProvider, EnsembleListener {
+
+    private final AtomicReference<String> connectionString = new AtomicReference<String>();
+
+    /**
+     * The connection string to use
+     *
+     * @param connectionString connection string
+     */
+    public DynamicEnsembleProvider(String connectionString)
+    {
+        this.connectionString.set(Preconditions.checkNotNull(connectionString, "connectionString cannot be null"));
+    }
+
+    @Override
+    public void start() throws Exception {
+        // NOP
+    }
+
+    @Override
+    public String getConnectionString() {
+        return connectionString.get();
+    }
+
+    @Override
+    public void close() throws IOException {
+        // NOP
+    }
+
+    @Override
+    public void connectionStringUpdated(String connectionString) {
+        this.connectionString.set(connectionString);
+    }
+}
diff --git a/curator-client/src/main/java/org/apache/curator/utils/DebugUtils.java b/curator-client/src/main/java/org/apache/curator/utils/DebugUtils.java
index e84e06b..383bc13 100644
--- a/curator-client/src/main/java/org/apache/curator/utils/DebugUtils.java
+++ b/curator-client/src/main/java/org/apache/curator/utils/DebugUtils.java
@@ -24,6 +24,7 @@
     public static final String          PROPERTY_DONT_LOG_CONNECTION_ISSUES = "curator-dont-log-connection-problems";
     public static final String          PROPERTY_LOG_ONLY_FIRST_CONNECTION_ISSUE_AS_ERROR_LEVEL = "curator-log-only-first-connection-issue-as-error-level";
     public static final String          PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND = "curator-remove-watchers-in-foreground";
+    public static final String          PROPERTY_RETRY_FAILED_TESTS = "curator-retry-failed-tests";
 
     private DebugUtils()
     {
diff --git a/curator-client/src/main/java/org/apache/curator/utils/EnsurePath.java b/curator-client/src/main/java/org/apache/curator/utils/EnsurePath.java
index f072775..3845a74 100644
--- a/curator-client/src/main/java/org/apache/curator/utils/EnsurePath.java
+++ b/curator-client/src/main/java/org/apache/curator/utils/EnsurePath.java
@@ -47,7 +47,10 @@
  *         ensurePath.ensure(zk);   // subsequent times are NOPs
  *         zk.create(nodePath, ...);
  * </pre>
+ *
+ * @deprecated Since 2.9.0 - Prefer CuratorFramework.create().creatingParentContainersIfNeeded() or CuratorFramework.exists().creatingParentContainersIfNeeded()
  */
+@Deprecated
 public class EnsurePath
 {
     private final String path;
@@ -64,7 +67,7 @@
         }
     };
 
-    private interface Helper
+    interface Helper
     {
         public void ensure(CuratorZookeeperClient client, String path, final boolean makeLastNode) throws Exception;
     }
@@ -110,7 +113,7 @@
         return new EnsurePath(path, helper, false, aclProvider);
     }
 
-    private EnsurePath(String path, AtomicReference<Helper> helper, boolean makeLastNode, InternalACLProvider aclProvider)
+    protected EnsurePath(String path, AtomicReference<Helper> helper, boolean makeLastNode, InternalACLProvider aclProvider)
     {
         this.path = path;
         this.makeLastNode = makeLastNode;
@@ -128,6 +131,11 @@
         return this.path;
     }
 
+    protected boolean asContainers()
+    {
+        return false;
+    }
+
     private class InitialHelper implements Helper
     {
         private boolean isSet = false;  // guarded by synchronization
@@ -145,7 +153,7 @@
                             @Override
                             public Object call() throws Exception
                             {
-                                ZKPaths.mkdirs(client.getZooKeeper(), path, makeLastNode, aclProvider);
+                                ZKPaths.mkdirs(client.getZooKeeper(), path, makeLastNode, aclProvider, asContainers());
                                 helper.set(doNothingHelper);
                                 isSet = true;
                                 return null;
diff --git a/curator-client/src/main/java/org/apache/curator/utils/ThreadUtils.java b/curator-client/src/main/java/org/apache/curator/utils/ThreadUtils.java
index 9665dfe..8960ec0 100644
--- a/curator-client/src/main/java/org/apache/curator/utils/ThreadUtils.java
+++ b/curator-client/src/main/java/org/apache/curator/utils/ThreadUtils.java
@@ -48,6 +48,11 @@
 
     public static ThreadFactory newThreadFactory(String processName)
     {
+        return newGenericThreadFactory("Curator-" + processName);
+    }
+
+    public static ThreadFactory newGenericThreadFactory(String processName)
+    {
         return new ThreadFactoryBuilder()
             .setNameFormat(processName + "-%d")
             .setDaemon(true)
diff --git a/curator-client/src/main/java/org/apache/curator/utils/ZKPaths.java b/curator-client/src/main/java/org/apache/curator/utils/ZKPaths.java
index 352bfd6..75e1171 100644
--- a/curator-client/src/main/java/org/apache/curator/utils/ZKPaths.java
+++ b/curator-client/src/main/java/org/apache/curator/utils/ZKPaths.java
@@ -26,12 +26,60 @@
 import org.apache.zookeeper.ZooDefs;
 import org.apache.zookeeper.ZooKeeper;
 import org.apache.zookeeper.data.ACL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import java.util.Collections;
 import java.util.List;
 
 public class ZKPaths
 {
     /**
+     * Zookeeper's path separator character.
+     */
+    public static final String PATH_SEPARATOR = "/";
+
+    private static final CreateMode NON_CONTAINER_MODE = CreateMode.PERSISTENT;
+
+    /**
+     * @return {@link CreateMode#CONTAINER} if the ZK JAR supports it. Otherwise {@link CreateMode#PERSISTENT}
+     */
+    public static CreateMode getContainerCreateMode()
+    {
+        return CreateModeHolder.containerCreateMode;
+    }
+
+    /**
+     * Returns true if the version of ZooKeeper client in use supports containers
+     *
+     * @return true/false
+     */
+    public static boolean hasContainerSupport()
+    {
+        return getContainerCreateMode() != NON_CONTAINER_MODE;
+    }
+
+    private static class CreateModeHolder
+    {
+        private static final Logger log = LoggerFactory.getLogger(ZKPaths.class);
+        private static final CreateMode containerCreateMode;
+
+        static
+        {
+            CreateMode localCreateMode;
+            try
+            {
+                localCreateMode = CreateMode.valueOf("CONTAINER");
+            }
+            catch ( IllegalArgumentException ignore )
+            {
+                localCreateMode = NON_CONTAINER_MODE;
+                log.warn("The version of ZooKeeper being used doesn't support Container nodes. CreateMode.PERSISTENT will be used instead.");
+            }
+            containerCreateMode = localCreateMode;
+        }
+    }
+
+    /**
      * Apply the namespace to the given path
      *
      * @param namespace namespace (can be null)
@@ -40,8 +88,21 @@
      */
     public static String fixForNamespace(String namespace, String path)
     {
+        return fixForNamespace(namespace, path, false);
+    }
+
+    /**
+     * Apply the namespace to the given path
+     *
+     * @param namespace    namespace (can be null)
+     * @param path         path
+     * @param isSequential if the path is being created with a sequential flag
+     * @return adjusted path
+     */
+    public static String fixForNamespace(String namespace, String path, boolean isSequential)
+    {
         // Child path must be valid in and of itself.
-        PathUtils.validatePath(path);
+        PathUtils.validatePath(path, isSequential);
 
         if ( namespace != null )
         {
@@ -59,7 +120,7 @@
     public static String getNodeFromPath(String path)
     {
         PathUtils.validatePath(path);
-        int i = path.lastIndexOf('/');
+        int i = path.lastIndexOf(PATH_SEPARATOR);
         if ( i < 0 )
         {
             return path;
@@ -102,21 +163,21 @@
     public static PathAndNode getPathAndNode(String path)
     {
         PathUtils.validatePath(path);
-        int i = path.lastIndexOf('/');
+        int i = path.lastIndexOf(PATH_SEPARATOR);
         if ( i < 0 )
         {
             return new PathAndNode(path, "");
         }
         if ( (i + 1) >= path.length() )
         {
-            return new PathAndNode("/", "");
+            return new PathAndNode(PATH_SEPARATOR, "");
         }
         String node = path.substring(i + 1);
-        String parentPath = (i > 0) ? path.substring(0, i) : "/";
+        String parentPath = (i > 0) ? path.substring(0, i) : PATH_SEPARATOR;
         return new PathAndNode(parentPath, node);
     }
 
-    private static final Splitter PATH_SPLITTER = Splitter.on('/').omitEmptyStrings();
+    private static final Splitter PATH_SPLITTER = Splitter.on(PATH_SEPARATOR).omitEmptyStrings();
 
     /**
      * Given a full path, return the the individual parts, without slashes.
@@ -142,7 +203,7 @@
      */
     public static void mkdirs(ZooKeeper zookeeper, String path) throws InterruptedException, KeeperException
     {
-        mkdirs(zookeeper, path, true, null);
+        mkdirs(zookeeper, path, true, null, false);
     }
 
     /**
@@ -157,7 +218,7 @@
      */
     public static void mkdirs(ZooKeeper zookeeper, String path, boolean makeLastNode) throws InterruptedException, KeeperException
     {
-        mkdirs(zookeeper, path, makeLastNode, null);
+        mkdirs(zookeeper, path, makeLastNode, null, false);
     }
 
     /**
@@ -173,12 +234,29 @@
      */
     public static void mkdirs(ZooKeeper zookeeper, String path, boolean makeLastNode, InternalACLProvider aclProvider) throws InterruptedException, KeeperException
     {
+        mkdirs(zookeeper, path, makeLastNode, aclProvider, false);
+    }
+
+    /**
+     * Make sure all the nodes in the path are created. NOTE: Unlike File.mkdirs(), Zookeeper doesn't distinguish
+     * between directories and files. So, every node in the path is created. The data for each node is an empty blob
+     *
+     * @param zookeeper    the client
+     * @param path         path to ensure
+     * @param makeLastNode if true, all nodes are created. If false, only the parent nodes are created
+     * @param aclProvider  if not null, the ACL provider to use when creating parent nodes
+     * @param asContainers if true, nodes are created as {@link CreateMode#CONTAINER}
+     * @throws InterruptedException                 thread interruption
+     * @throws org.apache.zookeeper.KeeperException Zookeeper errors
+     */
+    public static void mkdirs(ZooKeeper zookeeper, String path, boolean makeLastNode, InternalACLProvider aclProvider, boolean asContainers) throws InterruptedException, KeeperException
+    {
         PathUtils.validatePath(path);
 
         int pos = 1; // skip first slash, root is guaranteed to exist
         do
         {
-            pos = path.indexOf('/', pos + 1);
+            pos = path.indexOf(PATH_SEPARATOR, pos + 1);
 
             if ( pos == -1 )
             {
@@ -210,7 +288,7 @@
                     {
                         acl = ZooDefs.Ids.OPEN_ACL_UNSAFE;
                     }
-                    zookeeper.create(subPath, new byte[0], acl, CreateMode.PERSISTENT);
+                    zookeeper.create(subPath, new byte[0], acl, getCreateMode(asContainers));
                 }
                 catch ( KeeperException.NodeExistsException e )
                 {
@@ -288,14 +366,57 @@
     {
         StringBuilder path = new StringBuilder();
 
+        joinPath(path, parent, child);
+
+        return path.toString();
+    }
+
+    /**
+     * Given a parent path and a list of children nodes, create a combined full path
+     *
+     * @param parent       the parent
+     * @param firstChild   the first children in the path
+     * @param restChildren the rest of the children in the path
+     * @return full path
+     */
+    public static String makePath(String parent, String firstChild, String... restChildren)
+    {
+        StringBuilder path = new StringBuilder();
+
+        joinPath(path, parent, firstChild);
+
+        if ( restChildren == null )
+        {
+            return path.toString();
+        }
+        else
+        {
+            for ( String child : restChildren )
+            {
+                joinPath(path, "", child);
+            }
+
+            return path.toString();
+        }
+    }
+
+    /**
+     * Given a parent and a child node, join them in the given {@link StringBuilder path}
+     *
+     * @param path   the {@link StringBuilder} used to make the path
+     * @param parent the parent
+     * @param child  the child
+     */
+    private static void joinPath(StringBuilder path, String parent, String child)
+    {
         // Add parent piece, with no trailing slash.
         if ( (parent != null) && (parent.length() > 0) )
         {
-            if ( !parent.startsWith("/") )
+            if ( !parent.startsWith(PATH_SEPARATOR) )
             {
-                path.append('/');
+                path.append(PATH_SEPARATOR);
             }
-            if ( parent.endsWith("/") )
+            if ( parent.endsWith(PATH_SEPARATOR) )
             {
                 path.append(parent.substring(0, parent.length() - 1));
             }
@@ -305,33 +426,39 @@
             }
         }
 
-        if ( (child == null) || (child.length() == 0) || (child.equals("/")) )
+        if ( (child == null) || (child.length() == 0) || (child.equals(PATH_SEPARATOR)) )
         {
             // Special case, empty parent and child
             if ( path.length() == 0 )
             {
-                return "/";
+                path.append(PATH_SEPARATOR);
             }
-            return path.toString();
+            return;
         }
 
         // Now add the separator between parent and child.
-        path.append('/');
+        path.append(PATH_SEPARATOR);
+
+        if ( child.startsWith(PATH_SEPARATOR) )
+        {
+            child = child.substring(1);
+        }
+
+        if ( child.endsWith(PATH_SEPARATOR) )
+        {
+            child = child.substring(0, child.length() - 1);
+        }
 
         // Finally, add the child.
-        if ( child.startsWith("/") )
-        {
-            path.append(child.substring(1));
-        }
-        else
-        {
-            path.append(child);
-        }
-
-        return path.toString();
+        path.append(child);
     }
 
     private ZKPaths()
     {
     }
+
+    private static CreateMode getCreateMode(boolean asContainers)
+    {
+        return asContainers ? getContainerCreateMode() : CreateMode.PERSISTENT;
+    }
 }
diff --git a/curator-client/src/test/java/org/apache/curator/utils/TestZKPaths.java b/curator-client/src/test/java/org/apache/curator/utils/TestZKPaths.java
index 04d07c5..ef05d34 100644
--- a/curator-client/src/test/java/org/apache/curator/utils/TestZKPaths.java
+++ b/curator-client/src/test/java/org/apache/curator/utils/TestZKPaths.java
@@ -26,6 +26,7 @@
 
 public class TestZKPaths
 {
+    @SuppressWarnings("NullArgumentToVariableArgMethod")
     @Test
     public void testMakePath()
     {
@@ -63,6 +64,16 @@
         Assert.assertEquals(ZKPaths.makePath("/foo", "bar"), "/foo/bar");
         Assert.assertEquals(ZKPaths.makePath("foo", "/bar"), "/foo/bar");
         Assert.assertEquals(ZKPaths.makePath("/foo", "/bar"), "/foo/bar");
+        Assert.assertEquals(ZKPaths.makePath("/foo", "bar/"), "/foo/bar");
+        Assert.assertEquals(ZKPaths.makePath("/foo/", "/bar/"), "/foo/bar");
+
+        Assert.assertEquals(ZKPaths.makePath("foo", "bar", "baz"), "/foo/bar/baz");
+        Assert.assertEquals(ZKPaths.makePath("foo", "bar", "baz", "qux"), "/foo/bar/baz/qux");
+        Assert.assertEquals(ZKPaths.makePath("/foo", "/bar", "/baz"), "/foo/bar/baz");
+        Assert.assertEquals(ZKPaths.makePath("/foo/", "/bar/", "/baz/"), "/foo/bar/baz");
+        Assert.assertEquals(ZKPaths.makePath("foo", null, null), "/foo");
+        Assert.assertEquals(ZKPaths.makePath("foo", "bar", null), "/foo/bar");
+        Assert.assertEquals(ZKPaths.makePath("foo", null, "baz"), "/foo/baz");
     }
 
     @Test
diff --git a/curator-examples/pom.xml b/curator-examples/pom.xml
index 825ca16..536f727 100644
--- a/curator-examples/pom.xml
+++ b/curator-examples/pom.xml
@@ -48,5 +48,11 @@
             <groupId>org.apache.curator</groupId>
             <artifactId>curator-x-discovery</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/curator-examples/src/main/java/cache/PathCacheExample.java b/curator-examples/src/main/java/cache/PathCacheExample.java
index 7c25ec1..e121337 100644
--- a/curator-examples/src/main/java/cache/PathCacheExample.java
+++ b/curator-examples/src/main/java/cache/PathCacheExample.java
@@ -231,7 +231,7 @@
         }
         catch ( KeeperException.NoNodeException e )
         {
-            client.create().creatingParentsIfNeeded().forPath(path, bytes);
+            client.create().creatingParentContainersIfNeeded().forPath(path, bytes);
         }
     }
 
diff --git a/curator-examples/src/main/java/framework/TransactionExamples.java b/curator-examples/src/main/java/framework/TransactionExamples.java
index f559b5a..7ff8064 100644
--- a/curator-examples/src/main/java/framework/TransactionExamples.java
+++ b/curator-examples/src/main/java/framework/TransactionExamples.java
@@ -19,25 +19,24 @@
 package framework;
 
 import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.framework.api.transaction.CuratorTransaction;
-import org.apache.curator.framework.api.transaction.CuratorTransactionFinal;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.transaction.CuratorOp;
 import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.TestingServer;
 import java.util.Collection;
 
 public class TransactionExamples
 {
     public static Collection<CuratorTransactionResult>      transaction(CuratorFramework client) throws Exception
     {
-        // this example shows how to use ZooKeeper's new transactions
+        // this example shows how to use ZooKeeper's transactions
 
-        Collection<CuratorTransactionResult>    results = client.inTransaction()
-                .create().forPath("/a/path", "some data".getBytes())
-            .and()
-                .setData().forPath("/another/path", "other data".getBytes())
-            .and()
-                .delete().forPath("/yet/another/path")
-            .and()
-                .commit();  // IMPORTANT! The transaction is not submitted until commit() is called
+        CuratorOp createOp = client.transactionOp().create().forPath("/a/path", "some data".getBytes());
+        CuratorOp setDataOp = client.transactionOp().setData().forPath("/another/path", "other data".getBytes());
+        CuratorOp deleteOp = client.transactionOp().delete().forPath("/yet/another/path");
+
+        Collection<CuratorTransactionResult>    results = client.transaction().forOperations(createOp, setDataOp, deleteOp);
 
         for ( CuratorTransactionResult result : results )
         {
@@ -46,33 +45,4 @@
 
         return results;
     }
-
-    /*
-            These next four methods show how to use Curator's transaction APIs in a more
-            traditional - one-at-a-time - manner
-     */
-
-    public static CuratorTransaction        startTransaction(CuratorFramework client)
-    {
-        // start the transaction builder
-        return client.inTransaction();
-    }
-
-    public static CuratorTransactionFinal   addCreateToTransaction(CuratorTransaction transaction) throws Exception
-    {
-        // add a create operation
-        return transaction.create().forPath("/a/path", "some data".getBytes()).and();
-    }
-
-    public static CuratorTransactionFinal   addDeleteToTransaction(CuratorTransaction transaction) throws Exception
-    {
-        // add a delete operation
-        return transaction.delete().forPath("/another/path").and();
-    }
-
-    public static void                      commitTransaction(CuratorTransactionFinal transaction) throws Exception
-    {
-        // commit the transaction
-        transaction.commit();
-    }
 }
diff --git a/curator-framework/pom.xml b/curator-framework/pom.xml
index a45ce77..a7df78c 100644
--- a/curator-framework/pom.xml
+++ b/curator-framework/pom.xml
@@ -55,6 +55,18 @@
             <artifactId>curator-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/AuthInfo.java b/curator-framework/src/main/java/org/apache/curator/framework/AuthInfo.java
new file mode 100644
index 0000000..2f879c5
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/AuthInfo.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework;
+
+import java.util.Arrays;
+
+public class AuthInfo
+{
+    final String    scheme;
+    final byte[]    auth;
+
+    public AuthInfo(String scheme, byte[] auth)
+    {
+        this.scheme = scheme;
+        this.auth = auth;
+    }
+
+    public String getScheme() {
+        return scheme;
+    }
+
+    public byte[] getAuth() {
+        return auth;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "AuthInfo{" +
+            "scheme='" + scheme + '\'' +
+            ", auth=" + Arrays.toString(auth) +
+            '}';
+    }
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/CuratorFramework.java b/curator-framework/src/main/java/org/apache/curator/framework/CuratorFramework.java
index 7de6308..58c5bf5 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/CuratorFramework.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/CuratorFramework.java
@@ -1,251 +1,300 @@
-/**

- * 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.curator.framework;

-

-import org.apache.curator.CuratorZookeeperClient;

-import org.apache.curator.framework.api.*;

-import org.apache.curator.framework.api.transaction.CuratorTransaction;

-import org.apache.curator.framework.imps.CuratorFrameworkState;

-import org.apache.curator.framework.listen.Listenable;

-import org.apache.curator.framework.state.ConnectionStateListener;

-import org.apache.curator.utils.EnsurePath;

-import org.apache.zookeeper.Watcher;

-

-import java.io.Closeable;

-import java.util.concurrent.TimeUnit;

-

-/**

- * Zookeeper framework-style client

- */

-public interface CuratorFramework extends Closeable

-{

-    /**

-     * Start the client. Most mutator methods will not work until the client is started

-     */

-    public void start();

-

-    /**

-     * Stop the client

-     */

-    public void close();

-

-    /**

-     * Returns the state of this instance

-     *

-     * @return state

-     */

-    public CuratorFrameworkState getState();

-

-    /**

-     * Return true if the client is started, not closed, etc.

-     *

-     * @return true/false

-     * @deprecated use {@link #getState()} instead

-     */

-    public boolean isStarted();

-

-    /**

-     * Start a create builder

-     *

-     * @return builder object

-     */

-    public CreateBuilder create();

-

-    /**

-     * Start a delete builder

-     *

-     * @return builder object

-     */

-    public DeleteBuilder delete();

-

-    /**

-     * Start an exists builder

-     * <p>

-     * The builder will return a Stat object as if org.apache.zookeeper.ZooKeeper.exists() were called.  Thus, a null

-     * means that it does not exist and an actual Stat object means it does exist.

-     *

-     * @return builder object

-     */

-    public ExistsBuilder checkExists();

-

-    /**

-     * Start a get data builder

-     *

-     * @return builder object

-     */

-    public GetDataBuilder getData();

-

-    /**

-     * Start a set data builder

-     *

-     * @return builder object

-     */

-    public SetDataBuilder setData();

-

-    /**

-     * Start a get children builder

-     *

-     * @return builder object

-     */

-    public GetChildrenBuilder getChildren();

-

-    /**

-     * Start a get ACL builder

-     *

-     * @return builder object

-     */

-    public GetACLBuilder getACL();

-

-    /**

-     * Start a set ACL builder

-     *

-     * @return builder object

-     */

-    public SetACLBuilder setACL();

-

-    /**

-     * Start a transaction builder

-     *

-     * @return builder object

-     */

-    public CuratorTransaction inTransaction();

-

-    /**

-     * Perform a sync on the given path - syncs are always in the background

-     *

-     * @param path                    the path

-     * @param backgroundContextObject optional context

-     * @deprecated use {@link #sync()} instead

-     */

-    public void sync(String path, Object backgroundContextObject);

-

-    /**

-     * Start a sync builder. Note: sync is ALWAYS in the background even

-     * if you don't use one of the background() methods

-     *

-     * @return builder object

-     */

-    public SyncBuilder sync();

-

-    /**

-     * Start a remove watches builder.

-     * @return builder object

-     */

-    public RemoveWatchesBuilder watches();    

-

-    /**

-     * Returns the listenable interface for the Connect State

-     *

-     * @return listenable

-     */

-    public Listenable<ConnectionStateListener> getConnectionStateListenable();

-

-    /**

-     * Returns the listenable interface for events

-     *

-     * @return listenable

-     */

-    public Listenable<CuratorListener> getCuratorListenable();

-

-    /**

-     * Returns the listenable interface for unhandled errors

-     *

-     * @return listenable

-     */

-    public Listenable<UnhandledErrorListener> getUnhandledErrorListenable();

-

-    /**

-     * Returns a facade of the current instance that does _not_ automatically

-     * pre-pend the namespace to all paths

-     *

-     * @return facade

-     * @deprecated use {@link #usingNamespace} passing <code>null</code>

-     */

-    public CuratorFramework nonNamespaceView();

-

-    /**

-     * Returns a facade of the current instance that uses the specified namespace

-     * or no namespace if <code>newNamespace</code> is <code>null</code>.

-     *

-     * @param newNamespace the new namespace or null for none

-     * @return facade

-     */

-    public CuratorFramework usingNamespace(String newNamespace);

-

-    /**

-     * Return the current namespace or "" if none

-     *

-     * @return namespace

-     */

-    public String getNamespace();

-

-    /**

-     * Return the managed zookeeper client

-     *

-     * @return client

-     */

-    public CuratorZookeeperClient getZookeeperClient();

-

-    /**

-     * Allocates an ensure path instance that is namespace aware

-     *

-     * @param path path to ensure

-     * @return new EnsurePath instance

-     */

-    public EnsurePath newNamespaceAwareEnsurePath(String path);

-

-    /**

-     * Curator can hold internal references to watchers that may inhibit garbage collection.

-     * Call this method on watchers you are no longer interested in.

-     *

-     * @param watcher the watcher

-     * 

-     * @deprecated As of ZooKeeper 3.5 Curators recipes will handle removing watcher references

-     * when they are no longer used.

-     */

-    @Deprecated

-    public void clearWatcherReferences(Watcher watcher);

-        

-    /**

-     * Block until a connection to ZooKeeper is available or the maxWaitTime has been exceeded

-     * @param maxWaitTime The maximum wait time. Specify a value &lt;= 0 to wait indefinitely

-     * @param units The time units for the maximum wait time.

-     * @return True if connection has been established, false otherwise.

-     * @throws InterruptedException If interrupted while waiting

-     */

-    public boolean blockUntilConnected(int maxWaitTime, TimeUnit units) throws InterruptedException;

-    

-    /**

-     * Block until a connection to ZooKeeper is available. This method will not return until a

-     * connection is available or it is interrupted, in which case an InterruptedException will

-     * be thrown

-     * @throws InterruptedException If interrupted while waiting

-     */

-    public void blockUntilConnected() throws InterruptedException;

-

-    /**

-     * Returns a facade of the current instance that tracks

-     * watchers created and allows a one-shot removal of all watchers

-     * via {@link WatcherRemoveCuratorFramework#removeWatchers()}

-     *

-     * @return facade

-     */

-    public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework();

-}

+/**
+ * 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.curator.framework;
+
+import org.apache.curator.CuratorZookeeperClient;
+import org.apache.curator.framework.api.*;
+import org.apache.curator.framework.api.transaction.CuratorMultiTransaction;
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.framework.api.transaction.CuratorTransaction;
+import org.apache.curator.framework.api.transaction.TransactionOp;
+import org.apache.curator.framework.imps.CuratorFrameworkState;
+import org.apache.curator.framework.listen.Listenable;
+import org.apache.curator.framework.state.ConnectionStateListener;
+import org.apache.curator.utils.EnsurePath;
+import org.apache.zookeeper.Watcher;
+
+import java.io.Closeable;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Zookeeper framework-style client
+ */
+public interface CuratorFramework extends Closeable
+{
+    /**
+     * Start the client. Most mutator methods will not work until the client is started
+     */
+    public void start();
+
+    /**
+     * Stop the client
+     */
+    public void close();
+
+    /**
+     * Returns the state of this instance
+     *
+     * @return state
+     */
+    public CuratorFrameworkState getState();
+
+    /**
+     * Return true if the client is started, not closed, etc.
+     *
+     * @return true/false
+     * @deprecated use {@link #getState()} instead
+     */
+    @Deprecated
+    public boolean isStarted();
+
+    /**
+     * Start a create builder
+     *
+     * @return builder object
+     */
+    public CreateBuilder create();
+
+    /**
+     * Start a delete builder
+     *
+     * @return builder object
+     */
+    public DeleteBuilder delete();
+
+    /**
+     * Start an exists builder
+     * <p>
+     * The builder will return a Stat object as if org.apache.zookeeper.ZooKeeper.exists() were called.  Thus, a null
+     * means that it does not exist and an actual Stat object means it does exist.
+     *
+     * @return builder object
+     */
+    public ExistsBuilder checkExists();
+
+    /**
+     * Start a get data builder
+     *
+     * @return builder object
+     */
+    public GetDataBuilder getData();
+
+    /**
+     * Start a set data builder
+     *
+     * @return builder object
+     */
+    public SetDataBuilder setData();
+
+    /**
+     * Start a get children builder
+     *
+     * @return builder object
+     */
+    public GetChildrenBuilder getChildren();
+
+    /**
+     * Start a get ACL builder
+     *
+     * @return builder object
+     */
+    public GetACLBuilder getACL();
+
+    /**
+     * Start a set ACL builder
+     *
+     * @return builder object
+     */
+    public SetACLBuilder setACL();
+
+    /**
+     * Start a reconfig builder
+     *
+     * @return builder object
+     */
+    public ReconfigBuilder reconfig();
+
+    /**
+     * Start a getConfig builder
+     *
+     * @return builder object
+     */
+    public GetConfigBuilder getConfig();
+
+    /**
+     * Start a transaction builder
+     *
+     * @return builder object
+     * @deprecated use {@link #transaction()} instead
+     */
+    public CuratorTransaction inTransaction();
+
+    /**
+     * Start a transaction builder
+     *
+     * @return builder object
+     */
+    public CuratorMultiTransaction transaction();
+
+    /**
+     * Allocate an operation that can be used with {@link #transaction()}.
+     * NOTE: {@link CuratorOp} instances created by this builder are
+     * reusable.
+     *
+     * @return operation builder
+     */
+    public TransactionOp transactionOp();
+
+    /**
+     * Perform a sync on the given path - syncs are always in the background
+     *
+     * @param path                    the path
+     * @param backgroundContextObject optional context
+     * @deprecated use {@link #sync()} instead
+     */
+    @Deprecated
+    public void sync(String path, Object backgroundContextObject);
+
+    /**
+     * Create all nodes in the specified path as containers if they don't
+     * already exist
+     *
+     * @param path path to create
+     * @throws Exception errors
+     */
+    public void createContainers(String path) throws Exception;
+
+    /**
+     * Start a sync builder. Note: sync is ALWAYS in the background even
+     * if you don't use one of the background() methods
+     *
+     * @return builder object
+     */
+    public SyncBuilder sync();
+
+    /**
+     * Start a remove watches builder.
+     * @return builder object
+     */
+    public RemoveWatchesBuilder watches();
+
+    /**
+     * Returns the listenable interface for the Connect State
+     *
+     * @return listenable
+     */
+    public Listenable<ConnectionStateListener> getConnectionStateListenable();
+
+    /**
+     * Returns the listenable interface for events
+     *
+     * @return listenable
+     */
+    public Listenable<CuratorListener> getCuratorListenable();
+
+    /**
+     * Returns the listenable interface for unhandled errors
+     *
+     * @return listenable
+     */
+    public Listenable<UnhandledErrorListener> getUnhandledErrorListenable();
+
+    /**
+     * Returns a facade of the current instance that does _not_ automatically
+     * pre-pend the namespace to all paths
+     *
+     * @return facade
+     * @deprecated Since 2.9.0 - use {@link #usingNamespace} passing <code>null</code>
+     */
+    @Deprecated
+    public CuratorFramework nonNamespaceView();
+
+    /**
+     * Returns a facade of the current instance that uses the specified namespace
+     * or no namespace if <code>newNamespace</code> is <code>null</code>.
+     *
+     * @param newNamespace the new namespace or null for none
+     * @return facade
+     */
+    public CuratorFramework usingNamespace(String newNamespace);
+
+    /**
+     * Return the current namespace or "" if none
+     *
+     * @return namespace
+     */
+    public String getNamespace();
+
+    /**
+     * Return the managed zookeeper client
+     *
+     * @return client
+     */
+    public CuratorZookeeperClient getZookeeperClient();
+
+    /**
+     * Allocates an ensure path instance that is namespace aware
+     *
+     * @param path path to ensure
+     * @return new EnsurePath instance
+     * @deprecated Since 2.9.0 - prefer {@link CreateBuilder#creatingParentContainersIfNeeded()}, {@link ExistsBuilder#creatingParentContainersIfNeeded()}
+     * or {@link CuratorFramework#createContainers(String)}
+     */
+    @Deprecated
+    public EnsurePath newNamespaceAwareEnsurePath(String path);
+
+    /**
+     * Curator can hold internal references to watchers that may inhibit garbage collection.
+     * Call this method on watchers you are no longer interested in.
+     *
+     * @param watcher the watcher
+     * 
+     * @deprecated As of ZooKeeper 3.5 Curators recipes will handle removing watcher references
+     * when they are no longer used.
+     */
+    @Deprecated
+    public void clearWatcherReferences(Watcher watcher);
+        
+    /**
+     * Block until a connection to ZooKeeper is available or the maxWaitTime has been exceeded
+     * @param maxWaitTime The maximum wait time. Specify a value &lt;= 0 to wait indefinitely
+     * @param units The time units for the maximum wait time.
+     * @return True if connection has been established, false otherwise.
+     * @throws InterruptedException If interrupted while waiting
+     */
+    public boolean blockUntilConnected(int maxWaitTime, TimeUnit units) throws InterruptedException;
+    
+    /**
+     * Block until a connection to ZooKeeper is available. This method will not return until a
+     * connection is available or it is interrupted, in which case an InterruptedException will
+     * be thrown
+     * @throws InterruptedException If interrupted while waiting
+     */
+    public void blockUntilConnected() throws InterruptedException;
+
+    /**
+     * Returns a facade of the current instance that tracks
+     * watchers created and allows a one-shot removal of all watchers
+     * via {@link WatcherRemoveCuratorFramework#removeWatchers()}
+     *
+     * @return facade
+     */
+    public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework();
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/CuratorFrameworkFactory.java b/curator-framework/src/main/java/org/apache/curator/framework/CuratorFrameworkFactory.java
index 8ef2580..dcb2ee6 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/CuratorFrameworkFactory.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/CuratorFrameworkFactory.java
@@ -16,13 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.curator.framework;
 
+import com.google.common.collect.ImmutableList;
 import org.apache.curator.RetryPolicy;
 import org.apache.curator.ensemble.EnsembleProvider;
 import org.apache.curator.ensemble.fixed.FixedEnsembleProvider;
 import org.apache.curator.framework.api.ACLProvider;
 import org.apache.curator.framework.api.CompressionProvider;
+import org.apache.curator.framework.api.CreateBuilder;
 import org.apache.curator.framework.api.PathAndBytesable;
 import org.apache.curator.framework.imps.CuratorFrameworkImpl;
 import org.apache.curator.framework.imps.CuratorTempFrameworkImpl;
@@ -30,11 +33,13 @@
 import org.apache.curator.framework.imps.GzipCompressionProvider;
 import org.apache.curator.utils.DefaultZookeeperFactory;
 import org.apache.curator.utils.ZookeeperFactory;
+import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.Watcher;
 import org.apache.zookeeper.ZooKeeper;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 
@@ -43,23 +48,23 @@
  */
 public class CuratorFrameworkFactory
 {
-    private static final int        DEFAULT_SESSION_TIMEOUT_MS = Integer.getInteger("curator-default-session-timeout", 60 * 1000);
-    private static final int        DEFAULT_CONNECTION_TIMEOUT_MS = Integer.getInteger("curator-default-connection-timeout", 15 * 1000);
+    private static final int DEFAULT_SESSION_TIMEOUT_MS = Integer.getInteger("curator-default-session-timeout", 60 * 1000);
+    private static final int DEFAULT_CONNECTION_TIMEOUT_MS = Integer.getInteger("curator-default-connection-timeout", 15 * 1000);
 
-    private static final byte[]     LOCAL_ADDRESS = getLocalAddress();
+    private static final byte[] LOCAL_ADDRESS = getLocalAddress();
 
-    private static final CompressionProvider        DEFAULT_COMPRESSION_PROVIDER = new GzipCompressionProvider();
-    private static final DefaultZookeeperFactory    DEFAULT_ZOOKEEPER_FACTORY = new DefaultZookeeperFactory();
-    private static final DefaultACLProvider         DEFAULT_ACL_PROVIDER = new DefaultACLProvider();
-    private static final long                       DEFAULT_INACTIVE_THRESHOLD_MS = (int)TimeUnit.MINUTES.toMillis(3);
-    private static final int                        DEFAULT_CLOSE_WAIT_MS = (int)TimeUnit.SECONDS.toMillis(1);
+    private static final CompressionProvider DEFAULT_COMPRESSION_PROVIDER = new GzipCompressionProvider();
+    private static final DefaultZookeeperFactory DEFAULT_ZOOKEEPER_FACTORY = new DefaultZookeeperFactory();
+    private static final DefaultACLProvider DEFAULT_ACL_PROVIDER = new DefaultACLProvider();
+    private static final long DEFAULT_INACTIVE_THRESHOLD_MS = (int)TimeUnit.MINUTES.toMillis(3);
+    private static final int DEFAULT_CLOSE_WAIT_MS = (int)TimeUnit.SECONDS.toMillis(1);
 
     /**
      * Return a new builder that builds a CuratorFramework
      *
      * @return new builder
      */
-    public static Builder       builder()
+    public static Builder builder()
     {
         return new Builder();
     }
@@ -67,9 +72,8 @@
     /**
      * Create a new client with default session timeout and default connection timeout
      *
-     *
      * @param connectString list of servers to connect to
-     * @param retryPolicy retry policy to use
+     * @param retryPolicy   retry policy to use
      * @return client
      */
     public static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy)
@@ -80,11 +84,10 @@
     /**
      * Create a new client
      *
-     *
-     * @param connectString list of servers to connect to
-     * @param sessionTimeoutMs session timeout
+     * @param connectString       list of servers to connect to
+     * @param sessionTimeoutMs    session timeout
      * @param connectionTimeoutMs connection timeout
-     * @param retryPolicy retry policy to use
+     * @param retryPolicy         retry policy to use
      * @return client
      */
     public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy)
@@ -99,20 +102,20 @@
 
     public static class Builder
     {
-        private EnsembleProvider    ensembleProvider;
-        private int                 sessionTimeoutMs = DEFAULT_SESSION_TIMEOUT_MS;
-        private int                 connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
-        private int                 maxCloseWaitMs = DEFAULT_CLOSE_WAIT_MS;
-        private RetryPolicy         retryPolicy;
-        private ThreadFactory       threadFactory = null;
-        private String              namespace;
-        private String              authScheme = null;
-        private byte[]              authValue = null;
-        private byte[]              defaultData = LOCAL_ADDRESS;
+        private EnsembleProvider ensembleProvider;
+        private int sessionTimeoutMs = DEFAULT_SESSION_TIMEOUT_MS;
+        private int connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
+        private int maxCloseWaitMs = DEFAULT_CLOSE_WAIT_MS;
+        private RetryPolicy retryPolicy;
+        private ThreadFactory threadFactory = null;
+        private String namespace;
+        private List<AuthInfo> authInfos = null;
+        private byte[] defaultData = LOCAL_ADDRESS;
         private CompressionProvider compressionProvider = DEFAULT_COMPRESSION_PROVIDER;
-        private ZookeeperFactory    zookeeperFactory = DEFAULT_ZOOKEEPER_FACTORY;
-        private ACLProvider         aclProvider = DEFAULT_ACL_PROVIDER;
-        private boolean             canBeReadOnly = false;
+        private ZookeeperFactory zookeeperFactory = DEFAULT_ZOOKEEPER_FACTORY;
+        private ACLProvider aclProvider = DEFAULT_ACL_PROVIDER;
+        private boolean canBeReadOnly = false;
+        private boolean useContainerParentsIfAvailable = true;
 
         /**
          * Apply the current values and build a new CuratorFramework
@@ -144,7 +147,7 @@
          * are limited. Further, the connection will be closed after <code>inactiveThresholdMs</code> milliseconds of inactivity.
          *
          * @param inactiveThreshold number of milliseconds of inactivity to cause connection close
-         * @param unit threshold unit
+         * @param unit              threshold unit
          * @return temp instance
          */
         public CuratorTempFramework buildTemp(long inactiveThreshold, TimeUnit unit)
@@ -154,15 +157,30 @@
 
         /**
          * Add connection authorization
+         * 
+         * Subsequent calls to this method overwrite the prior calls.
          *
          * @param scheme the scheme
-         * @param auth the auth bytes
+         * @param auth   the auth bytes
          * @return this
          */
-        public Builder  authorization(String scheme, byte[] auth)
+        public Builder authorization(String scheme, byte[] auth)
         {
-            this.authScheme = scheme;
-            this.authValue = (auth != null) ? Arrays.copyOf(auth, auth.length) : null;
+            return authorization(ImmutableList.of(new AuthInfo(scheme, (auth != null) ? Arrays.copyOf(auth, auth.length) : null)));
+        }
+
+        /**
+         * Add connection authorization. The supplied authInfos are appended to those added via call to
+         * {@link #authorization(java.lang.String, byte[])} for backward compatibility.
+         * <p/>
+         * Subsequent calls to this method overwrite the prior calls.
+         *
+         * @param authInfos list of {@link AuthInfo} objects with scheme and auth
+         * @return this
+         */
+        public Builder authorization(List<AuthInfo> authInfos)
+        {
+            this.authInfos = ImmutableList.copyOf(authInfos);
             return this;
         }
 
@@ -313,6 +331,18 @@
             return this;
         }
 
+        /**
+         * By default, Curator uses {@link CreateBuilder#creatingParentContainersIfNeeded()}
+         * if the ZK JAR supports {@link CreateMode#CONTAINER}. Call this method to turn off this behavior.
+         *
+         * @return this
+         */
+        public Builder dontUseContainerParents()
+        {
+            this.useContainerParentsIfAvailable = false;
+            return this;
+        }
+
         public ACLProvider getAclProvider()
         {
             return aclProvider;
@@ -363,14 +393,61 @@
             return namespace;
         }
 
-        public String getAuthScheme()
+        public boolean useContainerParentsIfAvailable()
         {
-            return authScheme;
+            return useContainerParentsIfAvailable;
         }
 
+        @Deprecated
+        public String getAuthScheme()
+        {
+            int qty = (authInfos != null) ? authInfos.size() : 0;
+            switch ( qty )
+            {
+                case 0:
+                {
+                    return null;
+                }
+
+                case 1:
+                {
+                    return authInfos.get(0).scheme;
+                }
+
+                default:
+                {
+                    throw new IllegalStateException("More than 1 auth has been added");
+                }
+            }
+        }
+
+        @Deprecated
         public byte[] getAuthValue()
         {
-            return (authValue != null) ? Arrays.copyOf(authValue, authValue.length) : null;
+            int qty = (authInfos != null) ? authInfos.size() : 0;
+            switch ( qty )
+            {
+                case 0:
+                {
+                    return null;
+                }
+
+                case 1:
+                {
+                    byte[] bytes = authInfos.get(0).getAuth();
+                    return (bytes != null) ? Arrays.copyOf(bytes, bytes.length) : null;
+                }
+
+                default:
+                {
+                    throw new IllegalStateException("More than 1 auth has been added");
+                }
+            }
+        }
+
+        public List<AuthInfo> getAuthInfos()
+        {
+            return authInfos;
         }
 
         public byte[] getDefaultData()
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/AddStatConfigEnsembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/AddStatConfigEnsembleable.java
new file mode 100644
index 0000000..16f78a2
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/AddStatConfigEnsembleable.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+/**
+ * An non-incremental reconfiguration builder.
+ * This builder has access only to the non-incremental reconfiguration methods withMembers, so that we prevent
+ * mixing concepts that can't be used together.
+ */
+public interface AddStatConfigEnsembleable extends
+    Addable<StatConfigEnsembleable>,
+    StatConfigEnsembleable
+{
+
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/Addable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/Addable.java
new file mode 100644
index 0000000..e908f1e
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/Addable.java
@@ -0,0 +1,43 @@
+/**
+ * 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.curator.framework.api;
+
+import java.util.List;
+
+public interface Addable<T>
+{
+    /**
+     * Sets one or more members that are meant to be part of the ensemble.
+     * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port]
+     *
+     * @param server The server to add as a member of the ensemble.
+     * @return this
+     */
+    T adding(String... server);
+
+    /**
+     * Sets one or more members that are meant to be part of the ensemble.
+     * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port]
+     *
+     * @param servers The server to add as a member of the ensemble.
+     * @return this
+     */
+    T adding(List<String> servers);
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/AsyncReconfigurable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/AsyncReconfigurable.java
new file mode 100644
index 0000000..fc7fd57
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/AsyncReconfigurable.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.framework.api;
+
+public interface AsyncReconfigurable  {
+
+    /**
+     * Sets the configuration version to use.
+     * @param config The version of the configuration.
+     * @throws Exception
+     */
+    void fromConfig(long config) throws Exception;
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/BackgroundStatable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/BackgroundStatable.java
new file mode 100644
index 0000000..77c4e96
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/BackgroundStatable.java
@@ -0,0 +1,24 @@
+/**
+ * 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.curator.framework.api;
+
+public interface BackgroundStatable<T> extends
+    Backgroundable<T>,
+    Statable<T> {
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/Configurable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/Configurable.java
new file mode 100644
index 0000000..2bc0494
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/Configurable.java
@@ -0,0 +1,31 @@
+/**
+ * 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.curator.framework.api;
+
+public interface Configurable
+{
+
+    /**
+     * Sets the configuration version to use.
+     * @param config The version of the configuration.
+     * @throws Exception
+     */
+    StatEnsembleable<byte[]> fromConfig(long config) throws Exception;
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBackgroundModeACLable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBackgroundModeACLable.java
index d2a4e27..e821d3b 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBackgroundModeACLable.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBackgroundModeACLable.java
@@ -18,6 +18,8 @@
  */
 package org.apache.curator.framework.api;
 
+import org.apache.zookeeper.CreateMode;
+
 public interface CreateBackgroundModeACLable extends
     BackgroundPathAndBytesable<String>,
     CreateModable<ACLBackgroundPathAndBytesable<String>>,
@@ -31,6 +33,16 @@
     public ACLCreateModePathAndBytesable<String>    creatingParentsIfNeeded();
 
     /**
+     * Causes any parent nodes to get created using {@link CreateMode#CONTAINER} if they haven't already been.
+     * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper.
+     * If the ZooKeeper version you're using does not support containers, the parent nodes
+     * are created as ordinary PERSISTENT nodes.
+     *
+     * @return this
+     */
+    public ACLCreateModePathAndBytesable<String> creatingParentContainersIfNeeded();
+
+    /**
      * <p>
      *     Hat-tip to https://github.com/sbridges for pointing this out
      * </p>
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java
index d29b475..0db2094 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/CreateBuilder.java
@@ -18,6 +18,8 @@
  */
 package org.apache.curator.framework.api;
 
+import org.apache.zookeeper.CreateMode;
+
 public interface CreateBuilder extends
     BackgroundPathAndBytesable<String>,
     CreateModable<ACLBackgroundPathAndBytesable<String>>,
@@ -32,12 +34,23 @@
     public ProtectACLCreateModePathAndBytesable<String> creatingParentsIfNeeded();
 
     /**
+     * Causes any parent nodes to get created using {@link CreateMode#CONTAINER} if they haven't already been.
+     * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper.
+     * If the ZooKeeper version you're using does not support containers, the parent nodes
+     * are created as ordinary PERSISTENT nodes.
+     *
+     * @return this
+     */
+    public ProtectACLCreateModePathAndBytesable<String> creatingParentContainersIfNeeded();
+
+    /**
      * @deprecated this has been generalized to support all create modes. Instead, use:
      * <pre>
      *     client.create().withProtection().withMode(CreateMode.PERSISTENT_SEQUENTIAL)...
      * </pre>
      * @return this
      */
+    @Deprecated
     public ACLPathAndBytesable<String>              withProtectedEphemeralSequential();
 
     /**
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/CuratorEvent.java b/curator-framework/src/main/java/org/apache/curator/framework/api/CuratorEvent.java
index 2a5408c..673613c 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/CuratorEvent.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/CuratorEvent.java
@@ -18,6 +18,8 @@
  */
 package org.apache.curator.framework.api;
 
+import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.zookeeper.OpResult;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.data.ACL;
 import org.apache.zookeeper.data.Stat;
@@ -80,6 +82,11 @@
     public List<ACL> getACLList();
 
     /**
+     * @return any operation results or null
+     */
+    public List<CuratorTransactionResult> getOpResults();
+
+    /**
      * If {@link #getType()} returns {@link CuratorEventType#WATCHED} this will
      * return the WatchedEvent
      *
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/CuratorEventType.java b/curator-framework/src/main/java/org/apache/curator/framework/api/CuratorEventType.java
index 480d5ec..5dea211 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/CuratorEventType.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/CuratorEventType.java
@@ -1,85 +1,100 @@
-/**

- * 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.curator.framework.api;

-

-import org.apache.curator.framework.CuratorFramework;

-import org.apache.zookeeper.Watcher;

-

-public enum CuratorEventType

-{

-    /**

-     * Corresponds to {@link CuratorFramework#create()}

-     */

-    CREATE,

-

-    /**

-     * Corresponds to {@link CuratorFramework#delete()}

-     */

-    DELETE,

-

-    /**

-     * Corresponds to {@link CuratorFramework#checkExists()}

-     */

-    EXISTS,

-

-    /**

-     * Corresponds to {@link CuratorFramework#getData()}

-     */

-    GET_DATA,

-

-    /**

-     * Corresponds to {@link CuratorFramework#setData()}

-     */

-    SET_DATA,

-

-    /**

-     * Corresponds to {@link CuratorFramework#getChildren()}

-     */

-    CHILDREN,

-

-    /**

-     * Corresponds to {@link CuratorFramework#sync(String, Object)}

-     */

-    SYNC,

-

-    /**

-     * Corresponds to {@link CuratorFramework#getACL()}

-     */

-    GET_ACL,

-

-    /**

-     * Corresponds to {@link CuratorFramework#setACL()}

-     */

-    SET_ACL,

-

-    /**

-     * Corresponds to {@link Watchable#usingWatcher(Watcher)} or {@link Watchable#watched()}

-     */

-    WATCHED,

-    

-    /**

-     * Corresponds to {@link CuratorFramework#removeWatches()}

-     */

-    REMOVE_WATCHES,

-

-    /**

-     * Event sent when client is being closed

-     */

-    CLOSING

-}

+/**
+ * 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.curator.framework.api;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.zookeeper.Watcher;
+
+public enum CuratorEventType
+{
+    /**
+     * Corresponds to {@link CuratorFramework#create()}
+     */
+    CREATE,
+
+    /**
+     * Corresponds to {@link CuratorFramework#delete()}
+     */
+    DELETE,
+
+    /**
+     * Corresponds to {@link CuratorFramework#checkExists()}
+     */
+    EXISTS,
+
+    /**
+     * Corresponds to {@link CuratorFramework#getData()}
+     */
+    GET_DATA,
+
+    /**
+     * Corresponds to {@link CuratorFramework#setData()}
+     */
+    SET_DATA,
+
+    /**
+     * Corresponds to {@link CuratorFramework#getChildren()}
+     */
+    CHILDREN,
+
+    /**
+     * Corresponds to {@link CuratorFramework#sync(String, Object)}
+     */
+    SYNC,
+
+    /**
+     * Corresponds to {@link CuratorFramework#getACL()}
+     */
+    GET_ACL,
+
+    /**
+     * Corresponds to {@link CuratorFramework#setACL()}
+     */
+    SET_ACL,
+
+    /**
+     * Corresponds to {@link CuratorFramework#transaction()}
+     */
+    TRANSACTION,
+
+    /**
+     * Corresponds to {@link CuratorFramework#getConfig()}
+     */
+    GET_CONFIG,
+
+    /**
+     * Corresponds to {@link CuratorFramework#reconfig()}
+     */
+    RECONFIG,
+
+    /**
+     * Corresponds to {@link Watchable#usingWatcher(Watcher)} or {@link Watchable#watched()}
+     */
+    WATCHED,
+
+    /**
+     * Corresponds to {@link CuratorFramework#watches()} ()}
+     */
+    REMOVE_WATCHES,
+
+    /**
+     * Event sent when client is being closed
+     */
+    CLOSING
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/DataCallbackable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/DataCallbackable.java
new file mode 100644
index 0000000..75ded65
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/DataCallbackable.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.framework.api;
+
+import org.apache.zookeeper.AsyncCallback.DataCallback;
+
+public interface DataCallbackable<T> {
+
+    /**
+     * Passes a callback and a context object to the config/reconfig command.
+     * @param callback  The async callback to use.
+     * @param ctx       An object that will be passed to the callback.
+     * @return this
+     */
+    T usingDataCallback(DataCallback callback, Object ctx);
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/Ensembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/Ensembleable.java
new file mode 100644
index 0000000..c8a82fe
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/Ensembleable.java
@@ -0,0 +1,24 @@
+/**
+ * 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.curator.framework.api;
+
+public interface Ensembleable<T> {
+
+    T forEnsemble() throws Exception;
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/ExistsBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/ExistsBuilder.java
index b39fea9..7fb00ac 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/ExistsBuilder.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/ExistsBuilder.java
@@ -16,12 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.curator.framework.api;
 
-import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.CreateMode;
 
 public interface ExistsBuilder extends
-    Watchable<BackgroundPathable<Stat>>,
-    BackgroundPathable<Stat>
+    ExistsBuilderMain
 {
+    /**
+     * Causes any parent nodes to get created using {@link CreateMode#CONTAINER} if they haven't already been.
+     * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper.
+     * If the ZooKeeper version you're using does not support containers, the parent nodes
+     * are created as ordinary PERSISTENT nodes.
+     *
+     * @return this
+     */
+    ExistsBuilderMain creatingParentContainersIfNeeded();
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/ExistsBuilderMain.java b/curator-framework/src/main/java/org/apache/curator/framework/api/ExistsBuilderMain.java
new file mode 100644
index 0000000..2519616
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/ExistsBuilderMain.java
@@ -0,0 +1,27 @@
+/**
+ * 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.curator.framework.api;
+
+import org.apache.zookeeper.data.Stat;
+
+public interface ExistsBuilderMain extends
+    Watchable<BackgroundPathable<Stat>>,
+    BackgroundPathable<Stat>
+{
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/GetConfigBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/GetConfigBuilder.java
new file mode 100644
index 0000000..c42e4cb
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/GetConfigBuilder.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+public interface GetConfigBuilder extends
+    Watchable<BackgroundStatable<Ensembleable<byte[]>>>,
+    BackgroundStatable<Ensembleable<byte[]>>,
+    Ensembleable<byte[]>
+{
+}
+
+
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/IncrementalReconfigBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/IncrementalReconfigBuilder.java
new file mode 100644
index 0000000..0ad6426
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/IncrementalReconfigBuilder.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.framework.api;
+
+/**
+ * An incremental reconfiguration builder.
+ * This builder has access only to the incremental reconfiguration methods join and leave, so that we prevent
+ * mixing concepts that can't be used together.
+ * @param <T>
+ */
+public interface IncrementalReconfigBuilder<T> extends
+        Joinable<IncrementalReconfigBuilder<T>>,
+        Leaveable<IncrementalReconfigBuilder<T>>,
+        DataCallbackable<AsyncReconfigurable>,
+        Statable<SyncReconfigurable> {
+
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/JoinAddStatConfigEnsembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/JoinAddStatConfigEnsembleable.java
new file mode 100644
index 0000000..a905dd1
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/JoinAddStatConfigEnsembleable.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+/**
+ * An incremental reconfiguration builder.
+ * This builder has access only to the incremental reconfiguration methods joining and leaving, so that we prevent
+ * mixing concepts that can't be used together.
+ */
+public interface JoinAddStatConfigEnsembleable extends
+    Joinable<AddStatConfigEnsembleable>,
+    Addable<JoinStatConfigurable>,
+    StatConfigEnsembleable
+{
+
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/JoinLeaveStatConfigEnsembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/JoinLeaveStatConfigEnsembleable.java
new file mode 100644
index 0000000..9642297
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/JoinLeaveStatConfigEnsembleable.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+/**
+ * An incremental reconfiguration builder.
+ * This builder has access only to the incremental reconfiguration methods joining and leaving, so that we prevent
+ * mixing concepts that can't be used together.
+ */
+public interface JoinLeaveStatConfigEnsembleable extends
+    Joinable<LeaveStatConfigEnsembleable>,
+    Leaveable<JoinStatConfigEnsembleable>,
+    StatConfigEnsembleable
+{
+
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/JoinStatConfigEnsembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/JoinStatConfigEnsembleable.java
new file mode 100644
index 0000000..5fe7a8c
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/JoinStatConfigEnsembleable.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+/**
+ * An incremental reconfiguration builder.
+ * This builder has access only to the incremental reconfiguration methods joining and leaving, so that we prevent
+ * mixing concepts that can't be used together.
+ */
+public interface JoinStatConfigEnsembleable extends
+    Joinable<StatConfigEnsembleable>,
+    StatConfigEnsembleable
+{
+
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/JoinStatConfigurable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/JoinStatConfigurable.java
new file mode 100644
index 0000000..ef17ef4
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/JoinStatConfigurable.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+/**
+ * An incremental reconfiguration builder.
+ * This builder has access only to the incremental reconfiguration methods joining and leaving, so that we prevent
+ * mixing concepts that can't be used together.
+ */
+public interface JoinStatConfigurable extends
+    Joinable<Configurable>
+{
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/Joinable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/Joinable.java
new file mode 100644
index 0000000..5cebe4d
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/Joinable.java
@@ -0,0 +1,43 @@
+/**
+ * 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.curator.framework.api;
+
+import java.util.List;
+
+public interface Joinable<T>
+{
+    /**
+     * Adds one or more servers to joining the ensemble.
+     * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port]
+     *
+     * @param server The server joining.
+     * @return this
+     */
+    T joining(String... server);
+
+    /**
+     * Adds one or more servers to joining the ensemble.
+     * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port]
+     *
+     * @param servers The servers joining.
+     * @return this
+     */
+    T joining(List<String> servers);
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/LeaveAddStatConfigEnsembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/LeaveAddStatConfigEnsembleable.java
new file mode 100644
index 0000000..7912d45
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/LeaveAddStatConfigEnsembleable.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+/**
+ * An non-incremental reconfiguration builder.
+ * This builder has access only to the non-incremental reconfiguration methods withMembers, so that we prevent
+ * mixing concepts that can't be used together.
+ */
+public interface LeaveAddStatConfigEnsembleable extends
+    Leaveable<AddStatConfigEnsembleable>,
+    Addable<LeaveStatConfigEnsembleable>,
+    StatConfigEnsembleable
+{
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/LeaveStatConfigEnsembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/LeaveStatConfigEnsembleable.java
new file mode 100644
index 0000000..ddad854
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/LeaveStatConfigEnsembleable.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+/**
+ * An incremental reconfiguration builder.
+ * This builder has access only to the incremental reconfiguration methods joining and leaving, so that we prevent
+ * mixing concepts that can't be used together.
+ */
+public interface LeaveStatConfigEnsembleable extends
+    Leaveable<StatConfigEnsembleable>,
+    StatConfigEnsembleable
+{
+
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/Leaveable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/Leaveable.java
new file mode 100644
index 0000000..6ec3542
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/Leaveable.java
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+import java.util.List;
+
+public interface Leaveable<T>
+{
+    /**
+     * Sets one or more servers to leaving the ensemble.
+     *
+     * @param server The server ids
+     * @return this
+     */
+    T leaving(String... server);
+
+    /**
+     * Sets one or more servers to leaving the ensemble.
+     *
+     * @param servers The server ids
+     * @return this
+     */
+    T leaving(List<String> servers);
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/ReconfigBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/ReconfigBuilder.java
new file mode 100644
index 0000000..438abcf
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/ReconfigBuilder.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+public interface ReconfigBuilder extends
+    Joinable<LeaveAddStatConfigEnsembleable>,
+    Leaveable<JoinAddStatConfigEnsembleable>,
+    Addable<JoinLeaveStatConfigEnsembleable>,
+    Backgroundable<ReconfigBuilderMain>
+{
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/ReconfigBuilderMain.java b/curator-framework/src/main/java/org/apache/curator/framework/api/ReconfigBuilderMain.java
new file mode 100644
index 0000000..b86af2d
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/ReconfigBuilderMain.java
@@ -0,0 +1,27 @@
+/**
+ * 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.curator.framework.api;
+
+public interface ReconfigBuilderMain extends
+    Joinable<LeaveAddStatConfigEnsembleable>,
+    Leaveable<JoinAddStatConfigEnsembleable>,
+    Addable<JoinLeaveStatConfigEnsembleable>
+{
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/StatConfigEnsembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/StatConfigEnsembleable.java
new file mode 100644
index 0000000..4700c8c
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/StatConfigEnsembleable.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+public interface StatConfigEnsembleable extends
+    Configurable,
+    StatEnsembleable<byte[]>
+{
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/StatEnsembleable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/StatEnsembleable.java
new file mode 100644
index 0000000..0993b50
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/StatEnsembleable.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.curator.framework.api;
+
+public interface StatEnsembleable<T> extends
+    Statable<Ensembleable<T>>,
+    Ensembleable<T>
+{
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/SyncReconfigurable.java b/curator-framework/src/main/java/org/apache/curator/framework/api/SyncReconfigurable.java
new file mode 100644
index 0000000..bd7b96b
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/SyncReconfigurable.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.framework.api;
+
+public interface SyncReconfigurable  {
+
+    /**
+     * Sets the configuration version to use.
+     * @param config    The version of the configuration.
+     * @return The configuration data.
+     * @throws Exception
+     */
+    byte[] fromConfig(long config) throws Exception;
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorMultiTransaction.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorMultiTransaction.java
new file mode 100644
index 0000000..07bf191
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorMultiTransaction.java
@@ -0,0 +1,27 @@
+/**
+ * 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.curator.framework.api.transaction;
+
+import org.apache.curator.framework.api.Backgroundable;
+
+public interface CuratorMultiTransaction extends
+    Backgroundable<CuratorMultiTransactionMain>,
+    CuratorMultiTransactionMain
+{
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorMultiTransactionMain.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorMultiTransactionMain.java
new file mode 100644
index 0000000..2425f5b
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorMultiTransactionMain.java
@@ -0,0 +1,45 @@
+/**
+ * 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.curator.framework.api.transaction;
+
+import org.apache.curator.framework.CuratorFramework;
+import java.util.List;
+
+public interface CuratorMultiTransactionMain
+{
+    /**
+     * Commit the given operations as a single transaction. Create the
+     * operation instances via {@link CuratorFramework#transactionOp()}
+     *
+     * @param operations operations that make up the transaction.
+     * @return result details for foreground operations or <code>null</code> for background operations
+     * @throws Exception errors
+     */
+    List<CuratorTransactionResult> forOperations(CuratorOp... operations) throws Exception;
+
+    /**
+     * Commit the given operations as a single transaction. Create the
+     * operation instances via {@link CuratorFramework#transactionOp()}
+     *
+     * @param operations operations that make up the transaction.
+     * @return result details for foreground operations or <code>null</code> for background operations
+     * @throws Exception errors
+     */
+    List<CuratorTransactionResult> forOperations(List<CuratorOp> operations) throws Exception;
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorOp.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorOp.java
new file mode 100644
index 0000000..23bc76c
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorOp.java
@@ -0,0 +1,31 @@
+/**
+ * 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.curator.framework.api.transaction;
+
+import org.apache.zookeeper.Op;
+
+/**
+ * Internal representation of a transaction operation
+ */
+public interface CuratorOp
+{
+    Op get();
+
+    TypeAndPath getTypeAndPath();
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorTransaction.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorTransaction.java
index 3901abf..5d60b5c 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorTransaction.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorTransaction.java
@@ -18,6 +18,7 @@
  */
 package org.apache.curator.framework.api.transaction;
 
+import org.apache.curator.framework.CuratorFramework;
 import org.apache.zookeeper.ZooKeeper;
 
 /**
@@ -49,6 +50,8 @@
  *     <b>Important:</b> the operations are not submitted until
  *     {@link CuratorTransactionFinal#commit()} is called.
  * </p>
+ *
+ * @deprecated Use {@link CuratorFramework#transaction()}
  */
 public interface CuratorTransaction
 {
@@ -57,26 +60,26 @@
      *
      * @return builder object
      */
-    public TransactionCreateBuilder create();
+    public TransactionCreateBuilder<CuratorTransactionBridge> create();
 
     /**
      * Start a delete builder in the transaction
      *
      * @return builder object
      */
-    public TransactionDeleteBuilder delete();
+    public TransactionDeleteBuilder<CuratorTransactionBridge> delete();
 
     /**
      * Start a setData builder in the transaction
      *
      * @return builder object
      */
-    public TransactionSetDataBuilder setData();
+    public TransactionSetDataBuilder<CuratorTransactionBridge> setData();
 
     /**
      * Start a check builder in the transaction
-     *ChildData
+     *
      * @return builder object
      */
-    public TransactionCheckBuilder check();
+    public TransactionCheckBuilder<CuratorTransactionBridge> check();
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorTransactionResult.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorTransactionResult.java
index 03bbca2..8d8dc2d 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorTransactionResult.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/CuratorTransactionResult.java
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.curator.framework.api.transaction;
 
 import com.google.common.base.Predicate;
@@ -27,9 +28,9 @@
 public class CuratorTransactionResult
 {
     private final OperationType type;
-    private final String        forPath;
-    private final String        resultPath;
-    private final Stat          resultStat;
+    private final String forPath;
+    private final String resultPath;
+    private final Stat resultStat;
 
     /**
      * Utility that can be passed to Google Guava to find a particular result. E.g.
@@ -41,7 +42,7 @@
      * @param forPath path
      * @return predicate
      */
-    public static Predicate<CuratorTransactionResult>       ofTypeAndPath(final OperationType type, final String forPath)
+    public static Predicate<CuratorTransactionResult> ofTypeAndPath(final OperationType type, final String forPath)
     {
         return new Predicate<CuratorTransactionResult>()
         {
@@ -73,7 +74,7 @@
 
     /**
      * Returns the path that was passed to the operation when added
-     * 
+     *
      * @return operation input path
      */
     public String getForPath()
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/OperationType.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/OperationType.java
index 56dcd33..c0aec68 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/OperationType.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/OperationType.java
@@ -24,22 +24,22 @@
 public enum OperationType
 {
     /**
-     * {@link CuratorTransaction#create()}
+     * {@link TransactionOp#create()}
      */
     CREATE,
 
     /**
-     * {@link CuratorTransaction#delete()}
+     * {@link TransactionOp#delete()}
      */
     DELETE,
 
     /**
-     * {@link CuratorTransaction#setData()}
+     * {@link TransactionOp#setData()}
      */
     SET_DATA,
 
     /**
-     * {@link CuratorTransaction#check()}
+     * {@link TransactionOp#check()}
      */
     CHECK
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionCheckBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionCheckBuilder.java
index 2bc13d1..6de675c 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionCheckBuilder.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionCheckBuilder.java
@@ -21,8 +21,8 @@
 import org.apache.curator.framework.api.Pathable;
 import org.apache.curator.framework.api.Versionable;
 
-public interface TransactionCheckBuilder extends
-    Pathable<CuratorTransactionBridge>,
-    Versionable<Pathable<CuratorTransactionBridge>>
+public interface TransactionCheckBuilder<T> extends
+    Pathable<T>,
+    Versionable<Pathable<T>>
 {
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionCreateBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionCreateBuilder.java
index 6ac3069..cba0cba 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionCreateBuilder.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionCreateBuilder.java
@@ -23,10 +23,10 @@
 import org.apache.curator.framework.api.CreateModable;
 import org.apache.curator.framework.api.PathAndBytesable;
 
-public interface TransactionCreateBuilder extends
-    PathAndBytesable<CuratorTransactionBridge>,
-    CreateModable<ACLPathAndBytesable<CuratorTransactionBridge>>,
-    ACLPathAndBytesable<CuratorTransactionBridge>,
-    Compressible<ACLPathAndBytesable<CuratorTransactionBridge>>
+public interface TransactionCreateBuilder<T> extends
+    PathAndBytesable<T>,
+    CreateModable<ACLPathAndBytesable<T>>,
+    ACLPathAndBytesable<T>,
+    Compressible<ACLPathAndBytesable<T>>
 {
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionDeleteBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionDeleteBuilder.java
index e165394..d977290 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionDeleteBuilder.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionDeleteBuilder.java
@@ -21,8 +21,8 @@
 import org.apache.curator.framework.api.Pathable;
 import org.apache.curator.framework.api.Versionable;
 
-public interface TransactionDeleteBuilder extends
-    Pathable<CuratorTransactionBridge>,
-    Versionable<Pathable<CuratorTransactionBridge>>
+public interface TransactionDeleteBuilder<T> extends
+    Pathable<T>,
+    Versionable<Pathable<T>>
 {
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionOp.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionOp.java
new file mode 100644
index 0000000..84808a1
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionOp.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.framework.api.transaction;
+
+import org.apache.curator.framework.CuratorFramework;
+
+/**
+ * Builds operations that can be committed as a transaction
+ * via {@link CuratorFramework#transaction()}
+ */
+public interface TransactionOp
+{
+    /**
+     * Start a create builder in the transaction
+     *
+     * @return builder object
+     */
+    TransactionCreateBuilder<CuratorOp> create();
+
+    /**
+     * Start a delete builder in the transaction
+     *
+     * @return builder object
+     */
+    TransactionDeleteBuilder<CuratorOp> delete();
+
+    /**
+     * Start a setData builder in the transaction
+     *
+     * @return builder object
+     */
+    TransactionSetDataBuilder<CuratorOp> setData();
+
+    /**
+     * Start a check builder in the transaction
+     *
+     * @return builder object
+     */
+    TransactionCheckBuilder<CuratorOp> check();
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionSetDataBuilder.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionSetDataBuilder.java
index 777537a..2d4d255 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionSetDataBuilder.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TransactionSetDataBuilder.java
@@ -22,9 +22,9 @@
 import org.apache.curator.framework.api.PathAndBytesable;
 import org.apache.curator.framework.api.Versionable;
 
-public interface TransactionSetDataBuilder extends
-    PathAndBytesable<CuratorTransactionBridge>,
-    Versionable<PathAndBytesable<CuratorTransactionBridge>>,
-    Compressible<PathAndBytesable<CuratorTransactionBridge>>
+public interface TransactionSetDataBuilder<T> extends
+    PathAndBytesable<T>,
+    Versionable<PathAndBytesable<T>>,
+    Compressible<PathAndBytesable<T>>
 {
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TypeAndPath.java b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TypeAndPath.java
new file mode 100644
index 0000000..b1cea95
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/TypeAndPath.java
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.framework.api.transaction;
+
+public class TypeAndPath
+{
+    private final OperationType type;
+    private final String forPath;
+
+    public TypeAndPath(OperationType type, String forPath)
+    {
+        this.type = type;
+        this.forPath = forPath;
+    }
+
+    public OperationType getType()
+    {
+        return type;
+    }
+
+    public String getForPath()
+    {
+        return forPath;
+    }
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/ensemble/EnsembleTracker.java b/curator-framework/src/main/java/org/apache/curator/framework/ensemble/EnsembleTracker.java
new file mode 100644
index 0000000..375e1f0
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/ensemble/EnsembleTracker.java
@@ -0,0 +1,191 @@
+/**
+ * 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.curator.framework.ensemble;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import org.apache.curator.ensemble.EnsembleListener;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.BackgroundCallback;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.CuratorWatcher;
+import org.apache.curator.framework.listen.ListenerContainer;
+import org.apache.curator.framework.state.ConnectionState;
+import org.apache.curator.framework.state.ConnectionStateListener;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.server.quorum.QuorumPeer;
+import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
+import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tracks changes to the ensemble and notifies registered {@link org.apache.curator.ensemble.EnsembleListener} instances.
+ */
+public class EnsembleTracker implements Closeable
+{
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final CuratorFramework client;
+    private final AtomicReference<State> state = new AtomicReference<>(State.LATENT);
+    private final ListenerContainer<EnsembleListener> listeners = new ListenerContainer<>();
+    private final ConnectionStateListener connectionStateListener = new ConnectionStateListener()
+    {
+        @Override
+        public void stateChanged(CuratorFramework client, ConnectionState newState)
+        {
+            if ( (newState == ConnectionState.CONNECTED) || (newState == ConnectionState.RECONNECTED) )
+            {
+                try
+                {
+                    reset();
+                }
+                catch ( Exception e )
+                {
+                    log.error("Trying to reset after reconnection", e);
+                }
+            }
+        }
+    };
+
+    private final CuratorWatcher watcher = new CuratorWatcher()
+    {
+        @Override
+        public void process(WatchedEvent event) throws Exception
+        {
+            if ( event.getType() == Watcher.Event.EventType.NodeDataChanged )
+            {
+                reset();
+            }
+        }
+    };
+
+    private enum State
+    {
+        LATENT,
+        STARTED,
+        CLOSED
+    }
+
+    private final BackgroundCallback backgroundCallback = new BackgroundCallback()
+    {
+        @Override
+        public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+        {
+            processBackgroundResult(event);
+        }
+    };
+
+    public EnsembleTracker(CuratorFramework client)
+    {
+        this.client = client;
+    }
+
+    public void start() throws Exception
+    {
+        Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once");
+        client.getConnectionStateListenable().addListener(connectionStateListener);
+        reset();
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        if ( state.compareAndSet(State.STARTED, State.CLOSED) )
+        {
+            listeners.clear();
+        }
+        client.getConnectionStateListenable().removeListener(connectionStateListener);
+    }
+
+    /**
+     * Return the ensemble listenable
+     *
+     * @return listenable
+     */
+    public ListenerContainer<EnsembleListener> getListenable()
+    {
+        Preconditions.checkState(state.get() != State.CLOSED, "Closed");
+
+        return listeners;
+    }
+
+    private void reset() throws Exception
+    {
+        client.getConfig().usingWatcher(watcher).inBackground(backgroundCallback).forEnsemble();
+    }
+
+    private void processBackgroundResult(CuratorEvent event) throws Exception
+    {
+        switch ( event.getType() )
+        {
+            case GET_CONFIG:
+            {
+                if ( event.getResultCode() == KeeperException.Code.OK.intValue() )
+                {
+                    processConfigData(event.getData());
+                }
+            }
+        }
+    }
+
+    private void processConfigData(byte[] data) throws Exception
+    {
+        Properties properties = new Properties();
+        properties.load(new ByteArrayInputStream(data));
+        QuorumVerifier qv = new QuorumMaj(properties);
+        StringBuilder sb = new StringBuilder();
+        for ( QuorumPeer.QuorumServer server : qv.getAllMembers().values() )
+        {
+            if ( sb.length() != 0 )
+            {
+                sb.append(",");
+            }
+            sb.append(server.clientAddr.getAddress().getHostAddress()).append(":").append(server.clientAddr.getPort());
+        }
+
+        final String connectionString = sb.toString();
+        listeners.forEach
+            (
+                new Function<EnsembleListener, Void>()
+                {
+                    @Override
+                    public Void apply(EnsembleListener listener)
+                    {
+                        try
+                        {
+                            listener.connectionStringUpdated(connectionString);
+                        }
+                        catch ( Exception e )
+                        {
+                            log.error("Calling listener", e);
+                        }
+                        return null;
+                    }
+                }
+            );
+    }
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/BackgroundSyncImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/BackgroundSyncImpl.java
index af8e458..f0994e3 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/BackgroundSyncImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/BackgroundSyncImpl.java
@@ -46,7 +46,7 @@
                 public void processResult(int rc, String path, Object ctx)
                 {
                     trace.commit();
-                    CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.SYNC, rc, path, null, ctx, null, null, null, null, null);
+                    CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.SYNC, rc, path, null, ctx, null, null, null, null, null, null);
                     client.processBackgroundOperation(operationAndData, event);
                 }
             },
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
index d74cc56..7184c39 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
@@ -26,7 +26,6 @@
 import org.apache.curator.TimeTrace;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.api.*;
-import org.apache.curator.framework.api.transaction.CuratorTransactionBridge;
 import org.apache.curator.framework.api.transaction.OperationType;
 import org.apache.curator.framework.api.transaction.TransactionCreateBuilder;
 import org.apache.curator.utils.ZKPaths;
@@ -47,6 +46,7 @@
     private CreateMode createMode;
     private Backgrounding backgrounding;
     private boolean createParentsIfNeeded;
+    private boolean createParentsAsContainers;
     private boolean doProtected;
     private boolean compress;
     private String protectedId;
@@ -65,44 +65,45 @@
         backgrounding = new Backgrounding();
         acling = new ACLing(client.getAclProvider());
         createParentsIfNeeded = false;
+        createParentsAsContainers = false;
         compress = false;
         doProtected = false;
         protectedId = null;
     }
 
-    TransactionCreateBuilder asTransactionCreateBuilder(final CuratorTransactionImpl curatorTransaction, final CuratorMultiTransactionRecord transaction)
+    <T> TransactionCreateBuilder<T> asTransactionCreateBuilder(final T context, final CuratorMultiTransactionRecord transaction)
     {
-        return new TransactionCreateBuilder()
+        return new TransactionCreateBuilder<T>()
         {
             @Override
-            public PathAndBytesable<CuratorTransactionBridge> withACL(List<ACL> aclList)
+            public PathAndBytesable<T> withACL(List<ACL> aclList)
             {
                 CreateBuilderImpl.this.withACL(aclList);
                 return this;
             }
 
             @Override
-            public ACLPathAndBytesable<CuratorTransactionBridge> withMode(CreateMode mode)
+            public ACLPathAndBytesable<T> withMode(CreateMode mode)
             {
                 CreateBuilderImpl.this.withMode(mode);
                 return this;
             }
             
             @Override
-            public ACLPathAndBytesable<CuratorTransactionBridge> compressed()
+            public ACLPathAndBytesable<T> compressed()
             {
                 CreateBuilderImpl.this.compressed();
                 return this;
             }
             
             @Override
-            public CuratorTransactionBridge forPath(String path) throws Exception
+            public T forPath(String path) throws Exception
             {
                 return forPath(path, client.getDefaultData());
             }
 
             @Override
-            public CuratorTransactionBridge forPath(String path, byte[] data) throws Exception
+            public T forPath(String path, byte[] data) throws Exception
             {               
                 if ( compress )
                 {
@@ -111,7 +112,7 @@
                 
                 String fixedPath = client.fixForNamespace(path);
                 transaction.add(Op.create(fixedPath, data, acling.getAclList(path), createMode), OperationType.CREATE, path);
-                return curatorTransaction;
+                return context;
             }
         };
     }
@@ -130,6 +131,13 @@
             }
 
             @Override
+            public ACLCreateModePathAndBytesable<String> creatingParentContainersIfNeeded()
+            {
+                setCreateParentsAsContainers();
+                return creatingParentsIfNeeded();
+            }
+
+            @Override
             public ACLPathAndBytesable<String> withProtectedEphemeralSequential()
             {
                 return CreateBuilderImpl.this.withProtectedEphemeralSequential();
@@ -260,6 +268,21 @@
     }
 
     @Override
+    public ProtectACLCreateModePathAndBytesable<String> creatingParentContainersIfNeeded()
+    {
+        setCreateParentsAsContainers();
+        return creatingParentsIfNeeded();
+    }
+
+    private void setCreateParentsAsContainers()
+    {
+        if ( client.useContainerParentsIfAvailable() )
+        {
+            createParentsAsContainers = true;
+        }
+    }
+
+    @Override
     public ProtectACLCreateModePathAndBytesable<String> creatingParentsIfNeeded()
     {
         createParentsIfNeeded = true;
@@ -431,7 +454,7 @@
             data = client.getCompressionProvider().compress(givenPath, data);
         }
 
-        final String adjustedPath = adjustPath(client.fixForNamespace(givenPath));
+        final String adjustedPath = adjustPath(client.fixForNamespace(givenPath, createMode.isSequential()));
 
         String returnPath = null;
         if ( backgrounding.inBackground() )
@@ -493,7 +516,7 @@
 
                         if ( (rc == KeeperException.Code.NONODE.intValue()) && createParentsIfNeeded )
                         {
-                            backgroundCreateParentsThenNode(operationAndData);
+                            backgroundCreateParentsThenNode(client, operationAndData, operationAndData.getData().getPath(), backgrounding, createParentsAsContainers);
                         }
                         else
                         {
@@ -510,16 +533,16 @@
         return PROTECTED_PREFIX + protectedId + "-";
     }
 
-    private void backgroundCreateParentsThenNode(final OperationAndData<PathAndBytes> mainOperationAndData)
+    static <T> void backgroundCreateParentsThenNode(final CuratorFrameworkImpl client, final OperationAndData<T> mainOperationAndData, final String path, Backgrounding backgrounding, final boolean createParentsAsContainers)
     {
-        BackgroundOperation<PathAndBytes> operation = new BackgroundOperation<PathAndBytes>()
+        BackgroundOperation<T> operation = new BackgroundOperation<T>()
         {
             @Override
-            public void performBackgroundOperation(OperationAndData<PathAndBytes> dummy) throws Exception
+            public void performBackgroundOperation(OperationAndData<T> dummy) throws Exception
             {
                 try
                 {
-                    ZKPaths.mkdirs(client.getZooKeeper(), mainOperationAndData.getData().getPath(), false, client.getAclProvider());
+                    ZKPaths.mkdirs(client.getZooKeeper(), path, false, client.getAclProvider(), createParentsAsContainers);
                 }
                 catch ( KeeperException e )
                 {
@@ -528,7 +551,7 @@
                 client.queueOperation(mainOperationAndData);
             }
         };
-        OperationAndData<PathAndBytes> parentOperation = new OperationAndData<PathAndBytes>(operation, mainOperationAndData.getData(), null, null, backgrounding.getContext());
+        OperationAndData<T> parentOperation = new OperationAndData<T>(operation, mainOperationAndData.getData(), null, null, backgrounding.getContext());
         client.queueOperation(parentOperation);
     }
 
@@ -537,7 +560,7 @@
         path = client.unfixForNamespace(path);
         name = client.unfixForNamespace(name);
 
-        CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.CREATE, rc, path, name, ctx, null, null, null, null, null);
+        CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.CREATE, rc, path, name, ctx, null, null, null, null, null, null);
         client.processBackgroundOperation(operationAndData, event);
     }
 
@@ -597,6 +620,9 @@
         };
     }
 
+    @VisibleForTesting
+    volatile boolean debugForceFindProtectedNode = false;
+
     private void pathInBackground(final String path, final byte[] data, final String givenPath)
     {
         final AtomicBoolean firstTime = new AtomicBoolean(true);
@@ -620,9 +646,10 @@
             void callPerformBackgroundOperation() throws Exception
             {
                 boolean callSuper = true;
-                boolean localFirstTime = firstTime.getAndSet(false);
+                boolean localFirstTime = firstTime.getAndSet(false) && !debugForceFindProtectedNode;
                 if ( !localFirstTime && doProtected )
                 {
+                    debugForceFindProtectedNode = false;
                     String createdPath = null;
                     try
                     {
@@ -637,7 +664,7 @@
                     {
                         try
                         {
-                            sendBackgroundResponse(KeeperException.Code.OK.intValue(), createdPath, backgrounding.getContext(), ZKPaths.getNodeFromPath(createdPath), this);
+                            sendBackgroundResponse(KeeperException.Code.OK.intValue(), createdPath, backgrounding.getContext(), createdPath, this);
                         }
                         catch ( Exception e )
                         {
@@ -676,11 +703,12 @@
                     @Override
                     public String call() throws Exception
                     {
-                        boolean localFirstTime = firstTime.getAndSet(false);
+                        boolean localFirstTime = firstTime.getAndSet(false) && !debugForceFindProtectedNode;
 
                         String createdPath = null;
                         if ( !localFirstTime && doProtected )
                         {
+                            debugForceFindProtectedNode = false;
                             createdPath = findProtectedNodeInForeground(path);
                         }
 
@@ -694,7 +722,7 @@
                             {
                                 if ( createParentsIfNeeded )
                                 {
-                                    ZKPaths.mkdirs(client.getZooKeeper(), path, false, client.getAclProvider());
+                                    ZKPaths.mkdirs(client.getZooKeeper(), path, false, client.getAclProvider(), createParentsAsContainers);
                                     createdPath = client.getZooKeeper().create(path, data, acling.getAclList(path), createMode);
                                 }
                                 else
@@ -751,7 +779,8 @@
         return returnPath;
     }
 
-    private String adjustPath(String path) throws Exception
+    @VisibleForTesting
+    String adjustPath(String path) throws Exception
     {
         if ( doProtected )
         {
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorEventImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorEventImpl.java
index 929fe6d..4aa125f 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorEventImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorEventImpl.java
@@ -16,11 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.curator.framework.imps;
 
 import com.google.common.collect.ImmutableList;
 import org.apache.curator.framework.api.CuratorEvent;
 import org.apache.curator.framework.api.CuratorEventType;
+import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.data.ACL;
 import org.apache.zookeeper.data.Stat;
@@ -29,16 +31,17 @@
 
 class CuratorEventImpl implements CuratorEvent
 {
-    private final CuratorEventType  type;
-    private final int               resultCode;
-    private final String            path;
-    private final String            name;
-    private final List<String>      children;
-    private final Object            context;
-    private final Stat              stat;
-    private final byte[]            data;
-    private final WatchedEvent      watchedEvent;
-    private final List<ACL>         aclList;
+    private final CuratorEventType type;
+    private final int resultCode;
+    private final String path;
+    private final String name;
+    private final List<String> children;
+    private final Object context;
+    private final Stat stat;
+    private final byte[] data;
+    private final WatchedEvent watchedEvent;
+    private final List<ACL> aclList;
+    private final List<CuratorTransactionResult> opResults;
 
     @Override
     public CuratorEventType getType()
@@ -101,6 +104,12 @@
     }
 
     @Override
+    public List<CuratorTransactionResult> getOpResults()
+    {
+        return opResults;
+    }
+
+    @Override
     public String toString()
     {
         return "CuratorEventImpl{" +
@@ -114,20 +123,22 @@
             ", data=" + Arrays.toString(data) +
             ", watchedEvent=" + watchedEvent +
             ", aclList=" + aclList +
+            ", opResults=" + opResults +
             '}';
     }
 
-    CuratorEventImpl(CuratorFrameworkImpl client, CuratorEventType type, int resultCode, String path, String name, Object context, Stat stat, byte[] data, List<String> children, WatchedEvent watchedEvent, List<ACL> aclList)
+    CuratorEventImpl(CuratorFrameworkImpl client, CuratorEventType type, int resultCode, String path, String name, Object context, Stat stat, byte[] data, List<String> children, WatchedEvent watchedEvent, List<ACL> aclList, List<CuratorTransactionResult> opResults)
     {
         this.type = type;
         this.resultCode = resultCode;
+        this.opResults = (opResults != null) ? ImmutableList.copyOf(opResults) : null;
         this.path = client.unfixForNamespace(path);
         this.name = name;
         this.context = context;
         this.stat = stat;
         this.data = data;
         this.children = children;
-        this.watchedEvent = (watchedEvent != null) ? new NamespaceWatchedEvent(client, watchedEvent) : watchedEvent;
+        this.watchedEvent = (watchedEvent != null) ? new NamespaceWatchedEvent(client, watchedEvent) : null;
         this.aclList = (aclList != null) ? ImmutableList.copyOf(aclList) : null;
     }
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorFrameworkImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorFrameworkImpl.java
index b078768..41bb7cd 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorFrameworkImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorFrameworkImpl.java
@@ -22,16 +22,19 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
-
+import com.google.common.collect.ImmutableList;
 import org.apache.curator.CuratorConnectionLossException;
 import org.apache.curator.CuratorZookeeperClient;
 import org.apache.curator.RetryLoop;
 import org.apache.curator.TimeTrace;
+import org.apache.curator.framework.AuthInfo;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
 import org.apache.curator.framework.WatcherRemoveCuratorFramework;
 import org.apache.curator.framework.api.*;
+import org.apache.curator.framework.api.transaction.CuratorMultiTransaction;
 import org.apache.curator.framework.api.transaction.CuratorTransaction;
+import org.apache.curator.framework.api.transaction.TransactionOp;
 import org.apache.curator.framework.listen.Listenable;
 import org.apache.curator.framework.listen.ListenerContainer;
 import org.apache.curator.framework.state.ConnectionState;
@@ -40,6 +43,7 @@
 import org.apache.curator.utils.DebugUtils;
 import org.apache.curator.utils.EnsurePath;
 import org.apache.curator.utils.ThreadUtils;
+import org.apache.curator.utils.ZKPaths;
 import org.apache.curator.utils.ZookeeperFactory;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.WatchedEvent;
@@ -47,8 +51,8 @@
 import org.apache.zookeeper.ZooKeeper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
 import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
 import java.util.concurrent.DelayQueue;
@@ -70,7 +74,7 @@
     private final BlockingQueue<OperationAndData<?>> backgroundOperations;
     private final NamespaceImpl namespace;
     private final ConnectionStateManager connectionStateManager;
-    private final AtomicReference<AuthInfo> authInfo = new AtomicReference<AuthInfo>();
+    private final List<AuthInfo> authInfos;
     private final byte[] defaultData;
     private final FailedDeleteManager failedDeleteManager;
     private final FailedRemoveWatchManager failedRemoveWatcherManager;
@@ -78,6 +82,7 @@
     private final ACLProvider aclProvider;
     private final NamespaceFacadeCache namespaceFacadeCache;
     private final NamespaceWatcherMap namespaceWatcherMap = new NamespaceWatcherMap(this);
+    private final boolean useContainerParentsIfAvailable;
 
     private volatile ExecutorService executorService;
     private final AtomicBoolean logAsErrorConnectionErrors = new AtomicBoolean(false);
@@ -95,27 +100,6 @@
 
     private final AtomicReference<CuratorFrameworkState> state;
 
-    private static class AuthInfo
-    {
-        final String scheme;
-        final byte[] auth;
-
-        private AuthInfo(String scheme, byte[] auth)
-        {
-            this.scheme = scheme;
-            this.auth = auth;
-        }
-
-        @Override
-        public String toString()
-        {
-            return "AuthInfo{" +
-                "scheme='" + scheme + '\'' +
-                ", auth=" + Arrays.toString(auth) +
-                '}';
-        }
-    }
-
     public CuratorFrameworkImpl(CuratorFrameworkFactory.Builder builder)
     {
         ZookeeperFactory localZookeeperFactory = makeZookeeperFactory(builder.getZookeeperFactory());
@@ -124,7 +108,7 @@
             @Override
             public void process(WatchedEvent watchedEvent)
             {
-                CuratorEvent event = new CuratorEventImpl(CuratorFrameworkImpl.this, CuratorEventType.WATCHED, watchedEvent.getState().getIntValue(), unfixForNamespace(watchedEvent.getPath()), null, null, null, null, null, watchedEvent, null);
+                CuratorEvent event = new CuratorEventImpl(CuratorFrameworkImpl.this, CuratorEventType.WATCHED, watchedEvent.getState().getIntValue(), unfixForNamespace(watchedEvent.getPath()), null, null, null, null, null, watchedEvent, null, null);
                 processEvent(event);
             }
         }, builder.getRetryPolicy(), builder.canBeReadOnly());
@@ -139,20 +123,27 @@
         compressionProvider = builder.getCompressionProvider();
         aclProvider = builder.getAclProvider();
         state = new AtomicReference<CuratorFrameworkState>(CuratorFrameworkState.LATENT);
+        useContainerParentsIfAvailable = builder.useContainerParentsIfAvailable();
 
         byte[] builderDefaultData = builder.getDefaultData();
         defaultData = (builderDefaultData != null) ? Arrays.copyOf(builderDefaultData, builderDefaultData.length) : new byte[0];
-
-        if ( builder.getAuthScheme() != null )
-        {
-            authInfo.set(new AuthInfo(builder.getAuthScheme(), builder.getAuthValue()));
-        }
+        authInfos = buildAuths(builder);
 
         failedDeleteManager = new FailedDeleteManager(this);
         failedRemoveWatcherManager = new FailedRemoveWatchManager(this);
         namespaceFacadeCache = new NamespaceFacadeCache(this);
     }
 
+    private List<AuthInfo> buildAuths(CuratorFrameworkFactory.Builder builder)
+    {
+        ImmutableList.Builder<AuthInfo> builder1 = ImmutableList.builder();
+        if ( builder.getAuthInfos() != null )
+        {
+            builder1.addAll(builder.getAuthInfos());
+        }
+        return builder1.build();
+    }
+
     @Override
     public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework()
     {
@@ -167,10 +158,9 @@
             public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws Exception
             {
                 ZooKeeper zooKeeper = actualZookeeperFactory.newZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly);
-                AuthInfo auth = authInfo.get();
-                if ( auth != null )
+                for ( AuthInfo auth : authInfos )
                 {
-                    zooKeeper.addAuthInfo(auth.scheme, auth.auth);
+                    zooKeeper.addAuthInfo(auth.getScheme(), auth.getAuth());
                 }
 
                 return zooKeeper;
@@ -183,7 +173,7 @@
         ThreadFactory threadFactory = builder.getThreadFactory();
         if ( threadFactory == null )
         {
-            threadFactory = ThreadUtils.newThreadFactory("CuratorFramework");
+            threadFactory = ThreadUtils.newThreadFactory("Framework");
         }
         return threadFactory;
     }
@@ -205,6 +195,14 @@
         namespaceFacadeCache = parent.namespaceFacadeCache;
         namespace = new NamespaceImpl(this, null);
         state = parent.state;
+        authInfos = parent.authInfos;
+        useContainerParentsIfAvailable = parent.useContainerParentsIfAvailable;
+    }
+
+    @Override
+    public void createContainers(String path) throws Exception
+    {
+        checkExists().creatingParentContainersIfNeeded().forPath(ZKPaths.makePath(path, "foo"));
     }
 
     @Override
@@ -248,8 +246,7 @@
         log.info("Starting");
         if ( !state.compareAndSet(CuratorFrameworkState.LATENT, CuratorFrameworkState.STARTED) )
         {
-            IllegalStateException ise = new IllegalStateException("Cannot be started more than once");
-            throw ise;
+            throw new IllegalStateException("Cannot be started more than once");
         }
 
         try
@@ -275,14 +272,14 @@
             executorService = Executors.newFixedThreadPool(2, threadFactory);  // 1 for listeners, 1 for background ops
 
             executorService.submit(new Callable<Object>()
+            {
+                @Override
+                public Object call() throws Exception
                 {
-                    @Override
-                    public Object call() throws Exception
-                    {
-                        backgroundOperationsLoop();
-                        return null;
-                    }
-                });
+                    backgroundOperationsLoop();
+                    return null;
+                }
+            });
         }
         catch ( Exception e )
         {
@@ -297,22 +294,22 @@
         if ( state.compareAndSet(CuratorFrameworkState.STARTED, CuratorFrameworkState.STOPPED) )
         {
             listeners.forEach(new Function<CuratorListener, Void>()
+            {
+                @Override
+                public Void apply(CuratorListener listener)
                 {
-                    @Override
-                    public Void apply(CuratorListener listener)
+                    CuratorEvent event = new CuratorEventImpl(CuratorFrameworkImpl.this, CuratorEventType.CLOSING, 0, null, null, null, null, null, null, null, null, null);
+                    try
                     {
-                        CuratorEvent event = new CuratorEventImpl(CuratorFrameworkImpl.this, CuratorEventType.CLOSING, 0, null, null, null, null, null, null, null, null);
-                        try
-                        {
-                            listener.eventReceived(CuratorFrameworkImpl.this, event);
-                        }
-                        catch ( Exception e )
-                        {
-                            log.error("Exception while sending Closing event", e);
-                        }
-                        return null;
+                        listener.eventReceived(CuratorFrameworkImpl.this, event);
                     }
-                });
+                    catch ( Exception e )
+                    {
+                        log.error("Exception while sending Closing event", e);
+                    }
+                    return null;
+                }
+            });
 
             if ( executorService != null )
             {
@@ -423,6 +420,18 @@
     }
 
     @Override
+    public ReconfigBuilder reconfig()
+    {
+        return new ReconfigBuilderImpl(this);
+    }
+
+    @Override
+    public GetConfigBuilder getConfig()
+    {
+        return new GetConfigBuilderImpl(this);
+    }
+
+    @Override
     public CuratorTransaction inTransaction()
     {
         Preconditions.checkState(getState() == CuratorFrameworkState.STARTED, "instance must be started before calling this method");
@@ -431,6 +440,22 @@
     }
 
     @Override
+    public CuratorMultiTransaction transaction()
+    {
+        Preconditions.checkState(getState() == CuratorFrameworkState.STARTED, "instance must be started before calling this method");
+
+        return new CuratorMultiTransactionImpl(this);
+    }
+
+    @Override
+    public TransactionOp transactionOp()
+    {
+        Preconditions.checkState(getState() == CuratorFrameworkState.STARTED, "instance must be started before calling this method");
+
+        return new TransactionOpImpl(this);
+    }
+
+    @Override
     public Listenable<ConnectionStateListener> getConnectionStateListenable()
     {
         return connectionStateManager.getListenable();
@@ -463,7 +488,7 @@
     {
         return new SyncBuilderImpl(this);
     }
-    
+
     @Override
     public RemoveWatchesBuilder watches()
     {
@@ -487,7 +512,7 @@
     {
         return namespace.newNamespaceAwareEnsurePath(path);
     }
-    
+
     ACLProvider getAclProvider()
     {
         return aclProvider;
@@ -497,11 +522,11 @@
     {
         return failedDeleteManager;
     }
-    
+
     FailedRemoveWatchManager getFailedRemoveWatcherManager()
     {
         return failedRemoveWatcherManager;
-    }    
+    }
 
     RetryLoop newRetryLoop()
     {
@@ -518,6 +543,11 @@
         return compressionProvider;
     }
 
+    boolean useContainerParentsIfAvailable()
+    {
+        return useContainerParentsIfAvailable;
+    }
+
     <DATA_TYPE> void processBackgroundOperation(OperationAndData<DATA_TYPE> operationAndData, CuratorEvent event)
     {
         boolean isInitialExecution = (event == null);
@@ -607,7 +637,12 @@
 
     String fixForNamespace(String path)
     {
-        return namespace.fixForNamespace(path);
+        return namespace.fixForNamespace(path, false);
+    }
+
+    String fixForNamespace(String path, boolean isSequential)
+    {
+        return namespace.fixForNamespace(path, isSequential);
     }
 
     byte[] getDefaultData()
@@ -858,7 +893,7 @@
             if ( e instanceof CuratorConnectionLossException )
             {
                 WatchedEvent watchedEvent = new WatchedEvent(Watcher.Event.EventType.None, Watcher.Event.KeeperState.Disconnected, null);
-                CuratorEvent event = new CuratorEventImpl(this, CuratorEventType.WATCHED, KeeperException.Code.CONNECTIONLOSS.intValue(), null, null, operationAndData.getContext(), null, null, null, watchedEvent, null);
+                CuratorEvent event = new CuratorEventImpl(this, CuratorEventType.WATCHED, KeeperException.Code.CONNECTIONLOSS.intValue(), null, null, operationAndData.getContext(), null, null, null, watchedEvent, null, null);
                 if ( checkBackgroundRetry(operationAndData, event) )
                 {
                     queueOperation(operationAndData);
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorMultiTransactionImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorMultiTransactionImpl.java
new file mode 100644
index 0000000..577b0d6
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorMultiTransactionImpl.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.curator.framework.imps;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.apache.curator.RetryLoop;
+import org.apache.curator.TimeTrace;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.BackgroundCallback;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.CuratorEventType;
+import org.apache.curator.framework.api.transaction.CuratorMultiTransaction;
+import org.apache.curator.framework.api.transaction.CuratorMultiTransactionMain;
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.zookeeper.AsyncCallback;
+import org.apache.zookeeper.OpResult;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+
+public class CuratorMultiTransactionImpl implements
+    CuratorMultiTransaction,
+    CuratorMultiTransactionMain,
+    BackgroundOperation<CuratorMultiTransactionRecord>
+{
+    private final CuratorFrameworkImpl client;
+    private Backgrounding backgrounding = new Backgrounding();
+
+    public CuratorMultiTransactionImpl(CuratorFrameworkImpl client)
+    {
+        this.client = client;
+    }
+
+    @Override
+    public CuratorMultiTransactionMain inBackground()
+    {
+        backgrounding = new Backgrounding(true);
+        return this;
+    }
+
+    @Override
+    public CuratorMultiTransactionMain inBackground(Object context)
+    {
+        backgrounding = new Backgrounding(context);
+        return this;
+    }
+
+    @Override
+    public CuratorMultiTransactionMain inBackground(BackgroundCallback callback)
+    {
+        backgrounding = new Backgrounding(callback);
+        return this;
+    }
+
+    @Override
+    public CuratorMultiTransactionMain inBackground(BackgroundCallback callback, Object context)
+    {
+        backgrounding = new Backgrounding(callback, context);
+        return this;
+    }
+
+    @Override
+    public CuratorMultiTransactionMain inBackground(BackgroundCallback callback, Executor executor)
+    {
+        backgrounding = new Backgrounding(callback, executor);
+        return this;
+    }
+
+    @Override
+    public CuratorMultiTransactionMain inBackground(BackgroundCallback callback, Object context, Executor executor)
+    {
+        backgrounding = new Backgrounding(client, callback, context, executor);
+        return this;
+    }
+
+    @Override
+    public List<CuratorTransactionResult> forOperations(CuratorOp... operations) throws Exception
+    {
+        List<CuratorOp> ops = (operations != null) ? Arrays.asList(operations) : Lists.<CuratorOp>newArrayList();
+        return forOperations(ops);
+    }
+
+    @Override
+    public List<CuratorTransactionResult> forOperations(List<CuratorOp> operations) throws Exception
+    {
+        operations = Preconditions.checkNotNull(operations, "operations cannot be null");
+        Preconditions.checkArgument(!operations.isEmpty(), "operations list cannot be empty");
+
+        CuratorMultiTransactionRecord record = new CuratorMultiTransactionRecord();
+        for ( CuratorOp curatorOp : operations )
+        {
+            record.add(curatorOp.get(), curatorOp.getTypeAndPath().getType(), curatorOp.getTypeAndPath().getForPath());
+        }
+
+        if ( backgrounding.inBackground() )
+        {
+            client.processBackgroundOperation(new OperationAndData<>(this, record, backgrounding.getCallback(), null, backgrounding.getContext()), null);
+            return null;
+        }
+        else
+        {
+            return forOperationsInForeground(record);
+        }
+    }
+
+    @Override
+    public void performBackgroundOperation(final OperationAndData<CuratorMultiTransactionRecord> operationAndData) throws Exception
+    {
+        final TimeTrace trace = client.getZookeeperClient().startTracer("CuratorMultiTransactionImpl-Background");
+        AsyncCallback.MultiCallback callback = new AsyncCallback.MultiCallback()
+        {
+            @Override
+            public void processResult(int rc, String path, Object ctx, List<OpResult> opResults)
+            {
+                trace.commit();
+                List<CuratorTransactionResult> curatorResults = (opResults != null) ? CuratorTransactionImpl.wrapResults(client, opResults, operationAndData.getData()) : null;
+                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.TRANSACTION, rc, path, null, ctx, null, null, null, null, null, curatorResults);
+                client.processBackgroundOperation(operationAndData, event);
+            }
+        };
+        client.getZooKeeper().multi(operationAndData.getData(), callback, backgrounding.getContext());
+    }
+
+    private List<CuratorTransactionResult> forOperationsInForeground(final CuratorMultiTransactionRecord record) throws Exception
+    {
+        TimeTrace trace = client.getZookeeperClient().startTracer("CuratorMultiTransactionImpl-Foreground");
+        List<OpResult> responseData = RetryLoop.callWithRetry
+        (
+            client.getZookeeperClient(),
+            new Callable<List<OpResult>>()
+            {
+                @Override
+                public List<OpResult> call() throws Exception
+                {
+                    return client.getZooKeeper().multi(record);
+                }
+            }
+        );
+        trace.commit();
+
+        return CuratorTransactionImpl.wrapResults(client, responseData, record);
+    }
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorMultiTransactionRecord.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorMultiTransactionRecord.java
index 1500d6d..0611df6 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorMultiTransactionRecord.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorMultiTransactionRecord.java
@@ -20,6 +20,7 @@
 
 import com.google.common.collect.Lists;
 import org.apache.curator.framework.api.transaction.OperationType;
+import org.apache.curator.framework.api.transaction.TypeAndPath;
 import org.apache.zookeeper.MultiTransactionRecord;
 import org.apache.zookeeper.Op;
 import java.util.List;
@@ -28,30 +29,18 @@
 {
     private final List<TypeAndPath>     metadata = Lists.newArrayList();
 
-    static class TypeAndPath
-    {
-        final OperationType type;
-        final String forPath;
-
-        TypeAndPath(OperationType type, String forPath)
-        {
-            this.type = type;
-            this.forPath = forPath;
-        }
-    }
-
     @Override
     public final void add(Op op)
     {
         throw new UnsupportedOperationException();
     }
-    
+
     void add(Op op, OperationType type, String forPath)
     {
         super.add(op);
         metadata.add(new TypeAndPath(type, forPath));
     }
-    
+
     TypeAndPath     getMetadata(int index)
     {
         return metadata.get(index);
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorTempFrameworkImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorTempFrameworkImpl.java
index 01204d9..d5baa80 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorTempFrameworkImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorTempFrameworkImpl.java
@@ -19,11 +19,13 @@
 package org.apache.curator.framework.imps;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.framework.CuratorFrameworkFactory;
 import org.apache.curator.framework.CuratorTempFramework;
 import org.apache.curator.framework.api.TempGetDataBuilder;
 import org.apache.curator.framework.api.transaction.CuratorTransaction;
+import org.apache.curator.utils.ThreadUtils;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadFactory;
@@ -98,7 +100,13 @@
         if ( cleanup == null )
         {
             ThreadFactory threadFactory = factory.getThreadFactory();
-            cleanup = (threadFactory != null) ? Executors.newScheduledThreadPool(1, threadFactory) : Executors.newScheduledThreadPool(1);
+
+            if (threadFactory == null)
+            {
+                threadFactory = ThreadUtils.newGenericThreadFactory("CuratorTempFrameworkImpl");
+            }
+
+            cleanup = Executors.newScheduledThreadPool(1, threadFactory);
 
             Runnable        command = new Runnable()
             {
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorTransactionImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorTransactionImpl.java
index 13ffe82..20ec274 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorTransactionImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorTransactionImpl.java
@@ -16,21 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.curator.framework.imps;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import org.apache.curator.RetryLoop;
 import org.apache.curator.framework.api.Pathable;
-import org.apache.curator.framework.api.transaction.CuratorTransaction;
-import org.apache.curator.framework.api.transaction.CuratorTransactionBridge;
-import org.apache.curator.framework.api.transaction.CuratorTransactionFinal;
-import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
-import org.apache.curator.framework.api.transaction.OperationType;
-import org.apache.curator.framework.api.transaction.TransactionCheckBuilder;
-import org.apache.curator.framework.api.transaction.TransactionCreateBuilder;
-import org.apache.curator.framework.api.transaction.TransactionDeleteBuilder;
-import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder;
+import org.apache.curator.framework.api.transaction.*;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.Op;
 import org.apache.zookeeper.OpResult;
@@ -43,10 +36,10 @@
 
 class CuratorTransactionImpl implements CuratorTransaction, CuratorTransactionBridge, CuratorTransactionFinal
 {
-    private final CuratorFrameworkImpl              client;
-    private final CuratorMultiTransactionRecord     transaction;
+    private final CuratorFrameworkImpl client;
+    private final CuratorMultiTransactionRecord transaction;
 
-    private boolean         isCommitted = false;
+    private boolean isCommitted = false;
 
     CuratorTransactionImpl(CuratorFrameworkImpl client)
     {
@@ -61,49 +54,58 @@
     }
 
     @Override
-    public TransactionCreateBuilder create()
+    public TransactionCreateBuilder<CuratorTransactionBridge> create()
     {
         Preconditions.checkState(!isCommitted, "transaction already committed");
 
-        return new CreateBuilderImpl(client).asTransactionCreateBuilder(this, transaction);
+        CuratorTransactionBridge asBridge = this;
+        return new CreateBuilderImpl(client).asTransactionCreateBuilder(asBridge, transaction);
     }
 
     @Override
-    public TransactionDeleteBuilder delete()
+    public TransactionDeleteBuilder<CuratorTransactionBridge> delete()
     {
         Preconditions.checkState(!isCommitted, "transaction already committed");
 
-        return new DeleteBuilderImpl(client).asTransactionDeleteBuilder(this, transaction);
+        CuratorTransactionBridge asBridge = this;
+        return new DeleteBuilderImpl(client).asTransactionDeleteBuilder(asBridge, transaction);
     }
 
     @Override
-    public TransactionSetDataBuilder setData()
+    public TransactionSetDataBuilder<CuratorTransactionBridge> setData()
     {
         Preconditions.checkState(!isCommitted, "transaction already committed");
 
-        return new SetDataBuilderImpl(client).asTransactionSetDataBuilder(this, transaction);
+        CuratorTransactionBridge asBridge = this;
+        return new SetDataBuilderImpl(client).asTransactionSetDataBuilder(asBridge, transaction);
     }
 
     @Override
-    public TransactionCheckBuilder check()
+    public TransactionCheckBuilder<CuratorTransactionBridge> check()
     {
         Preconditions.checkState(!isCommitted, "transaction already committed");
 
-        return new TransactionCheckBuilder()
+        CuratorTransactionBridge asBridge = this;
+        return makeTransactionCheckBuilder(client, asBridge, transaction);
+    }
+
+    static <T> TransactionCheckBuilder<T> makeTransactionCheckBuilder(final CuratorFrameworkImpl client, final T context, final CuratorMultiTransactionRecord transaction)
+    {
+        return new TransactionCheckBuilder<T>()
         {
-            private int         version = -1;
+            private int version = -1;
 
             @Override
-            public CuratorTransactionBridge forPath(String path) throws Exception
+            public T forPath(String path) throws Exception
             {
-                String      fixedPath = client.fixForNamespace(path);
+                String fixedPath = client.fixForNamespace(path);
                 transaction.add(Op.check(fixedPath, version), OperationType.CHECK, path);
 
-                return CuratorTransactionImpl.this;
+                return context;
             }
 
             @Override
-            public Pathable<CuratorTransactionBridge> withVersion(int version)
+            public Pathable<T> withVersion(int version)
             {
                 this.version = version;
                 return this;
@@ -118,65 +120,44 @@
         isCommitted = true;
 
         final AtomicBoolean firstTime = new AtomicBoolean(true);
-        List<OpResult>      resultList = RetryLoop.callWithRetry
-        (
-            client.getZookeeperClient(),
-            new Callable<List<OpResult>>()
-            {
-                @Override
-                public List<OpResult> call() throws Exception
+        List<OpResult> resultList = RetryLoop.callWithRetry
+            (
+                client.getZookeeperClient(),
+                new Callable<List<OpResult>>()
                 {
-                    return doOperation(firstTime);
+                    @Override
+                    public List<OpResult> call() throws Exception
+                    {
+                        return doOperation(firstTime);
+                    }
                 }
-            }
-        );
-        
+            );
+
         if ( resultList.size() != transaction.metadataSize() )
         {
             throw new IllegalStateException(String.format("Result size (%d) doesn't match input size (%d)", resultList.size(), transaction.metadataSize()));
         }
 
-        ImmutableList.Builder<CuratorTransactionResult>     builder = ImmutableList.builder();
+        return wrapResults(client, resultList, transaction);
+    }
+
+    static List<CuratorTransactionResult> wrapResults(CuratorFrameworkImpl client, List<OpResult> resultList, CuratorMultiTransactionRecord transaction)
+    {
+        ImmutableList.Builder<CuratorTransactionResult> builder = ImmutableList.builder();
         for ( int i = 0; i < resultList.size(); ++i )
         {
-            OpResult                                    opResult = resultList.get(i);
-            CuratorMultiTransactionRecord.TypeAndPath   metadata = transaction.getMetadata(i);
-            CuratorTransactionResult                    curatorResult = makeCuratorResult(opResult, metadata);
+            OpResult opResult = resultList.get(i);
+            TypeAndPath metadata = transaction.getMetadata(i);
+            CuratorTransactionResult curatorResult = makeCuratorResult(client, opResult, metadata);
             builder.add(curatorResult);
         }
 
         return builder.build();
     }
 
-    private List<OpResult> doOperation(AtomicBoolean firstTime) throws Exception
+    static CuratorTransactionResult makeCuratorResult(CuratorFrameworkImpl client, OpResult opResult, TypeAndPath metadata)
     {
-        boolean         localFirstTime = firstTime.getAndSet(false);
-        if ( !localFirstTime )
-        {
-
-        }
-
-        List<OpResult>  opResults = client.getZooKeeper().multi(transaction);
-        if ( opResults.size() > 0 )
-        {
-            OpResult        firstResult = opResults.get(0);
-            if ( firstResult.getType() == ZooDefs.OpCode.error )
-            {
-                OpResult.ErrorResult        error = (OpResult.ErrorResult)firstResult;
-                KeeperException.Code        code = KeeperException.Code.get(error.getErr());
-                if ( code == null )
-                {
-                    code = KeeperException.Code.UNIMPLEMENTED;
-                }
-                throw KeeperException.create(code);
-            }
-        }
-        return opResults;
-    }
-
-    private CuratorTransactionResult makeCuratorResult(OpResult opResult, CuratorMultiTransactionRecord.TypeAndPath metadata)
-    {
-        String                                      resultPath = null;
+        String resultPath = null;
         Stat resultStat = null;
         switch ( opResult.getType() )
         {
@@ -188,19 +169,45 @@
 
             case ZooDefs.OpCode.create:
             {
-                OpResult.CreateResult       createResult = (OpResult.CreateResult)opResult;
+                OpResult.CreateResult createResult = (OpResult.CreateResult)opResult;
                 resultPath = client.unfixForNamespace(createResult.getPath());
                 break;
             }
 
             case ZooDefs.OpCode.setData:
             {
-                OpResult.SetDataResult      setDataResult = (OpResult.SetDataResult)opResult;
+                OpResult.SetDataResult setDataResult = (OpResult.SetDataResult)opResult;
                 resultStat = setDataResult.getStat();
                 break;
             }
         }
 
-        return new CuratorTransactionResult(metadata.type, metadata.forPath, resultPath, resultStat);
+        return new CuratorTransactionResult(metadata.getType(), metadata.getForPath(), resultPath, resultStat);
+    }
+
+    private List<OpResult> doOperation(AtomicBoolean firstTime) throws Exception
+    {
+        boolean localFirstTime = firstTime.getAndSet(false);
+        if ( !localFirstTime )
+        {
+            // TODO
+        }
+
+        List<OpResult> opResults = client.getZooKeeper().multi(transaction);
+        if ( opResults.size() > 0 )
+        {
+            OpResult firstResult = opResults.get(0);
+            if ( firstResult.getType() == ZooDefs.OpCode.error )
+            {
+                OpResult.ErrorResult error = (OpResult.ErrorResult)firstResult;
+                KeeperException.Code code = KeeperException.Code.get(error.getErr());
+                if ( code == null )
+                {
+                    code = KeeperException.Code.UNIMPLEMENTED;
+                }
+                throw KeeperException.create(code);
+            }
+        }
+        return opResults;
     }
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/DeleteBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/DeleteBuilderImpl.java
index 51691dd..2a98f56 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/DeleteBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/DeleteBuilderImpl.java
@@ -28,7 +28,6 @@
 import org.apache.curator.framework.api.CuratorEventType;
 import org.apache.curator.framework.api.DeleteBuilder;
 import org.apache.curator.framework.api.Pathable;
-import org.apache.curator.framework.api.transaction.CuratorTransactionBridge;
 import org.apache.curator.framework.api.transaction.OperationType;
 import org.apache.curator.framework.api.transaction.TransactionDeleteBuilder;
 import org.apache.curator.utils.ZKPaths;
@@ -40,11 +39,11 @@
 
 class DeleteBuilderImpl implements DeleteBuilder, BackgroundOperation<String>
 {
-    private final CuratorFrameworkImpl  client;
-    private int                         version;
-    private Backgrounding               backgrounding;
-    private boolean                     deletingChildrenIfNeeded;
-    private boolean                     guaranteed;
+    private final CuratorFrameworkImpl client;
+    private int version;
+    private Backgrounding backgrounding;
+    private boolean deletingChildrenIfNeeded;
+    private boolean guaranteed;
 
     DeleteBuilderImpl(CuratorFrameworkImpl client)
     {
@@ -55,20 +54,20 @@
         guaranteed = false;
     }
 
-    TransactionDeleteBuilder    asTransactionDeleteBuilder(final CuratorTransactionImpl curatorTransaction, final CuratorMultiTransactionRecord transaction)
+    <T> TransactionDeleteBuilder<T> asTransactionDeleteBuilder(final T context, final CuratorMultiTransactionRecord transaction)
     {
-        return new TransactionDeleteBuilder()
+        return new TransactionDeleteBuilder<T>()
         {
             @Override
-            public CuratorTransactionBridge forPath(String path) throws Exception
+            public T forPath(String path) throws Exception
             {
-                String      fixedPath = client.fixForNamespace(path);
+                String fixedPath = client.fixForNamespace(path);
                 transaction.add(Op.delete(fixedPath, version), OperationType.DELETE, path);
-                return curatorTransaction;
+                return context;
             }
 
             @Override
-            public Pathable<CuratorTransactionBridge> withVersion(int version)
+            public Pathable<T> withVersion(int version)
             {
                 DeleteBuilderImpl.this.withVersion(version);
                 return this;
@@ -142,32 +141,35 @@
     @Override
     public void performBackgroundOperation(final OperationAndData<String> operationAndData) throws Exception
     {
-        final TimeTrace   trace = client.getZookeeperClient().startTracer("DeleteBuilderImpl-Background");
+        final TimeTrace trace = client.getZookeeperClient().startTracer("DeleteBuilderImpl-Background");
         client.getZooKeeper().delete
-        (
-            operationAndData.getData(),
-            version,
-            new AsyncCallback.VoidCallback()
-            {
-                @Override
-                public void processResult(int rc, String path, Object ctx)
+            (
+                operationAndData.getData(),
+                version,
+                new AsyncCallback.VoidCallback()
                 {
-                    trace.commit();
-                    if ((rc == KeeperException.Code.NOTEMPTY.intValue()) && deletingChildrenIfNeeded) {
-                        backgroundDeleteChildrenThenNode(operationAndData);
-                    } else {
-                        CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.DELETE, rc, path, null, ctx, null, null, null, null, null);
-                        client.processBackgroundOperation(operationAndData, event);
+                    @Override
+                    public void processResult(int rc, String path, Object ctx)
+                    {
+                        trace.commit();
+                        if ( (rc == KeeperException.Code.NOTEMPTY.intValue()) && deletingChildrenIfNeeded )
+                        {
+                            backgroundDeleteChildrenThenNode(operationAndData);
+                        }
+                        else
+                        {
+                            CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.DELETE, rc, path, null, ctx, null, null, null, null, null, null);
+                            client.processBackgroundOperation(operationAndData, event);
+                        }
                     }
-                }
-            },
-            backgrounding.getContext()
-        );
+                },
+                backgrounding.getContext()
+            );
     }
 
     private void backgroundDeleteChildrenThenNode(final OperationAndData<String> mainOperationAndData)
     {
-        BackgroundOperation<String>     operation = new BackgroundOperation<String>()
+        BackgroundOperation<String> operation = new BackgroundOperation<String>()
         {
             @Override
             public void performBackgroundOperation(OperationAndData<String> dummy) throws Exception
@@ -190,12 +192,12 @@
     @Override
     public Void forPath(String path) throws Exception
     {
-        final String        unfixedPath = path;
+        final String unfixedPath = path;
         path = client.fixForNamespace(path);
 
         if ( backgrounding.inBackground() )
         {
-            OperationAndData.ErrorCallback<String>  errorCallback = null;
+            OperationAndData.ErrorCallback<String> errorCallback = null;
             if ( guaranteed )
             {
                 errorCallback = new OperationAndData.ErrorCallback<String>()
@@ -223,35 +225,41 @@
 
     private void pathInForeground(final String path, String unfixedPath) throws Exception
     {
-        TimeTrace       trace = client.getZookeeperClient().startTracer("DeleteBuilderImpl-Foreground");
+        TimeTrace trace = client.getZookeeperClient().startTracer("DeleteBuilderImpl-Foreground");
         try
         {
             RetryLoop.callWithRetry
-            (
-                client.getZookeeperClient(),
-                new Callable<Void>()
-                {
-                    @Override
-                    public Void call() throws Exception
+                (
+                    client.getZookeeperClient(),
+                    new Callable<Void>()
                     {
-                        try {
-                        client.getZooKeeper().delete(path, version);
-                        } catch (KeeperException.NotEmptyException e) {
-                            if (deletingChildrenIfNeeded) {
-                                ZKPaths.deleteChildren(client.getZooKeeper(), path, true);
-                            } else {
-                                throw e;
+                        @Override
+                        public Void call() throws Exception
+                        {
+                            try
+                            {
+                                client.getZooKeeper().delete(path, version);
                             }
+                            catch ( KeeperException.NotEmptyException e )
+                            {
+                                if ( deletingChildrenIfNeeded )
+                                {
+                                    ZKPaths.deleteChildren(client.getZooKeeper(), path, true);
+                                }
+                                else
+                                {
+                                    throw e;
+                                }
+                            }
+                            return null;
                         }
-                        return null;
                     }
-                }
-            );
-        }      
+                );
+        }
         catch ( Exception e )
         {
             //Only retry a guaranteed delete if it's a retryable error
-            if( RetryLoop.isRetryException(e) && guaranteed )
+            if( (RetryLoop.isRetryException(e) || (e instanceof InterruptedException)) && guaranteed )
             {
                 client.getFailedDeleteManager().addFailedOperation(unfixedPath);
             }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/ExistsBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/ExistsBuilderImpl.java
index d5f7b5b..a6316ac 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/ExistsBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/ExistsBuilderImpl.java
@@ -26,8 +26,11 @@
 import org.apache.curator.framework.api.CuratorEventType;
 import org.apache.curator.framework.api.CuratorWatcher;
 import org.apache.curator.framework.api.ExistsBuilder;
+import org.apache.curator.framework.api.ExistsBuilderMain;
 import org.apache.curator.framework.api.Pathable;
+import org.apache.curator.utils.ZKPaths;
 import org.apache.zookeeper.AsyncCallback;
+import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.Watcher;
 import org.apache.zookeeper.data.Stat;
 import java.util.concurrent.Callable;
@@ -38,12 +41,21 @@
     private final CuratorFrameworkImpl client;
     private Backgrounding backgrounding;
     private Watching watching;
+    private boolean createParentContainersIfNeeded;
 
     ExistsBuilderImpl(CuratorFrameworkImpl client)
     {
         this.client = client;
         backgrounding = new Backgrounding();
         watching = new Watching();
+        createParentContainersIfNeeded = false;
+    }
+
+    @Override
+    public ExistsBuilderMain creatingParentContainersIfNeeded()
+    {
+        createParentContainersIfNeeded = true;
+        return this;
     }
 
     @Override
@@ -119,7 +131,7 @@
             public void processResult(int rc, String path, Object ctx, Stat stat)
             {
                 trace.commit();
-                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.EXISTS, rc, path, null, ctx, stat, null, null, null, null);
+                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.EXISTS, rc, path, null, ctx, stat, null, null, null, null, null);
                 client.processBackgroundOperation(operationAndData, event);
             }
         };
@@ -141,7 +153,15 @@
         Stat        returnStat = null;
         if ( backgrounding.inBackground() )
         {
-            client.processBackgroundOperation(new OperationAndData<String>(this, path, backgrounding.getCallback(), null, backgrounding.getContext()), null);
+            OperationAndData<String> operationAndData = new OperationAndData<String>(this, path, backgrounding.getCallback(), null, backgrounding.getContext());
+            if ( createParentContainersIfNeeded )
+            {
+                CreateBuilderImpl.backgroundCreateParentsThenNode(client, operationAndData, operationAndData.getData(), backgrounding, true);
+            }
+            else
+            {
+                client.processBackgroundOperation(operationAndData, null);
+            }
         }
         else
         {
@@ -153,6 +173,40 @@
 
     private Stat pathInForeground(final String path) throws Exception
     {
+        if ( createParentContainersIfNeeded )
+        {
+            final String parent = ZKPaths.getPathAndNode(path).getPath();
+            if ( !parent.equals(ZKPaths.PATH_SEPARATOR) )
+            {
+                TimeTrace   trace = client.getZookeeperClient().startTracer("ExistsBuilderImpl-Foreground-CreateParents");
+                RetryLoop.callWithRetry
+                (
+                    client.getZookeeperClient(),
+                    new Callable<Void>()
+                    {
+                        @Override
+                        public Void call() throws Exception
+                        {
+                            try
+                            {
+                                ZKPaths.mkdirs(client.getZooKeeper(), parent, true, client.getAclProvider(), true);
+                            }
+                            catch ( KeeperException e )
+                            {
+                                // ignore
+                            }
+                            return null;
+                        }
+                    }
+                );
+                trace.commit();
+            }
+        }
+        return pathInForegroundStandard(path);
+    }
+
+    private Stat pathInForegroundStandard(final String path) throws Exception
+    {
         TimeTrace   trace = client.getZookeeperClient().startTracer("ExistsBuilderImpl-Foreground");
         Stat        returnStat = RetryLoop.callWithRetry
         (
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/ExtractingCuratorOp.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/ExtractingCuratorOp.java
new file mode 100644
index 0000000..7a5db69
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/ExtractingCuratorOp.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.framework.imps;
+
+import com.google.common.base.Preconditions;
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.framework.api.transaction.TypeAndPath;
+import org.apache.zookeeper.Op;
+
+class ExtractingCuratorOp implements CuratorOp
+{
+    private final CuratorMultiTransactionRecord record = new CuratorMultiTransactionRecord();
+
+    CuratorMultiTransactionRecord getRecord()
+    {
+        return record;
+    }
+
+    @Override
+    public TypeAndPath getTypeAndPath()
+    {
+        validate();
+        return record.getMetadata(0);
+    }
+
+    @Override
+    public Op get()
+    {
+        validate();
+        return record.iterator().next();
+    }
+
+    private void validate()
+    {
+        Preconditions.checkArgument(record.size() > 0, "No operation has been added");
+        Preconditions.checkArgument(record.size() == 1, "Multiple operations added");
+    }
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/GetACLBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/GetACLBuilderImpl.java
index 250c2c8..f65c933 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/GetACLBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/GetACLBuilderImpl.java
@@ -104,7 +104,7 @@
             public void processResult(int rc, String path, Object ctx, List<ACL> acl, Stat stat)
             {
                 trace.commit();
-                CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.GET_ACL, rc, path, null, ctx, stat, null, null, null, acl);
+                CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.GET_ACL, rc, path, null, ctx, stat, null, null, null, acl, null);
                 client.processBackgroundOperation(operationAndData, event);
             }
         };
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/GetChildrenBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/GetChildrenBuilderImpl.java
index f9f784f..7929ba3 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/GetChildrenBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/GetChildrenBuilderImpl.java
@@ -162,7 +162,7 @@
                 {
                     strings = Lists.newArrayList();
                 }
-                CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.CHILDREN, rc, path, null, o, stat, null, strings, null, null);
+                CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.CHILDREN, rc, path, null, o, stat, null, strings, null, null, null);
                 client.processBackgroundOperation(operationAndData, event);
             }
         };
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/GetConfigBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/GetConfigBuilderImpl.java
new file mode 100644
index 0000000..5468bd4
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/GetConfigBuilderImpl.java
@@ -0,0 +1,188 @@
+/**
+ * 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.curator.framework.imps;
+
+import org.apache.curator.RetryLoop;
+import org.apache.curator.TimeTrace;
+import org.apache.curator.framework.api.BackgroundCallback;
+import org.apache.curator.framework.api.BackgroundStatable;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.CuratorEventType;
+import org.apache.curator.framework.api.CuratorWatcher;
+import org.apache.curator.framework.api.Ensembleable;
+import org.apache.curator.framework.api.GetConfigBuilder;
+import org.apache.zookeeper.AsyncCallback;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.Stat;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+
+public class GetConfigBuilderImpl implements GetConfigBuilder, BackgroundOperation<Void>
+{
+    private final CuratorFrameworkImpl client;
+
+    private Backgrounding backgrounding;
+    private Watching watching;
+    private Stat stat;
+
+    public GetConfigBuilderImpl(CuratorFrameworkImpl client)
+    {
+        this.client = client;
+        backgrounding = new Backgrounding();
+        watching = new Watching();
+    }
+
+    @Override
+    public Ensembleable<byte[]> storingStatIn(Stat stat)
+    {
+        this.stat = stat;
+        return this;
+    }
+
+    @Override
+    public BackgroundStatable<Ensembleable<byte[]>> watched()
+    {
+        watching = new Watching(true);
+        return this;
+    }
+
+    @Override
+    public GetConfigBuilder usingWatcher(Watcher watcher)
+    {
+        watching = new Watching(client, watcher);
+        return this;
+    }
+
+    @Override
+    public GetConfigBuilder usingWatcher(final CuratorWatcher watcher)
+    {
+        watching = new Watching(client, watcher);
+        return this;
+    }
+
+    @Override
+    public Ensembleable<byte[]> inBackground()
+    {
+        backgrounding = new Backgrounding(true);
+        return this;
+    }
+
+    @Override
+    public Ensembleable<byte[]> inBackground(Object context)
+    {
+        backgrounding = new Backgrounding(context);
+        return this;
+    }
+
+    @Override
+    public Ensembleable<byte[]> inBackground(BackgroundCallback callback)
+    {
+        backgrounding = new Backgrounding(callback);
+        return this;
+    }
+
+    @Override
+    public Ensembleable<byte[]> inBackground(BackgroundCallback callback, Object context)
+    {
+        backgrounding = new Backgrounding(callback, context);
+        return this;
+    }
+
+    @Override
+    public Ensembleable<byte[]> inBackground(BackgroundCallback callback, Executor executor)
+    {
+        backgrounding = new Backgrounding(callback, executor);
+        return this;
+    }
+
+    @Override
+    public Ensembleable<byte[]> inBackground(BackgroundCallback callback, Object context, Executor executor)
+    {
+        backgrounding = new Backgrounding(client, callback, context, executor);
+        return this;
+    }
+
+    @Override
+    public byte[] forEnsemble() throws Exception
+    {
+        if ( backgrounding.inBackground() )
+        {
+            client.processBackgroundOperation(new OperationAndData<Void>(this, null, backgrounding.getCallback(), null, backgrounding.getContext()), null);
+            return null;
+        }
+        else
+        {
+            return configInForeground();
+        }
+    }
+
+    @Override
+    public void performBackgroundOperation(final OperationAndData<Void> operationAndData) throws Exception
+    {
+        final TimeTrace trace = client.getZookeeperClient().startTracer("GetDataBuilderImpl-Background");
+        AsyncCallback.DataCallback callback = new AsyncCallback.DataCallback()
+        {
+            @Override
+            public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat)
+            {
+                trace.commit();
+                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.GET_CONFIG, rc, path, null, ctx, stat, data, null, null, null, null);
+                client.processBackgroundOperation(operationAndData, event);
+            }
+        };
+        if ( watching.isWatched() )
+        {
+            client.getZooKeeper().getConfig(true, callback, backgrounding.getContext());
+        }
+        else
+        {
+            client.getZooKeeper().getConfig(watching.getWatcher(client, ZooDefs.CONFIG_NODE), callback, backgrounding.getContext());
+        }
+    }
+
+    private byte[] configInForeground() throws Exception
+    {
+        TimeTrace trace = client.getZookeeperClient().startTracer("GetConfigBuilderImpl-Foreground");
+        try
+        {
+            return RetryLoop.callWithRetry
+            (
+                client.getZookeeperClient(),
+                new Callable<byte[]>()
+                {
+                    @Override
+                    public byte[] call() throws Exception
+                    {
+                        if ( watching.isWatched() )
+                        {
+                            return client.getZooKeeper().getConfig(true, stat);
+                        }
+                        return client.getZooKeeper().getConfig(watching.getWatcher(client, ZooDefs.CONFIG_NODE), stat);
+                    }
+                }
+            );
+        }
+        finally
+        {
+            trace.commit();
+        }
+    }
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/GetDataBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/GetDataBuilderImpl.java
index 7077839..31ad598 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/GetDataBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/GetDataBuilderImpl.java
@@ -250,7 +250,7 @@
                         rc = KeeperException.Code.DATAINCONSISTENCY.intValue();
                     }
                 }
-                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.GET_DATA, rc, path, null, ctx, stat, data, null, null, null);
+                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.GET_DATA, rc, path, null, ctx, stat, data, null, null, null, null);
                 client.processBackgroundOperation(operationAndData, event);
             }
         };
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/NamespaceFacade.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/NamespaceFacade.java
index c8b952a..60ef647 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/NamespaceFacade.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/NamespaceFacade.java
@@ -21,8 +21,9 @@
 import org.apache.curator.CuratorZookeeperClient;
 import org.apache.curator.RetryLoop;
 import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.framework.WatcherRemoveCuratorFramework;
-import org.apache.curator.framework.api.*;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.CuratorListener;
+import org.apache.curator.framework.api.UnhandledErrorListener;
 import org.apache.curator.framework.listen.Listenable;
 import org.apache.curator.framework.state.ConnectionStateListener;
 import org.apache.curator.utils.EnsurePath;
@@ -42,6 +43,12 @@
     }
 
     @Override
+    public void createContainers(String path) throws Exception
+    {
+        client.createContainers(path);
+    }
+
+    @Override
     public CuratorFramework nonNamespaceView()
     {
         return usingNamespace(null);
@@ -134,7 +141,13 @@
     @Override
     String fixForNamespace(String path)
     {
-        return namespace.fixForNamespace(path);
+        return namespace.fixForNamespace(path, false);
+    }
+
+    @Override
+    String fixForNamespace(String path, boolean isSequential)
+    {
+        return namespace.fixForNamespace(path, isSequential);
     }
 
     @Override
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/NamespaceImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/NamespaceImpl.java
index 3fc8e80..74f6320 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/NamespaceImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/NamespaceImpl.java
@@ -18,15 +18,21 @@
  */
 package org.apache.curator.framework.imps;
 
+import org.apache.curator.CuratorZookeeperClient;
+import org.apache.curator.RetryLoop;
+import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.utils.EnsurePath;
-import org.apache.curator.utils.ZKPaths;
 import org.apache.curator.utils.PathUtils;
+import org.apache.curator.utils.ZKPaths;
+import org.apache.zookeeper.ZooDefs;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 class NamespaceImpl
 {
     private final CuratorFrameworkImpl client;
     private final String namespace;
-    private final EnsurePath ensurePath;
+    private final AtomicBoolean ensurePathNeeded;
 
     NamespaceImpl(CuratorFrameworkImpl client, String namespace)
     {
@@ -44,7 +50,7 @@
 
         this.client = client;
         this.namespace = namespace;
-        ensurePath = (namespace != null) ? new EnsurePath(ZKPaths.makePath("/", namespace)) : null;
+        ensurePathNeeded = new AtomicBoolean(namespace != null);
     }
 
     String getNamespace()
@@ -65,13 +71,27 @@
         return path;
     }
 
-    String    fixForNamespace(String path)
+    String    fixForNamespace(String path, boolean isSequential)
     {
-        if ( ensurePath != null )
+        if ( ensurePathNeeded.get() )
         {
             try
             {
-                ensurePath.ensure(client.getZookeeperClient());
+                final CuratorZookeeperClient zookeeperClient = client.getZookeeperClient();
+                RetryLoop.callWithRetry
+                (
+                    zookeeperClient,
+                    new Callable<Object>()
+                    {
+                        @Override
+                        public Object call() throws Exception
+                        {
+                            ZKPaths.mkdirs(zookeeperClient.getZooKeeper(), ZKPaths.makePath("/", namespace), true, client.getAclProvider(), true);
+                            return null;
+                        }
+                    }
+                );
+                ensurePathNeeded.set(false);
             }
             catch ( Exception e )
             {
@@ -79,11 +99,11 @@
             }
         }
 
-        return ZKPaths.fixForNamespace(namespace, path);
+        return ZKPaths.fixForNamespace(namespace, path, isSequential);
     }
 
     EnsurePath newNamespaceAwareEnsurePath(String path)
     {
-        return new EnsurePath(fixForNamespace(path), client.getAclProvider());
+        return new EnsurePath(fixForNamespace(path, false), client.getAclProvider());
     }
 }
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/ReconfigBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/ReconfigBuilderImpl.java
new file mode 100644
index 0000000..0efa481
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/ReconfigBuilderImpl.java
@@ -0,0 +1,504 @@
+/**
+ * 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.curator.framework.imps;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.curator.RetryLoop;
+import org.apache.curator.TimeTrace;
+import org.apache.curator.framework.api.*;
+import org.apache.zookeeper.AsyncCallback;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.server.DataTree;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+
+public class ReconfigBuilderImpl implements
+    ReconfigBuilder,
+    ReconfigBuilderMain,
+    StatEnsembleable<byte[]>,
+    Configurable,
+    StatConfigEnsembleable,
+    BackgroundOperation<Void>
+{
+    private final CuratorFrameworkImpl client;
+
+    private Backgrounding backgrounding = new Backgrounding();
+    private Stat responseStat;
+    private long fromConfig = -1;
+    private List<String> adding;
+    private List<String> joining;
+    private List<String> leaving;
+
+    public ReconfigBuilderImpl(CuratorFrameworkImpl client)
+    {
+        this.client = client;
+    }
+
+    @Override
+    public byte[] forEnsemble() throws Exception
+    {
+        if ( backgrounding.inBackground() )
+        {
+            client.processBackgroundOperation(new OperationAndData<>(this, null, backgrounding.getCallback(), null, backgrounding.getContext()), null);
+            return new byte[0];
+        }
+        else
+        {
+            return ensembleInForeground();
+        }
+    }
+
+    @Override
+    public Ensembleable<byte[]> storingStatIn(Stat stat)
+    {
+        responseStat = stat;
+        return this;
+    }
+
+    @Override
+    public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+    {
+        fromConfig = config;
+        return this;
+    }
+
+    @Override
+    public JoinLeaveStatConfigEnsembleable adding(String... server)
+    {
+        return adding((server != null) ? Arrays.asList(server) : null);
+    }
+
+    @Override
+    public JoinLeaveStatConfigEnsembleable adding(List<String> servers)
+    {
+        this.adding = (servers != null) ? ImmutableList.copyOf(servers) : ImmutableList.<String>of();
+
+        return new JoinLeaveStatConfigEnsembleable()
+        {
+            @Override
+            public byte[] forEnsemble() throws Exception
+            {
+                return ReconfigBuilderImpl.this.forEnsemble();
+            }
+
+            @Override
+            public Ensembleable<byte[]> storingStatIn(Stat stat)
+            {
+                return ReconfigBuilderImpl.this.storingStatIn(stat);
+            }
+
+            @Override
+            public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+            {
+                return ReconfigBuilderImpl.this.fromConfig(config);
+            }
+
+            @Override
+            public LeaveStatConfigEnsembleable joining(String... server)
+            {
+                return joining((server != null) ? Arrays.asList(server) : null);
+            }
+
+            @Override
+            public LeaveStatConfigEnsembleable joining(List<String> servers)
+            {
+                return new LeaveStatConfigEnsembleable()
+                {
+                    @Override
+                    public byte[] forEnsemble() throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.forEnsemble();
+                    }
+
+                    @Override
+                    public Ensembleable<byte[]> storingStatIn(Stat stat)
+                    {
+                        return ReconfigBuilderImpl.this.storingStatIn(stat);
+                    }
+
+                    @Override
+                    public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.fromConfig(config);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable leaving(List<String> servers)
+                    {
+                        return ReconfigBuilderImpl.this.leaving(servers);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable leaving(String... server)
+                    {
+                        return ReconfigBuilderImpl.this.leaving(server);
+                    }
+                };
+            }
+
+            @Override
+            public JoinStatConfigEnsembleable leaving(String... server)
+            {
+                return leaving((server != null) ? Arrays.asList(server) : null);
+            }
+
+            @Override
+            public JoinStatConfigEnsembleable leaving(List<String> servers)
+            {
+                return new JoinStatConfigEnsembleable()
+                {
+                    @Override
+                    public byte[] forEnsemble() throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.forEnsemble();
+                    }
+
+                    @Override
+                    public Ensembleable<byte[]> storingStatIn(Stat stat)
+                    {
+                        return ReconfigBuilderImpl.this.storingStatIn(stat);
+                    }
+
+                    @Override
+                    public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.fromConfig(config);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable joining(List<String> servers)
+                    {
+                        return ReconfigBuilderImpl.this.joining(servers);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable joining(String... server)
+                    {
+                        return ReconfigBuilderImpl.this.joining(server);
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public ReconfigBuilderMain inBackground()
+    {
+        backgrounding = new Backgrounding(true);
+        return this;
+    }
+
+    @Override
+    public ReconfigBuilderMain inBackground(Object context)
+    {
+        backgrounding = new Backgrounding(context);
+        return this;
+    }
+
+    @Override
+    public ReconfigBuilderMain inBackground(BackgroundCallback callback)
+    {
+        backgrounding = new Backgrounding(callback);
+        return this;
+    }
+
+    @Override
+    public ReconfigBuilderMain inBackground(BackgroundCallback callback, Object context)
+    {
+        backgrounding = new Backgrounding(callback, context);
+        return this;
+    }
+
+    @Override
+    public ReconfigBuilderMain inBackground(BackgroundCallback callback, Executor executor)
+    {
+        backgrounding = new Backgrounding(callback, executor);
+        return this;
+    }
+
+    @Override
+    public ReconfigBuilderMain inBackground(BackgroundCallback callback, Object context, Executor executor)
+    {
+        backgrounding = new Backgrounding(client, callback, context, executor);
+        return this;
+    }
+
+    @Override
+    public LeaveAddStatConfigEnsembleable joining(String... server)
+    {
+        return joining((server != null) ? Arrays.asList(server) : null);
+    }
+
+    @Override
+    public LeaveAddStatConfigEnsembleable joining(List<String> servers)
+    {
+        joining = (servers != null) ? ImmutableList.copyOf(servers) : ImmutableList.<String>of();
+
+        return new LeaveAddStatConfigEnsembleable()
+        {
+            @Override
+            public byte[] forEnsemble() throws Exception
+            {
+                return ReconfigBuilderImpl.this.forEnsemble();
+            }
+
+            @Override
+            public Ensembleable<byte[]> storingStatIn(Stat stat)
+            {
+                return ReconfigBuilderImpl.this.storingStatIn(stat);
+            }
+
+            @Override
+            public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+            {
+                return ReconfigBuilderImpl.this.fromConfig(config);
+            }
+
+            @Override
+            public LeaveStatConfigEnsembleable adding(String... server)
+            {
+                return adding((server != null) ? Arrays.asList(server) : null);
+            }
+
+            @Override
+            public LeaveStatConfigEnsembleable adding(List<String> servers)
+            {
+                return new LeaveStatConfigEnsembleable()
+                {
+                    @Override
+                    public byte[] forEnsemble() throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.forEnsemble();
+                    }
+
+                    @Override
+                    public Ensembleable<byte[]> storingStatIn(Stat stat)
+                    {
+                        return ReconfigBuilderImpl.this.storingStatIn(stat);
+                    }
+
+                    @Override
+                    public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.fromConfig(config);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable leaving(List<String> servers)
+                    {
+                        return ReconfigBuilderImpl.this.leaving(servers);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable leaving(String... server)
+                    {
+                        return ReconfigBuilderImpl.this.leaving(server);
+                    }
+                };
+            }
+
+            @Override
+            public AddStatConfigEnsembleable leaving(String... server)
+            {
+                return leaving((server != null) ? Arrays.asList(server) : null);
+            }
+
+            @Override
+            public AddStatConfigEnsembleable leaving(List<String> servers)
+            {
+                return new AddStatConfigEnsembleable()
+                {
+                    @Override
+                    public byte[] forEnsemble() throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.forEnsemble();
+                    }
+
+                    @Override
+                    public Ensembleable<byte[]> storingStatIn(Stat stat)
+                    {
+                        return ReconfigBuilderImpl.this.storingStatIn(stat);
+                    }
+
+                    @Override
+                    public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.fromConfig(config);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable adding(List<String> servers)
+                    {
+                        return ReconfigBuilderImpl.this.adding(servers);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable adding(String... server)
+                    {
+                        return ReconfigBuilderImpl.this.adding(server);
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public JoinAddStatConfigEnsembleable leaving(String... server)
+    {
+        return leaving((server != null) ? Arrays.asList(server) : null);
+    }
+
+    @Override
+    public JoinAddStatConfigEnsembleable leaving(List<String> servers)
+    {
+        leaving = (servers != null) ? ImmutableList.copyOf(servers) : ImmutableList.<String>of();
+
+        return new JoinAddStatConfigEnsembleable()
+        {
+            @Override
+            public byte[] forEnsemble() throws Exception
+            {
+                return ReconfigBuilderImpl.this.forEnsemble();
+            }
+
+            @Override
+            public Ensembleable<byte[]> storingStatIn(Stat stat)
+            {
+                return ReconfigBuilderImpl.this.storingStatIn(stat);
+            }
+
+            @Override
+            public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+            {
+                return ReconfigBuilderImpl.this.fromConfig(config);
+            }
+
+            @Override
+            public JoinStatConfigurable adding(String... server)
+            {
+                return adding((server != null) ? Arrays.asList(server) : null);
+            }
+
+            @Override
+            public JoinStatConfigurable adding(List<String> servers)
+            {
+                return new JoinStatConfigurable()
+                {
+                    @Override
+                    public Configurable joining(List<String> servers)
+                    {
+                        return ReconfigBuilderImpl.this.joining(servers);
+                    }
+
+                    @Override
+                    public Configurable joining(String... server)
+                    {
+                        return ReconfigBuilderImpl.this.joining(server);
+                    }
+                };
+            }
+
+            @Override
+            public AddStatConfigEnsembleable joining(String... server)
+            {
+                return joining((server != null) ? Arrays.asList(server) : null);
+            }
+
+            @Override
+            public AddStatConfigEnsembleable joining(List<String> servers)
+            {
+                return new AddStatConfigEnsembleable()
+                {
+                    @Override
+                    public byte[] forEnsemble() throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.forEnsemble();
+                    }
+
+                    @Override
+                    public Ensembleable<byte[]> storingStatIn(Stat stat)
+                    {
+                        return ReconfigBuilderImpl.this.storingStatIn(stat);
+                    }
+
+                    @Override
+                    public StatEnsembleable<byte[]> fromConfig(long config) throws Exception
+                    {
+                        return ReconfigBuilderImpl.this.fromConfig(config);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable adding(List<String> servers)
+                    {
+                        return ReconfigBuilderImpl.this.adding(servers);
+                    }
+
+                    @Override
+                    public StatConfigEnsembleable adding(String... server)
+                    {
+                        return ReconfigBuilderImpl.this.adding(server);
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public void performBackgroundOperation(final OperationAndData<Void> data) throws Exception
+    {
+        final TimeTrace trace = client.getZookeeperClient().startTracer("ReconfigBuilderImpl-Background");
+        AsyncCallback.DataCallback callback = new AsyncCallback.DataCallback()
+        {
+            @Override
+            public void processResult(int rc, String path, Object ctx, byte[] bytes, Stat stat)
+            {
+                trace.commit();
+                if ( (responseStat != null) && (stat != null) )
+                {
+                    DataTree.copyStat(stat, responseStat);
+                }
+                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.RECONFIG, rc, path, null, ctx, stat, bytes, null, null, null, null);
+                client.processBackgroundOperation(data, event);
+            }
+        };
+        client.getZooKeeper().reconfig(joining, leaving, adding, fromConfig, callback, backgrounding.getContext());
+    }
+
+    private byte[] ensembleInForeground() throws Exception
+    {
+        TimeTrace trace = client.getZookeeperClient().startTracer("ReconfigBuilderImpl-Foreground");
+        byte[] responseData = RetryLoop.callWithRetry
+            (
+                client.getZookeeperClient(),
+                new Callable<byte[]>()
+                {
+                    @Override
+                    public byte[] call() throws Exception
+                    {
+                        return client.getZooKeeper().reconfig(joining, leaving, adding, fromConfig, responseStat);
+                    }
+                }
+            );
+        trace.commit();
+        return responseData;
+    }
+}
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/SetACLBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/SetACLBuilderImpl.java
index f7b2480..17e88f8 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/SetACLBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/SetACLBuilderImpl.java
@@ -138,7 +138,7 @@
                 public void processResult(int rc, String path, Object ctx, Stat stat)
                 {
                     trace.commit();
-                    CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SET_ACL, rc, path, null, ctx, stat, null, null, null, null);
+                    CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SET_ACL, rc, path, null, ctx, stat, null, null, null, null, null);
                     client.processBackgroundOperation(operationAndData, event);
                 }
             },
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/SetDataBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/SetDataBuilderImpl.java
index 8e93cbf..3ea704c 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/SetDataBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/SetDataBuilderImpl.java
@@ -20,7 +20,6 @@
 
 import org.apache.curator.RetryLoop;
 import org.apache.curator.TimeTrace;
-import org.apache.curator.framework.api.ACLPathAndBytesable;
 import org.apache.curator.framework.api.BackgroundCallback;
 import org.apache.curator.framework.api.BackgroundPathAndBytesable;
 import org.apache.curator.framework.api.CuratorEvent;
@@ -28,13 +27,11 @@
 import org.apache.curator.framework.api.PathAndBytesable;
 import org.apache.curator.framework.api.SetDataBackgroundVersionable;
 import org.apache.curator.framework.api.SetDataBuilder;
-import org.apache.curator.framework.api.transaction.CuratorTransactionBridge;
 import org.apache.curator.framework.api.transaction.OperationType;
 import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder;
 import org.apache.zookeeper.AsyncCallback;
 import org.apache.zookeeper.Op;
 import org.apache.zookeeper.data.Stat;
-
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executor;
 
@@ -53,12 +50,12 @@
         compress = false;
     }
 
-    TransactionSetDataBuilder   asTransactionSetDataBuilder(final CuratorTransactionImpl curatorTransaction, final CuratorMultiTransactionRecord transaction)
+    <T> TransactionSetDataBuilder<T> asTransactionSetDataBuilder(final T context, final CuratorMultiTransactionRecord transaction)
     {
-        return new TransactionSetDataBuilder()
+        return new TransactionSetDataBuilder<T>()
         {
             @Override
-            public CuratorTransactionBridge forPath(String path, byte[] data) throws Exception
+            public T forPath(String path, byte[] data) throws Exception
             {
                 if ( compress )
                 {
@@ -67,26 +64,26 @@
                 
                 String      fixedPath = client.fixForNamespace(path);
                 transaction.add(Op.setData(fixedPath, data, version), OperationType.SET_DATA, path);
-                return curatorTransaction;
+                return context;
             }
 
             @Override
-            public CuratorTransactionBridge forPath(String path) throws Exception
+            public T forPath(String path) throws Exception
             {
                 return forPath(path, client.getDefaultData());
             }
 
             @Override
-            public PathAndBytesable<CuratorTransactionBridge> withVersion(int version)
+            public PathAndBytesable<T> withVersion(int version)
             {
                 SetDataBuilderImpl.this.withVersion(version);
                 return this;
             }
 
             @Override
-            public PathAndBytesable<CuratorTransactionBridge> compressed() {
+            public PathAndBytesable<T> compressed()
+            {
                 compress = true;
-                
                 return this;
             }
         };
@@ -219,7 +216,7 @@
                 public void processResult(int rc, String path, Object ctx, Stat stat)
                 {
                     trace.commit();
-                    CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SET_DATA, rc, path, null, ctx, stat, null, null, null, null);
+                    CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SET_DATA, rc, path, null, ctx, stat, null, null, null, null, null);
                     client.processBackgroundOperation(operationAndData, event);
                 }
             },
@@ -246,7 +243,7 @@
         Stat        resultStat = null;
         if ( backgrounding.inBackground()  )
         {
-            client.processBackgroundOperation(new OperationAndData<PathAndBytes>(this, new PathAndBytes(path, data), backgrounding.getCallback(), null, backgrounding.getContext()), null);
+            client.processBackgroundOperation(new OperationAndData<>(this, new PathAndBytes(path, data), backgrounding.getCallback(), null, backgrounding.getContext()), null);
         }
         else
         {
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/SyncBuilderImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/SyncBuilderImpl.java
index 2d3e9c0..cab31ae 100644
--- a/curator-framework/src/main/java/org/apache/curator/framework/imps/SyncBuilderImpl.java
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/SyncBuilderImpl.java
@@ -93,7 +93,7 @@
             public void processResult(int rc, String path, Object ctx)
             {
                 trace.commit();
-                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SYNC, rc, path, path, ctx, null, null, null, null, null);
+                CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SYNC, rc, path, path, ctx, null, null, null, null, null, null);
                 client.processBackgroundOperation(operationAndData, event);
             }
         };
diff --git a/curator-framework/src/main/java/org/apache/curator/framework/imps/TransactionOpImpl.java b/curator-framework/src/main/java/org/apache/curator/framework/imps/TransactionOpImpl.java
new file mode 100644
index 0000000..381842b
--- /dev/null
+++ b/curator-framework/src/main/java/org/apache/curator/framework/imps/TransactionOpImpl.java
@@ -0,0 +1,65 @@
+/**
+ * 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.curator.framework.imps;
+
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.framework.api.transaction.TransactionCheckBuilder;
+import org.apache.curator.framework.api.transaction.TransactionCreateBuilder;
+import org.apache.curator.framework.api.transaction.TransactionDeleteBuilder;
+import org.apache.curator.framework.api.transaction.TransactionOp;
+import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder;
+
+public class TransactionOpImpl implements TransactionOp
+{
+    private final CuratorFrameworkImpl client;
+
+    public TransactionOpImpl(CuratorFrameworkImpl client)
+    {
+        this.client = client;
+    }
+
+    @Override
+    public TransactionCreateBuilder<CuratorOp> create()
+    {
+        ExtractingCuratorOp op = new ExtractingCuratorOp();
+        return new CreateBuilderImpl(client).<CuratorOp>asTransactionCreateBuilder(op, op.getRecord());
+    }
+
+    @Override
+    public TransactionDeleteBuilder<CuratorOp> delete()
+    {
+        ExtractingCuratorOp op = new ExtractingCuratorOp();
+        return new DeleteBuilderImpl(client).<CuratorOp>asTransactionDeleteBuilder(op, op.getRecord());
+    }
+
+    @Override
+    public TransactionSetDataBuilder<CuratorOp> setData()
+    {
+        ExtractingCuratorOp op = new ExtractingCuratorOp();
+        return new SetDataBuilderImpl(client).<CuratorOp>asTransactionSetDataBuilder(op, op.getRecord());
+    }
+
+    @Override
+    public TransactionCheckBuilder<CuratorOp> check()
+    {
+        ExtractingCuratorOp op = new ExtractingCuratorOp();
+        return CuratorTransactionImpl.<CuratorOp>makeTransactionCheckBuilder(client, op, op.getRecord());
+    }
+
+}
diff --git a/curator-framework/src/site/confluence/index.confluence b/curator-framework/src/site/confluence/index.confluence
index 84f794f..b79ece4 100644
--- a/curator-framework/src/site/confluence/index.confluence
+++ b/curator-framework/src/site/confluence/index.confluence
@@ -42,7 +42,12 @@
 |getData()|Begins an operation to get a ZNode's data. Call additional methods (watch, background or get stat) and finalize the operation by calling forPath()|
 |setData()|Begins an operation to set a ZNode's data. Call additional methods (version or background) and finalize the operation by calling forPath()|
 |getChildren()|Begins an operation to get a ZNode's list of children ZNodes. Call additional methods (watch, background or get stat) and finalize the operation by calling forPath()|
-|inTransaction()|Begins an atomic ZooKeeper transaction. Combine create, setData, check, and/or delete operations and then commit() as a unit.|
+|transactionOp()|Used to allocate operations to be used with transaction().|
+|transaction()|Atomically submit a set of operations as a transaction.|
+|getACL()|Begins an operation to return a ZNode's ACL settings. Call additional methods and finalize the operation by calling forPath()|
+|setACL()|Begins an operation to set a ZNode's ACL settings. Call additional methods and finalize the operation by calling forPath()|
+|getConfig()|Begins an operation to return the last committed configuration. Call additional methods and finalize the operation by calling forEnsemble()|
+|reconfig()|Begins an operation to change the configuration. Call additional methods and finalize the operation by calling forEnsemble()|
 
 h3. Notifications
 Notifications for background operations and watches are published via the ClientListener interface. You register listeners with the
@@ -51,18 +56,24 @@
 |eventReceived()|A background operation has completed or a watch has triggered. Examine the given event for details|
 <td>clientClosedDueToError()|An unrecoverable error has occurred. The CuratorFramework instance has been shut down|
 
-h3. ClientEvent
-The ClientEvent object is a super\-set POJO that can hold every type of background notification and triggered watch. The useful fields of
-ClientEvent depend on the type of event which is exposed via the getType() method.
+h3. CuratorEvent
+The CuratorEvent object is a super\-set POJO that can hold every type of background notification and triggered watch. The useful fields of
+CuratorEvent depend on the type of event which is exposed via the getType() method.
 
 ||Event Type||Event Methods||
 |CREATE|getResultCode() and getPath()|
 |DELETE|getResultCode() and getPath()|
 |EXISTS|getResultCode(), getPath() and getStat()|
-|GET_DATA|getResultCode(), getPath(), getStat() and getData()|
-|SET_DATA|getResultCode(), getPath() and getStat()|
+|GET\_DATA|getResultCode(), getPath(), getStat() and getData()|
+|SET\_DATA|getResultCode(), getPath() and getStat()|
 |CHILDREN|getResultCode(), getPath(), getStat(), getChildren()|
+|SYNC|getResultCode(), getStat()|
+|GET\_ACL|getResultCode(), getACLList()|
+|SET\_ACL|getResultCode()|
+|TRANSACTION|getResultCode(), getOpResults()|
 |WATCHED|getWatchedEvent()|
+|GET\_CONFIG|getResultCode(), getData()|
+|RECONFIG|getResultCode(), getData()|
 
 h2. Namespaces
 Because a ZooKeeper cluster is a shared environment, it's vital that a namespace convention is
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransaction.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransactionNew.java
similarity index 78%
copy from curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransaction.java
copy to curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransactionNew.java
index c18af99..d302119 100644
--- a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransaction.java
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransactionNew.java
@@ -18,18 +18,16 @@
  */
 package org.apache.curator.framework.imps;
 
-import org.apache.curator.test.BaseClassForTests;
-import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
-import org.apache.curator.framework.api.CompressionProvider;
+import org.apache.curator.framework.api.transaction.CuratorOp;
 import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.utils.CloseableUtils;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class TestCompressionInTransaction extends BaseClassForTests
+public class TestCompressionInTransactionNew extends BaseClassForTests
 {
     @Test
     public void testSetData() throws Exception
@@ -43,11 +41,13 @@
             client.start();
 
             //Create uncompressed data in a transaction
-            client.inTransaction().create().forPath(path, data).and().commit();
+            CuratorOp op = client.transactionOp().create().forPath(path, data);
+            client.transaction().forOperations(op);
             Assert.assertEquals(data, client.getData().forPath(path));
 
             //Create compressed data in transaction
-            client.inTransaction().setData().compressed().forPath(path, data).and().commit();
+            op = client.transactionOp().setData().compressed().forPath(path, data);
+            client.transaction().forOperations(op);
             Assert.assertEquals(data, client.getData().decompressed().forPath(path));
         }
         finally
@@ -71,16 +71,18 @@
             client.start();
 
             //Create the nodes
-            client.inTransaction().create().compressed().forPath(path1).and().
-            create().forPath(path2).and().commit();
+            CuratorOp op1 = client.transactionOp().create().compressed().forPath(path1);
+            CuratorOp op2 = client.transactionOp().create().forPath(path2);
+            client.transaction().forOperations(op1, op2);
 
             //Check they exist
             Assert.assertNotNull(client.checkExists().forPath(path1));
             Assert.assertNotNull(client.checkExists().forPath(path2));
             
             //Set the nodes, path1 compressed, path2 uncompressed.
-            client.inTransaction().setData().compressed().forPath(path1, data1).and().
-            setData().forPath(path2, data2).and().commit();
+            op1 = client.transactionOp().setData().compressed().forPath(path1, data1);
+            op2 = client.transactionOp().setData().forPath(path2, data2);
+            client.transaction().forOperations(op1, op2);
             
             Assert.assertNotEquals(data1, client.getData().forPath(path1));
             Assert.assertEquals(data1, client.getData().decompressed().forPath(path1));
@@ -107,8 +109,10 @@
         {
             client.start();
 
-            client.inTransaction().create().compressed().forPath(path1, data1).and().
-            create().compressed().forPath(path2, data2).and().commit();
+            CuratorOp op1 = client.transactionOp().create().compressed().forPath(path1, data1);
+            CuratorOp op2 = client.transactionOp().create().compressed().forPath(path2, data2);
+
+            client.transaction().forOperations(op1, op2);
 
             Assert.assertNotEquals(data1, client.getData().forPath(path1));
             Assert.assertEquals(data1, client.getData().decompressed().forPath(path1));
@@ -141,8 +145,9 @@
         {
             client.start();
 
-            client.inTransaction().create().compressed().forPath(path1, data1).and().
-            create().forPath(path2, data2).and().commit();
+            CuratorOp op1 = client.transactionOp().create().compressed().forPath(path1, data1);
+            CuratorOp op2 = client.transactionOp().create().forPath(path2, data2);
+            client.transaction().forOperations(op1, op2);
 
             Assert.assertNotEquals(data1, client.getData().forPath(path1));
             Assert.assertEquals(data1, client.getData().decompressed().forPath(path1));
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransaction.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransactionOld.java
similarity index 97%
rename from curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransaction.java
rename to curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransactionOld.java
index c18af99..ebf591b 100644
--- a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransaction.java
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCompressionInTransactionOld.java
@@ -29,7 +29,8 @@
 
 import java.util.concurrent.atomic.AtomicInteger;
 
-public class TestCompressionInTransaction extends BaseClassForTests
+@SuppressWarnings("deprecation")
+public class TestCompressionInTransactionOld extends BaseClassForTests
 {
     @Test
     public void testSetData() throws Exception
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFramework.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFramework.java
index 5a640db..811631c 100644
--- a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFramework.java
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFramework.java
@@ -19,6 +19,7 @@
 package org.apache.curator.framework.imps;
 
 import com.google.common.collect.Lists;
+import org.apache.curator.framework.AuthInfo;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
 import org.apache.curator.framework.api.BackgroundCallback;
@@ -32,6 +33,7 @@
 import org.apache.curator.test.Timing;
 import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.utils.EnsurePath;
+import org.apache.curator.utils.ZKPaths;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.WatchedEvent;
@@ -40,7 +42,10 @@
 import org.apache.zookeeper.data.ACL;
 import org.apache.zookeeper.data.Stat;
 import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
@@ -50,15 +55,31 @@
 @SuppressWarnings("deprecation")
 public class TestFramework extends BaseClassForTests
 {
-    @Test
-    public void     testConnectionState() throws Exception
+    @BeforeMethod
+    @Override
+    public void setup() throws Exception
     {
-        Timing                  timing = new Timing();
-        CuratorFramework        client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+        System.setProperty("container.checkIntervalMs", "1000");
+        super.setup();
+    }
+
+    @AfterMethod
+    @Override
+    public void teardown() throws Exception
+    {
+        System.clearProperty("container.checkIntervalMs");
+        super.teardown();
+    }
+
+    @Test
+    public void testConnectionState() throws Exception
+    {
+        Timing timing = new Timing();
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
         try
         {
-            final BlockingQueue<ConnectionState>        queue = new LinkedBlockingQueue<ConnectionState>();
-            ConnectionStateListener                     listener = new ConnectionStateListener()
+            final BlockingQueue<ConnectionState> queue = new LinkedBlockingQueue<ConnectionState>();
+            ConnectionStateListener listener = new ConnectionStateListener()
             {
                 @Override
                 public void stateChanged(CuratorFramework client, ConnectionState newState)
@@ -81,15 +102,15 @@
     }
 
     @Test
-    public void     testNamespaceWithWatcher() throws Exception
+    public void testNamespaceWithWatcher() throws Exception
     {
         CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
         CuratorFramework client = builder.connectString(server.getConnectString()).namespace("aisa").retryPolicy(new RetryOneTime(1)).build();
         client.start();
         try
         {
-            final BlockingQueue<String>     queue = new LinkedBlockingQueue<String>();
-            Watcher                         watcher = new Watcher()
+            final BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
+            Watcher watcher = new Watcher()
             {
                 @Override
                 public void process(WatchedEvent event)
@@ -108,25 +129,25 @@
             client.getChildren().usingWatcher(watcher).forPath("/base");
             client.create().forPath("/base/child");
 
-            String      path = queue.take();
+            String path = queue.take();
             Assert.assertEquals(path, "/base");
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testNamespaceInBackground() throws Exception
+    public void testNamespaceInBackground() throws Exception
     {
         CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
         CuratorFramework client = builder.connectString(server.getConnectString()).namespace("aisa").retryPolicy(new RetryOneTime(1)).build();
         client.start();
         try
         {
-            final BlockingQueue<String>     queue = new LinkedBlockingQueue<String>();
-            CuratorListener                 listener = new CuratorListener()
+            final BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
+            CuratorListener listener = new CuratorListener()
             {
                 @Override
                 public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
@@ -142,12 +163,12 @@
             client.create().forPath("/base");
             client.checkExists().inBackground().forPath("/base");
 
-            String      path = queue.poll(10, TimeUnit.SECONDS);
+            String path = queue.poll(10, TimeUnit.SECONDS);
             Assert.assertEquals(path, "/base");
 
             client.getCuratorListenable().removeListener(listener);
 
-            BackgroundCallback      callback = new BackgroundCallback()
+            BackgroundCallback callback = new BackgroundCallback()
             {
                 @Override
                 public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
@@ -161,17 +182,17 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testCreateACL() throws Exception
+    public void testCreateACLSingleAuth() throws Exception
     {
         CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
         CuratorFramework client = builder
             .connectString(server.getConnectString())
-            .authorization("digest", "me:pass".getBytes())
+            .authorization("digest", "me1:pass1".getBytes())
             .retryPolicy(new RetryOneTime(1))
             .build();
         client.start();
@@ -182,9 +203,10 @@
             client.create().withACL(aclList).forPath("/test", "test".getBytes());
             client.close();
 
+            // Try setting data with me1:pass1
             client = builder
                 .connectString(server.getConnectString())
-                .authorization("digest", "me:pass".getBytes())
+                .authorization("digest", "me1:pass1".getBytes())
                 .retryPolicy(new RetryOneTime(1))
                 .build();
             client.start();
@@ -198,6 +220,7 @@
             }
             client.close();
 
+            // Try setting data with something:else
             client = builder
                 .connectString(server.getConnectString())
                 .authorization("digest", "something:else".getBytes())
@@ -216,12 +239,105 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testCreateACLWithReset() throws Exception
+    public void testACLDeprecatedApis() throws Exception
+    {
+        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
+            .connectString(server.getConnectString())
+            .retryPolicy(new RetryOneTime(1));
+        Assert.assertNull(builder.getAuthScheme());
+        Assert.assertNull(builder.getAuthValue());
+
+        builder = builder.authorization("digest", "me1:pass1".getBytes());
+        Assert.assertEquals(builder.getAuthScheme(), "digest");
+        Assert.assertEquals(builder.getAuthValue(), "me1:pass1".getBytes());
+    }
+
+    @Test
+    public void testCreateACLMultipleAuths() throws Exception
+    {
+        // Add a few authInfos
+        List<AuthInfo> authInfos = new ArrayList<AuthInfo>();
+        authInfos.add(new AuthInfo("digest", "me1:pass1".getBytes()));
+        authInfos.add(new AuthInfo("digest", "me2:pass2".getBytes()));
+
+        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
+        CuratorFramework client = builder
+            .connectString(server.getConnectString())
+            .authorization(authInfos)
+            .retryPolicy(new RetryOneTime(1))
+            .build();
+        client.start();
+        try
+        {
+            ACL acl = new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.AUTH_IDS);
+            List<ACL> aclList = Lists.newArrayList(acl);
+            client.create().withACL(aclList).forPath("/test", "test".getBytes());
+            client.close();
+
+            // Try setting data with me1:pass1
+            client = builder
+                .connectString(server.getConnectString())
+                .authorization("digest", "me1:pass1".getBytes())
+                .retryPolicy(new RetryOneTime(1))
+                .build();
+            client.start();
+            try
+            {
+                client.setData().forPath("/test", "test".getBytes());
+            }
+            catch ( KeeperException.NoAuthException e )
+            {
+                Assert.fail("Auth failed");
+            }
+            client.close();
+
+            // Try setting data with me1:pass1
+            client = builder
+                .connectString(server.getConnectString())
+                .authorization("digest", "me2:pass2".getBytes())
+                .retryPolicy(new RetryOneTime(1))
+                .build();
+            client.start();
+            try
+            {
+                client.setData().forPath("/test", "test".getBytes());
+            }
+            catch ( KeeperException.NoAuthException e )
+            {
+                Assert.fail("Auth failed");
+            }
+            client.close();
+
+            // Try setting data with something:else
+            client = builder
+                .connectString(server.getConnectString())
+                .authorization("digest", "something:else".getBytes())
+                .retryPolicy(new RetryOneTime(1))
+                .build();
+            client.start();
+            try
+            {
+                client.setData().forPath("/test", "test".getBytes());
+                Assert.fail("Should have failed with auth exception");
+            }
+            catch ( KeeperException.NoAuthException e )
+            {
+                // expected
+            }
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testCreateACLWithReset() throws Exception
     {
         Timing timing = new Timing();
         CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
@@ -282,15 +398,15 @@
     }
 
     @Test
-    public void     testCreateParents() throws Exception
+    public void testCreateParents() throws Exception
     {
-        CuratorFrameworkFactory.Builder      builder = CuratorFrameworkFactory.builder();
+        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
         CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
         client.start();
         try
         {
             client.create().creatingParentsIfNeeded().forPath("/one/two/three", "foo".getBytes());
-            byte[]      data = client.getData().forPath("/one/two/three");
+            byte[] data = client.getData().forPath("/one/two/three");
             Assert.assertEquals(data, "foo".getBytes());
 
             client.create().creatingParentsIfNeeded().forPath("/one/two/another", "bar".getBytes());
@@ -299,16 +415,166 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testEnsurePathWithNamespace() throws Exception
+    public void testOverrideCreateParentContainers() throws Exception
+    {
+        if ( !checkForContainers() )
+        {
+            return;
+        }
+
+        CuratorFramework client = CuratorFrameworkFactory.builder()
+            .connectString(server.getConnectString())
+            .retryPolicy(new RetryOneTime(1))
+            .dontUseContainerParents()
+            .build();
+        try
+        {
+            client.start();
+            client.create().creatingParentContainersIfNeeded().forPath("/one/two/three", "foo".getBytes());
+            byte[] data = client.getData().forPath("/one/two/three");
+            Assert.assertEquals(data, "foo".getBytes());
+
+            client.delete().forPath("/one/two/three");
+            new Timing().sleepABit();
+
+            Assert.assertNotNull(client.checkExists().forPath("/one/two"));
+            new Timing().sleepABit();
+            Assert.assertNotNull(client.checkExists().forPath("/one"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testCreateParentContainers() throws Exception
+    {
+        if ( !checkForContainers() )
+        {
+            return;
+        }
+
+        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
+        CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
+        try
+        {
+            client.start();
+            client.create().creatingParentContainersIfNeeded().forPath("/one/two/three", "foo".getBytes());
+            byte[] data = client.getData().forPath("/one/two/three");
+            Assert.assertEquals(data, "foo".getBytes());
+
+            client.delete().forPath("/one/two/three");
+            new Timing().sleepABit();
+
+            Assert.assertNull(client.checkExists().forPath("/one/two"));
+            new Timing().sleepABit();
+            Assert.assertNull(client.checkExists().forPath("/one"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    private boolean checkForContainers()
+    {
+        if ( ZKPaths.getContainerCreateMode() == CreateMode.PERSISTENT )
+        {
+            System.out.println("Not using CreateMode.CONTAINER enabled version of ZooKeeper");
+            return false;
+        }
+        return true;
+    }
+
+    @Test
+    public void testCreatingParentsTheSame() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+        try
+        {
+            client.start();
+
+            Assert.assertNull(client.checkExists().forPath("/one/two"));
+            client.create().creatingParentContainersIfNeeded().forPath("/one/two/three");
+            Assert.assertNotNull(client.checkExists().forPath("/one/two"));
+
+            client.delete().deletingChildrenIfNeeded().forPath("/one");
+            Assert.assertNull(client.checkExists().forPath("/one"));
+
+            Assert.assertNull(client.checkExists().forPath("/one/two"));
+            client.checkExists().creatingParentContainersIfNeeded().forPath("/one/two/three");
+            Assert.assertNotNull(client.checkExists().forPath("/one/two"));
+            Assert.assertNull(client.checkExists().forPath("/one/two/three"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testExistsCreatingParents() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+        try
+        {
+            client.start();
+
+            Assert.assertNull(client.checkExists().forPath("/one/two"));
+            client.checkExists().creatingParentContainersIfNeeded().forPath("/one/two/three");
+            Assert.assertNotNull(client.checkExists().forPath("/one/two"));
+            Assert.assertNull(client.checkExists().forPath("/one/two/three"));
+            Assert.assertNull(client.checkExists().creatingParentContainersIfNeeded().forPath("/one/two/three"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testExistsCreatingParentsInBackground() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+        try
+        {
+            client.start();
+
+            Assert.assertNull(client.checkExists().forPath("/one/two"));
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            BackgroundCallback callback = new BackgroundCallback()
+            {
+                @Override
+                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+                {
+                    latch.countDown();
+                }
+            };
+            client.checkExists().creatingParentContainersIfNeeded().inBackground(callback).forPath("/one/two/three");
+            Assert.assertTrue(new Timing().awaitLatch(latch));
+            Assert.assertNotNull(client.checkExists().forPath("/one/two"));
+            Assert.assertNull(client.checkExists().forPath("/one/two/three"));
+            Assert.assertNull(client.checkExists().creatingParentContainersIfNeeded().forPath("/one/two/three"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testEnsurePathWithNamespace() throws Exception
     {
         final String namespace = "jz";
 
-        CuratorFrameworkFactory.Builder      builder = CuratorFrameworkFactory.builder();
+        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
         CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace(namespace).build();
         client.start();
         try
@@ -323,21 +589,21 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testNamespace() throws Exception
+    public void testNamespace() throws Exception
     {
         final String namespace = "TestNamespace";
-        
-        CuratorFrameworkFactory.Builder      builder = CuratorFrameworkFactory.builder();
+
+        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
         CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace(namespace).build();
         client.start();
         try
         {
-            String      actualPath = client.create().forPath("/test");
+            String actualPath = client.create().forPath("/test");
             Assert.assertEquals(actualPath, "/test");
             Assert.assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/" + namespace + "/test", false));
             Assert.assertNull(client.getZookeeperClient().getZooKeeper().exists("/test", false));
@@ -347,7 +613,7 @@
             Assert.assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/non", false));
 
             client.create().forPath("/test/child", "hey".getBytes());
-            byte[]      bytes = client.getData().forPath("/test/child");
+            byte[] bytes = client.getData().forPath("/test/child");
             Assert.assertEquals(bytes, "hey".getBytes());
 
             bytes = client.usingNamespace(null).getData().forPath("/" + namespace + "/test/child");
@@ -355,19 +621,19 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testCustomCallback() throws Exception
+    public void testCustomCallback() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
         try
         {
-            final CountDownLatch    latch = new CountDownLatch(1);
-            BackgroundCallback      callback = new BackgroundCallback()
+            final CountDownLatch latch = new CountDownLatch(1);
+            BackgroundCallback callback = new BackgroundCallback()
             {
                 @Override
                 public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
@@ -386,48 +652,48 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testSync() throws Exception
+    public void testSync() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
         try
         {
             client.getCuratorListenable().addListener
-            (
-                new CuratorListener()
-                {
-                    @Override
-                    public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
+                (
+                    new CuratorListener()
                     {
-                        if ( event.getType() == CuratorEventType.SYNC )
+                        @Override
+                        public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
                         {
-                            Assert.assertEquals(event.getPath(), "/head");
-                            ((CountDownLatch)event.getContext()).countDown();
+                            if ( event.getType() == CuratorEventType.SYNC )
+                            {
+                                Assert.assertEquals(event.getPath(), "/head");
+                                ((CountDownLatch)event.getContext()).countDown();
+                            }
                         }
                     }
-                }
-            );
+                );
 
             client.create().forPath("/head");
             Assert.assertNotNull(client.checkExists().forPath("/head"));
 
-            CountDownLatch      latch = new CountDownLatch(1);
+            CountDownLatch latch = new CountDownLatch(1);
             client.sync("/head", latch);
             Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testSyncNew() throws Exception
+    public void testSyncNew() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
@@ -436,7 +702,7 @@
             client.create().forPath("/head");
             Assert.assertNotNull(client.checkExists().forPath("/head"));
 
-            final CountDownLatch      latch = new CountDownLatch(1);
+            final CountDownLatch latch = new CountDownLatch(1);
             BackgroundCallback callback = new BackgroundCallback()
             {
                 @Override
@@ -453,86 +719,86 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testBackgroundDelete() throws Exception
+    public void testBackgroundDelete() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
         try
         {
             client.getCuratorListenable().addListener
-            (
-                new CuratorListener()
-                {
-                    @Override
-                    public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
+                (
+                    new CuratorListener()
                     {
-                        if ( event.getType() == CuratorEventType.DELETE )
+                        @Override
+                        public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
                         {
-                            Assert.assertEquals(event.getPath(), "/head");
-                            ((CountDownLatch)event.getContext()).countDown();
+                            if ( event.getType() == CuratorEventType.DELETE )
+                            {
+                                Assert.assertEquals(event.getPath(), "/head");
+                                ((CountDownLatch)event.getContext()).countDown();
+                            }
                         }
                     }
-                }
-            );
+                );
 
             client.create().forPath("/head");
             Assert.assertNotNull(client.checkExists().forPath("/head"));
 
-            CountDownLatch      latch = new CountDownLatch(1);
+            CountDownLatch latch = new CountDownLatch(1);
             client.delete().inBackground(latch).forPath("/head");
             Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
             Assert.assertNull(client.checkExists().forPath("/head"));
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testBackgroundDeleteWithChildren() throws Exception
+    public void testBackgroundDeleteWithChildren() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
         try
         {
             client.getCuratorListenable().addListener
-                    (
-                            new CuratorListener()
+                (
+                    new CuratorListener()
+                    {
+                        @Override
+                        public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
+                        {
+                            if ( event.getType() == CuratorEventType.DELETE )
                             {
-                                @Override
-                                public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
-                                {
-                                    if ( event.getType() == CuratorEventType.DELETE )
-                                    {
-                                        Assert.assertEquals(event.getPath(), "/one/two");
-                                        ((CountDownLatch)event.getContext()).countDown();
-                                    }
-                                }
+                                Assert.assertEquals(event.getPath(), "/one/two");
+                                ((CountDownLatch)event.getContext()).countDown();
                             }
-                    );
+                        }
+                    }
+                );
 
             client.create().creatingParentsIfNeeded().forPath("/one/two/three/four");
             Assert.assertNotNull(client.checkExists().forPath("/one/two/three/four"));
 
-            CountDownLatch      latch = new CountDownLatch(1);
+            CountDownLatch latch = new CountDownLatch(1);
             client.delete().deletingChildrenIfNeeded().inBackground(latch).forPath("/one/two");
             Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
             Assert.assertNull(client.checkExists().forPath("/one/two"));
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testDelete() throws Exception
+    public void testDelete() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
@@ -545,14 +811,14 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
     public void testDeleteWithChildren() throws Exception
     {
-        CuratorFrameworkFactory.Builder      builder = CuratorFrameworkFactory.builder();
+        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
         CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
         client.start();
         try
@@ -565,14 +831,14 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
     public void testDeleteGuaranteedWithChildren() throws Exception
     {
-        CuratorFrameworkFactory.Builder      builder = CuratorFrameworkFactory.builder();
+        CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
         CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
         client.start();
         try
@@ -585,12 +851,12 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testGetSequentialChildren() throws Exception
+    public void testGetSequentialChildren() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
@@ -603,116 +869,116 @@
                 client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/head/child");
             }
 
-            List<String>        children = client.getChildren().forPath("/head");
+            List<String> children = client.getChildren().forPath("/head");
             Assert.assertEquals(children.size(), 10);
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testBackgroundGetDataWithWatch() throws Exception
+    public void testBackgroundGetDataWithWatch() throws Exception
     {
-        final byte[]        data1 = {1, 2, 3};
-        final byte[]        data2 = {4, 5, 6, 7};
+        final byte[] data1 = {1, 2, 3};
+        final byte[] data2 = {4, 5, 6, 7};
 
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
         try
         {
-            final CountDownLatch          watchedLatch = new CountDownLatch(1);
+            final CountDownLatch watchedLatch = new CountDownLatch(1);
             client.getCuratorListenable().addListener
-            (
-                new CuratorListener()
-                {
-                    @Override
-                    public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
+                (
+                    new CuratorListener()
                     {
-                        if ( event.getType() == CuratorEventType.GET_DATA )
+                        @Override
+                        public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
                         {
-                            Assert.assertEquals(event.getPath(), "/test");
-                            Assert.assertEquals(event.getData(), data1);
-                            ((CountDownLatch)event.getContext()).countDown();
-                        }
-                        else if ( event.getType() == CuratorEventType.WATCHED )
-                        {
-                            if ( event.getWatchedEvent().getType() == Watcher.Event.EventType.NodeDataChanged )
+                            if ( event.getType() == CuratorEventType.GET_DATA )
                             {
                                 Assert.assertEquals(event.getPath(), "/test");
-                                watchedLatch.countDown();
+                                Assert.assertEquals(event.getData(), data1);
+                                ((CountDownLatch)event.getContext()).countDown();
+                            }
+                            else if ( event.getType() == CuratorEventType.WATCHED )
+                            {
+                                if ( event.getWatchedEvent().getType() == Watcher.Event.EventType.NodeDataChanged )
+                                {
+                                    Assert.assertEquals(event.getPath(), "/test");
+                                    watchedLatch.countDown();
+                                }
                             }
                         }
                     }
-                }
-            );
+                );
 
             client.create().forPath("/test", data1);
 
-            CountDownLatch      backgroundLatch = new CountDownLatch(1);
+            CountDownLatch backgroundLatch = new CountDownLatch(1);
             client.getData().watched().inBackground(backgroundLatch).forPath("/test");
             Assert.assertTrue(backgroundLatch.await(10, TimeUnit.SECONDS));
 
             client.setData().forPath("/test", data2);
             Assert.assertTrue(watchedLatch.await(10, TimeUnit.SECONDS));
-            byte[]      checkData = client.getData().forPath("/test");
+            byte[] checkData = client.getData().forPath("/test");
             Assert.assertEquals(checkData, data2);
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testBackgroundCreate() throws Exception
+    public void testBackgroundCreate() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
         try
         {
             client.getCuratorListenable().addListener
-            (
-                new CuratorListener()
-                {
-                    @Override
-                    public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
+                (
+                    new CuratorListener()
                     {
-                        if ( event.getType() == CuratorEventType.CREATE )
+                        @Override
+                        public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception
                         {
-                            Assert.assertEquals(event.getPath(), "/test");
-                            ((CountDownLatch)event.getContext()).countDown();
+                            if ( event.getType() == CuratorEventType.CREATE )
+                            {
+                                Assert.assertEquals(event.getPath(), "/test");
+                                ((CountDownLatch)event.getContext()).countDown();
+                            }
                         }
                     }
-                }
-            );
+                );
 
-            CountDownLatch     latch = new CountDownLatch(1);
+            CountDownLatch latch = new CountDownLatch(1);
             client.create().inBackground(latch).forPath("/test", new byte[]{1, 2, 3});
             Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testCreateModes() throws Exception
+    public void testCreateModes() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
         try
         {
-            byte[]  writtenBytes = {1, 2, 3};
+            byte[] writtenBytes = {1, 2, 3};
             client.create().forPath("/test", writtenBytes); // should be persistent
 
             client.close();
             client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
             client.start();
 
-            byte[]  readBytes = client.getData().forPath("/test");
+            byte[] readBytes = client.getData().forPath("/test");
             Assert.assertEquals(writtenBytes, readBytes);
 
             client.create().withMode(CreateMode.EPHEMERAL).forPath("/ghost", writtenBytes);
@@ -723,10 +989,10 @@
 
             readBytes = client.getData().forPath("/test");
             Assert.assertEquals(writtenBytes, readBytes);
-            Stat    stat = client.checkExists().forPath("/ghost");
+            Stat stat = client.checkExists().forPath("/ghost");
             Assert.assertNull(stat);
 
-            String  realPath = client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/pseq", writtenBytes);
+            String realPath = client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/pseq", writtenBytes);
             Assert.assertNotSame(realPath, "/pseq");
 
             client.close();
@@ -748,23 +1014,41 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
     @Test
-    public void     testSimple() throws Exception
+    public void testSimple() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
         client.start();
         try
         {
-            String    path = client.create().withMode(CreateMode.PERSISTENT).forPath("/test", new byte[]{1, 2, 3});
+            String path = client.create().withMode(CreateMode.PERSISTENT).forPath("/test", new byte[]{1, 2, 3});
             Assert.assertEquals(path, "/test");
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testSequentialWithTrailingSeparator() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+        client.start();
+        try
+        {
+            client.create().forPath("/test");
+            //This should create a node in the form of "/test/00000001"
+            String path = client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/test/");
+            Assert.assertTrue(path.startsWith("/test/"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
         }
     }
 }
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFrameworkBackground.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFrameworkBackground.java
index 3f1c41f..26cc941 100644
--- a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFrameworkBackground.java
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFrameworkBackground.java
@@ -48,7 +48,7 @@
     @Test
     public void testListenerConnectedAtStart() throws Exception
     {
-        server.close();
+        server.stop();
 
         Timing timing = new Timing(2);
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryNTimes(0, 0));
@@ -80,7 +80,7 @@
             // due to CURATOR-72, this was causing a LOST event to precede the CONNECTED event
             client.create().inBackground().forPath("/foo");
 
-            server = new TestingServer(server.getPort());
+            server.restart();
 
             Assert.assertTrue(timing.awaitLatch(connectedLatch));
             Assert.assertFalse(firstListenerAction.get());
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFrameworkEdges.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFrameworkEdges.java
index 9c02c7d..cd3ae77 100644
--- a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFrameworkEdges.java
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestFrameworkEdges.java
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.curator.framework.imps;
 
 import com.google.common.collect.Queues;
@@ -44,6 +43,7 @@
 import org.apache.zookeeper.data.Stat;
 import org.testng.Assert;
 import org.testng.annotations.Test;
+import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Semaphore;
@@ -56,6 +56,69 @@
     private final Timing timing = new Timing();
 
     @Test
+    public void testPathsFromProtectingInBackground() throws Exception
+    {
+        for ( CreateMode mode : CreateMode.values() )
+        {
+            internalTestPathsFromProtectingInBackground(mode);
+        }
+    }
+
+    private void internalTestPathsFromProtectingInBackground(CreateMode mode) throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 1, new RetryOneTime(1));
+        try
+        {
+            client.start();
+
+            client.create().creatingParentsIfNeeded().forPath("/a/b/c");
+
+            final BlockingQueue<String> paths = new ArrayBlockingQueue<String>(2);
+            BackgroundCallback callback = new BackgroundCallback()
+            {
+                @Override
+                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+                {
+                    paths.put(event.getName());
+                    paths.put(event.getPath());
+                }
+            };
+            final String TEST_PATH = "/a/b/c/test-";
+            client.create().withMode(mode).inBackground(callback).forPath(TEST_PATH);
+
+            String name1 = paths.take();
+            String path1 = paths.take();
+
+            client.close();
+
+            client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 1, new RetryOneTime(1));
+            client.start();
+            CreateBuilderImpl createBuilder = (CreateBuilderImpl)client.create().withProtection();
+
+            client.create().forPath(createBuilder.adjustPath(TEST_PATH));
+
+            createBuilder.debugForceFindProtectedNode = true;
+            createBuilder.withMode(mode).inBackground(callback).forPath(TEST_PATH);
+
+            String name2 = paths.take();
+            String path2 = paths.take();
+
+            Assert.assertEquals(ZKPaths.getPathAndNode(name1).getPath(), ZKPaths.getPathAndNode(TEST_PATH).getPath());
+            Assert.assertEquals(ZKPaths.getPathAndNode(name2).getPath(), ZKPaths.getPathAndNode(TEST_PATH).getPath());
+            Assert.assertEquals(ZKPaths.getPathAndNode(path1).getPath(), ZKPaths.getPathAndNode(TEST_PATH).getPath());
+            Assert.assertEquals(ZKPaths.getPathAndNode(path2).getPath(), ZKPaths.getPathAndNode(TEST_PATH).getPath());
+
+            client.delete().deletingChildrenIfNeeded().forPath("/a/b/c");
+            client.delete().forPath("/a/b");
+            client.delete().forPath("/a");
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
     public void connectionLossWithBackgroundTest() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 1, new RetryOneTime(1));
@@ -66,20 +129,20 @@
             client.getZookeeperClient().blockUntilConnectedOrTimedOut();
             server.close();
             client.getChildren().inBackground
-            (
-                new BackgroundCallback()
-                {
-                    public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+                (
+                    new BackgroundCallback()
                     {
-                        latch.countDown();
+                        public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+                        {
+                            latch.countDown();
+                        }
                     }
-                }
-            ).forPath("/");
+                ).forPath("/");
             Assert.assertTrue(timing.awaitLatch(latch));
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
@@ -107,7 +170,7 @@
 
             client.checkExists().forPath("/");
 
-            server.close();
+            server.stop();
 
             Assert.assertTrue(timing.awaitLatch(lostLatch));
 
@@ -121,7 +184,7 @@
                 // correct
             }
 
-            server = new TestingServer(server.getPort());
+            server.restart();
             client.checkExists().forPath("/");
         }
         finally
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestReconfiguration.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestReconfiguration.java
new file mode 100644
index 0000000..133e690
--- /dev/null
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestReconfiguration.java
@@ -0,0 +1,417 @@
+/**
+ * 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.curator.framework.imps;
+
+import org.apache.curator.ensemble.EnsembleListener;
+import org.apache.curator.ensemble.dynamic.DynamicEnsembleProvider;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.BackgroundCallback;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.ensemble.EnsembleTracker;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.InstanceSpec;
+import org.apache.curator.test.TestingCluster;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
+import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class TestReconfiguration
+{
+    private TestingCluster cluster;
+    private DynamicEnsembleProvider dynamicEnsembleProvider;
+    private WaitOnDelegateListener waitOnDelegateListener;
+    private EnsembleTracker ensembleTracker;
+    private CuratorFramework client;
+
+    private String connectionString1to5;
+    private String connectionString2to5;
+    private String connectionString3to5;
+
+    @BeforeMethod
+    public void setup() throws Exception
+    {
+        cluster = new TestingCluster(5);
+        cluster.start();
+
+        connectionString1to5 = cluster.getConnectString();
+        connectionString2to5 = getConnectionString(cluster, 2, 3, 4, 5);
+        connectionString3to5 = getConnectionString(cluster, 3, 4, 5);
+
+        dynamicEnsembleProvider = new DynamicEnsembleProvider(connectionString1to5);
+        client = CuratorFrameworkFactory.builder()
+            .ensembleProvider(dynamicEnsembleProvider)
+            .retryPolicy(new RetryOneTime(1))
+            .build();
+        client.start();
+        client.blockUntilConnected();
+
+        //Wrap around the dynamic ensemble provider, so that we can wait until it has received the event.
+        waitOnDelegateListener = new WaitOnDelegateListener(dynamicEnsembleProvider);
+        ensembleTracker = new EnsembleTracker(client);
+        ensembleTracker.getListenable().addListener(waitOnDelegateListener);
+        ensembleTracker.start();
+        //Wait for the initial event.
+        waitOnDelegateListener.waitForEvent();
+    }
+
+    @AfterMethod
+    public void tearDown() throws IOException
+    {
+        CloseableUtils.closeQuietly(ensembleTracker);
+        CloseableUtils.closeQuietly(client);
+        CloseableUtils.closeQuietly(cluster);
+    }
+
+    @Test
+    public void testSyncIncremental() throws Exception
+    {
+        Stat stat = new Stat();
+        byte[] bytes = client.getConfig().storingStatIn(stat).forEnsemble();
+        Assert.assertNotNull(bytes);
+        QuorumVerifier qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 5);
+        String server1 = getServerString(qv, cluster, 1L);
+        String server2 = getServerString(qv, cluster, 2L);
+
+        //Remove Servers
+        bytes = client.reconfig().leaving("1").fromConfig(qv.getVersion()).storingStatIn(stat).forEnsemble();
+        qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 4);
+
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString2to5);
+
+        bytes = client.reconfig().leaving("2").fromConfig(qv.getVersion()).storingStatIn(stat).forEnsemble();
+        qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 3);
+
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString3to5);
+
+        //Add Servers
+        bytes = client.reconfig().joining("server.2=" + server2).fromConfig(qv.getVersion()).storingStatIn(stat).forEnsemble();
+        qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 4);
+
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString2to5);
+
+        bytes = client.reconfig().joining("server.1=" + server1).fromConfig(qv.getVersion()).storingStatIn(stat).forEnsemble();
+        qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 5);
+
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString1to5);
+    }
+
+    @Test
+    public void testAsyncIncremental() throws Exception
+    {
+        final AtomicReference<byte[]> bytes = new AtomicReference<>();
+        final BackgroundCallback callback = new BackgroundCallback()
+        {
+            @Override
+            public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+            {
+                bytes.set(event.getData());
+                //We only need the latch on getConfig.
+                if ( event.getContext() != null )
+                {
+                    ((CountDownLatch)event.getContext()).countDown();
+                }
+            }
+
+        };
+
+        CountDownLatch latch = new CountDownLatch(1);
+        client.getConfig().inBackground(callback, latch).forEnsemble();
+        latch.await(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(bytes.get());
+        QuorumVerifier qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 5);
+        String server1 = getServerString(qv, cluster, 1L);
+        String server2 = getServerString(qv, cluster, 2L);
+
+        //Remove Servers
+        client.reconfig().inBackground(callback).leaving("1").fromConfig(qv.getVersion()).forEnsemble();
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString2to5);
+        qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 4);
+
+        client.reconfig().inBackground(callback, latch).leaving("2").fromConfig(qv.getVersion()).forEnsemble();
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString3to5);
+        qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 3);
+
+        //Add Servers
+        client.reconfig().inBackground(callback, latch).joining("server.2=" + server2).fromConfig(qv.getVersion()).forEnsemble();
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString2to5);
+        qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 4);
+
+        client.reconfig().inBackground(callback, latch).joining("server.1=" + server1).fromConfig(qv.getVersion()).forEnsemble();
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString1to5);
+        qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 5);
+    }
+
+    @Test
+    public void testSyncNonIncremental() throws Exception
+    {
+        Stat stat = new Stat();
+        byte[] bytes = client.getConfig().storingStatIn(stat).forEnsemble();
+        Assert.assertNotNull(bytes);
+        QuorumVerifier qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 5);
+        String server1 = getServerString(qv, cluster, 1L);
+        String server2 = getServerString(qv, cluster, 2L);
+        String server3 = getServerString(qv, cluster, 3L);
+        String server4 = getServerString(qv, cluster, 4L);
+        String server5 = getServerString(qv, cluster, 5L);
+
+        //Remove Servers
+        bytes = client.reconfig()
+            .adding("server.2=" + server2,
+                "server.3=" + server3,
+                "server.4=" + server4,
+                "server.5=" + server5)
+            .fromConfig(qv.getVersion()).storingStatIn(stat).forEnsemble();
+        qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 4);
+
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString2to5);
+
+        bytes = client.reconfig()
+            .adding("server.3=" + server3,
+                "server.4=" + server4,
+                "server.5=" + server5)
+            .fromConfig(qv.getVersion()).storingStatIn(stat).forEnsemble();
+
+        qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 3);
+
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString3to5);
+
+        //Add Servers
+        bytes = client.reconfig()
+            .adding("server.2=" + server2,
+                "server.3=" + server3,
+                "server.4=" + server4,
+                "server.5=" + server5)
+            .fromConfig(qv.getVersion()).storingStatIn(stat).forEnsemble();
+        qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 4);
+
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString2to5);
+
+        bytes = client.reconfig()
+            .adding("server.1=" + server1,
+                "server.2=" + server2,
+                "server.3=" + server3,
+                "server.4=" + server4,
+                "server.5=" + server5)
+            .fromConfig(qv.getVersion()).storingStatIn(stat).forEnsemble();
+        qv = getQuorumVerifier(bytes);
+        Assert.assertEquals(qv.getAllMembers().size(), 5);
+
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString1to5);
+    }
+
+    @Test
+    public void testAsyncNonIncremental() throws Exception
+    {
+        final AtomicReference<byte[]> bytes = new AtomicReference<>();
+        final BackgroundCallback callback = new BackgroundCallback()
+        {
+            @Override
+            public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+            {
+                bytes.set(event.getData());
+                ((CountDownLatch)event.getContext()).countDown();
+            }
+
+        };
+
+        CountDownLatch latch = new CountDownLatch(1);
+        client.getConfig().inBackground(callback, latch).forEnsemble();
+        latch.await(5, TimeUnit.SECONDS);
+        Assert.assertNotNull(bytes.get());
+        QuorumVerifier qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 5);
+        String server1 = getServerString(qv, cluster, 1L);
+        String server2 = getServerString(qv, cluster, 2L);
+        String server3 = getServerString(qv, cluster, 3L);
+        String server4 = getServerString(qv, cluster, 4L);
+        String server5 = getServerString(qv, cluster, 5L);
+
+        //Remove Servers
+        client.reconfig().inBackground(callback, latch)
+            .adding("server.2=" + server2,
+                "server.3=" + server3,
+                "server.4=" + server4,
+                "server.5=" + server5)
+            .fromConfig(qv.getVersion()).forEnsemble();
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString2to5);
+        qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 4);
+
+        client.reconfig().inBackground(callback, latch)
+            .adding("server.3=" + server3,
+                "server.4=" + server4,
+                "server.5=" + server5)
+            .fromConfig(qv.getVersion()).forEnsemble();
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString3to5);
+        qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 3);
+
+        //Add Servers
+        client.reconfig().inBackground(callback, latch)
+            .adding("server.2=" + server2,
+                "server.3=" + server3,
+                "server.4=" + server4,
+                "server.5=" + server5)
+            .fromConfig(qv.getVersion()).forEnsemble();
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString2to5);
+        qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 4);
+
+        client.reconfig().inBackground(callback, latch)
+            .adding("server.1=" + server1,
+                "server.2=" + server2,
+                "server.3=" + server3,
+                "server.4=" + server4,
+                "server.5=" + server5)
+            .fromConfig(qv.getVersion()).forEnsemble();
+        waitOnDelegateListener.waitForEvent();
+        Assert.assertEquals(dynamicEnsembleProvider.getConnectionString(), connectionString1to5);
+        qv = getQuorumVerifier(bytes.get());
+        Assert.assertEquals(qv.getAllMembers().size(), 5);
+    }
+
+    static QuorumVerifier getQuorumVerifier(byte[] bytes) throws Exception
+    {
+        Properties properties = new Properties();
+        properties.load(new StringReader(new String(bytes)));
+        return new QuorumMaj(properties);
+    }
+
+    static InstanceSpec getInstance(TestingCluster cluster, int id)
+    {
+        for ( InstanceSpec spec : cluster.getInstances() )
+        {
+            if ( spec.getServerId() == id )
+            {
+                return spec;
+            }
+        }
+        throw new IllegalStateException("InstanceSpec with id:" + id + " not found");
+    }
+
+    static String getServerString(QuorumVerifier qv, TestingCluster cluster, long id) throws Exception
+    {
+        String str = qv.getAllMembers().get(id).toString();
+        //check if connection string is already there.
+        if ( str.contains(";") )
+        {
+            return str;
+        }
+        else
+        {
+            return str + ";" + getInstance(cluster, (int)id).getConnectString();
+        }
+    }
+
+    static String getConnectionString(TestingCluster cluster, long... ids) throws Exception
+    {
+        StringBuilder sb = new StringBuilder();
+        Map<Long, InstanceSpec> specs = new HashMap<>();
+        for ( InstanceSpec spec : cluster.getInstances() )
+        {
+            specs.put((long)spec.getServerId(), spec);
+        }
+        for ( long id : ids )
+        {
+            if ( sb.length() != 0 )
+            {
+                sb.append(",");
+            }
+            sb.append(specs.get(id).getConnectString());
+        }
+        return sb.toString();
+    }
+
+    //Simple EnsembleListener that can wait until the delegate handles the event.
+    private static class WaitOnDelegateListener implements EnsembleListener
+    {
+        private CountDownLatch latch = new CountDownLatch(1);
+
+        private final EnsembleListener delegate;
+
+        private WaitOnDelegateListener(EnsembleListener delegate)
+        {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void connectionStringUpdated(String connectionString)
+        {
+            delegate.connectionStringUpdated(connectionString);
+            latch.countDown();
+        }
+
+        public void waitForEvent() throws InterruptedException, TimeoutException
+        {
+            if ( latch.await(5, TimeUnit.SECONDS) )
+            {
+                latch = new CountDownLatch(1);
+            }
+            else
+            {
+                throw new TimeoutException("Failed to receive event in time.");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactionsNew.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactionsNew.java
new file mode 100644
index 0000000..eaf94f8
--- /dev/null
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactionsNew.java
@@ -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.
+ */
+
+package org.apache.curator.framework.imps;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Queues;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.BackgroundCallback;
+import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.transaction.CuratorOp;
+import org.apache.curator.framework.api.transaction.CuratorTransactionResult;
+import org.apache.curator.framework.api.transaction.OperationType;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.data.Stat;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class TestTransactionsNew extends BaseClassForTests
+{
+    @Test
+    public void testCheckVersion() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+        try
+        {
+            client.start();
+            client.create().forPath("/foo");
+            Stat stat = client.setData().forPath("/foo", "new".getBytes());  // up the version
+
+            CuratorOp statOp = client.transactionOp().check().withVersion(stat.getVersion() + 1).forPath("/foo");
+            CuratorOp createOp = client.transactionOp().create().forPath("/bar");
+            try
+            {
+                client.transaction().forOperations(statOp, createOp);
+                Assert.fail();
+            }
+            catch ( KeeperException.BadVersionException correct )
+            {
+                // correct
+            }
+
+            Assert.assertNull(client.checkExists().forPath("/bar"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testWithNamespace() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace("galt").build();
+        try
+        {
+            client.start();
+            CuratorOp createOp1 = client.transactionOp().create().forPath("/foo", "one".getBytes());
+            CuratorOp createOp2 = client.transactionOp().create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/test-", "one".getBytes());
+            CuratorOp setDataOp = client.transactionOp().setData().forPath("/foo", "two".getBytes());
+            CuratorOp createOp3 = client.transactionOp().create().forPath("/foo/bar");
+            CuratorOp deleteOp = client.transactionOp().delete().forPath("/foo/bar");
+
+            Collection<CuratorTransactionResult> results = client.transaction().forOperations(createOp1, createOp2, setDataOp, createOp3, deleteOp);
+
+            Assert.assertTrue(client.checkExists().forPath("/foo") != null);
+            Assert.assertTrue(client.usingNamespace(null).checkExists().forPath("/galt/foo") != null);
+            Assert.assertEquals(client.getData().forPath("/foo"), "two".getBytes());
+            Assert.assertTrue(client.checkExists().forPath("/foo/bar") == null);
+
+            CuratorTransactionResult ephemeralResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/test-"));
+            Assert.assertNotNull(ephemeralResult);
+            Assert.assertNotEquals(ephemeralResult.getResultPath(), "/test-");
+            Assert.assertTrue(ephemeralResult.getResultPath().startsWith("/test-"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testBasic() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+        try
+        {
+            client.start();
+            CuratorOp createOp1 = client.transactionOp().create().forPath("/foo");
+            CuratorOp createOp2 = client.transactionOp().create().forPath("/foo/bar", "snafu".getBytes());
+
+            Collection<CuratorTransactionResult> results = client.transaction().forOperations(createOp1, createOp2);
+
+            Assert.assertTrue(client.checkExists().forPath("/foo/bar") != null);
+            Assert.assertEquals(client.getData().forPath("/foo/bar"), "snafu".getBytes());
+
+            CuratorTransactionResult fooResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo"));
+            CuratorTransactionResult fooBarResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo/bar"));
+            Assert.assertNotNull(fooResult);
+            Assert.assertNotNull(fooBarResult);
+            Assert.assertNotSame(fooResult, fooBarResult);
+            Assert.assertEquals(fooResult.getResultPath(), "/foo");
+            Assert.assertEquals(fooBarResult.getResultPath(), "/foo/bar");
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testBackground() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+        try
+        {
+            client.start();
+            CuratorOp createOp1 = client.transactionOp().create().forPath("/foo");
+            CuratorOp createOp2 = client.transactionOp().create().forPath("/foo/bar", "snafu".getBytes());
+
+            final BlockingQueue<List<CuratorTransactionResult>> queue = Queues.newLinkedBlockingQueue();
+            BackgroundCallback callback = new BackgroundCallback()
+            {
+                @Override
+                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+                {
+                    queue.add(event.getOpResults());
+                }
+            };
+            client.transaction().inBackground(callback).forOperations(createOp1, createOp2);
+            Collection<CuratorTransactionResult> results = queue.poll(5, TimeUnit.SECONDS);
+
+            Assert.assertNotNull(results);
+            Assert.assertTrue(client.checkExists().forPath("/foo/bar") != null);
+            Assert.assertEquals(client.getData().forPath("/foo/bar"), "snafu".getBytes());
+
+            CuratorTransactionResult fooResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo"));
+            CuratorTransactionResult fooBarResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo/bar"));
+            Assert.assertNotNull(fooResult);
+            Assert.assertNotNull(fooBarResult);
+            Assert.assertNotSame(fooResult, fooBarResult);
+            Assert.assertEquals(fooResult.getResultPath(), "/foo");
+            Assert.assertEquals(fooBarResult.getResultPath(), "/foo/bar");
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testBackgroundWithNamespace() throws Exception
+    {
+        CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace("galt").build();
+        try
+        {
+            client.start();
+            CuratorOp createOp1 = client.transactionOp().create().forPath("/foo", "one".getBytes());
+            CuratorOp createOp2 = client.transactionOp().create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/test-", "one".getBytes());
+            CuratorOp setDataOp = client.transactionOp().setData().forPath("/foo", "two".getBytes());
+            CuratorOp createOp3 = client.transactionOp().create().forPath("/foo/bar");
+            CuratorOp deleteOp = client.transactionOp().delete().forPath("/foo/bar");
+
+            final BlockingQueue<List<CuratorTransactionResult>> queue = Queues.newLinkedBlockingQueue();
+            BackgroundCallback callback = new BackgroundCallback()
+            {
+                @Override
+                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
+                {
+                    queue.add(event.getOpResults());
+                }
+            };
+            client.transaction().inBackground(callback).forOperations(createOp1, createOp2, setDataOp, createOp3, deleteOp);
+
+            Collection<CuratorTransactionResult> results = queue.poll(5, TimeUnit.SECONDS);
+
+            Assert.assertNotNull(results);
+            Assert.assertTrue(client.checkExists().forPath("/foo") != null);
+            Assert.assertTrue(client.usingNamespace(null).checkExists().forPath("/galt/foo") != null);
+            Assert.assertEquals(client.getData().forPath("/foo"), "two".getBytes());
+            Assert.assertTrue(client.checkExists().forPath("/foo/bar") == null);
+
+            CuratorTransactionResult ephemeralResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/test-"));
+            Assert.assertNotNull(ephemeralResult);
+            Assert.assertNotEquals(ephemeralResult.getResultPath(), "/test-");
+            Assert.assertTrue(ephemeralResult.getResultPath().startsWith("/test-"));
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+}
diff --git a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactions.java b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactionsOld.java
similarity index 93%
rename from curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactions.java
rename to curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactionsOld.java
index ae2cf1d..f0147d5 100644
--- a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactions.java
+++ b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestTransactionsOld.java
@@ -25,6 +25,7 @@
 import org.apache.curator.framework.api.transaction.OperationType;
 import org.apache.curator.retry.RetryOneTime;
 import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.utils.CloseableUtils;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.data.Stat;
@@ -32,15 +33,16 @@
 import org.testng.annotations.Test;
 import java.util.Collection;
 
-public class TestTransactions extends BaseClassForTests
+@SuppressWarnings("deprecation")
+public class TestTransactionsOld extends BaseClassForTests
 {
     @Test
     public void     testCheckVersion() throws Exception
     {
         CuratorFramework        client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
-        client.start();
         try
         {
+            client.start();
             client.create().forPath("/foo");
             Stat        stat = client.setData().forPath("/foo", "new".getBytes());  // up the version
 
@@ -64,7 +66,7 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
@@ -72,9 +74,9 @@
     public void     testWithNamespace() throws Exception
     {
         CuratorFramework        client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace("galt").build();
-        client.start();
         try
         {
+            client.start();
             Collection<CuratorTransactionResult>    results =
                 client.inTransaction()
                     .create().forPath("/foo", "one".getBytes())
@@ -101,7 +103,7 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 
@@ -109,9 +111,9 @@
     public void     testBasic() throws Exception
     {
         CuratorFramework        client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
-        client.start();
         try
         {
+            client.start();
             Collection<CuratorTransactionResult>    results =
                 client.inTransaction()
                     .create().forPath("/foo")
@@ -133,7 +135,7 @@
         }
         finally
         {
-            client.close();
+            CloseableUtils.closeQuietly(client);
         }
     }
 }
diff --git a/curator-recipes/pom.xml b/curator-recipes/pom.xml
index 9d1d081..3394361 100644
--- a/curator-recipes/pom.xml
+++ b/curator-recipes/pom.xml
@@ -61,5 +61,17 @@
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/DistributedAtomicValue.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/DistributedAtomicValue.java
index c90fb2b..bbd9203 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/DistributedAtomicValue.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/DistributedAtomicValue.java
@@ -22,12 +22,10 @@
 import org.apache.curator.RetryPolicy;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.recipes.locks.InterProcessMutex;
-import org.apache.curator.utils.EnsurePath;
+import org.apache.curator.utils.PathUtils;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.data.Stat;
 import java.util.Arrays;
-import org.apache.curator.utils.PathUtils;
-import org.apache.zookeeper.ZKUtil;
 
 /**
  * <p>A distributed value that attempts atomic sets. It first tries uses optimistic locking. If that fails,
@@ -44,7 +42,6 @@
     private final RetryPolicy       retryPolicy;
     private final PromotedToLock    promotedToLock;
     private final InterProcessMutex mutex;
-    private final EnsurePath        ensurePath;
 
     /**
      * Creates in optimistic mode only - i.e. the promotion to a mutex is not done
@@ -75,7 +72,6 @@
         this.retryPolicy = retryPolicy;
         this.promotedToLock = promotedToLock;
         mutex = (promotedToLock != null) ? new InterProcessMutex(client, promotedToLock.getPath()) : null;
-        ensurePath = client.newNamespaceAwareEnsurePath(path).excludingLast();
     }
 
     /**
@@ -104,14 +100,13 @@
     {
         try
         {
-            ensurePath.ensure(client.getZookeeperClient());
             client.setData().forPath(path, newValue);
         }
         catch ( KeeperException.NoNodeException dummy )
         {
             try
             {
-                client.create().forPath(path, newValue);
+                client.create().creatingParentContainersIfNeeded().forPath(path, newValue);
             }
             catch ( KeeperException.NodeExistsException dummy2 )
             {
@@ -199,10 +194,9 @@
      */
     public boolean initialize(byte[] value) throws Exception
     {
-        ensurePath.ensure(client.getZookeeperClient());
         try
         {
-            client.create().forPath(path, value);
+            client.create().creatingParentContainersIfNeeded().forPath(path, value);
         }
         catch ( KeeperException.NodeExistsException ignore )
         {
@@ -251,7 +245,6 @@
         boolean             createIt = false;
         try
         {
-            ensurePath.ensure(client.getZookeeperClient());
             result.preValue = client.getData().storingStatIn(stat).forPath(path);
         }
         catch ( KeeperException.NoNodeException e )
@@ -335,7 +328,7 @@
             byte[]  newValue = makeValue.makeFrom(result.preValue);
             if ( createIt )
             {
-                client.create().forPath(path, newValue);
+                client.create().creatingParentContainersIfNeeded().forPath(path, newValue);
             }
             else
             {
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/DistributedBarrier.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/DistributedBarrier.java
index b891b2b..8a376f1 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/DistributedBarrier.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/DistributedBarrier.java
@@ -67,7 +67,7 @@
     {
         try
         {
-            client.create().creatingParentsIfNeeded().forPath(barrierPath);
+            client.create().creatingParentContainersIfNeeded().forPath(barrierPath);
         }
         catch ( KeeperException.NodeExistsException ignore )
         {
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/DistributedDoubleBarrier.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/DistributedDoubleBarrier.java
index 5034b0a..b3bdf2c 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/DistributedDoubleBarrier.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/DistributedDoubleBarrier.java
@@ -118,7 +118,7 @@
         long            maxWaitMs = hasMaxWait ? TimeUnit.MILLISECONDS.convert(maxWait, unit) : Long.MAX_VALUE;
 
         boolean         readyPathExists = (client.checkExists().usingWatcher(watcher).forPath(readyPath) != null);
-        client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(ourPath);
+        client.create().creatingParentContainersIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(ourPath);
 
         boolean         result = (readyPathExists || internalEnter(startMs, hasMaxWait, maxWaitMs));
         if ( connectionLost.get() )
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java
index 4d87732..49b9a3f 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/NodeCache.java
@@ -30,7 +30,7 @@
 import org.apache.curator.framework.listen.ListenerContainer;
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.curator.framework.state.ConnectionStateListener;
-import org.apache.curator.utils.EnsurePath;
+import org.apache.curator.utils.PathUtils;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.data.Stat;
@@ -41,7 +41,6 @@
 import java.util.concurrent.Exchanger;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
-import org.apache.curator.utils.PathUtils;
 
 /**
  * <p>A utility that attempts to keep the data from a node locally cached. This class
@@ -58,7 +57,6 @@
     private final WatcherRemoveCuratorFramework client;
     private final String path;
     private final boolean dataIsCompressed;
-    private final EnsurePath ensurePath;
     private final AtomicReference<ChildData> data = new AtomicReference<ChildData>(null);
     private final AtomicReference<State> state = new AtomicReference<State>(State.LATENT);
     private final ListenerContainer<NodeCacheListener> listeners = new ListenerContainer<NodeCacheListener>();
@@ -133,7 +131,6 @@
         this.client = client.newWatcherRemoveCuratorFramework();
         this.path = PathUtils.validatePath(path);
         this.dataIsCompressed = dataIsCompressed;
-        ensurePath = client.newNamespaceAwareEnsurePath(path).excludingLast();
     }
 
     /**
@@ -157,10 +154,10 @@
     {
         Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once");
 
-        ensurePath.ensure(client.getZookeeperClient());
-
         client.getConnectionStateListenable().addListener(connectionStateListener);
 
+        client.checkExists().creatingParentContainersIfNeeded().forPath(path);
+
         if ( buildInitial )
         {
             internalRebuild();
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java
index 5c413b6..99a652d 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCache.java
@@ -34,7 +34,6 @@
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.curator.framework.state.ConnectionStateListener;
 import org.apache.curator.utils.CloseableExecutorService;
-import org.apache.curator.utils.EnsurePath;
 import org.apache.curator.utils.PathUtils;
 import org.apache.curator.utils.ThreadUtils;
 import org.apache.curator.utils.ZKPaths;
@@ -44,16 +43,13 @@
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Exchanger;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -74,7 +70,6 @@
     private final CloseableExecutorService executorService;
     private final boolean cacheData;
     private final boolean dataIsCompressed;
-    private final EnsurePath ensurePath;
     private final ListenerContainer<PathChildrenCacheListener> listeners = new ListenerContainer<PathChildrenCacheListener>();
     private final ConcurrentMap<String, ChildData> currentData = Maps.newConcurrentMap();
     private final AtomicReference<Map<String, ChildData>> initialSet = new AtomicReference<Map<String, ChildData>>();
@@ -143,6 +138,7 @@
      * @param mode   caching mode
      * @deprecated use {@link #PathChildrenCache(CuratorFramework, String, boolean)} instead
      */
+    @Deprecated
     @SuppressWarnings("deprecation")
     public PathChildrenCache(CuratorFramework client, String path, PathChildrenCacheMode mode)
     {
@@ -156,6 +152,7 @@
      * @param threadFactory factory to use when creating internal threads
      * @deprecated use {@link #PathChildrenCache(CuratorFramework, String, boolean, ThreadFactory)} instead
      */
+    @Deprecated
     @SuppressWarnings("deprecation")
     public PathChildrenCache(CuratorFramework client, String path, PathChildrenCacheMode mode, ThreadFactory threadFactory)
     {
@@ -200,7 +197,7 @@
      * @param path             path to watch
      * @param cacheData        if true, node contents are cached in addition to the stat
      * @param dataIsCompressed if true, data in the path is compressed
-     * @param executorService  ExecutorService to use for the PathChildrenCache's background thread
+     * @param executorService  ExecutorService to use for the PathChildrenCache's background thread. This service should be single threaded, otherwise the cache may see inconsistent results.
      */
     public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, final ExecutorService executorService)
     {
@@ -212,7 +209,7 @@
      * @param path             path to watch
      * @param cacheData        if true, node contents are cached in addition to the stat
      * @param dataIsCompressed if true, data in the path is compressed
-     * @param executorService  Closeable ExecutorService to use for the PathChildrenCache's background thread
+     * @param executorService  Closeable ExecutorService to use for the PathChildrenCache's background thread. This service should be single threaded, otherwise the cache may see inconsistent results.
      */
     public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, final CloseableExecutorService executorService)
     {
@@ -221,7 +218,6 @@
         this.cacheData = cacheData;
         this.dataIsCompressed = dataIsCompressed;
         this.executorService = executorService;
-        ensurePath = client.newNamespaceAwareEnsurePath(path);
     }
 
     /**
@@ -243,6 +239,7 @@
      * @throws Exception errors
      * @deprecated use {@link #start(StartMode)}
      */
+    @Deprecated
     public void start(boolean buildInitial) throws Exception
     {
         start(buildInitial ? StartMode.BUILD_INITIAL_CACHE : StartMode.NORMAL);
@@ -320,7 +317,7 @@
     {
         Preconditions.checkState(!executorService.isShutdown(), "cache has been closed");
 
-        ensurePath.ensure(client.getZookeeperClient());
+        ensurePath();
 
         clear();
 
@@ -352,7 +349,7 @@
         Preconditions.checkArgument(ZKPaths.getPathAndNode(fullPath).getPath().equals(path), "Node is not part of this cache: " + fullPath);
         Preconditions.checkState(!executorService.isShutdown(), "cache has been closed");
 
-        ensurePath.ensure(client.getZookeeperClient());
+        ensurePath();
         internalRebuildNode(fullPath);
 
         // this is necessary so that any updates that occurred while rebuilding are taken
@@ -482,7 +479,7 @@
 
     void refresh(final RefreshMode mode) throws Exception
     {
-        ensurePath.ensure(client.getZookeeperClient());
+        ensurePath();
 
         final BackgroundCallback callback = new BackgroundCallback()
         {
@@ -613,6 +610,11 @@
         }
     }
 
+    private void ensurePath() throws Exception
+    {
+        client.createContainers(path);
+    }
+
     private void handleStateChange(ConnectionState newState)
     {
         switch ( newState )
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCacheMode.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCacheMode.java
index dcd9be3..5c15fda 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCacheMode.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/PathChildrenCacheMode.java
@@ -27,6 +27,7 @@
  * @deprecated no longer used. Instead use either {@link PathChildrenCache#PathChildrenCache(CuratorFramework, String, boolean)}
  * or {@link PathChildrenCache#PathChildrenCache(CuratorFramework, String, boolean, ThreadFactory)}
  */
+@Deprecated
 public enum PathChildrenCacheMode
 {
     /**
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/TreeCache.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/TreeCache.java
index c3958aa..bda00bf 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/TreeCache.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/TreeCache.java
@@ -34,6 +34,7 @@
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.curator.framework.state.ConnectionStateListener;
 import org.apache.curator.utils.CloseableExecutorService;
+import org.apache.curator.utils.PathUtils;
 import org.apache.curator.utils.ThreadUtils;
 import org.apache.curator.utils.ZKPaths;
 import org.apache.zookeeper.KeeperException;
@@ -45,6 +46,7 @@
 import java.io.Closeable;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
@@ -70,6 +72,7 @@
 public class TreeCache implements Closeable
 {
     private static final Logger LOG = LoggerFactory.getLogger(TreeCache.class);
+    private final boolean createParentNodes;
 
     public static final class Builder
     {
@@ -79,6 +82,7 @@
         private boolean dataIsCompressed = false;
         private CloseableExecutorService executorService = null;
         private int maxDepth = Integer.MAX_VALUE;
+        private boolean createParentNodes = false;
 
         private Builder(CuratorFramework client, String path)
         {
@@ -96,7 +100,7 @@
             {
                 executor = new CloseableExecutorService(Executors.newSingleThreadExecutor(defaultThreadFactory));
             }
-            return new TreeCache(client, path, cacheData, dataIsCompressed, maxDepth, executor);
+            return new TreeCache(client, path, cacheData, dataIsCompressed, maxDepth, executor, createParentNodes);
         }
 
         /**
@@ -160,6 +164,19 @@
             this.maxDepth = maxDepth;
             return this;
         }
+
+        /**
+         * By default, TreeCache does not auto-create parent nodes for the cached path. Change
+         * this behavior with this method. NOTE: parent nodes are created as containers
+         *
+         * @param createParentNodes true to create parent nodes
+         * @return this for chaining
+         */
+        public Builder setCreateParentNodes(boolean createParentNodes)
+        {
+            this.createParentNodes = createParentNodes;
+            return this;
+        }
     }
 
     /**
@@ -285,7 +302,8 @@
                 return;
             }
 
-            if ( nodeState.compareAndSet(NodeState.LIVE, NodeState.DEAD) )
+            NodeState oldState = nodeState.getAndSet(NodeState.DEAD);
+            if ( oldState == NodeState.LIVE )
             {
                 publishEvent(TreeCacheEvent.Type.NODE_REMOVED, path);
             }
@@ -293,7 +311,7 @@
             if ( parent == null )
             {
                 // Root node; use an exist query to watch for existence.
-                client.checkExists().usingWatcher(this).inBackground().forPath(path);
+                client.checkExists().usingWatcher(this).inBackground(this).forPath(path);
             }
             else
             {
@@ -347,10 +365,6 @@
                     nodeState.compareAndSet(NodeState.DEAD, NodeState.PENDING);
                     wasCreated();
                 }
-                else if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() )
-                {
-                    wasDeleted();
-                }
                 break;
             case CHILDREN:
                 if ( event.getResultCode() == KeeperException.Code.OK.intValue() )
@@ -413,7 +427,8 @@
                     }
 
                     Stat oldStat = stat.getAndSet(newStat);
-                    if ( nodeState.compareAndSet(NodeState.PENDING, NodeState.LIVE) )
+                    NodeState oldState = nodeState.getAndSet(NodeState.LIVE);
+                    if ( oldState != NodeState.LIVE )
                     {
                         publishEvent(TreeCacheEvent.Type.NODE_ADDED, new ChildData(event.getPath(), newStat, event.getData()));
                     }
@@ -501,7 +516,7 @@
      */
     public TreeCache(CuratorFramework client, String path)
     {
-        this(client, path, true, false, Integer.MAX_VALUE, new CloseableExecutorService(Executors.newSingleThreadExecutor(defaultThreadFactory), true));
+        this(client, path, true, false, Integer.MAX_VALUE, new CloseableExecutorService(Executors.newSingleThreadExecutor(defaultThreadFactory), true), false);
     }
 
     /**
@@ -510,9 +525,11 @@
      * @param cacheData        if true, node contents are cached in addition to the stat
      * @param dataIsCompressed if true, data in the path is compressed
      * @param executorService  Closeable ExecutorService to use for the TreeCache's background thread
+     * @param createParentNodes true to create parent nodes as containers
      */
-    TreeCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, int maxDepth, final CloseableExecutorService executorService)
+    TreeCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, int maxDepth, final CloseableExecutorService executorService, boolean createParentNodes)
     {
+        this.createParentNodes = createParentNodes;
         this.root = new TreeNode(validatePath(path), null);
         this.client = client.newWatcherRemoveCuratorFramework();
         this.cacheData = cacheData;
@@ -530,6 +547,10 @@
     public TreeCache start() throws Exception
     {
         Preconditions.checkState(treeState.compareAndSet(TreeState.LATENT, TreeState.STARTED), "already started");
+        if ( createParentNodes )
+        {
+            client.createContainers(root.path);
+        }
         client.getConnectionStateListenable().addListener(connectionStateListener);
         if ( client.getZookeeperClient().isConnected() )
         {
@@ -582,33 +603,36 @@
         return errorListeners;
     }
 
-    private TreeNode find(String fullPath)
+    private TreeNode find(String findPath)
     {
-        if ( !fullPath.startsWith(root.path) )
-        {
-            return null;
+        PathUtils.validatePath(findPath);
+        LinkedList<String> rootElements = new LinkedList<String>(ZKPaths.split(root.path));
+        LinkedList<String> findElements = new LinkedList<String>(ZKPaths.split(findPath));
+        while (!rootElements.isEmpty()) {
+            if (findElements.isEmpty()) {
+                // Target path shorter than root path
+                return null;
+            }
+            String nextRoot = rootElements.removeFirst();
+            String nextFind = findElements.removeFirst();
+            if (!nextFind.equals(nextRoot)) {
+                // Initial root path does not match
+                return null;
+            }
         }
 
         TreeNode current = root;
-        if ( fullPath.length() > root.path.length() )
-        {
-            if ( root.path.length() > 1 )
+        while (!findElements.isEmpty()) {
+            String nextFind = findElements.removeFirst();
+            ConcurrentMap<String, TreeNode> map = current.children.get();
+            if ( map == null )
             {
-                fullPath = fullPath.substring(root.path.length());
+                return null;
             }
-            List<String> split = ZKPaths.split(fullPath);
-            for ( String part : split )
+            current = map.get(nextFind);
+            if ( current == null )
             {
-                ConcurrentMap<String, TreeNode> map = current.children.get();
-                if ( map == null )
-                {
-                    return null;
-                }
-                current = map.get(part);
-                if ( current == null )
-                {
-                    return null;
-                }
+                return null;
             }
         }
         return current;
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/LeaderLatch.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/LeaderLatch.java
index 8d2d0f0..da9b8b2 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/LeaderLatch.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/LeaderLatch.java
@@ -502,7 +502,7 @@
                 }
             }
         };
-        client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).inBackground(callback).forPath(ZKPaths.makePath(latchPath, LOCK_NAME), LeaderSelector.getIdBytes(id));
+        client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).inBackground(callback).forPath(ZKPaths.makePath(latchPath, LOCK_NAME), LeaderSelector.getIdBytes(id));
     }
 
     private synchronized void internalStart()
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/LeaderSelector.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/LeaderSelector.java
index 9c09b4c..716ca96 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/LeaderSelector.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/LeaderSelector.java
@@ -230,7 +230,7 @@
         return internalRequeue();
     }
 
-    public synchronized boolean internalRequeue()
+    private synchronized boolean internalRequeue()
     {
         if ( !isQueued && (state.get() == State.STARTED) )
         {
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/ChildReaper.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/ChildReaper.java
index 5f28f82..9d196e8 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/ChildReaper.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/ChildReaper.java
@@ -1,3 +1,4 @@
+
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -18,38 +19,55 @@
  */
 package org.apache.curator.framework.recipes.locks;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
-import org.apache.curator.utils.CloseableUtils;
+import com.google.common.collect.Sets;
 import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.leader.LeaderLatch;
 import org.apache.curator.utils.CloseableScheduledExecutorService;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.curator.utils.PathUtils;
 import org.apache.curator.utils.ThreadUtils;
 import org.apache.curator.utils.ZKPaths;
+import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
-import org.apache.curator.utils.PathUtils;
 
 /**
  * Utility to reap empty child nodes of a parent node. Periodically calls getChildren on
  * the node and adds empty nodes to an internally managed {@link Reaper}
+ *
+ * @deprecated Since 2.9.0 - Reaper/ChildReaper are no longer needed. Use {@link CreateMode#CONTAINER}.
+ * Also, all Curator recipes create container parents.
  */
+@Deprecated
 public class ChildReaper implements Closeable
 {
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final Reaper reaper;
     private final AtomicReference<State> state = new AtomicReference<State>(State.LATENT);
     private final CuratorFramework client;
-    private final String path;
+    private final Collection<String> paths = Sets.newConcurrentHashSet();
+    private volatile Iterator<String> pathIterator = null;
     private final Reaper.Mode mode;
     private final CloseableScheduledExecutorService executor;
     private final int reapingThresholdMs;
+    private final LeaderLatch leaderLatch;
+    private final Set<String> lockSchema;
+    private final AtomicInteger maxChildren = new AtomicInteger(-1);
 
     private volatile Future<?> task;
 
@@ -103,12 +121,36 @@
      */
     public ChildReaper(CuratorFramework client, String path, Reaper.Mode mode, ScheduledExecutorService executor, int reapingThresholdMs, String leaderPath)
     {
+        this(client, path, mode, executor, reapingThresholdMs, leaderPath, Collections.<String>emptySet());
+    }
+
+
+    /**
+     * @param client the client
+     * @param path path to reap children from
+     * @param executor executor to use for background tasks
+     * @param reapingThresholdMs threshold in milliseconds that determines that a path can be deleted
+     * @param mode reaping mode
+     * @param leaderPath if not null, uses a leader selection so that only 1 reaper is active in the cluster
+     * @param lockSchema a set of the possible subnodes of the children of path that must be reaped in addition to the child nodes
+     */
+    public ChildReaper(CuratorFramework client, String path, Reaper.Mode mode, ScheduledExecutorService executor, int reapingThresholdMs, String leaderPath, Set<String> lockSchema)
+    {
         this.client = client;
-        this.path = PathUtils.validatePath(path);
         this.mode = mode;
         this.executor = new CloseableScheduledExecutorService(executor);
         this.reapingThresholdMs = reapingThresholdMs;
-        this.reaper = new Reaper(client, executor, reapingThresholdMs, leaderPath);
+        if (leaderPath != null)
+        {
+            leaderLatch = new LeaderLatch(client, leaderPath);
+        }
+        else
+        {
+            leaderLatch = null;
+        }
+        this.reaper = new Reaper(client, executor, reapingThresholdMs, leaderLatch);
+        this.lockSchema = lockSchema;
+        addPath(path);
     }
 
     /**
@@ -134,7 +176,10 @@
             reapingThresholdMs,
             TimeUnit.MILLISECONDS
         );
-
+        if (leaderLatch != null)
+        {
+            leaderLatch.start();
+        }
         reaper.start();
     }
 
@@ -144,33 +189,118 @@
         if ( state.compareAndSet(State.STARTED, State.CLOSED) )
         {
             CloseableUtils.closeQuietly(reaper);
+            if (leaderLatch != null)
+            {
+                CloseableUtils.closeQuietly(leaderLatch);
+            }
             task.cancel(true);
         }
     }
 
-    private static ScheduledExecutorService newExecutorService()
+    /**
+     * Add a path to reap children from
+     *
+     * @param path the path
+     * @return this for chaining
+     */
+    public ChildReaper addPath(String path)
+    {
+        paths.add(PathUtils.validatePath(path));
+        return this;
+    }
+
+    /**
+     * Remove a path from reaping
+     *
+     * @param path the path
+     * @return true if the path existed and was removed
+     */
+    public boolean removePath(String path)
+    {
+        return paths.remove(PathUtils.validatePath(path));
+    }
+
+    /**
+     * If a node has so many children that {@link CuratorFramework#getChildren()} will fail
+     * (due to jute.maxbuffer) it can cause connection instability. Set the max number of
+     * children here to prevent the path from being queried in these cases. The number should usually
+     * be: average-node-name-length/1000000
+     *
+     * @param maxChildren max children
+     */
+    public void setMaxChildren(int maxChildren)
+    {
+        this.maxChildren.set(maxChildren);
+    }
+
+    public static ScheduledExecutorService newExecutorService()
     {
         return ThreadUtils.newFixedThreadScheduledPool(2, "ChildReaper");
     }
 
+    @VisibleForTesting
+    protected void warnMaxChildren(String path, Stat stat)
+    {
+        log.warn(String.format("Skipping %s as it has too many children: %d", path, stat.getNumChildren()));
+    }
+
     private void doWork()
     {
-        try
+        if ( shouldDoWork() )
         {
-            List<String>        children = client.getChildren().forPath(path);
-            for ( String name : children )
+            if ( (pathIterator == null) || !pathIterator.hasNext() )
             {
-                String  thisPath = ZKPaths.makePath(path, name);
-                Stat    stat = client.checkExists().forPath(thisPath);
-                if ( (stat != null) && (stat.getNumChildren() == 0) )
+                pathIterator = paths.iterator();
+            }
+            while ( pathIterator.hasNext() )
+            {
+                String path = pathIterator.next();
+                try
                 {
-                    reaper.addPath(thisPath, mode);
+                    int maxChildren = this.maxChildren.get();
+                    if ( maxChildren > 0 )
+                    {
+                        Stat stat = client.checkExists().forPath(path);
+                        if ( (stat != null) && (stat.getNumChildren() > maxChildren) )
+                        {
+                            warnMaxChildren(path, stat);
+                            continue;
+                        }
+                    }
+
+                    List<String> children = client.getChildren().forPath(path);
+                    log.info(String.format("Found %d children for %s", children.size(), path));
+                    for ( String name : children )
+                    {
+                        String childPath = ZKPaths.makePath(path, name);
+                        addPathToReaperIfEmpty(childPath);
+                        for ( String subNode : lockSchema )
+                        {
+                            addPathToReaperIfEmpty(ZKPaths.makePath(childPath, subNode));
+                        }
+
+                    }
+                }
+                catch ( Exception e )
+                {
+                    log.error("Could not get children for path: " + path, e);
                 }
             }
         }
-        catch ( Exception e )
+    }
+
+    private void addPathToReaperIfEmpty(String path) throws Exception
+    {
+        Stat stat = client.checkExists().forPath(path);
+        if ( (stat != null) && (stat.getNumChildren() == 0) )
         {
-            log.error("Could not get children for path: " + path, e);
+            log.info("Adding " + path);
+            reaper.addPath(path, mode);
         }
     }
+
+    private boolean shouldDoWork()
+    {
+        return this.leaderLatch == null || this.leaderLatch.hasLeadership();
+    }
 }
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessReadWriteLock.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessReadWriteLock.java
index 3a0dc44..57af212 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessReadWriteLock.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessReadWriteLock.java
@@ -193,7 +193,7 @@
 
         int         index = 0;
         int         firstWriteIndex = Integer.MAX_VALUE;
-        int         ourIndex = Integer.MAX_VALUE;
+        int         ourIndex = -1;
         for ( String node : children )
         {
             if ( node.contains(WRITE_LOCK_NAME) )
@@ -208,6 +208,7 @@
 
             ++index;
         }
+
         StandardLockInternalsDriver.validateOurIndex(sequenceNodeName, ourIndex);
 
         boolean     getsTheLock = (ourIndex < firstWriteIndex);
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessSemaphore.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessSemaphore.java
index 2f4b1ed..3d29aa8 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessSemaphore.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessSemaphore.java
@@ -63,6 +63,7 @@
  *
  * @deprecated Use {@link InterProcessSemaphoreV2} instead of this class. It uses a better algorithm.
  */
+@Deprecated
 public class InterProcessSemaphore
 {
     private final Logger        log = LoggerFactory.getLogger(getClass());
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessSemaphoreV2.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessSemaphoreV2.java
index 2a55107..3bf2ec3 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessSemaphoreV2.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/InterProcessSemaphoreV2.java
@@ -21,15 +21,17 @@
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import org.apache.curator.framework.WatcherRemoveCuratorFramework;
-import org.apache.curator.utils.CloseableUtils;
+import com.google.common.collect.Sets;
 import org.apache.curator.RetryLoop;
 import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.WatcherRemoveCuratorFramework;
 import org.apache.curator.framework.api.PathAndBytesable;
 import org.apache.curator.framework.imps.CuratorFrameworkState;
 import org.apache.curator.framework.recipes.shared.SharedCountListener;
 import org.apache.curator.framework.recipes.shared.SharedCountReader;
 import org.apache.curator.framework.state.ConnectionState;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.curator.utils.PathUtils;
 import org.apache.curator.utils.ZKPaths;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
@@ -37,12 +39,13 @@
 import org.apache.zookeeper.Watcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
-import org.apache.curator.utils.PathUtils;
 
 /**
  * <p>
@@ -93,6 +96,10 @@
     private static final String LOCK_PARENT = "locks";
     private static final String LEASE_PARENT = "leases";
     private static final String LEASE_BASE_NAME = "lease-";
+    public static final Set<String> LOCK_SCHEMA = Sets.newHashSet(
+            LOCK_PARENT,
+            LEASE_PARENT
+    );
 
     /**
      * @param client    the client
@@ -341,7 +348,7 @@
         }
         try
         {
-            PathAndBytesable<String> createBuilder = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL);
+            PathAndBytesable<String> createBuilder = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL);
             String path = (nodeData != null) ? createBuilder.forPath(ZKPaths.makePath(leasesPath, LEASE_BASE_NAME), nodeData) : createBuilder.forPath(ZKPaths.makePath(leasesPath, LEASE_BASE_NAME));
             String nodeName = ZKPaths.getNodeFromPath(path);
             builder.add(makeLease(path));
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/Reaper.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/Reaper.java
index 8802372..a7a575f 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/Reaper.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/Reaper.java
@@ -26,6 +26,7 @@
 import org.apache.curator.framework.recipes.leader.LeaderLatchListener;
 import org.apache.curator.utils.CloseableScheduledExecutorService;
 import org.apache.curator.utils.ThreadUtils;
+import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
@@ -41,7 +42,11 @@
 
 /**
  * Utility to clean up parent lock nodes so that they don't stay around as garbage
+ *
+ * @deprecated Since 2.9.0 - Reaper/ChildReaper are no longer needed. Use {@link CreateMode#CONTAINER}.
+ * Also, all Curator recipes create container parents.
  */
+@Deprecated
 public class Reaper implements Closeable
 {
     private final Logger log = LoggerFactory.getLogger(getClass());
@@ -52,6 +57,7 @@
     private final AtomicReference<State> state = new AtomicReference<State>(State.LATENT);
     private final LeaderLatch leaderLatch;
     private final AtomicBoolean reapingIsActive = new AtomicBoolean(true);
+    private final boolean ownsLeaderLatch;
 
     private enum State
     {
@@ -111,7 +117,7 @@
      */
     public Reaper(CuratorFramework client)
     {
-        this(client, newExecutorService(), DEFAULT_REAPING_THRESHOLD_MS, null);
+        this(client, newExecutorService(), DEFAULT_REAPING_THRESHOLD_MS, (String) null);
     }
 
     /**
@@ -122,7 +128,7 @@
      */
     public Reaper(CuratorFramework client, int reapingThresholdMs)
     {
-        this(client, newExecutorService(), reapingThresholdMs, null);
+        this(client, newExecutorService(), reapingThresholdMs, (String) null);
     }
 
     /**
@@ -132,7 +138,7 @@
      */
     public Reaper(CuratorFramework client, ScheduledExecutorService executor, int reapingThresholdMs)
     {
-        this(client, executor, reapingThresholdMs, null);
+        this(client, executor, reapingThresholdMs, (String) null);
     }
 
     /**
@@ -143,18 +149,41 @@
      */
     public Reaper(CuratorFramework client, ScheduledExecutorService executor, int reapingThresholdMs, String leaderPath)
     {
+        this(client, executor, reapingThresholdMs, makeLeaderLatchIfPathNotNull(client, leaderPath), true);
+    }
+
+    /**
+     * @param client             client
+     * @param executor           thread pool
+     * @param reapingThresholdMs threshold in milliseconds that determines that a path can be deleted
+     * @param leaderLatch        a pre-created leader latch to ensure only 1 reaper is active in the cluster
+     */
+    public Reaper(CuratorFramework client, ScheduledExecutorService executor, int reapingThresholdMs, LeaderLatch leaderLatch)
+    {
+        this(client, executor, reapingThresholdMs, leaderLatch, false);
+    }
+
+    /**
+     * @param client             client
+     * @param executor           thread pool
+     * @param reapingThresholdMs threshold in milliseconds that determines that a path can be deleted
+     * @param leaderLatch        a pre-created leader latch to ensure only 1 reaper is active in the cluster
+     * @param ownsLeaderLatch    indicates whether or not the reaper owns the leader latch (if it exists) and thus should start/stop it
+     * */
+    private Reaper(CuratorFramework client, ScheduledExecutorService executor, int reapingThresholdMs, LeaderLatch leaderLatch, boolean ownsLeaderLatch)
+    {
         this.client = client;
         this.executor = new CloseableScheduledExecutorService(executor);
         this.reapingThresholdMs = reapingThresholdMs / EMPTY_COUNT_THRESHOLD;
-
-        LeaderLatch localLeaderLatch = null;
-        if ( leaderPath != null )
+        this.leaderLatch = leaderLatch;
+        if (leaderLatch != null)
         {
-            localLeaderLatch = makeLeaderLatch(client, leaderPath);
+            addListenerToLeaderLatch(leaderLatch);
         }
-        leaderLatch = localLeaderLatch;
+        this.ownsLeaderLatch = ownsLeaderLatch;
     }
 
+
     /**
      * Add a path (using Mode.REAP_INDEFINITELY) to be checked by the reaper. The path will be checked periodically
      * until the reaper is closed.
@@ -200,7 +229,7 @@
     {
         Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once");
 
-        if ( leaderLatch != null )
+        if ( leaderLatch != null && ownsLeaderLatch)
         {
             leaderLatch.start();
         }
@@ -212,7 +241,7 @@
         if ( state.compareAndSet(State.STARTED, State.CLOSED) )
         {
             executor.close();
-            if ( leaderLatch != null )
+            if ( leaderLatch != null && ownsLeaderLatch )
             {
                 leaderLatch.close();
             }
@@ -310,11 +339,9 @@
         return ThreadUtils.newSingleThreadScheduledExecutor("Reaper");
     }
 
-    private LeaderLatch makeLeaderLatch(CuratorFramework client, String leaderPath)
+    private void addListenerToLeaderLatch(LeaderLatch leaderLatch)
     {
-        reapingIsActive.set(false);
 
-        LeaderLatch localLeaderLatch = new LeaderLatch(client, leaderPath);
         LeaderLatchListener listener = new LeaderLatchListener()
         {
             @Override
@@ -333,7 +360,20 @@
                 reapingIsActive.set(false);
             }
         };
-        localLeaderLatch.addListener(listener);
-        return localLeaderLatch;
+        leaderLatch.addListener(listener);
+
+        reapingIsActive.set(leaderLatch.hasLeadership());
+    }
+
+    private static LeaderLatch makeLeaderLatchIfPathNotNull(CuratorFramework client, String leaderPath)
+    {
+        if (leaderPath == null)
+        {
+            return null;
+        }
+        else
+        {
+            return new LeaderLatch(client, leaderPath);
+        }
     }
 }
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/StandardLockInternalsDriver.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/StandardLockInternalsDriver.java
index 0c9b6de..43184f5 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/StandardLockInternalsDriver.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/StandardLockInternalsDriver.java
@@ -47,11 +47,11 @@
         String ourPath;
         if ( lockNodeBytes != null )
         {
-            ourPath = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes);
+            ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes);
         }
         else
         {
-            ourPath = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path);
+            ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path);
         }
         return ourPath;
     }
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java
index 98b09c9..0b482ef 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java
@@ -20,6 +20,7 @@
 package org.apache.curator.framework.recipes.nodes;
 
 import com.google.common.base.Preconditions;
+
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.WatcherRemoveCuratorFramework;
 import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable;
@@ -32,14 +33,17 @@
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.Watcher.Event.EventType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
+
 import org.apache.curator.utils.PathUtils;
 
 /**
@@ -68,7 +72,10 @@
         @Override
         public void process(WatchedEvent event)
         {
-            createNode();
+        	if ( event.getType() == EventType.NodeDeleted)
+        	{
+        		createNode();
+        	}
         }
     };
     private final BackgroundCallback checkExistsCallback = new BackgroundCallback()
@@ -82,6 +89,21 @@
             }
         }
     };
+    private final BackgroundCallback setDataCallback = new BackgroundCallback() {
+		
+		@Override
+		public void processResult(CuratorFramework client, CuratorEvent event)
+				throws Exception {
+			//If the result is ok then initialisation is complete (if we're still initialising)
+			//Don't retry on other errors as the only recoverable cases will be connection loss
+			//and the node not existing, both of which are already handled by other watches.
+			if ( event.getResultCode() == KeeperException.Code.OK.intValue() )
+			{
+				//Update is ok, mark initialisation as complete if required.
+				initialisationComplete();
+			}
+		}
+	};	
     private final ConnectionStateListener connectionStateListener = new ConnectionStateListener()
     {
         @Override
@@ -189,12 +211,12 @@
      * @param basePath the base path for the node
      * @param data     data for the node
      */
-    public PersistentEphemeralNode(CuratorFramework client, Mode mode, String basePath, byte[] data)
+    public PersistentEphemeralNode(CuratorFramework client, Mode mode, String basePath, byte[] initData)
     {
         this.client = Preconditions.checkNotNull(client, "client cannot be null").newWatcherRemoveCuratorFramework();
         this.basePath = PathUtils.validatePath(basePath);
         this.mode = Preconditions.checkNotNull(mode, "mode cannot be null");
-        data = Preconditions.checkNotNull(data, "data cannot be null");
+        final byte[] data = Preconditions.checkNotNull(initData, "data cannot be null");
 
         backgroundCallback = new BackgroundCallback()
         {
@@ -202,9 +224,11 @@
             public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
             {
                 String path = null;
+                boolean nodeExists = false;
                 if ( event.getResultCode() == KeeperException.Code.NODEEXISTS.intValue() )
-                {
-                    path = event.getPath();
+                {                	
+                	path = event.getPath();
+                	nodeExists = true;
                 }
                 else if ( event.getResultCode() == KeeperException.Code.OK.intValue() )
                 {
@@ -215,10 +239,13 @@
                     nodePath.set(path);
                     watchNode();
 
-                    CountDownLatch localLatch = initialCreateLatch.getAndSet(null);
-                    if ( localLatch != null )
+                    if(nodeExists)
                     {
-                        localLatch.countDown();
+                       client.setData().inBackground(setDataCallback).forPath(getActualPath(), getData());
+                    }
+                    else
+                    {
+                    	initialisationComplete();
                     }
                 }
                 else
@@ -228,9 +255,18 @@
             }
         };
 
-        createMethod = mode.isProtected() ? client.create().creatingParentsIfNeeded().withProtection() : client.create().creatingParentsIfNeeded();
+        createMethod = mode.isProtected() ? client.create().creatingParentContainersIfNeeded().withProtection() : client.create().creatingParentContainersIfNeeded();
         this.data.set(Arrays.copyOf(data, data.length));
     }
+    
+    private void initialisationComplete()
+    {
+        CountDownLatch localLatch = initialCreateLatch.getAndSet(null);
+        if ( localLatch != null )
+        {
+            localLatch.countDown();
+        }
+    }
 
     /**
      * You must call start() to initiate the persistent ephemeral node. An attempt to create the node
@@ -305,10 +341,14 @@
         this.data.set(Arrays.copyOf(data, data.length));
         if ( isActive() )
         {
-            client.setData().inBackground().forPath(getActualPath(), this.data.get());
+            client.setData().inBackground().forPath(getActualPath(), getData());
         }
     }
 
+    private byte[] getData() {
+        return this.data.get();
+    }
+
     private void deleteNode() throws Exception
     {
         String localNodePath = nodePath.getAndSet(null);
@@ -339,7 +379,7 @@
         try
         {
             String existingPath = nodePath.get();
-            String createPath = (existingPath != null) ? existingPath : basePath;
+            String createPath = (existingPath != null && !mode.isProtected()) ? existingPath : basePath;
             createMethod.withMode(mode.getCreateMode(existingPath != null)).inBackground(backgroundCallback).forPath(createPath, data.get());
         }
         catch ( Exception e )
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/DistributedQueue.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/DistributedQueue.java
index 9dd2217..3ed3218 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/DistributedQueue.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/DistributedQueue.java
@@ -163,7 +163,7 @@
 
         try
         {
-            client.create().creatingParentsIfNeeded().forPath(queuePath);
+            client.create().creatingParentContainersIfNeeded().forPath(queuePath);
         }
         catch ( KeeperException.NodeExistsException ignore )
         {
@@ -173,7 +173,7 @@
         {
             try
             {
-                client.create().creatingParentsIfNeeded().forPath(lockPath);
+                client.create().creatingParentContainersIfNeeded().forPath(lockPath);
             }
             catch ( KeeperException.NodeExistsException ignore )
             {
@@ -756,7 +756,7 @@
                 client.inTransaction()
                     .delete().forPath(itemPath)
                     .and()
-                    .create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(makeItemPath(), bytes)
+                    .create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(itemPath, bytes)
                     .and()
                     .commit();
             }
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/QueueSharder.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/QueueSharder.java
index 2dbd484..455794c 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/QueueSharder.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/QueueSharder.java
@@ -111,7 +111,7 @@
     {
         Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once");
 
-        client.newNamespaceAwareEnsurePath(queuePath).ensure(client.getZookeeperClient());
+        client.createContainers(queuePath);
 
         getInitialQueues();
         leaderLatch.start();
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/SimpleDistributedQueue.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/SimpleDistributedQueue.java
index 0c386cd..9650ffb 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/SimpleDistributedQueue.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/SimpleDistributedQueue.java
@@ -19,7 +19,7 @@
 package org.apache.curator.framework.recipes.queue;
 
 import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.utils.EnsurePath;
+import org.apache.curator.utils.PathUtils;
 import org.apache.curator.utils.ZKPaths;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
@@ -32,7 +32,6 @@
 import java.util.NoSuchElementException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import org.apache.curator.utils.PathUtils;
 
 /**
  * <p>
@@ -50,7 +49,6 @@
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final CuratorFramework client;
     private final String path;
-    private final EnsurePath ensurePath;
 
     private final String PREFIX = "qn-";
 
@@ -62,7 +60,6 @@
     {
         this.client = client;
         this.path = PathUtils.validatePath(path);
-        ensurePath = client.newNamespaceAwareEnsurePath(path);
     }
 
     /**
@@ -119,10 +116,8 @@
      */
     public boolean offer(byte[] data) throws Exception
     {
-        ensurePath.ensure(client.getZookeeperClient());
-
         String thisPath = ZKPaths.makePath(path, PREFIX);
-        client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(thisPath, data);
+        client.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(thisPath, data);
         return true;
     }
 
@@ -181,7 +176,7 @@
 
     private byte[] internalPoll(long timeout, TimeUnit unit) throws Exception
     {
-        ensurePath.ensure(client.getZookeeperClient());
+        ensurePath();
 
         long            startMs = System.currentTimeMillis();
         boolean         hasTimeout = (unit != null);
@@ -220,9 +215,14 @@
         }
     }
 
+    private void ensurePath() throws Exception
+    {
+        client.createContainers(path);
+    }
+
     private byte[] internalElement(boolean removeIt, Watcher watcher) throws Exception
     {
-        ensurePath.ensure(client.getZookeeperClient());
+        ensurePath();
 
         List<String> nodes;
         try
diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/SharedValue.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/SharedValue.java
index e6d8157..dcd30ba 100644
--- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/SharedValue.java
+++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/SharedValue.java
@@ -221,7 +221,7 @@
         client.getConnectionStateListenable().addListener(connectionStateListener);
         try
         {
-            client.create().creatingParentsIfNeeded().forPath(path, seedValue);
+            client.create().creatingParentContainersIfNeeded().forPath(path, seedValue);
         }
         catch ( KeeperException.NodeExistsException ignore )
         {
diff --git a/curator-recipes/src/site/confluence/index.confluence b/curator-recipes/src/site/confluence/index.confluence
index a43963c..4f3a032 100644
--- a/curator-recipes/src/site/confluence/index.confluence
+++ b/curator-recipes/src/site/confluence/index.confluence
@@ -1,6 +1,8 @@
 h1. Recipes
 
 Curator implements all of the recipes listed on the ZooKeeper recipes doc (except two phase commit). Click on the recipe name below for detailed documentation.
+NOTE: Most Curator recipes will auto-create parent nodes of paths given to the recipe as CreateMode.CONTAINER. Also, see [[Tech Note 7|https://cwiki.apache.org/confluence/display/CURATOR/TN7]]
+regarding "Curator Recipes Own Their ZNode/Paths".
 
 ||Elections||
 |[[Leader Latch|leader-latch.html]] \- In distributed computing, leader election is the process of designating a single process as the organizer of some task distributed among several computers (nodes). Before the task is begun, all network nodes are unaware which node will serve as the "leader," or coordinator, of the task. After a leader election algorithm has been run, however, each node throughout the network recognizes a particular, unique node as the task leader.|
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/client/TestBackgroundStates.java b/curator-recipes/src/test/java/org/apache/curator/framework/client/TestBackgroundStates.java
index ac7c7a9..f264615 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/client/TestBackgroundStates.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/client/TestBackgroundStates.java
@@ -48,10 +48,11 @@
 
         Timing timing = new Timing();
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+        PersistentEphemeralNode node = null;
         try
         {
             client.start();
-            PersistentEphemeralNode node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, "/abc/node", "hello".getBytes());
+            node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, "/abc/node", "hello".getBytes());
             node.start();
 
             final CountDownLatch connectedLatch = new CountDownLatch(1);
@@ -79,9 +80,7 @@
             Assert.assertTrue(timing.awaitLatch(connectedLatch));
             timing.sleepABit();
             Assert.assertTrue(node.waitForInitialCreate(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS));
-            server.close();
-            timing.sleepABit();
-            server = new TestingServer(server.getPort());
+            server.restart();
             timing.sleepABit();
             Assert.assertTrue(timing.awaitLatch(reconnectedLatch));
             timing.sleepABit();
@@ -90,6 +89,7 @@
         finally
         {
             CloseableUtils.closeQuietly(client);
+            CloseableUtils.closeQuietly(node);
         }
     }
 
@@ -103,8 +103,6 @@
         try
         {
             client.start();
-            PersistentEphemeralNode node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, "/abc/node", "hello".getBytes());
-            node.start();
 
             final BlockingQueue<ConnectionState> stateVector = Queues.newLinkedBlockingQueue(1);
             ConnectionStateListener listener = new ConnectionStateListener()
@@ -121,10 +119,10 @@
             client.getConnectionStateListenable().addListener(listener);
             server = new TestingServer(server.getPort());
             Assert.assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED);
-            server.close();
+            server.stop();
             Assert.assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED);
             Assert.assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST);
-            server = new TestingServer(server.getPort());
+            server.restart();
             Assert.assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED);
             server.close();
             Assert.assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED);
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/client/TestResetConnectionWithBackgroundFailure.java b/curator-recipes/src/test/java/org/apache/curator/framework/client/TestResetConnectionWithBackgroundFailure.java
index 83025af..7d2cb89 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/client/TestResetConnectionWithBackgroundFailure.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/client/TestResetConnectionWithBackgroundFailure.java
@@ -51,7 +51,7 @@
     @Test
     public void testConnectionStateListener() throws Exception
     {
-        server.close();
+        server.stop();
 
         final StringBuilder listenerSequence = new StringBuilder();
         LeaderSelector selector = null;
@@ -85,15 +85,15 @@
 
             client.getConnectionStateListenable().addListener(listener1);
             log.debug("Starting ZK server");
-            server = new TestingServer(server.getPort());
+            server.restart();
             timing.forWaiting().sleepABit();
 
             log.debug("Stopping ZK server");
-            server.close();
+            server.stop();
             timing.forWaiting().sleepABit();
 
             log.debug("Starting ZK server");
-            server = new TestingServer(server.getPort());
+            server.restart();
             timing.forWaiting().sleepABit();
 
             log.debug("Stopping ZK server");
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestPathChildrenCache.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestPathChildrenCache.java
index ffcf251..3571ca7 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestPathChildrenCache.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestPathChildrenCache.java
@@ -31,6 +31,7 @@
 import org.apache.curator.framework.imps.TestCleanState;
 import org.apache.curator.retry.RetryOneTime;
 import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.test.ExecuteCalledWatchingExecutorService;
 import org.apache.curator.test.KillSession;
 import org.apache.curator.test.Timing;
 import org.apache.curator.utils.CloseableUtils;
@@ -101,82 +102,6 @@
     }
 
     @Test
-    public void testClientClosedDuringRefreshErrorMessage() throws Exception
-    {
-        // Fiddle with logging so we can intercept the error events for org.apache.curator
-        final List<LoggingEvent> events = Lists.newArrayList();
-        Collection<String> messages = Collections2.transform(events, new Function<LoggingEvent, String>() {
-            @Override
-            public String apply(LoggingEvent loggingEvent) {
-                return loggingEvent.getRenderedMessage();
-            }
-        });
-        Appender appender = new AppenderSkeleton(true) {
-            @Override
-            protected void append(LoggingEvent event) {
-                if (event.getLevel().equals(Level.ERROR)) {
-                    events.add(event);
-                }
-            }
-
-            @Override
-            public void close() {
-
-            }
-
-            @Override
-            public boolean requiresLayout() {
-                return false;
-            }
-        };
-        appender.setLayout(new SimpleLayout());
-        Logger logger = Logger.getLogger("org.apache.curator");
-        logger.addAppender(appender);
-
-        // Check that we can intercept error log messages from the client
-        CuratorFramework clientTestLogSetup = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
-        clientTestLogSetup.start();
-        try {
-            Pathable<byte[]> callback = clientTestLogSetup.getData().inBackground(new BackgroundCallback() {
-                @Override
-                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
-                    // ignore result
-                }
-            });
-            CloseableUtils.closeQuietly(clientTestLogSetup);
-            callback.forPath("/test/aaa"); // this should cause an error log message
-        } catch (IllegalStateException ise) {
-            // ok, excpected
-        } finally {
-            CloseableUtils.closeQuietly(clientTestLogSetup);
-        }
-
-        Assert.assertTrue(messages.contains("Background exception was not retry-able or retry gave up"),
-                "The expected error was not logged. This is an indication that this test could be broken due to" +
-                        " an incomplete logging setup.");
-
-        // try to reproduce a bunch of times because it doesn't happen reliably
-        for (int i = 0; i < 50; i++) {
-            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
-            client.start();
-            try {
-                PathChildrenCache cache = new PathChildrenCache(client, "/test", true);
-                cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
-                client.newNamespaceAwareEnsurePath("/test/aaa").ensure(client.getZookeeperClient());
-                client.setData().forPath("/test/aaa", new byte[]{1, 2, 3, 4, 5});
-                cache.rebuildNode("/test/aaa");
-                CloseableUtils.closeQuietly(cache);
-            } finally {
-                CloseableUtils.closeQuietly(client);
-            }
-        }
-
-        Assert.assertEquals(messages.size(), 1, "There should not be any error events except for the test message, " +
-                "but got:\n" + Joiner.on("\n").join(messages));
-
-    }
-
-    @Test
     public void testAsyncInitialPopulation() throws Exception
     {
         Timing timing = new Timing();
@@ -1047,127 +972,4 @@
             TestCleanState.closeAndTestClean(client);
         }
     }
-
-    public static class ExecuteCalledWatchingExecutorService extends DelegatingExecutorService
-    {
-        boolean executeCalled = false;
-
-        public ExecuteCalledWatchingExecutorService(ExecutorService delegate)
-        {
-            super(delegate);
-        }
-
-        @Override
-        public synchronized void execute(Runnable command)
-        {
-            executeCalled = true;
-            super.execute(command);
-        }
-
-        public synchronized boolean isExecuteCalled()
-        {
-            return executeCalled;
-        }
-
-        public synchronized void setExecuteCalled(boolean executeCalled)
-        {
-            this.executeCalled = executeCalled;
-        }
-    }
-
-    public static class DelegatingExecutorService implements ExecutorService
-    {
-        private final ExecutorService delegate;
-
-        public DelegatingExecutorService(
-                ExecutorService delegate
-        )
-        {
-            this.delegate = delegate;
-        }
-
-
-        @Override
-        public void shutdown()
-        {
-            delegate.shutdown();
-        }
-
-        @Override
-        public List<Runnable> shutdownNow()
-        {
-            return delegate.shutdownNow();
-        }
-
-        @Override
-        public boolean isShutdown()
-        {
-            return delegate.isShutdown();
-        }
-
-        @Override
-        public boolean isTerminated()
-        {
-            return delegate.isTerminated();
-        }
-
-        @Override
-        public boolean awaitTermination(long timeout, TimeUnit unit)
-                throws InterruptedException
-        {
-            return delegate.awaitTermination(timeout, unit);
-        }
-
-        @Override
-        public <T> Future<T> submit(Callable<T> task)
-        {
-            return delegate.submit(task);
-        }
-
-        @Override
-        public <T> Future<T> submit(Runnable task, T result)
-        {
-            return delegate.submit(task, result);
-        }
-
-        @Override
-        public Future<?> submit(Runnable task)
-        {
-            return delegate.submit(task);
-        }
-
-        @Override
-        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
-                throws InterruptedException
-        {
-            return delegate.invokeAll(tasks);
-        }
-
-        @Override
-        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
-                throws InterruptedException
-        {
-            return delegate.invokeAll(tasks, timeout, unit);
-        }
-
-        @Override
-        public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
-                throws InterruptedException, ExecutionException
-        {
-            return delegate.invokeAny(tasks);
-        }
-
-        @Override
-        public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
-                throws InterruptedException, ExecutionException, TimeoutException
-        {
-            return delegate.invokeAny(tasks, timeout, unit);
-        }
-
-        @Override
-        public void execute(Runnable command)
-        {
-            delegate.execute(command);
-        }
-    }
 }
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestTreeCache.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestTreeCache.java
index bfd051a..0bccb54 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestTreeCache.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/TestTreeCache.java
@@ -56,6 +56,19 @@
     }
 
     @Test
+    public void testCreateParents() throws Exception
+    {
+        cache = newTreeCacheWithListeners(client, "/one/two/three");
+        cache.start();
+        Assert.assertNull(client.checkExists().forPath("/one/two/three"));
+        cache.close();
+
+        cache = TreeCache.newBuilder(client, "/one/two/three").setCreateParentNodes(true).build();
+        cache.start();
+        Assert.assertNotNull(client.checkExists().forPath("/one/two/three"));
+    }
+
+    @Test
     public void testStartEmpty() throws Exception
     {
         cache = newTreeCacheWithListeners(client, "/test");
@@ -68,6 +81,20 @@
     }
 
     @Test
+    public void testStartEmptyDeeper() throws Exception
+    {
+        cache = newTreeCacheWithListeners(client, "/test/foo/bar");
+        cache.start();
+        assertEvent(TreeCacheEvent.Type.INITIALIZED);
+
+        client.create().creatingParentsIfNeeded().forPath("/test/foo");
+        assertNoMoreEvents();
+        client.create().forPath("/test/foo/bar");
+        assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo/bar");
+        assertNoMoreEvents();
+    }
+
+    @Test
     public void testDepth0() throws Exception
     {
         client.create().forPath("/test");
@@ -302,6 +329,35 @@
         client.create().forPath("/test/foo", "two".getBytes());
         assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo");
 
+        client.delete().forPath("/test/foo");
+        assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/foo");
+        client.create().forPath("/test/foo", "two".getBytes());
+        assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo");
+
+        assertNoMoreEvents();
+    }
+
+    @Test
+    public void testDeleteThenCreateRoot() throws Exception
+    {
+        client.create().forPath("/test");
+        client.create().forPath("/test/foo", "one".getBytes());
+
+        cache = newTreeCacheWithListeners(client, "/test/foo");
+        cache.start();
+        assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo");
+        assertEvent(TreeCacheEvent.Type.INITIALIZED);
+
+        client.delete().forPath("/test/foo");
+        assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/foo");
+        client.create().forPath("/test/foo", "two".getBytes());
+        assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo");
+
+        client.delete().forPath("/test/foo");
+        assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/foo");
+        client.create().forPath("/test/foo", "two".getBytes());
+        assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo");
+
         assertNoMoreEvents();
     }
 
@@ -339,11 +395,16 @@
         assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test");
         assertEvent(TreeCacheEvent.Type.INITIALIZED);
         Assert.assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of());
+        Assert.assertNull(cache.getCurrentChildren("/t"));
+        Assert.assertNull(cache.getCurrentChildren("/testing"));
 
         client.create().forPath("/test/one", "hey there".getBytes());
         assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one");
         Assert.assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("one"));
         Assert.assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there");
+        Assert.assertEquals(cache.getCurrentChildren("/test/one").keySet(), ImmutableSet.of());
+        Assert.assertNull(cache.getCurrentChildren("/test/o"));
+        Assert.assertNull(cache.getCurrentChildren("/test/onely"));
 
         client.setData().forPath("/test/one", "sup!".getBytes());
         assertEvent(TreeCacheEvent.Type.NODE_UPDATED, "/test/one");
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/TestLeaderSelector.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/TestLeaderSelector.java
index ec909f7..c7f415c 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/TestLeaderSelector.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/TestLeaderSelector.java
@@ -23,6 +23,7 @@
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
 import org.apache.curator.framework.state.ConnectionState;
+import org.apache.curator.framework.state.ConnectionStateListener;
 import org.apache.curator.retry.RetryOneTime;
 import org.apache.curator.test.BaseClassForTests;
 import org.apache.curator.test.KillSession;
@@ -34,6 +35,7 @@
 import org.testng.internal.annotations.Sets;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -48,6 +50,71 @@
     private static final String PATH_NAME = "/one/two/me";
 
     @Test
+    public void testLeaderNodeDeleteOnInterrupt() throws Exception
+    {
+        Timing timing = new Timing();
+        LeaderSelector selector = null;
+        CuratorFramework client = null;
+        try
+        {
+            client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+            final CountDownLatch reconnectedLatch = new CountDownLatch(1);
+            ConnectionStateListener connectionStateListener = new ConnectionStateListener()
+            {
+                @Override
+                public void stateChanged(CuratorFramework client, ConnectionState newState)
+                {
+                    if ( newState == ConnectionState.RECONNECTED )
+                    {
+                        reconnectedLatch.countDown();
+                    }
+                }
+            };
+            client.getConnectionStateListenable().addListener(connectionStateListener);
+            client.start();
+
+            final BlockingQueue<Thread> queue = new ArrayBlockingQueue<Thread>(1);
+            LeaderSelectorListener listener = new LeaderSelectorListener()
+            {
+                @Override
+                public void takeLeadership(CuratorFramework client) throws Exception
+                {
+                    queue.add(Thread.currentThread());
+                    try
+                    {
+                        Thread.currentThread().join();
+                    }
+                    catch ( InterruptedException e )
+                    {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+
+                @Override
+                public void stateChanged(CuratorFramework client, ConnectionState newState)
+                {
+                }
+            };
+            selector = new LeaderSelector(client, "/leader", listener);
+            selector.start();
+
+            Thread leaderThread = queue.take();
+            server.stop();
+            leaderThread.interrupt();
+            server.restart();
+            Assert.assertTrue(timing.awaitLatch(reconnectedLatch));
+            timing.sleepABit();
+
+            Assert.assertEquals(client.getChildren().forPath("/leader").size(), 0);
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(selector);
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
     public void testInterruptLeadershipWithRequeue() throws Exception
     {
         Timing timing = new Timing();
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestChildReaper.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestChildReaper.java
index 309bd99..cafb9a3 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestChildReaper.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestChildReaper.java
@@ -18,32 +18,158 @@
  */
 package org.apache.curator.framework.recipes.locks;
 
-import org.apache.curator.test.BaseClassForTests;
-import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.recipes.leader.LeaderLatch;
+import org.apache.curator.retry.ExponentialBackoffRetry;
 import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.test.TestingServer;
 import org.apache.curator.test.Timing;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.data.Stat;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 import java.util.Random;
+import java.util.concurrent.CountDownLatch;
 
 public class TestChildReaper extends BaseClassForTests
 {
     @Test
-    public void     testSomeNodes() throws Exception
+    public void testMaxChildren() throws Exception
     {
+        server.close();
 
-        Timing                  timing = new Timing();
-        ChildReaper             reaper = null;
-        CuratorFramework        client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+        final int LARGE_QTY = 10000;
+
+        System.setProperty("jute.maxbuffer", "" + LARGE_QTY);
+        server = new TestingServer();
+        try
+        {
+            Timing timing = new Timing();
+            ChildReaper reaper = null;
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3));
+            try
+            {
+                client.start();
+
+                for ( int i = 0; i < LARGE_QTY; ++i )
+                {
+                    if ( (i % 1000) == 0 )
+                    {
+                        System.out.println(i);
+                    }
+                    client.create().creatingParentsIfNeeded().forPath("/big/node-" + i);
+                }
+
+                try
+                {
+                    client.getChildren().forPath("/big");
+                    Assert.fail("Should have been a connection loss");
+                }
+                catch ( KeeperException.ConnectionLossException e )
+                {
+                    // expected
+                }
+
+                final CountDownLatch latch = new CountDownLatch(1);
+                reaper = new ChildReaper(client, "/big", Reaper.Mode.REAP_UNTIL_DELETE, 1)
+                {
+                    @Override
+                    protected void warnMaxChildren(String path, Stat stat)
+                    {
+                        latch.countDown();
+                        super.warnMaxChildren(path, stat);
+                    }
+                };
+                reaper.setMaxChildren(100);
+                reaper.start();
+                Assert.assertTrue(timing.awaitLatch(latch));
+            }
+            finally
+            {
+                CloseableUtils.closeQuietly(reaper);
+                CloseableUtils.closeQuietly(client);
+            }
+        }
+        finally
+        {
+            System.clearProperty("jute.maxbuffer");
+        }
+    }
+
+    @Test
+    public void testLargeNodes() throws Exception
+    {
+        server.close();
+
+        final int LARGE_QTY = 10000;
+        final int SMALL_QTY = 100;
+
+        System.setProperty("jute.maxbuffer", "" + LARGE_QTY);
+        server = new TestingServer();
+        try
+        {
+            Timing timing = new Timing();
+            ChildReaper reaper = null;
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3));
+            try
+            {
+                client.start();
+
+                for ( int i = 0; i < LARGE_QTY; ++i )
+                {
+                    if ( (i % 1000) == 0 )
+                    {
+                        System.out.println(i);
+                    }
+                    client.create().creatingParentsIfNeeded().forPath("/big/node-" + i);
+
+                    if ( i < SMALL_QTY )
+                    {
+                        client.create().creatingParentsIfNeeded().forPath("/small/node-" + i);
+                    }
+                }
+
+                reaper = new ChildReaper(client, "/foo", Reaper.Mode.REAP_UNTIL_DELETE, 1);
+                reaper.start();
+
+                reaper.addPath("/big");
+                reaper.addPath("/small");
+
+                int count = -1;
+                for ( int i = 0; (i < 10) && (count != 0); ++i )
+                {
+                    timing.sleepABit();
+                    count = client.checkExists().forPath("/small").getNumChildren();
+                }
+                Assert.assertEquals(count, 0);
+            }
+            finally
+            {
+                CloseableUtils.closeQuietly(reaper);
+                CloseableUtils.closeQuietly(client);
+            }
+        }
+        finally
+        {
+            System.clearProperty("jute.maxbuffer");
+        }
+    }
+
+    @Test
+    public void testSomeNodes() throws Exception
+    {
+        Timing timing = new Timing();
+        ChildReaper reaper = null;
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
         try
         {
             client.start();
 
-            Random              r = new Random();
-            int                 nonEmptyNodes = 0;
+            Random r = new Random();
+            int nonEmptyNodes = 0;
             for ( int i = 0; i < 10; ++i )
             {
                 client.create().creatingParentsIfNeeded().forPath("/test/" + Integer.toString(i));
@@ -59,7 +185,7 @@
 
             timing.forWaiting().sleepABit();
 
-            Stat    stat = client.checkExists().forPath("/test");
+            Stat stat = client.checkExists().forPath("/test");
             Assert.assertEquals(stat.getNumChildren(), nonEmptyNodes);
         }
         finally
@@ -70,11 +196,11 @@
     }
 
     @Test
-    public void     testSimple() throws Exception
+    public void testSimple() throws Exception
     {
-        Timing                  timing = new Timing();
-        ChildReaper             reaper = null;
-        CuratorFramework        client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+        Timing timing = new Timing();
+        ChildReaper reaper = null;
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
         try
         {
             client.start();
@@ -89,7 +215,7 @@
 
             timing.forWaiting().sleepABit();
 
-            Stat    stat = client.checkExists().forPath("/test");
+            Stat stat = client.checkExists().forPath("/test");
             Assert.assertEquals(stat.getNumChildren(), 0);
         }
         finally
@@ -100,11 +226,95 @@
     }
 
     @Test
-    public void     testNamespace() throws Exception
+    public void testLeaderElection() throws Exception
     {
-        Timing                  timing = new Timing();
-        ChildReaper             reaper = null;
-        CuratorFramework        client = CuratorFrameworkFactory.builder()
+        Timing timing = new Timing();
+        ChildReaper reaper = null;
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+        LeaderLatch otherLeader = null;
+        try
+        {
+            client.start();
+
+            for ( int i = 0; i < 10; ++i )
+            {
+                client.create().creatingParentsIfNeeded().forPath("/test/" + Integer.toString(i));
+            }
+
+            otherLeader = new LeaderLatch(client, "/test-leader");
+            otherLeader.start();
+            otherLeader.await();
+
+            reaper = new ChildReaper(client, "/test", Reaper.Mode.REAP_UNTIL_DELETE, ChildReaper.newExecutorService(), 1, "/test-leader");
+            reaper.start();
+
+            timing.forWaiting().sleepABit();
+
+            //Should not have reaped anything at this point since otherLeader is still leader
+            Stat stat = client.checkExists().forPath("/test");
+            Assert.assertEquals(stat.getNumChildren(), 10);
+
+            CloseableUtils.closeQuietly(otherLeader);
+
+            timing.forWaiting().sleepABit();
+
+            stat = client.checkExists().forPath("/test");
+            Assert.assertEquals(stat.getNumChildren(), 0);
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(reaper);
+            if ( otherLeader != null && otherLeader.getState() == LeaderLatch.State.STARTED )
+            {
+                CloseableUtils.closeQuietly(otherLeader);
+            }
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testMultiPath() throws Exception
+    {
+        Timing timing = new Timing();
+        ChildReaper reaper = null;
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+        try
+        {
+            client.start();
+
+            for ( int i = 0; i < 10; ++i )
+            {
+                client.create().creatingParentsIfNeeded().forPath("/test1/" + Integer.toString(i));
+                client.create().creatingParentsIfNeeded().forPath("/test2/" + Integer.toString(i));
+                client.create().creatingParentsIfNeeded().forPath("/test3/" + Integer.toString(i));
+            }
+
+            reaper = new ChildReaper(client, "/test2", Reaper.Mode.REAP_UNTIL_DELETE, 1);
+            reaper.start();
+            reaper.addPath("/test1");
+
+            timing.forWaiting().sleepABit();
+
+            Stat stat = client.checkExists().forPath("/test1");
+            Assert.assertEquals(stat.getNumChildren(), 0);
+            stat = client.checkExists().forPath("/test2");
+            Assert.assertEquals(stat.getNumChildren(), 0);
+            stat = client.checkExists().forPath("/test3");
+            Assert.assertEquals(stat.getNumChildren(), 10);
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(reaper);
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
+    public void testNamespace() throws Exception
+    {
+        Timing timing = new Timing();
+        ChildReaper reaper = null;
+        CuratorFramework client = CuratorFrameworkFactory.builder()
             .connectString(server.getConnectString())
             .sessionTimeoutMs(timing.session())
             .connectionTimeoutMs(timing.connection())
@@ -125,7 +335,7 @@
 
             timing.forWaiting().sleepABit();
 
-            Stat    stat = client.checkExists().forPath("/test");
+            Stat stat = client.checkExists().forPath("/test");
             Assert.assertEquals(stat.getNumChildren(), 0);
 
             stat = client.usingNamespace(null).checkExists().forPath("/foo/test");
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMultiMutex.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMultiMutex.java
index df6a2f5..dceff88 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMultiMutex.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMultiMutex.java
@@ -31,8 +31,8 @@
 
 public class TestInterProcessMultiMutex extends TestInterProcessMutexBase
 {
-    private static final String     LOCK_PATH_1 = "/locks/our-lock-1";
-    private static final String     LOCK_PATH_2 = "/locks/our-lock-2";
+    private static final String     LOCK_PATH_1 = LOCK_BASE_PATH + "/our-lock-1";
+    private static final String     LOCK_PATH_2 = LOCK_BASE_PATH + "/our-lock-2";
 
     @Override
     protected InterProcessLock makeLock(CuratorFramework client)
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMutex.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMutex.java
index d6f8a1d..c37d88d 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMutex.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMutex.java
@@ -36,7 +36,7 @@
 
 public class TestInterProcessMutex extends TestInterProcessMutexBase
 {
-    private static final String LOCK_PATH = "/locks/our-lock";
+    private static final String LOCK_PATH = LOCK_BASE_PATH + "/our-lock";
 
     @Override
     protected InterProcessLock makeLock(CuratorFramework client)
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMutexBase.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMutexBase.java
index 49e5d19..f44d238 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMutexBase.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessMutexBase.java
@@ -29,6 +29,9 @@
 import org.apache.curator.test.BaseClassForTests;
 import org.apache.curator.test.KillSession;
 import org.apache.curator.test.Timing;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.curator.utils.ZKPaths;
+import org.apache.zookeeper.CreateMode;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 import java.util.List;
@@ -45,6 +48,8 @@
 
 public abstract class TestInterProcessMutexBase extends BaseClassForTests
 {
+    protected static final String LOCK_BASE_PATH = "/locks";
+
     private volatile CountDownLatch waitLatchForBar = null;
     private volatile CountDownLatch countLatchForBar = null;
 
@@ -180,6 +185,82 @@
     }
 
     @Test
+    public void testContainerCleanup() throws Exception
+    {
+        if ( !ZKPaths.hasContainerSupport() )
+        {
+            System.out.println("ZooKeeper version does not support Containers. Skipping test");
+            return;
+        }
+
+        server.close();
+
+        System.setProperty("container.checkIntervalMs", "10");
+        try
+        {
+            server = new TestingServer();
+
+            final int THREAD_QTY = 10;
+
+            ExecutorService service = null;
+            final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(100, 3));
+            try
+            {
+                client.start();
+
+                List<Future<Object>> threads = Lists.newArrayList();
+                service = Executors.newCachedThreadPool();
+                for ( int i = 0; i < THREAD_QTY; ++i )
+                {
+                    Future<Object> t = service.submit
+                    (
+                        new Callable<Object>()
+                        {
+                            @Override
+                            public Object call() throws Exception
+                            {
+                                InterProcessLock lock = makeLock(client);
+                                lock.acquire();
+                                try
+                                {
+                                    Thread.sleep(10);
+                                }
+                                finally
+                                {
+                                    lock.release();
+                                }
+                                return null;
+                            }
+                        }
+                    );
+                    threads.add(t);
+                }
+
+                for ( Future<Object> t : threads )
+                {
+                    t.get();
+                }
+
+                new Timing().sleepABit();
+
+                Assert.assertNull(client.checkExists().forPath(LOCK_BASE_PATH));
+            }
+            finally
+            {
+                if ( service != null )
+                {
+                    service.shutdownNow();
+                }
+                CloseableUtils.closeQuietly(client);
+            }
+        }
+        finally
+        {
+            System.clearProperty("container.checkIntervalMs");
+        }
+    }
+
+    @Test
     public void testWithNamespace() throws Exception
     {
         CuratorFramework client = CuratorFrameworkFactory.builder().
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessSemaphore.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessSemaphore.java
index 2797b5f..3ba75d8 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessSemaphore.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessSemaphore.java
@@ -534,4 +534,44 @@
             TestCleanState.closeAndTestClean(client);
         }
     }
+
+    @Test
+    public void testChildReaperCleansUpLockNodes() throws Exception
+    {
+        Timing timing = new Timing();
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+        client.start();
+
+        ChildReaper childReaper = null;
+        try
+        {
+            InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test/lock", 1);
+            semaphore.returnLease(semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS));
+
+            Assert.assertTrue(client.getChildren().forPath("/test").size() > 0);
+
+            childReaper = new ChildReaper(
+                    client,
+                    "/test",
+                    Reaper.Mode.REAP_UNTIL_GONE,
+                    ChildReaper.newExecutorService(),
+                    1,
+                    "/test-leader",
+                    InterProcessSemaphoreV2.LOCK_SCHEMA
+            );
+            childReaper.start();
+
+            timing.forWaiting().sleepABit();
+
+            List<String> children = client.getChildren().forPath("/test");
+
+            Assert.assertEquals(children.size(), 0, "All children of /test should have been reaped");
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(childReaper);
+            CloseableUtils.closeQuietly(client);
+        }
+
+    }
 }
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessSemaphoreMutex.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessSemaphoreMutex.java
index 0af2bf4..cd8b83e 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessSemaphoreMutex.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestInterProcessSemaphoreMutex.java
@@ -23,7 +23,7 @@
 
 public class TestInterProcessSemaphoreMutex extends TestInterProcessMutexBase
 {
-    private static final String LOCK_PATH = "/locks/our-lock";
+    private static final String LOCK_PATH = LOCK_BASE_PATH + "/our-lock";
 
     @Override
     @Test(enabled = false)
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestReaper.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestReaper.java
index 83ec960..c47808f 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestReaper.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/TestReaper.java
@@ -21,6 +21,7 @@
 
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.recipes.leader.LeaderLatch;
 import org.apache.curator.framework.recipes.leader.LeaderSelector;
 import org.apache.curator.framework.recipes.leader.LeaderSelectorListener;
 import org.apache.curator.framework.state.ConnectionState;
@@ -48,7 +49,7 @@
 public class TestReaper extends BaseClassForTests
 {
     @Test
-    public void testUsingLeader() throws Exception
+    public void testUsingLeaderPath() throws Exception
     {
         final Timing timing = new Timing();
         CuratorFramework client = makeClient(timing, null);
@@ -118,6 +119,93 @@
     }
 
     @Test
+    public void testUsingLeaderLatch() throws Exception
+    {
+        final Timing timing = new Timing();
+        CuratorFramework client = makeClient(timing, null);
+        Reaper reaper1 = null;
+        Reaper reaper2 = null;
+        LeaderLatch leaderLatch1 = null;
+        LeaderLatch leaderLatch2 = null;
+        try
+        {
+            final AtomicInteger reaper1Count = new AtomicInteger();
+            leaderLatch1 = new LeaderLatch(client, "/reaper/leader");
+            reaper1 = new Reaper(client, Reaper.newExecutorService(), 1, leaderLatch1)
+            {
+                @Override
+                protected void reap(PathHolder holder)
+                {
+                    reaper1Count.incrementAndGet();
+                    super.reap(holder);
+                }
+            };
+
+            final AtomicInteger reaper2Count = new AtomicInteger();
+            leaderLatch2 = new LeaderLatch(client, "/reaper/leader");
+            reaper2 = new Reaper(client, Reaper.newExecutorService(), 1, leaderLatch2)
+            {
+                @Override
+                protected void reap(PathHolder holder)
+                {
+                    reaper2Count.incrementAndGet();
+                    super.reap(holder);
+                }
+            };
+
+            client.start();
+            client.create().creatingParentsIfNeeded().forPath("/one/two/three");
+
+            leaderLatch1.start();
+            leaderLatch2.start();
+
+            reaper1.start();
+            reaper2.start();
+
+            reaper1.addPath("/one/two/three");
+            reaper2.addPath("/one/two/three");
+
+            timing.sleepABit();
+
+            Assert.assertTrue((reaper1Count.get() == 0) || (reaper2Count.get() == 0));
+            Assert.assertTrue((reaper1Count.get() > 0) || (reaper2Count.get() > 0));
+
+            Reaper activeReaper;
+            LeaderLatch activeLeaderLeatch;
+            AtomicInteger inActiveReaperCount;
+            if ( reaper1Count.get() > 0 )
+            {
+                activeReaper = reaper1;
+                activeLeaderLeatch = leaderLatch1;
+                inActiveReaperCount = reaper2Count;
+            }
+            else
+            {
+                activeReaper = reaper2;
+                activeLeaderLeatch = leaderLatch2;
+                inActiveReaperCount = reaper1Count;
+            }
+            Assert.assertEquals(inActiveReaperCount.get(), 0);
+            activeReaper.close();
+            activeLeaderLeatch.close();
+            timing.sleepABit();
+            Assert.assertTrue(inActiveReaperCount.get() > 0);
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(reaper1);
+            CloseableUtils.closeQuietly(reaper2);
+            if (leaderLatch1 != null && LeaderLatch.State.STARTED == leaderLatch1.getState()) {
+                CloseableUtils.closeQuietly(leaderLatch1);
+            }
+            if (leaderLatch2 != null && LeaderLatch.State.STARTED == leaderLatch2.getState()) {
+                CloseableUtils.closeQuietly(leaderLatch2);
+            }
+            CloseableUtils.closeQuietly(client);
+        }
+    }
+
+    @Test
     public void testUsingManualLeader() throws Exception
     {
         final Timing timing = new Timing();
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java
index 5a58b2a..c81cc65 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java
@@ -22,26 +22,30 @@
 import com.google.common.collect.Lists;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.BackgroundCallback;
+import org.apache.curator.framework.api.CuratorEvent;
 import org.apache.curator.framework.imps.TestCleanState;
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.curator.framework.state.ConnectionStateListener;
 import org.apache.curator.retry.RetryOneTime;
 import org.apache.curator.test.BaseClassForTests;
 import org.apache.curator.test.KillSession;
-import org.apache.curator.test.TestingServer;
 import org.apache.curator.test.Timing;
 import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.utils.ZKPaths;
+import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.Watcher.Event.EventType;
 import org.apache.zookeeper.data.Stat;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
-import org.testng.annotations.AfterTest;
 import org.testng.annotations.Test;
+
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
@@ -76,7 +80,7 @@
     @Test
     public void testListenersReconnectedIsFast() throws Exception
     {
-        server.close();
+        server.stop();
 
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
         try
@@ -104,13 +108,13 @@
             };
             client.getConnectionStateListenable().addListener(listener);
             timing.sleepABit();
-            server = new TestingServer(server.getPort());
+            server.restart();
             Assert.assertTrue(timing.awaitLatch(connectedLatch));
             timing.sleepABit();
             Assert.assertTrue(node.waitForInitialCreate(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS));
-            server.close();
+            server.stop();
             timing.sleepABit();
-            server = new TestingServer(server.getPort());
+            server.restart();
             timing.sleepABit();
             Assert.assertTrue(timing.awaitLatch(reconnectedLatch));
         }
@@ -123,7 +127,7 @@
     @Test
     public void testNoServerAtStart() throws Exception
     {
-        server.close();
+        server.stop();
 
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
         PersistentEphemeralNode node = null;
@@ -149,7 +153,7 @@
 
             timing.sleepABit();
 
-            server = new TestingServer(server.getPort());
+            server.restart();
 
             Assert.assertTrue(timing.awaitLatch(connectedLatch));
 
@@ -462,6 +466,153 @@
             node.close();
         }
     }
+    
+    /**
+     * Test that if a persistent ephemeral node is created and the node already exists
+     * that if data is present in the PersistentEphermalNode that it is still set. 
+     * @throws Exception
+     */
+    @Test
+    public void testSetDataWhenNodeExists() throws Exception
+    {
+        CuratorFramework curator = newCurator();
+        curator.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(PATH, "InitialData".getBytes());
+        
+        byte[] data = "Hello World".getBytes();
+             
+        PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, data);
+        node.start();
+        try
+        {
+            node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS);
+            assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), data));
+        }
+        finally
+        {
+            node.close();
+        }
+    }
+    
+    @Test
+    public void testSetDataWhenDisconnected() throws Exception
+    {
+        CuratorFramework curator = newCurator();
+        
+        byte[] initialData = "Hello World".getBytes();
+        byte[] updatedData = "Updated".getBytes();
+             
+        PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, initialData);
+        node.start();
+        try
+        {
+            node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS);
+            assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), initialData));
+            
+            server.stop();
+            
+            final CountDownLatch dataUpdateLatch = new CountDownLatch(1);
+            
+            Watcher watcher = new Watcher()
+            {
+				@Override
+				public void process(WatchedEvent event)
+				{
+					if ( event.getType() == EventType.NodeDataChanged )
+					{
+						dataUpdateLatch.countDown();
+					}
+				}            	
+            };
+            
+            curator.getData().usingWatcher(watcher).inBackground().forPath(node.getActualPath());
+            
+            node.setData(updatedData);
+            server.restart();
+
+            assertTrue(timing.awaitLatch(dataUpdateLatch));
+                       
+            assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), updatedData));
+        }
+        finally
+        {
+            node.close();
+        }    	
+    }
+
+    @Test
+    public void testSetUpdatedDataWhenReconnected() throws Exception
+    {
+        CuratorFramework curator = newCurator();
+
+        byte[] initialData = "Hello World".getBytes();
+        byte[] updatedData = "Updated".getBytes();
+
+        PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, initialData);
+        node.start();
+        try
+        {
+            node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS);
+            assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), initialData));
+
+            node.setData(updatedData);
+            assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), updatedData));
+
+            server.restart();
+
+            final CountDownLatch dataUpdateLatch = new CountDownLatch(1);
+            curator.getData().inBackground(new BackgroundCallback() {
+
+                @Override
+                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
+                    dataUpdateLatch.countDown();
+                }
+            }).forPath(node.getActualPath());
+
+            assertTrue(timing.awaitLatch(dataUpdateLatch));
+
+            assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), updatedData));
+        }
+        finally
+        {
+            node.close();
+        }
+    }
+    
+    /**
+     * See CURATOR-190
+     * For protected nodes on reconnect the current protected name was passed to the create builder meaning that it got
+     * appended to the new protected node name. This meant that a new node got created on each reconnect.
+     * @throws Exception
+     */
+    @Test
+    public void testProtected() throws Exception
+    {
+        CuratorFramework curator = newCurator();
+
+        PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.PROTECTED_EPHEMERAL, PATH,
+                                                                   new byte[0]);
+        node.start();
+        try
+        {
+            node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS);
+            assertNodeExists(curator, node.getActualPath());
+
+            server.restart();            
+            
+            curator.blockUntilConnected(5, TimeUnit.SECONDS);
+
+            assertNodeExists(curator, node.getActualPath());
+            
+            //There should only be a single child, the persisted ephemeral node
+            List<String> children = curator.getChildren().forPath(DIR);
+            assertFalse(children == null);
+            assertEquals(children.size(), 1);
+        }
+        finally
+        {
+            node.close();
+        }
+    }
 
     private void assertNodeExists(CuratorFramework curator, String path) throws Exception
     {
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java
index 3a4eda8..ceff4c5 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java
@@ -39,7 +39,7 @@
     @Test
     public void testListenersReconnectedIsOK() throws Exception
     {
-        server.close();
+        server.stop();
 
         Timing timing = new Timing();
         CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
@@ -66,18 +66,15 @@
                     {
                         reconnectedLatch.countDown();
                     }
-                    System.out.println("XXXX " + newState);
                 }
             };
             client.getConnectionStateListenable().addListener(listener);
             timing.sleepABit();
-            server = new TestingServer(server.getPort());
+            server.restart();
             Assert.assertTrue(timing.awaitLatch(connectedLatch));
             timing.sleepABit();
             Assert.assertTrue(node.waitForInitialCreate(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS));
-            server.close();
-            timing.sleepABit();
-            server = new TestingServer(server.getPort());
+            server.restart();
             timing.sleepABit();
             Assert.assertTrue(timing.awaitLatch(reconnectedLatch));
             timing.sleepABit();
diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/TestDistributedIdQueue.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/TestDistributedIdQueue.java
index 30e552f..858086b 100644
--- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/TestDistributedIdQueue.java
+++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/TestDistributedIdQueue.java
@@ -124,4 +124,51 @@
             CloseableUtils.closeQuietly(client);
         }
     }
+
+    @Test
+    public void testRequeuingWithLock() throws Exception
+    {
+        DistributedIdQueue<TestQueueItem>  queue = null;
+        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+        client.start();
+        try
+        {
+            final CountDownLatch        consumingLatch = new CountDownLatch(1);
+
+            QueueConsumer<TestQueueItem> consumer = new QueueConsumer<TestQueueItem>()
+            {
+                @Override
+                public void consumeMessage(TestQueueItem message) throws Exception
+                {
+                    consumingLatch.countDown();
+                    // Throw an exception so requeuing occurs
+                    throw new Exception("Consumer failed");
+                }
+
+                @Override
+                public void stateChanged(CuratorFramework client, ConnectionState newState)
+                {
+                }
+            };
+
+            queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).lockPath("/locks").buildIdQueue();
+            queue.start();
+
+            queue.put(new TestQueueItem("test"), "id");
+
+            Assert.assertTrue(consumingLatch.await(10, TimeUnit.SECONDS));  // wait until consumer has it
+
+            // Sleep one more second
+
+            Thread.sleep(1000);
+
+            Assert.assertEquals(queue.remove("id"), 1);
+
+        }
+        finally
+        {
+            CloseableUtils.closeQuietly(queue);
+            CloseableUtils.closeQuietly(client);
+        }
+    }
 }
diff --git a/curator-test/pom.xml b/curator-test/pom.xml
index 1cbd93c..80eedb2 100644
--- a/curator-test/pom.xml
+++ b/curator-test/pom.xml
@@ -36,6 +36,11 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.apache.zookeeper</groupId>
+            <artifactId>zookeeper</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.javassist</groupId>
             <artifactId>javassist</artifactId>
         </dependency>
@@ -46,9 +51,20 @@
         </dependency>
 
         <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>provided</scope>
         </dependency>
+
+	<dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java b/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java
index d5c434f..6ef3bb0 100644
--- a/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java
+++ b/curator-test/src/main/java/org/apache/curator/test/BaseClassForTests.java
@@ -25,6 +25,7 @@
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.BeforeSuite;
+import java.io.IOException;
 import java.net.BindException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -35,20 +36,25 @@
     private static final int    RETRY_WAIT_MS = 5000;
     private static final String INTERNAL_PROPERTY_DONT_LOG_CONNECTION_ISSUES;
     private static final String INTERNAL_PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND;
+    private static final String INTERNAL_RETRY_FAILED_TESTS;
     static
     {
-        String s = null;
+        String logConnectionIssues = null;
+        String retryFailedTests = null;
         try
         {
             // use reflection to avoid adding a circular dependency in the pom
-            s = (String)Class.forName("org.apache.curator.utils.DebugUtils").getField("PROPERTY_DONT_LOG_CONNECTION_ISSUES").get(null);
+            Class<?> debugUtilsClazz = Class.forName("org.apache.curator.utils.DebugUtils");
+            logConnectionIssues = (String)debugUtilsClazz.getField("PROPERTY_DONT_LOG_CONNECTION_ISSUES").get(null);
+            retryFailedTests = (String)debugUtilsClazz.getField("PROPERTY_RETRY_FAILED_TESTS").get(null);
         }
         catch ( Exception e )
         {
             e.printStackTrace();
         }
-        INTERNAL_PROPERTY_DONT_LOG_CONNECTION_ISSUES = s;
-
+        INTERNAL_PROPERTY_DONT_LOG_CONNECTION_ISSUES = logConnectionIssues;
+        INTERNAL_RETRY_FAILED_TESTS = retryFailedTests;
+        String s = null;
         try
         {
             // use reflection to avoid adding a circular dependency in the pom
@@ -97,13 +103,23 @@
     public void teardown() throws Exception
     {
         System.clearProperty(INTERNAL_PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND);
-        server.close();
-        server = null;
+        if ( server != null )
+        {
+            try
+            {
+                server.close();
+            }
+            catch ( IOException e )
+            {
+                e.printStackTrace();
+            }
+            server = null;
+        }
     }
 
     private static class RetryTest implements IRetryAnalyzer
     {
-        private final AtomicBoolean hasBeenRetried = new AtomicBoolean(false);
+        private final AtomicBoolean hasBeenRetried = new AtomicBoolean(!Boolean.getBoolean(INTERNAL_RETRY_FAILED_TESTS));
 
         @Override
         public boolean retry(ITestResult result)
diff --git a/curator-test/src/main/java/org/apache/curator/test/DelegatingExecutorService.java b/curator-test/src/main/java/org/apache/curator/test/DelegatingExecutorService.java
new file mode 100644
index 0000000..eff34dd
--- /dev/null
+++ b/curator-test/src/main/java/org/apache/curator/test/DelegatingExecutorService.java
@@ -0,0 +1,119 @@
+/**
+ * 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.curator.test;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.*;
+
+public class DelegatingExecutorService implements ExecutorService
+{
+    private final ExecutorService delegate;
+
+    public DelegatingExecutorService(
+            ExecutorService delegate
+    )
+    {
+        this.delegate = delegate;
+    }
+
+
+    @Override
+    public void shutdown()
+    {
+        delegate.shutdown();
+    }
+
+    @Override
+    public List<Runnable> shutdownNow()
+    {
+        return delegate.shutdownNow();
+    }
+
+    @Override
+    public boolean isShutdown()
+    {
+        return delegate.isShutdown();
+    }
+
+    @Override
+    public boolean isTerminated()
+    {
+        return delegate.isTerminated();
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit)
+            throws InterruptedException
+    {
+        return delegate.awaitTermination(timeout, unit);
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task)
+    {
+        return delegate.submit(task);
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result)
+    {
+        return delegate.submit(task, result);
+    }
+
+    @Override
+    public Future<?> submit(Runnable task)
+    {
+        return delegate.submit(task);
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException
+    {
+        return delegate.invokeAll(tasks);
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws InterruptedException
+    {
+        return delegate.invokeAll(tasks, timeout, unit);
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException, ExecutionException
+    {
+        return delegate.invokeAny(tasks);
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException
+    {
+        return delegate.invokeAny(tasks, timeout, unit);
+    }
+
+    @Override
+    public void execute(Runnable command)
+    {
+        delegate.execute(command);
+    }
+}
diff --git a/curator-test/src/main/java/org/apache/curator/test/ExecuteCalledWatchingExecutorService.java b/curator-test/src/main/java/org/apache/curator/test/ExecuteCalledWatchingExecutorService.java
new file mode 100644
index 0000000..da7bc66
--- /dev/null
+++ b/curator-test/src/main/java/org/apache/curator/test/ExecuteCalledWatchingExecutorService.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.curator.test;
+
+import java.util.concurrent.ExecutorService;
+
+public class ExecuteCalledWatchingExecutorService extends DelegatingExecutorService
+{
+    boolean executeCalled = false;
+
+    public ExecuteCalledWatchingExecutorService(ExecutorService delegate)
+    {
+        super(delegate);
+    }
+
+    @Override
+    public synchronized void execute(Runnable command)
+    {
+        executeCalled = true;
+        super.execute(command);
+    }
+
+    public synchronized boolean isExecuteCalled()
+    {
+        return executeCalled;
+    }
+
+    public synchronized void setExecuteCalled(boolean executeCalled)
+    {
+        this.executeCalled = executeCalled;
+    }
+}
diff --git a/curator-test/src/main/java/org/apache/curator/test/InstanceSpec.java b/curator-test/src/main/java/org/apache/curator/test/InstanceSpec.java
index b39a949..6d495df 100644
--- a/curator-test/src/main/java/org/apache/curator/test/InstanceSpec.java
+++ b/curator-test/src/main/java/org/apache/curator/test/InstanceSpec.java
@@ -70,6 +70,10 @@
     private final int tickTime;
     private final int maxClientCnxns;
 
+    public static void reset() {
+        nextServerId.set(1);
+    }
+
     public static InstanceSpec newInstanceSpec()
     {
         return new InstanceSpec(null, -1, -1, -1, true, -1, -1, -1);
diff --git a/curator-test/src/main/java/org/apache/curator/test/QuorumConfigBuilder.java b/curator-test/src/main/java/org/apache/curator/test/QuorumConfigBuilder.java
index 8add08e..02979ee 100644
--- a/curator-test/src/main/java/org/apache/curator/test/QuorumConfigBuilder.java
+++ b/curator-test/src/main/java/org/apache/curator/test/QuorumConfigBuilder.java
@@ -99,7 +99,7 @@
         {
             for ( InstanceSpec thisSpec : instanceSpecs )
             {
-                properties.setProperty("server." + thisSpec.getServerId(), String.format("localhost:%d:%d", thisSpec.getQuorumPort(), thisSpec.getElectionPort()));
+                properties.setProperty("server." + thisSpec.getServerId(), String.format("localhost:%d:%d;localhost:%d", thisSpec.getQuorumPort(), thisSpec.getElectionPort(), thisSpec.getPort()));
             }
         }
 
diff --git a/curator-test/src/main/java/org/apache/curator/test/TestingCluster.java b/curator-test/src/main/java/org/apache/curator/test/TestingCluster.java
index cd86b72..f6bdbd8 100644
--- a/curator-test/src/main/java/org/apache/curator/test/TestingCluster.java
+++ b/curator-test/src/main/java/org/apache/curator/test/TestingCluster.java
@@ -249,6 +249,7 @@
 
     private static Map<InstanceSpec, Collection<InstanceSpec>> makeSpecs(int instanceQty)
     {
+        InstanceSpec.reset();
         ImmutableList.Builder<InstanceSpec> builder = ImmutableList.builder();
         for ( int i = 0; i < instanceQty; ++i )
         {
diff --git a/curator-x-discovery-server/pom.xml b/curator-x-discovery-server/pom.xml
index 352310f..0bd9670 100644
--- a/curator-x-discovery-server/pom.xml
+++ b/curator-x-discovery-server/pom.xml
@@ -62,6 +62,12 @@
         </dependency>
 
         <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>com.sun.jersey</groupId>
             <artifactId>jersey-server</artifactId>
             <scope>test</scope>
diff --git a/curator-x-discovery/pom.xml b/curator-x-discovery/pom.xml
index e211d89..6f380ae 100644
--- a/curator-x-discovery/pom.xml
+++ b/curator-x-discovery/pom.xml
@@ -60,5 +60,11 @@
             <artifactId>curator-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceCacheBuilder.java b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceCacheBuilder.java
index 10ce305..290d9b1 100644
--- a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceCacheBuilder.java
+++ b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceCacheBuilder.java
@@ -18,6 +18,8 @@
  */
 package org.apache.curator.x.discovery;
 
+import org.apache.curator.utils.CloseableExecutorService;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadFactory;
 
 public interface ServiceCacheBuilder<T>
@@ -38,10 +40,30 @@
     public ServiceCacheBuilder<T> name(String name);
 
     /**
-     * Optional thread factory to use for the cache's internal thread
+     * Optional thread factory to use for the cache's internal thread. The specified ExecutorService
+     * overrides any prior ThreadFactory or ExecutorService set on the ServiceCacheBuilder.
      *
      * @param threadFactory factory
      * @return this
      */
     public ServiceCacheBuilder<T> threadFactory(ThreadFactory threadFactory);
+
+    /**
+     * Optional ExecutorService to use for the cache's background thread. The specified ExecutorService
+     * will be wrapped in a CloseableExecutorService and overrides any prior ThreadFactory or ExecutorService
+     * set on the ServiceCacheBuilder.
+     *
+     * @param executorService executor service
+     * @return this
+     */
+    public ServiceCacheBuilder<T> executorService(ExecutorService executorService);
+
+    /**
+     * Optional CloseableExecutorService to use for the cache's background thread. The specified ExecutorService
+     * overrides any prior ThreadFactory or ExecutorService set on the ServiceCacheBuilder.
+     *
+     * @param executorService an instance of CloseableExecutorService
+     * @return this
+     */
+    public ServiceCacheBuilder<T> executorService(CloseableExecutorService executorService);
 }
diff --git a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceDiscoveryBuilder.java b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceDiscoveryBuilder.java
index 2b972ca..e25fc67 100644
--- a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceDiscoveryBuilder.java
+++ b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceDiscoveryBuilder.java
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.curator.x.discovery;
 
 import org.apache.curator.framework.CuratorFramework;
@@ -25,20 +26,21 @@
 
 public class ServiceDiscoveryBuilder<T>
 {
-    private CuratorFramework        client;
-    private String                  basePath;
-    private InstanceSerializer<T>   serializer;
-    private ServiceInstance<T>      thisInstance;
-    private Class<T>                payloadClass;
+    private CuratorFramework client;
+    private String basePath;
+    private InstanceSerializer<T> serializer;
+    private ServiceInstance<T> thisInstance;
+    private Class<T> payloadClass;
+    private boolean watchInstances = false;
 
     /**
      * Return a new builder.
      *
      * @param payloadClass the class of the payload of your service instance (you can use {@link Void}
-     * if your instances don't need a payload)
+     *                     if your instances don't need a payload)
      * @return new builder
      */
-    public static<T> ServiceDiscoveryBuilder<T>     builder(Class<T> payloadClass)
+    public static <T> ServiceDiscoveryBuilder<T> builder(Class<T> payloadClass)
     {
         return new ServiceDiscoveryBuilder<T>(payloadClass);
     }
@@ -49,12 +51,13 @@
      *
      * @return new service discovery
      */
-    public ServiceDiscovery<T>      build()
+    public ServiceDiscovery<T> build()
     {
-        if ( serializer == null ) {
+        if ( serializer == null )
+        {
             serializer(new JsonInstanceSerializer<T>(payloadClass));
         }
-        return new ServiceDiscoveryImpl<T>(client, basePath, serializer, thisInstance);
+        return new ServiceDiscoveryImpl<T>(client, basePath, serializer, thisInstance, watchInstances);
     }
 
     /**
@@ -63,7 +66,7 @@
      * @param client client
      * @return this
      */
-    public ServiceDiscoveryBuilder<T>   client(CuratorFramework client)
+    public ServiceDiscoveryBuilder<T> client(CuratorFramework client)
     {
         this.client = client;
         return this;
@@ -75,7 +78,7 @@
      * @param basePath base path
      * @return this
      */
-    public ServiceDiscoveryBuilder<T>   basePath(String basePath)
+    public ServiceDiscoveryBuilder<T> basePath(String basePath)
     {
         this.basePath = basePath;
         return this;
@@ -87,7 +90,7 @@
      * @param serializer the serializer
      * @return this
      */
-    public ServiceDiscoveryBuilder<T>   serializer(InstanceSerializer<T> serializer)
+    public ServiceDiscoveryBuilder<T> serializer(InstanceSerializer<T> serializer)
     {
         this.serializer = serializer;
         return this;
@@ -99,12 +102,26 @@
      * @param thisInstance initial instance
      * @return this
      */
-    public ServiceDiscoveryBuilder<T>   thisInstance(ServiceInstance<T> thisInstance)
+    public ServiceDiscoveryBuilder<T> thisInstance(ServiceInstance<T> thisInstance)
     {
         this.thisInstance = thisInstance;
         return this;
     }
 
+    /**
+     * Optional - if true, watches for changes to locally registered instances
+     * (via {@link #thisInstance(ServiceInstance)} or {@link ServiceDiscovery#registerService(ServiceInstance)}).
+     * If the data for instances changes, they are reloaded.
+     *
+     * @param watchInstances true to watch instances
+     * @return this
+     */
+    public ServiceDiscoveryBuilder<T> watchInstances(boolean watchInstances)
+    {
+        this.watchInstances = watchInstances;
+        return this;
+    }
+
     ServiceDiscoveryBuilder(Class<T> payloadClass)
     {
         this.payloadClass = payloadClass;
diff --git a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceCacheBuilderImpl.java b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceCacheBuilderImpl.java
index c4104f4..8922233 100644
--- a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceCacheBuilderImpl.java
+++ b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceCacheBuilderImpl.java
@@ -18,8 +18,10 @@
  */
 package org.apache.curator.x.discovery.details;
 
+import org.apache.curator.utils.CloseableExecutorService;
 import org.apache.curator.x.discovery.ServiceCache;
 import org.apache.curator.x.discovery.ServiceCacheBuilder;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadFactory;
 
 /**
@@ -30,6 +32,7 @@
     private ServiceDiscoveryImpl<T> discovery;
     private String name;
     private ThreadFactory threadFactory;
+    private CloseableExecutorService executorService;
 
     ServiceCacheBuilderImpl(ServiceDiscoveryImpl<T> discovery)
     {
@@ -44,7 +47,14 @@
     @Override
     public ServiceCache<T> build()
     {
-        return new ServiceCacheImpl<T>(discovery, name, threadFactory);
+        if (executorService != null)
+        {
+            return new ServiceCacheImpl<T>(discovery, name, executorService);
+        }
+        else
+        {
+            return new ServiceCacheImpl<T>(discovery, name, threadFactory);
+        }
     }
 
     /**
@@ -70,6 +80,33 @@
     public ServiceCacheBuilder<T> threadFactory(ThreadFactory threadFactory)
     {
         this.threadFactory = threadFactory;
+        this.executorService = null;
+        return this;
+    }
+
+    /**
+     * Optional executor service to use for the cache's background thread
+     *
+     * @param executorService executor service
+     * @return this
+     */
+    @Override
+    public ServiceCacheBuilder<T> executorService(ExecutorService executorService) {
+        this.executorService = new CloseableExecutorService(executorService);
+        this.threadFactory = null;
+        return this;
+    }
+
+    /**
+     * Optional CloseableExecutorService to use for the cache's background thread
+     *
+     * @param executorService an instance of CloseableExecutorService
+     * @return this
+     */
+    @Override
+    public ServiceCacheBuilder<T> executorService(CloseableExecutorService executorService) {
+        this.executorService = executorService;
+        this.threadFactory = null;
         return this;
     }
 }
diff --git a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceCacheImpl.java b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceCacheImpl.java
index 0269d24..b8f39d5 100644
--- a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceCacheImpl.java
+++ b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceCacheImpl.java
@@ -22,6 +22,7 @@
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import org.apache.curator.utils.CloseableExecutorService;
 import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.listen.ListenerContainer;
@@ -36,6 +37,7 @@
 import java.util.List;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -54,15 +56,26 @@
         STOPPED
     }
 
+    private static CloseableExecutorService convertThreadFactory(ThreadFactory threadFactory)
+    {
+        Preconditions.checkNotNull(threadFactory, "threadFactory cannot be null");
+        return new CloseableExecutorService(Executors.newSingleThreadExecutor(threadFactory));
+    }
+
     ServiceCacheImpl(ServiceDiscoveryImpl<T> discovery, String name, ThreadFactory threadFactory)
     {
+        this(discovery, name, convertThreadFactory(threadFactory));
+    }
+
+    ServiceCacheImpl(ServiceDiscoveryImpl<T> discovery, String name, CloseableExecutorService executorService)
+    {
         Preconditions.checkNotNull(discovery, "discovery cannot be null");
         Preconditions.checkNotNull(name, "name cannot be null");
-        Preconditions.checkNotNull(threadFactory, "threadFactory cannot be null");
+        Preconditions.checkNotNull(executorService, "executorService cannot be null");
 
         this.discovery = discovery;
 
-        cache = new PathChildrenCache(discovery.getClient(), discovery.pathForName(name), true, threadFactory);
+        cache = new PathChildrenCache(discovery.getClient(), discovery.pathForName(name), true, false, executorService);
         cache.getListenable().addListener(this);
     }
 
diff --git a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceDiscoveryImpl.java b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceDiscoveryImpl.java
index ad6ce89..21c9e07 100644
--- a/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceDiscoveryImpl.java
+++ b/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/ServiceDiscoveryImpl.java
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.curator.x.discovery.details;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -24,11 +25,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-
-import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.cache.NodeCache;
+import org.apache.curator.framework.recipes.cache.NodeCacheListener;
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.curator.framework.state.ConnectionStateListener;
+import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.utils.ThreadUtils;
 import org.apache.curator.utils.ZKPaths;
 import org.apache.curator.x.discovery.ServiceCache;
@@ -44,32 +46,31 @@
 import org.apache.zookeeper.Watcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.util.Collection;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
 
 /**
  * A mechanism to register and query service instances using ZooKeeper
  */
+@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
 public class ServiceDiscoveryImpl<T> implements ServiceDiscovery<T>
 {
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final CuratorFramework client;
     private final String basePath;
     private final InstanceSerializer<T> serializer;
-    private final Map<String, ServiceInstance<T>> services = Maps.newConcurrentMap();
+    private final ConcurrentMap<String, Entry<T>> services = Maps.newConcurrentMap();
     private final Collection<ServiceCache<T>> caches = Sets.newSetFromMap(Maps.<ServiceCache<T>, Boolean>newConcurrentMap());
     private final Collection<ServiceProvider<T>> providers = Sets.newSetFromMap(Maps.<ServiceProvider<T>, Boolean>newConcurrentMap());
+    private final boolean watchInstances;
     private final ConnectionStateListener connectionStateListener = new ConnectionStateListener()
     {
         @Override
         public void stateChanged(CuratorFramework client, ConnectionState newState)
         {
-            if ( newState == ConnectionState.RECONNECTED )
+            if ( (newState == ConnectionState.RECONNECTED) || (newState == ConnectionState.CONNECTED) )
             {
                 try
                 {
@@ -84,20 +85,35 @@
         }
     };
 
+    private static class Entry<T>
+    {
+        private volatile ServiceInstance<T> service;
+        private volatile NodeCache cache;
+
+        private Entry(ServiceInstance<T> service)
+        {
+            this.service = service;
+        }
+    }
+
     /**
      * @param client the client
      * @param basePath base path to store data
      * @param serializer serializer for instances (e.g. {@link JsonInstanceSerializer})
      * @param thisInstance instance that represents the service that is running. The instance will get auto-registered
+     * @param watchInstances if true, watches for changes to locally registered instances
      */
-    public ServiceDiscoveryImpl(CuratorFramework client, String basePath, InstanceSerializer<T> serializer, ServiceInstance<T> thisInstance)
+    public ServiceDiscoveryImpl(CuratorFramework client, String basePath, InstanceSerializer<T> serializer, ServiceInstance<T> thisInstance, boolean watchInstances)
     {
+        this.watchInstances = watchInstances;
         this.client = Preconditions.checkNotNull(client, "client cannot be null");
         this.basePath = Preconditions.checkNotNull(basePath, "basePath cannot be null");
         this.serializer = Preconditions.checkNotNull(serializer, "serializer cannot be null");
         if ( thisInstance != null )
         {
-            services.put(thisInstance.getId(), thisInstance);
+            Entry<T> entry = new Entry<T>(thisInstance);
+            entry.cache = makeNodeCache(thisInstance);
+            services.put(thisInstance.getId(), entry);
         }
     }
 
@@ -109,8 +125,15 @@
     @Override
     public void start() throws Exception
     {
+        try
+        {
+            reRegisterServices();
+        }
+        catch ( KeeperException e )
+        {
+            log.error("Could not register instances - will try again later", e);
+        }
         client.getConnectionStateListenable().addListener(connectionStateListener);
-        reRegisterServices();
     }
 
     @Override
@@ -125,18 +148,11 @@
             CloseableUtils.closeQuietly(provider);
         }
 
-        Iterator<ServiceInstance<T>> it = services.values().iterator();
-        while ( it.hasNext() )
+        for ( Entry<T> entry : services.values() )
         {
-            // Should not use unregisterService because of potential ConcurrentModificationException
-            // so we in-line the bulk of the method here
-            ServiceInstance<T> service = it.next();
-            String path = pathForInstance(service.getName(), service.getId());
-            boolean doRemove = true;
-            
             try
             {
-                client.delete().forPath(path);
+                internalUnregisterService(entry);
             }
             catch ( KeeperException.NoNodeException ignore )
             {
@@ -144,16 +160,10 @@
             }
             catch ( Exception e )
             {
-                doRemove = false;
-                log.error("Could not unregister instance: " + service.getName(), e);
-            }
-            
-            if ( doRemove )
-            {
-                it.remove();
+                log.error("Could not unregister instance: " + entry.service.getName(), e);
             }
         }
-        
+
         client.getConnectionStateListenable().removeListener(connectionStateListener);
     }
 
@@ -166,34 +176,50 @@
     @Override
     public void registerService(ServiceInstance<T> service) throws Exception
     {
-        services.put(service.getId(), service);
-        internalRegisterService(service);
+        Entry<T> newEntry = new Entry<T>(service);
+        Entry<T> oldEntry = services.putIfAbsent(service.getId(), newEntry);
+        Entry<T> useEntry = (oldEntry != null) ? oldEntry : newEntry;
+        synchronized(useEntry)
+        {
+            if ( useEntry == newEntry ) // i.e. is new
+            {
+                useEntry.cache = makeNodeCache(service);
+            }
+            internalRegisterService(service);
+        }
     }
 
     @Override
-    public void updateService(ServiceInstance<T> service) throws Exception
+    public void updateService(final ServiceInstance<T> service) throws Exception
     {
-        Preconditions.checkArgument(services.containsKey(service.getId()), "Service is not registered: " + service);
-
-        byte[]          bytes = serializer.serialize(service);
-        String          path = pathForInstance(service.getName(), service.getId());
-        client.setData().forPath(path, bytes);
+        Entry<T> entry = services.get(service.getId());
+        if ( entry == null )
+        {
+            throw new Exception("Service not registered: " + service);
+        }
+        synchronized(entry)
+        {
+            entry.service = service;
+            byte[] bytes = serializer.serialize(service);
+            String path = pathForInstance(service.getName(), service.getId());
+            client.setData().forPath(path, bytes);
+        }
     }
 
     @VisibleForTesting
-    protected void     internalRegisterService(ServiceInstance<T> service) throws Exception
+    protected void internalRegisterService(ServiceInstance<T> service) throws Exception
     {
-        byte[]          bytes = serializer.serialize(service);
-        String          path = pathForInstance(service.getName(), service.getId());
+        byte[] bytes = serializer.serialize(service);
+        String path = pathForInstance(service.getName(), service.getId());
 
-        final int       MAX_TRIES = 2;
-        boolean         isDone = false;
+        final int MAX_TRIES = 2;
+        boolean isDone = false;
         for ( int i = 0; !isDone && (i < MAX_TRIES); ++i )
         {
             try
             {
-                CreateMode      mode = (service.getServiceType() == ServiceType.DYNAMIC) ? CreateMode.EPHEMERAL : CreateMode.PERSISTENT;
-                client.create().creatingParentsIfNeeded().withMode(mode).forPath(path, bytes);
+                CreateMode mode = (service.getServiceType() == ServiceType.DYNAMIC) ? CreateMode.EPHEMERAL : CreateMode.PERSISTENT;
+                client.create().creatingParentContainersIfNeeded().withMode(mode).forPath(path, bytes);
                 isDone = true;
             }
             catch ( KeeperException.NodeExistsException e )
@@ -210,18 +236,10 @@
      * @throws Exception errors
      */
     @Override
-    public void     unregisterService(ServiceInstance<T> service) throws Exception
+    public void unregisterService(ServiceInstance<T> service) throws Exception
     {
-        String          path = pathForInstance(service.getName(), service.getId());
-        try
-        {
-            client.delete().forPath(path);
-        }
-        catch ( KeeperException.NoNodeException ignore )
-        {
-            // ignore
-        }
-        services.remove(service.getId());
+        Entry<T> entry = services.remove(service.getId());
+        internalUnregisterService(entry);
     }
 
     /**
@@ -256,9 +274,9 @@
      * @throws Exception errors
      */
     @Override
-    public Collection<String>   queryForNames() throws Exception
+    public Collection<String> queryForNames() throws Exception
     {
-        List<String>        names = client.getChildren().forPath(basePath);
+        List<String> names = client.getChildren().forPath(basePath);
         return ImmutableList.copyOf(names);
     }
 
@@ -270,7 +288,7 @@
      * @throws Exception errors
      */
     @Override
-    public Collection<ServiceInstance<T>>  queryForInstances(String name) throws Exception
+    public Collection<ServiceInstance<T>> queryForInstances(String name) throws Exception
     {
         return queryForInstances(name, null);
     }
@@ -286,10 +304,10 @@
     @Override
     public ServiceInstance<T> queryForInstance(String name, String id) throws Exception
     {
-        String          path = pathForInstance(name, id);
+        String path = pathForInstance(name, id);
         try
         {
-            byte[]          bytes = client.getData().forPath(path);
+            byte[] bytes = client.getData().forPath(path);
             return serializer.deserialize(bytes);
         }
         catch ( KeeperException.NoNodeException ignore )
@@ -299,22 +317,22 @@
         return null;
     }
 
-    void    cacheOpened(ServiceCache<T> cache)
+    void cacheOpened(ServiceCache<T> cache)
     {
         caches.add(cache);
     }
 
-    void    cacheClosed(ServiceCache<T> cache)
+    void cacheClosed(ServiceCache<T> cache)
     {
         caches.remove(cache);
     }
 
-    void    providerOpened(ServiceProvider<T> provider)
+    void providerOpened(ServiceProvider<T> provider)
     {
         providers.add(provider);
     }
 
-    void    providerClosed(ServiceProvider<T> cache)
+    void providerClosed(ServiceProvider<T> cache)
     {
         providers.remove(cache);
     }
@@ -324,7 +342,7 @@
         return client;
     }
 
-    String  pathForName(String name)
+    String pathForName(String name)
     {
         return ZKPaths.makePath(basePath, name);
     }
@@ -334,11 +352,11 @@
         return serializer;
     }
 
-    List<ServiceInstance<T>>  queryForInstances(String name, Watcher watcher) throws Exception
+    List<ServiceInstance<T>> queryForInstances(String name, Watcher watcher) throws Exception
     {
-        ImmutableList.Builder<ServiceInstance<T>>   builder = ImmutableList.builder();
-        String                  path = pathForName(name);
-        List<String>            instanceIds;
+        ImmutableList.Builder<ServiceInstance<T>> builder = ImmutableList.builder();
+        String path = pathForName(name);
+        List<String> instanceIds;
 
         if ( watcher != null )
         {
@@ -367,9 +385,15 @@
         return builder.build();
     }
 
+    @VisibleForTesting
+    int debugServicesQty()
+    {
+        return services.size();
+    }
+
     private List<String> getChildrenWatched(String path, Watcher watcher, boolean recurse) throws Exception
     {
-        List<String>    instanceIds;
+        List<String> instanceIds;
         try
         {
             instanceIds = client.getChildren().usingWatcher(watcher).forPath(path);
@@ -380,7 +404,7 @@
             {
                 try
                 {
-                    client.create().creatingParentsIfNeeded().forPath(path);
+                    client.create().creatingParentContainersIfNeeded().forPath(path);
                 }
                 catch ( KeeperException.NodeExistsException ignore )
                 {
@@ -396,16 +420,95 @@
         return instanceIds;
     }
 
-    private String  pathForInstance(String name, String id) throws UnsupportedEncodingException
+    @VisibleForTesting
+    String pathForInstance(String name, String id)
     {
         return ZKPaths.makePath(pathForName(name), id);
     }
 
+    @VisibleForTesting
+    ServiceInstance<T> getRegisteredService(String id)
+    {
+        Entry<T> entry = services.get(id);
+        return (entry != null) ? entry.service : null;
+    }
+
     private void reRegisterServices() throws Exception
     {
-        for ( ServiceInstance<T> service : services.values() )
+        for ( final Entry<T> entry : services.values() )
         {
-            internalRegisterService(service);
+            synchronized(entry)
+            {
+                internalRegisterService(entry.service);
+            }
+        }
+    }
+
+    private NodeCache makeNodeCache(final ServiceInstance<T> instance)
+    {
+        if ( !watchInstances )
+        {
+            return null;
+        }
+
+        final NodeCache nodeCache = new NodeCache(client, pathForInstance(instance.getName(), instance.getId()));
+        try
+        {
+            nodeCache.start(true);
+        }
+        catch ( Exception e )
+        {
+            log.error("Could not start node cache for: " + instance, e);
+        }
+        NodeCacheListener listener = new NodeCacheListener()
+        {
+            @Override
+            public void nodeChanged() throws Exception
+            {
+                if ( nodeCache.getCurrentData() != null )
+                {
+                    ServiceInstance<T> newInstance = serializer.deserialize(nodeCache.getCurrentData().getData());
+                    Entry<T> entry = services.get(newInstance.getId());
+                    if ( entry != null )
+                    {
+                        synchronized(entry)
+                        {
+                            entry.service = newInstance;
+                        }
+                    }
+                }
+                else
+                {
+                    log.warn("Instance data has been deleted for: " + instance);
+                }
+            }
+        };
+        nodeCache.getListenable().addListener(listener);
+        return nodeCache;
+    }
+
+    private void internalUnregisterService(final Entry<T> entry) throws Exception
+    {
+        if ( entry != null )
+        {
+            synchronized(entry)
+            {
+                if ( entry.cache != null )
+                {
+                    CloseableUtils.closeQuietly(entry.cache);
+                    entry.cache = null;
+                }
+
+                String path = pathForInstance(entry.service.getName(), entry.service.getId());
+                try
+                {
+                    client.delete().guaranteed().forPath(path);
+                }
+                catch ( KeeperException.NoNodeException ignore )
+                {
+                    // ignore
+                }
+            }
         }
     }
 }
diff --git a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestServiceCache.java b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestServiceCache.java
index be114d4..fda5c26 100644
--- a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestServiceCache.java
+++ b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestServiceCache.java
@@ -16,17 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.curator.x.discovery;
 
 import com.google.common.collect.Lists;
-import org.apache.curator.test.BaseClassForTests;
-import org.apache.curator.test.Timing;
-import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
 import org.apache.curator.framework.state.ConnectionState;
 import org.apache.curator.retry.RetryOneTime;
-import org.apache.curator.test.TestingServer;
+import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.test.ExecuteCalledWatchingExecutorService;
+import org.apache.curator.test.Timing;
+import org.apache.curator.utils.CloseableUtils;
 import org.apache.curator.x.discovery.details.ServiceCacheListener;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -35,13 +36,14 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 public class TestServiceCache extends BaseClassForTests
 {
     @Test
-    public void     testInitialLoad() throws Exception
+    public void testInitialLoad() throws Exception
     {
         List<Closeable> closeables = Lists.newArrayList();
         try
@@ -50,15 +52,15 @@
             closeables.add(client);
             client.start();
 
-            ServiceDiscovery<String>    discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build();
             closeables.add(discovery);
             discovery.start();
 
-            ServiceCache<String>        cache = discovery.serviceCacheBuilder().name("test").build();
+            ServiceCache<String> cache = discovery.serviceCacheBuilder().name("test").build();
             closeables.add(cache);
 
-            final CountDownLatch        latch = new CountDownLatch(3);
-            ServiceCacheListener        listener = new ServiceCacheListener()
+            final CountDownLatch latch = new CountDownLatch(3);
+            ServiceCacheListener listener = new ServiceCacheListener()
             {
                 @Override
                 public void cacheChanged()
@@ -74,16 +76,16 @@
             cache.addListener(listener);
             cache.start();
 
-            ServiceInstance<String>     instance1 = ServiceInstance.<String>builder().payload("test").name("test").port(10064).build();
-            ServiceInstance<String>     instance2 = ServiceInstance.<String>builder().payload("test").name("test").port(10065).build();
-            ServiceInstance<String>     instance3 = ServiceInstance.<String>builder().payload("test").name("test").port(10066).build();
+            ServiceInstance<String> instance1 = ServiceInstance.<String>builder().payload("test").name("test").port(10064).build();
+            ServiceInstance<String> instance2 = ServiceInstance.<String>builder().payload("test").name("test").port(10065).build();
+            ServiceInstance<String> instance3 = ServiceInstance.<String>builder().payload("test").name("test").port(10066).build();
             discovery.registerService(instance1);
             discovery.registerService(instance2);
             discovery.registerService(instance3);
 
             Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
 
-            ServiceCache<String>        cache2 = discovery.serviceCacheBuilder().name("test").build();
+            ServiceCache<String> cache2 = discovery.serviceCacheBuilder().name("test").build();
             closeables.add(cache2);
             cache2.start();
 
@@ -100,7 +102,7 @@
     }
 
     @Test
-    public void     testViaProvider() throws Exception
+    public void testViaProvider() throws Exception
     {
         Timing timing = new Timing();
 
@@ -111,19 +113,19 @@
             closeables.add(client);
             client.start();
 
-            ServiceDiscovery<String>    discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build();
             closeables.add(discovery);
             discovery.start();
 
-            ServiceProvider<String>     serviceProvider = discovery.serviceProviderBuilder().serviceName("test").build();
+            ServiceProvider<String> serviceProvider = discovery.serviceProviderBuilder().serviceName("test").build();
             closeables.add(serviceProvider);
             serviceProvider.start();
 
-            ServiceInstance<String>     instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
             discovery.registerService(instance);
 
-            int                         count = 0;
-            ServiceInstance<String>     foundInstance = null;
+            int count = 0;
+            ServiceInstance<String> foundInstance = null;
             while ( foundInstance == null )
             {
                 Assert.assertTrue(count++ < 5);
@@ -132,7 +134,7 @@
             }
             Assert.assertEquals(foundInstance, instance);
 
-            ServiceInstance<String>     instance2 = ServiceInstance.<String>builder().address("foo").payload("thing").name("test").port(10064).build();
+            ServiceInstance<String> instance2 = ServiceInstance.<String>builder().address("foo").payload("thing").name("test").port(10064).build();
             discovery.registerService(instance2);
             timing.sleepABit();
             Collection<ServiceInstance<String>> allInstances = serviceProvider.getAllInstances();
@@ -149,24 +151,24 @@
     }
 
     @Test
-    public void     testUpdate() throws Exception
+    public void testUpdate() throws Exception
     {
-        List<Closeable>     closeables = Lists.newArrayList();
+        List<Closeable> closeables = Lists.newArrayList();
         try
         {
             CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
             closeables.add(client);
             client.start();
 
-            ServiceInstance<String>     instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
-            ServiceDiscovery<String>    discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build();
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build();
             closeables.add(discovery);
             discovery.start();
 
             final CountDownLatch latch = new CountDownLatch(1);
-            ServiceCache<String>        cache = discovery.serviceCacheBuilder().name("test").build();
+            ServiceCache<String> cache = discovery.serviceCacheBuilder().name("test").build();
             closeables.add(cache);
-            ServiceCacheListener        listener = new ServiceCacheListener()
+            ServiceCacheListener listener = new ServiceCacheListener()
             {
                 @Override
                 public void cacheChanged()
@@ -210,7 +212,7 @@
             closeables.add(client);
             client.start();
 
-            ServiceDiscovery<String>    discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build();
             closeables.add(discovery);
             discovery.start();
 
@@ -219,7 +221,7 @@
             cache.start();
 
             final Semaphore semaphore = new Semaphore(0);
-            ServiceCacheListener    listener = new ServiceCacheListener()
+            ServiceCacheListener listener = new ServiceCacheListener()
             {
                 @Override
                 public void cacheChanged()
@@ -234,15 +236,15 @@
             };
             cache.addListener(listener);
 
-            ServiceInstance<String>     instance1 = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
-            ServiceInstance<String>     instance2 = ServiceInstance.<String>builder().payload("thing").name("test").port(10065).build();
+            ServiceInstance<String> instance1 = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceInstance<String> instance2 = ServiceInstance.<String>builder().payload("thing").name("test").port(10065).build();
             discovery.registerService(instance1);
             Assert.assertTrue(semaphore.tryAcquire(10, TimeUnit.SECONDS));
 
             discovery.registerService(instance2);
             Assert.assertTrue(semaphore.tryAcquire(3, TimeUnit.SECONDS));
 
-            ServiceInstance<String>     instance3 = ServiceInstance.<String>builder().payload("thing").name("another").port(10064).build();
+            ServiceInstance<String> instance3 = ServiceInstance.<String>builder().payload("thing").name("another").port(10064).build();
             discovery.registerService(instance3);
             Assert.assertFalse(semaphore.tryAcquire(3, TimeUnit.SECONDS));  // should not get called for a different service
         }
@@ -255,4 +257,57 @@
             }
         }
     }
+
+    @Test
+    public void testExecutorServiceIsInvoked() throws Exception
+    {
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build();
+            closeables.add(discovery);
+            discovery.start();
+
+            ExecuteCalledWatchingExecutorService exec = new ExecuteCalledWatchingExecutorService(Executors.newSingleThreadExecutor());
+            Assert.assertFalse(exec.isExecuteCalled());
+
+            ServiceCache<String> cache = discovery.serviceCacheBuilder().name("test").executorService(exec).build();
+            closeables.add(cache);
+            cache.start();
+
+            final Semaphore semaphore = new Semaphore(0);
+            ServiceCacheListener listener = new ServiceCacheListener()
+            {
+                @Override
+                public void cacheChanged()
+                {
+                    semaphore.release();
+                }
+
+                @Override
+                public void stateChanged(CuratorFramework client, ConnectionState newState)
+                {
+                }
+            };
+            cache.addListener(listener);
+
+            ServiceInstance<String> instance1 = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            discovery.registerService(instance1);
+            Assert.assertTrue(semaphore.tryAcquire(10, TimeUnit.SECONDS));
+
+            Assert.assertTrue(exec.isExecuteCalled());
+        }
+        finally
+        {
+            Collections.reverse(closeables);
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
 }
diff --git a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestServiceDiscovery.java b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestServiceDiscovery.java
deleted file mode 100644
index 73de7fc..0000000
--- a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/TestServiceDiscovery.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.curator.x.discovery;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.apache.curator.test.BaseClassForTests;
-import org.apache.curator.utils.CloseableUtils;
-import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.framework.CuratorFrameworkFactory;
-import org.apache.curator.retry.RetryOneTime;
-import org.apache.curator.test.KillSession;
-import org.apache.curator.test.Timing;
-import org.apache.curator.x.discovery.details.JsonInstanceSerializer;
-import org.apache.curator.x.discovery.details.ServiceDiscoveryImpl;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-import java.io.Closeable;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.concurrent.Semaphore;
-
-public class TestServiceDiscovery extends BaseClassForTests
-{
-    private static final Comparator<ServiceInstance<Void>>      comparator = new Comparator<ServiceInstance<Void>>()
-    {
-        @Override
-        public int compare(ServiceInstance<Void> o1, ServiceInstance<Void> o2)
-        {
-            return o1.getId().compareTo(o2.getId());
-        }
-    };
-
-    @Test
-    public void         testCrashedServerMultiInstances() throws Exception
-    {
-        List<Closeable>     closeables = Lists.newArrayList();
-        try
-        {
-            Timing              timing = new Timing();
-            CuratorFramework    client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
-            closeables.add(client);
-            client.start();
-
-            final Semaphore             semaphore = new Semaphore(0);
-            ServiceInstance<String>     instance1 = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
-            ServiceInstance<String>     instance2 = ServiceInstance.<String>builder().payload("thing").name("test").port(10065).build();
-            ServiceDiscovery<String>    discovery = new ServiceDiscoveryImpl<String>(client, "/test", new JsonInstanceSerializer<String>(String.class), instance1)
-            {
-                @Override
-                protected void internalRegisterService(ServiceInstance<String> service) throws Exception
-                {
-                    super.internalRegisterService(service);
-                    semaphore.release();
-                }
-            };
-            closeables.add(discovery);
-            discovery.start();
-            discovery.registerService(instance2);
-
-            timing.acquireSemaphore(semaphore, 2);
-            Assert.assertEquals(discovery.queryForInstances("test").size(), 2);
-
-            KillSession.kill(client.getZookeeperClient().getZooKeeper(), server.getConnectString());
-            server.stop();
-
-            server.restart();
-            closeables.add(server);
-
-            timing.acquireSemaphore(semaphore, 2);
-            Assert.assertEquals(discovery.queryForInstances("test").size(), 2);
-        }
-        finally
-        {
-            for ( Closeable c : closeables )
-            {
-                CloseableUtils.closeQuietly(c);
-            }
-        }
-    }
-
-    @Test
-    public void         testCrashedServer() throws Exception
-    {
-        List<Closeable>     closeables = Lists.newArrayList();
-        try
-        {
-            Timing              timing = new Timing();
-            CuratorFramework    client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
-            closeables.add(client);
-            client.start();
-
-            final Semaphore             semaphore = new Semaphore(0);
-            ServiceInstance<String>     instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
-            ServiceDiscovery<String>    discovery = new ServiceDiscoveryImpl<String>(client, "/test", new JsonInstanceSerializer<String>(String.class), instance)
-            {
-                @Override
-                protected void internalRegisterService(ServiceInstance<String> service) throws Exception
-                {
-                    super.internalRegisterService(service);
-                    semaphore.release();
-                }
-            };
-            closeables.add(discovery);
-            discovery.start();
-
-            timing.acquireSemaphore(semaphore);
-            Assert.assertEquals(discovery.queryForInstances("test").size(), 1);
-
-            KillSession.kill(client.getZookeeperClient().getZooKeeper(), server.getConnectString());
-            server.stop();
-
-            server.restart();
-            closeables.add(server);
-
-            timing.acquireSemaphore(semaphore);
-            Assert.assertEquals(discovery.queryForInstances("test").size(), 1);
-        }
-        finally
-        {
-            for ( Closeable c : closeables )
-            {
-                CloseableUtils.closeQuietly(c);
-            }
-        }
-    }
-
-    @Test
-    public void         testCrashedInstance() throws Exception
-    {
-        List<Closeable>     closeables = Lists.newArrayList();
-        try
-        {
-            Timing              timing = new Timing();
-
-            CuratorFramework    client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
-            closeables.add(client);
-            client.start();
-
-            ServiceInstance<String>     instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
-            ServiceDiscovery<String>    discovery = new ServiceDiscoveryImpl<String>(client, "/test", new JsonInstanceSerializer<String>(String.class), instance);
-            closeables.add(discovery);
-            discovery.start();
-
-            Assert.assertEquals(discovery.queryForInstances("test").size(), 1);
-            
-            KillSession.kill(client.getZookeeperClient().getZooKeeper(), server.getConnectString());
-            Thread.sleep(timing.multiple(1.5).session());
-
-            Assert.assertEquals(discovery.queryForInstances("test").size(), 1);
-        }
-        finally
-        {
-            Collections.reverse(closeables);
-            for ( Closeable c : closeables )
-            {
-                CloseableUtils.closeQuietly(c);
-            }
-        }
-    }
-
-    @Test
-    public void         testMultipleInstances() throws Exception
-    {
-        final String        SERVICE_ONE = "one";
-        final String        SERVICE_TWO = "two";
-
-        List<Closeable>     closeables = Lists.newArrayList();
-        try
-        {
-            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
-            closeables.add(client);
-            client.start();
-
-            ServiceInstance<Void>       s1_i1 = ServiceInstance.<Void>builder().name(SERVICE_ONE).build();
-            ServiceInstance<Void>       s1_i2 = ServiceInstance.<Void>builder().name(SERVICE_ONE).build();
-            ServiceInstance<Void>       s2_i1 = ServiceInstance.<Void>builder().name(SERVICE_TWO).build();
-            ServiceInstance<Void>       s2_i2 = ServiceInstance.<Void>builder().name(SERVICE_TWO).build();
-
-            ServiceDiscovery<Void>      discovery = ServiceDiscoveryBuilder.builder(Void.class).client(client).basePath("/test").build();
-            closeables.add(discovery);
-            discovery.start();
-
-            discovery.registerService(s1_i1);
-            discovery.registerService(s1_i2);
-            discovery.registerService(s2_i1);
-            discovery.registerService(s2_i2);
-
-            Assert.assertEquals(Sets.newHashSet(discovery.queryForNames()), Sets.newHashSet(SERVICE_ONE, SERVICE_TWO));
-
-            List<ServiceInstance<Void>> list = Lists.newArrayList();
-            list.add(s1_i1);
-            list.add(s1_i2);
-            Collections.sort(list, comparator);
-            List<ServiceInstance<Void>> queriedInstances = Lists.newArrayList(discovery.queryForInstances(SERVICE_ONE));
-            Collections.sort(queriedInstances, comparator);
-            Assert.assertEquals(queriedInstances, list, String.format("Not equal l: %s - d: %s", list, queriedInstances));
-
-            list.clear();
-
-            list.add(s2_i1);
-            list.add(s2_i2);
-            Collections.sort(list, comparator);
-            queriedInstances = Lists.newArrayList(discovery.queryForInstances(SERVICE_TWO));
-            Collections.sort(queriedInstances, comparator);
-            Assert.assertEquals(queriedInstances, list, String.format("Not equal 2: %s - d: %s", list, queriedInstances));
-        }
-        finally
-        {
-            Collections.reverse(closeables);
-            for ( Closeable c : closeables )
-            {
-                CloseableUtils.closeQuietly(c);
-            }
-        }
-    }
-
-    @Test
-    public void         testBasic() throws Exception
-    {
-        List<Closeable>     closeables = Lists.newArrayList();
-        try
-        {
-            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
-            closeables.add(client);
-            client.start();
-            
-            ServiceInstance<String>     instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
-            ServiceDiscovery<String>    discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build();
-            closeables.add(discovery);
-            discovery.start();
-
-            Assert.assertEquals(discovery.queryForNames(), Arrays.asList("test"));
-
-            List<ServiceInstance<String>> list = Lists.newArrayList();
-            list.add(instance);
-            Assert.assertEquals(discovery.queryForInstances("test"), list);
-        }
-        finally
-        {
-            Collections.reverse(closeables);
-            for ( Closeable c : closeables )
-            {
-                CloseableUtils.closeQuietly(c);
-            }
-        }
-    }
-}
diff --git a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestServiceDiscovery.java b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestServiceDiscovery.java
new file mode 100644
index 0000000..8b1e5fc
--- /dev/null
+++ b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestServiceDiscovery.java
@@ -0,0 +1,394 @@
+/**
+ * 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.curator.x.discovery.details;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.test.KillSession;
+import org.apache.curator.test.Timing;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.curator.x.discovery.ServiceDiscovery;
+import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
+import org.apache.curator.x.discovery.ServiceInstance;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import java.io.Closeable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+public class TestServiceDiscovery extends BaseClassForTests
+{
+    private static final Comparator<ServiceInstance<Void>> comparator = new Comparator<ServiceInstance<Void>>()
+    {
+        @Override
+        public int compare(ServiceInstance<Void> o1, ServiceInstance<Void> o2)
+        {
+            return o1.getId().compareTo(o2.getId());
+        }
+    };
+
+    @Test
+    public void testCrashedServerMultiInstances() throws Exception
+    {
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            Timing timing = new Timing();
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            final Semaphore semaphore = new Semaphore(0);
+            ServiceInstance<String> instance1 = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceInstance<String> instance2 = ServiceInstance.<String>builder().payload("thing").name("test").port(10065).build();
+            ServiceDiscovery<String> discovery = new ServiceDiscoveryImpl<String>(client, "/test", new JsonInstanceSerializer<String>(String.class), instance1, false)
+            {
+                @Override
+                protected void internalRegisterService(ServiceInstance<String> service) throws Exception
+                {
+                    super.internalRegisterService(service);
+                    semaphore.release();
+                }
+            };
+            closeables.add(discovery);
+            discovery.start();
+            discovery.registerService(instance2);
+
+            timing.acquireSemaphore(semaphore, 2);
+            Assert.assertEquals(discovery.queryForInstances("test").size(), 2);
+
+            KillSession.kill(client.getZookeeperClient().getZooKeeper(), server.getConnectString());
+            server.stop();
+
+            server.restart();
+            closeables.add(server);
+
+            timing.acquireSemaphore(semaphore, 2);
+            Assert.assertEquals(discovery.queryForInstances("test").size(), 2);
+        }
+        finally
+        {
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+
+    @Test
+    public void testCrashedServer() throws Exception
+    {
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            Timing timing = new Timing();
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            final Semaphore semaphore = new Semaphore(0);
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceDiscovery<String> discovery = new ServiceDiscoveryImpl<String>(client, "/test", new JsonInstanceSerializer<String>(String.class), instance, false)
+            {
+                @Override
+                protected void internalRegisterService(ServiceInstance<String> service) throws Exception
+                {
+                    super.internalRegisterService(service);
+                    semaphore.release();
+                }
+            };
+            closeables.add(discovery);
+            discovery.start();
+
+            timing.acquireSemaphore(semaphore);
+            Assert.assertEquals(discovery.queryForInstances("test").size(), 1);
+
+            KillSession.kill(client.getZookeeperClient().getZooKeeper(), server.getConnectString());
+            server.stop();
+
+            server.restart();
+            closeables.add(server);
+
+            timing.acquireSemaphore(semaphore);
+            Assert.assertEquals(discovery.queryForInstances("test").size(), 1);
+        }
+        finally
+        {
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+
+    @Test
+    public void testCrashedInstance() throws Exception
+    {
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            Timing timing = new Timing();
+
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceDiscovery<String> discovery = new ServiceDiscoveryImpl<String>(client, "/test", new JsonInstanceSerializer<String>(String.class), instance, false);
+            closeables.add(discovery);
+            discovery.start();
+
+            Assert.assertEquals(discovery.queryForInstances("test").size(), 1);
+
+            KillSession.kill(client.getZookeeperClient().getZooKeeper(), server.getConnectString());
+            Thread.sleep(timing.multiple(1.5).session());
+
+            Assert.assertEquals(discovery.queryForInstances("test").size(), 1);
+        }
+        finally
+        {
+            Collections.reverse(closeables);
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+
+    @Test
+    public void testMultipleInstances() throws Exception
+    {
+        final String SERVICE_ONE = "one";
+        final String SERVICE_TWO = "two";
+
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            ServiceInstance<Void> s1_i1 = ServiceInstance.<Void>builder().name(SERVICE_ONE).build();
+            ServiceInstance<Void> s1_i2 = ServiceInstance.<Void>builder().name(SERVICE_ONE).build();
+            ServiceInstance<Void> s2_i1 = ServiceInstance.<Void>builder().name(SERVICE_TWO).build();
+            ServiceInstance<Void> s2_i2 = ServiceInstance.<Void>builder().name(SERVICE_TWO).build();
+
+            ServiceDiscovery<Void> discovery = ServiceDiscoveryBuilder.builder(Void.class).client(client).basePath("/test").build();
+            closeables.add(discovery);
+            discovery.start();
+
+            discovery.registerService(s1_i1);
+            discovery.registerService(s1_i2);
+            discovery.registerService(s2_i1);
+            discovery.registerService(s2_i2);
+
+            Assert.assertEquals(Sets.newHashSet(discovery.queryForNames()), Sets.newHashSet(SERVICE_ONE, SERVICE_TWO));
+
+            List<ServiceInstance<Void>> list = Lists.newArrayList();
+            list.add(s1_i1);
+            list.add(s1_i2);
+            Collections.sort(list, comparator);
+            List<ServiceInstance<Void>> queriedInstances = Lists.newArrayList(discovery.queryForInstances(SERVICE_ONE));
+            Collections.sort(queriedInstances, comparator);
+            Assert.assertEquals(queriedInstances, list, String.format("Not equal l: %s - d: %s", list, queriedInstances));
+
+            list.clear();
+
+            list.add(s2_i1);
+            list.add(s2_i2);
+            Collections.sort(list, comparator);
+            queriedInstances = Lists.newArrayList(discovery.queryForInstances(SERVICE_TWO));
+            Collections.sort(queriedInstances, comparator);
+            Assert.assertEquals(queriedInstances, list, String.format("Not equal 2: %s - d: %s", list, queriedInstances));
+        }
+        finally
+        {
+            Collections.reverse(closeables);
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+
+    @Test
+    public void testBasic() throws Exception
+    {
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build();
+            closeables.add(discovery);
+            discovery.start();
+
+            Assert.assertEquals(discovery.queryForNames(), Collections.singletonList("test"));
+
+            List<ServiceInstance<String>> list = Lists.newArrayList();
+            list.add(instance);
+            Assert.assertEquals(discovery.queryForInstances("test"), list);
+        }
+        finally
+        {
+            Collections.reverse(closeables);
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+
+    @Test
+    public void testNoServerOnStart() throws Exception
+    {
+        server.stop();
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build();
+            closeables.add(discovery);
+            discovery.start();
+
+            server.restart();
+            Assert.assertEquals(discovery.queryForNames(), Collections.singletonList("test"));
+
+            List<ServiceInstance<String>> list = Lists.newArrayList();
+            list.add(instance);
+            Assert.assertEquals(discovery.queryForInstances("test"), list);
+        }
+        finally
+        {
+            Collections.reverse(closeables);
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+
+    // CURATOR-164
+    @Test
+    public void testUnregisterService() throws Exception
+    {
+        final String name = "name";
+
+        final CountDownLatch restartLatch = new CountDownLatch(1);
+        List<Closeable> closeables = Lists.newArrayList();
+
+        InstanceSerializer<String> slowSerializer = new JsonInstanceSerializer<String>(String.class)
+        {
+            private boolean first = true;
+
+            @Override
+            public byte[] serialize(ServiceInstance<String> instance) throws Exception
+            {
+                if ( first )
+                {
+                    System.out.println("Serializer first registration.");
+                    first = false;
+                }
+                else
+                {
+                    System.out.println("Waiting for reconnect to finish.");
+                    // Simulate the serialize method being slow.
+                    // This could just be a timed wait, but that's kind of non-deterministic.
+                    restartLatch.await();
+                }
+                return super.serialize(instance);
+            }
+        };
+
+        try
+        {
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name(name).port(10064).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).serializer(slowSerializer).watchInstances(true).build();
+            closeables.add(discovery);
+            discovery.start();
+
+            Assert.assertFalse(discovery.queryForInstances(name).isEmpty(), "Service should start registered.");
+
+            server.stop();
+            server.restart();
+
+            discovery.unregisterService(instance);
+            restartLatch.countDown();
+
+            new Timing().sleepABit(); // Wait for the rest of registration to finish.
+
+            Assert.assertTrue(discovery.queryForInstances(name).isEmpty(), "Service should have unregistered.");
+        }
+        finally
+        {
+            Collections.reverse(closeables);
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+
+    @Test
+    public void testCleaning() throws Exception
+    {
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build();
+            closeables.add(discovery);
+            discovery.start();
+            discovery.unregisterService(instance);
+
+            Assert.assertEquals(((ServiceDiscoveryImpl)discovery).debugServicesQty(), 0);
+        }
+        finally
+        {
+            Collections.reverse(closeables);
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+}
diff --git a/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestWatchedInstances.java b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestWatchedInstances.java
new file mode 100644
index 0000000..2d03c47
--- /dev/null
+++ b/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/TestWatchedInstances.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.curator.x.discovery.details;
+
+import com.google.common.collect.Lists;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.BaseClassForTests;
+import org.apache.curator.test.Timing;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.curator.x.discovery.ServiceDiscovery;
+import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
+import org.apache.curator.x.discovery.ServiceInstance;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import java.io.Closeable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class TestWatchedInstances extends BaseClassForTests
+{
+    @Test
+    public void testWatchedInstances() throws Exception
+    {
+        Timing timing = new Timing();
+        List<Closeable> closeables = Lists.newArrayList();
+        try
+        {
+            CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
+            closeables.add(client);
+            client.start();
+
+            ServiceInstance<String> instance = ServiceInstance.<String>builder().payload("thing").name("test").port(10064).build();
+            ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder
+                .builder(String.class)
+                .basePath("/test")
+                .client(client)
+                .thisInstance(instance)
+                .watchInstances(true)
+                .build();
+            closeables.add(discovery);
+            discovery.start();
+
+            Assert.assertEquals(discovery.queryForNames(), Arrays.asList("test"));
+
+            List<ServiceInstance<String>> list = Lists.newArrayList();
+            list.add(instance);
+            Assert.assertEquals(discovery.queryForInstances("test"), list);
+
+            ServiceDiscoveryImpl<String> discoveryImpl = (ServiceDiscoveryImpl<String>)discovery;
+            ServiceInstance<String> changedInstance = ServiceInstance.<String>builder()
+                .id(instance.getId())
+                .address(instance.getAddress())
+                .payload("different")
+                .name(instance.getName())
+                .port(instance.getPort())
+                .build();
+            String path = discoveryImpl.pathForInstance("test", instance.getId());
+            byte[] bytes = discoveryImpl.getSerializer().serialize(changedInstance);
+            client.setData().forPath(path, bytes);
+            timing.sleepABit();
+
+            ServiceInstance<String> registeredService = discoveryImpl.getRegisteredService(instance.getId());
+            Assert.assertNotNull(registeredService);
+            Assert.assertEquals(registeredService.getPayload(), "different");
+        }
+        finally
+        {
+            Collections.reverse(closeables);
+            for ( Closeable c : closeables )
+            {
+                CloseableUtils.closeQuietly(c);
+            }
+        }
+    }
+}
diff --git a/curator-x-rpc/pom.xml b/curator-x-rpc/pom.xml
index fe167e0..e6ed5e8 100644
--- a/curator-x-rpc/pom.xml
+++ b/curator-x-rpc/pom.xml
@@ -1,4 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+  -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
         <artifactId>apache-curator</artifactId>
@@ -93,6 +112,12 @@
             </exclusions>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -145,6 +170,14 @@
                     </execution>
                 </executions>
             </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>clirr-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip> <!-- Clirr plugin can't handle shaded JARs - TODO -->
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/services/CuratorProjectionService.java b/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/services/CuratorProjectionService.java
index 777472c..eb67341 100644
--- a/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/services/CuratorProjectionService.java
+++ b/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/services/CuratorProjectionService.java
@@ -125,6 +125,10 @@
             {
                 builder = castBuilder(builder, CreateBuilder.class).creatingParentsIfNeeded();
             }
+            if ( spec.creatingParentContainersIfNeeded )
+            {
+                builder = castBuilder(builder, CreateBuilder.class).creatingParentContainersIfNeeded();
+            }
             if ( spec.compressed )
             {
                 builder = castBuilder(builder, Compressible.class).compressed();
diff --git a/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/structs/CreateSpec.java b/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/structs/CreateSpec.java
index d0232a9..a15fe92 100644
--- a/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/structs/CreateSpec.java
+++ b/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/structs/CreateSpec.java
@@ -45,11 +45,14 @@
     @ThriftField(7)
     public boolean withProtection;
 
+    @ThriftField(8)
+    public boolean creatingParentContainersIfNeeded;
+
     public CreateSpec()
     {
     }
 
-    public CreateSpec(String path, byte[] data, RpcCreateMode mode, String asyncContext, boolean compressed, boolean creatingParentsIfNeeded, boolean withProtection)
+    public CreateSpec(String path, byte[] data, RpcCreateMode mode, String asyncContext, boolean compressed, boolean creatingParentsIfNeeded, boolean withProtection, boolean creatingParentContainersIfNeeded)
     {
         this.path = path;
         this.data = data;
@@ -58,5 +61,6 @@
         this.compressed = compressed;
         this.creatingParentsIfNeeded = creatingParentsIfNeeded;
         this.withProtection = withProtection;
+        this.creatingParentContainersIfNeeded = creatingParentContainersIfNeeded;
     }
 }
diff --git a/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/structs/RpcCreateMode.java b/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/structs/RpcCreateMode.java
index d50bb74..020f283 100644
--- a/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/structs/RpcCreateMode.java
+++ b/curator-x-rpc/src/main/java/org/apache/curator/x/rpc/idl/structs/RpcCreateMode.java
@@ -26,5 +26,6 @@
     PERSISTENT,
     PERSISTENT_SEQUENTIAL,
     EPHEMERAL,
-    EPHEMERAL_SEQUENTIAL
+    EPHEMERAL_SEQUENTIAL,
+    CONTAINER
 }
diff --git a/curator-x-rpc/src/main/thrift/curator.thrift b/curator-x-rpc/src/main/thrift/curator.thrift
index d6bcd94..41f2362 100644
--- a/curator-x-rpc/src/main/thrift/curator.thrift
+++ b/curator-x-rpc/src/main/thrift/curator.thrift
@@ -8,7 +8,7 @@
 }
 
 enum CreateMode {
-  PERSISTENT, PERSISTENT_SEQUENTIAL, EPHEMERAL, EPHEMERAL_SEQUENTIAL
+  PERSISTENT, PERSISTENT_SEQUENTIAL, EPHEMERAL, EPHEMERAL_SEQUENTIAL, CONTAINER
 }
 
 enum CuratorEventType {
@@ -175,6 +175,7 @@
   5: bool compressed;
   6: bool creatingParentsIfNeeded;
   7: bool withProtection;
+  8: bool creatingParentContainersIfNeeded;
 }
 
 struct DeleteSpec {
diff --git a/curator-x-rpc/src/site/confluence/reference.confluence b/curator-x-rpc/src/site/confluence/reference.confluence
index 68c3692..bb7ea46 100644
--- a/curator-x-rpc/src/site/confluence/reference.confluence
+++ b/curator-x-rpc/src/site/confluence/reference.confluence
@@ -57,6 +57,7 @@
 |compressed|bool|\-|if true, compress the data|
 |creatingParentsIfNeeded|bool|\-|if true, create any needed parent nodes|
 |withProtection|bool|\-|if true, use Curator protection|
+|creatingParentContainersIfNeeded|bool|\-|if true, create any needed parent nodes as CONTAINERs|
 
 h2. DeleteSpec
 
diff --git a/pom.xml b/pom.xml
index 3e122c2..8c984ab 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.apache</groupId>
         <artifactId>apache</artifactId>
-        <version>14</version>
+        <version>16</version>
     </parent>
 
     <groupId>org.apache.curator</groupId>
@@ -41,7 +41,7 @@
     <licenses>
         <license>
             <name>The Apache Software License, Version 2.0</name>
-            <url>file://${basedir}/LICENSE</url>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
             <distribution>repo</distribution>
         </license>
     </licenses>
@@ -56,24 +56,17 @@
         <project.build.resourceEncoding>UTF-8</project.build.resourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 
-        <jdk-version>1.6</jdk-version>
+        <jdk-version>1.7</jdk-version>
 
         <surefire-forkcount>1</surefire-forkcount>
 
         <!-- versions -->
+        <zookeeper-version>3.5.0-alpha</zookeeper-version>
         <maven-project-info-reports-plugin-version>2.7</maven-project-info-reports-plugin-version>
-        <maven-javadoc-plugin-version>2.9.1</maven-javadoc-plugin-version>
-        <maven-dependency-plugin-version>2.8</maven-dependency-plugin-version>
-        <maven-install-plugin-version>2.5.1</maven-install-plugin-version>
-        <maven-compiler-plugin-version>3.1</maven-compiler-plugin-version>
         <maven-bundle-plugin-version>2.3.7</maven-bundle-plugin-version>
-        <maven-surefire-plugin-version>2.17</maven-surefire-plugin-version>
-        <maven-site-plugin-version>3.3</maven-site-plugin-version>
-        <doxia-module-confluence-version>1.5</doxia-module-confluence-version>
-        <maven-scm-publish-plugin-version>1.0</maven-scm-publish-plugin-version>
+        <maven-javadoc-plugin-version>2.10.3</maven-javadoc-plugin-version>
+        <doxia-module-confluence-version>1.6</doxia-module-confluence-version>
         <maven-license-plugin-version>1.9.0</maven-license-plugin-version>
-        <maven-release-plugin-version>2.5</maven-release-plugin-version>
-        <apache-rat-plugin-version>0.10</apache-rat-plugin-version>
         <javassist-version>3.18.1-GA</javassist-version>
         <commons-math-version>2.2</commons-math-version>
         <jackson-mapper-asl-version>1.9.13</jackson-mapper-asl-version>
@@ -82,13 +75,13 @@
         <jetty-version>6.1.26</jetty-version>
         <scannotation-version>1.0.2</scannotation-version>
         <resteasy-jaxrs-version>2.3.0.GA</resteasy-jaxrs-version>
-        <zookeeper-version>3.5.0-alpha</zookeeper-version>
         <guava-version>16.0.1</guava-version>
         <testng-version>6.8.8</testng-version>
         <swift-version>0.12.0</swift-version>
         <dropwizard-version>0.7.0</dropwizard-version>
         <maven-shade-plugin-version>2.3</maven-shade-plugin-version>
         <slf4j-version>1.7.6</slf4j-version>
+        <clirr-maven-plugin-version>2.6.1</clirr-maven-plugin-version>
 
         <!-- OSGi Properties -->
         <osgi.export.package />
@@ -105,7 +98,7 @@
         <connection>scm:git:https://git-wip-us.apache.org/repos/asf/curator.git</connection>
         <developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/curator.git
         </developerConnection>
-        <tag>HEAD</tag>
+        <tag>apache-curator-2.8.0</tag>
     </scm>
 
     <issueManagement>
@@ -234,6 +227,18 @@
         </developer>
 
         <developer>
+            <id>mdrob</id>
+            <name>Mike Drob</name>
+            <email>mdrob@apache.org</email>
+            <timezone>-6</timezone>
+            <roles>
+                <role>Committer</role>
+                <role>PMC Member</role>
+            </roles>
+            <url>http://people.apache.org/~mdrob</url>
+        </developer>
+
+        <developer>
             <name>Patrick Hunt</name>
             <email>phunt1@gmail.com</email>
             <roles>
@@ -479,31 +484,6 @@
         </dependencies>
     </dependencyManagement>
 
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.zookeeper</groupId>
-            <artifactId>zookeeper</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>org.testng</groupId>
-            <artifactId>testng</artifactId>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-
-    </dependencies>
-
     <reporting>
         <plugins>
             <plugin>
@@ -524,6 +504,16 @@
                     <failOnError>false</failOnError>
                 </configuration>
             </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>clirr-maven-plugin</artifactId>
+                <configuration>
+                    <includes>
+                        <include>org/apache/curator/**</include>
+                    </includes>
+                </configuration>
+            </plugin>
         </plugins>
     </reporting>
 
@@ -531,54 +521,12 @@
         <pluginManagement>
             <plugins>
                 <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-dependency-plugin</artifactId>
-                    <version>${maven-dependency-plugin-version}</version>
-                </plugin>
-
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-install-plugin</artifactId>
-                    <version>${maven-install-plugin-version}</version>
-                </plugin>
-
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-compiler-plugin</artifactId>
-                    <version>${maven-compiler-plugin-version}</version>
-                </plugin>
-
-                <plugin>
                     <groupId>org.apache.felix</groupId>
                     <artifactId>maven-bundle-plugin</artifactId>
                     <version>${maven-bundle-plugin-version}</version>
                 </plugin>
 
                 <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-surefire-plugin</artifactId>
-                    <version>${maven-surefire-plugin-version}</version>
-                </plugin>
-
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-javadoc-plugin</artifactId>
-                    <version>${maven-javadoc-plugin-version}</version>
-                </plugin>
-
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-site-plugin</artifactId>
-                    <version>${maven-site-plugin-version}</version>
-                </plugin>
-
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-scm-publish-plugin</artifactId>
-                    <version>${maven-scm-publish-plugin-version}</version>
-                </plugin>
-
-                <plugin>
                     <groupId>com.mycila.maven-license-plugin</groupId>
                     <artifactId>maven-license-plugin</artifactId>
                     <version>${maven-license-plugin-version}</version>
@@ -587,7 +535,6 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-release-plugin</artifactId>
-                    <version>${maven-release-plugin-version}</version>
                     <configuration>
                         <autoVersionSubmodules>true</autoVersionSubmodules>
                         <tagNameFormat>${project.artifactId}-${project.version}</tagNameFormat>
@@ -597,16 +544,16 @@
                 </plugin>
 
                 <plugin>
-                    <groupId>org.apache.rat</groupId>
-                    <artifactId>apache-rat-plugin</artifactId>
-                    <version>${apache-rat-plugin-version}</version>
-                </plugin>
-
-                <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-shade-plugin</artifactId>
                     <version>${maven-shade-plugin-version}</version>
                 </plugin>
+
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>clirr-maven-plugin</artifactId>
+                    <version>${clirr-maven-plugin-version}</version>
+                </plugin>
             </plugins>
         </pluginManagement>
 
@@ -816,6 +763,26 @@
                     </excludes>
                 </configuration>
             </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>clirr-maven-plugin</artifactId>
+                <configuration>
+                    <failOnError>false</failOnError>
+                    <logResults>true</logResults>
+                    <includes>
+                        <include>org/apache/curator/**</include>
+                    </includes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/src/site/confluence/compatibility.confluence b/src/site/confluence/compatibility.confluence
new file mode 100644
index 0000000..ef3324f
--- /dev/null
+++ b/src/site/confluence/compatibility.confluence
@@ -0,0 +1,9 @@
+h1. API Compatibility
+
+A [[Clirr|http://clirr.sourceforge.net/]] report is generated for each Curator module:
+
+* [[Curator Client Report|curator-client/clirr-report.html]]
+* [[Curator Framework Report|curator-framework/clirr-report.html]]
+* [[Curator Recipes Report|curator-recipes/clirr-report.html]]
+* [[Curator Discovery Report|curator-x-discovery/clirr-report.html]]
+* [[Curator Discovery Server Report|curator-x-discovery-server/clirr-report.html]]
diff --git a/src/site/confluence/utilities.confluence b/src/site/confluence/utilities.confluence
index 7a8e95f..f0d927d 100644
--- a/src/site/confluence/utilities.confluence
+++ b/src/site/confluence/utilities.confluence
@@ -14,24 +14,6 @@
 * getSortedChildren: Return the children of the given path sorted by sequence number
 * makePath: Given a parent path and a child node, create a combined full path
 
-h2. EnsurePath
-Utility to ensure that a particular path is created.
-The first time it is used, a synchronized call to {{ZKPaths.mkdirs(ZooKeeper, String)}} is made to ensure that the entire path has been created (with an empty byte array if needed). Subsequent calls with the instance are un\-synchronized NOPs.
-
-Usage:
-{code}
-EnsurePath       ensurePath = new EnsurePath(aFullPathToEnsure);
-...
-String           nodePath = aFullPathToEnsure + "/foo";
-ensurePath.ensure(zk);   // first time syncs and creates if needed
-zk.create(nodePath, ...);
-...
-ensurePath.ensure(zk);   // subsequent times are NOPs
-zk.create(nodePath, ...);
-{code}
-
-*NOTE:* There's a method in the [[CuratorFramework class|curator-framework/index.html]] that returns an EnsurePath instance that is namespace aware.
-
 h2. BlockingQueueConsumer
 
 See: *[[DistributedQueue|curator-recipes/distributed-queue.html]]* and *[[DistributedPriorityQueue|curator-recipes/distributed-priority-queue.html]]*
@@ -44,16 +26,7 @@
 provides a facade over multiple distributed queues. It monitors the queues and if any one of them goes over a threshold, a new
 queue is added. Puts are distributed amongst the queues.
 
-h2. Reaper and ChildReaper
+h2. EnsembleTracker
 
-_Reaper_
-
-A Utility to delete parent paths of locks, etc. Periodically checks paths added to the reaper. If at check time, there are no
-children, the path is deleted. Clients should create one Reaper instance per application. Add lock paths to the reaper as
-needed and the reaper will periodically delete them. Curator's lock recipes will correctly handle parents getting deleted.
-
-_ChildReaper_
-
-Utility to reap the empty child nodes in a parent node. Periodically calls getChildren() on the node and adds empty nodes to an internally managed Reaper.
-
-*NOTE:* You should consider using LeaderSelector to run the Reapers as they don't need to run in every client.
+Utility to listen for ensemble/configuration changes via registered EnsembleListeners. Allocate a EnsembleTracker, add one or more listeners
+and start it.
diff --git a/src/site/site.xml b/src/site/site.xml
index 6d5f208..cd04ec4 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -83,6 +83,7 @@
             <item name="Source Code" href="source-repository.html"/>
             <item name="Project Team" href="team-list.html"/>
             <item name="Project Information" href="project-info.html"/>
+            <item name="API Compatibility" href="compatibility.html"/>
             <item name="Javadoc" href="apidocs/index.html"/>
             <item name="Wiki" href="https://cwiki.apache.org/confluence/display/CURATOR"/>
             <item name="Releases" href="https://cwiki.apache.org/confluence/display/CURATOR/Releases"/>