Add metrics-common, zookeeper-api, helix-common modules (#684)

We want to create a new module called zookeeper-api in order to decouple Helix's ZooKeeper APIs from helix-core. The goal is to enable non-Helix applications to use Helix's ZooKeeper APIs. This change also allows for better modularity and separation of concerns.
diff --git a/bump-up.command b/bump-up.command
index d67616c..b59f314 100755
--- a/bump-up.command
+++ b/bump-up.command
@@ -41,6 +41,40 @@
 # git diff pom.xml
 grep -C 1 "$new_version" pom.xml
 
+echo "bump up helix-common/pom.xml"
+sed -i "s/${version}/${new_version}/g" helix-common/pom.xml
+grep -C 1 "$new_version" helix-common/pom.xml
+# git diff helix-common/pom.xml
+
+ivy_file="helix-common-"$version".ivy"
+new_ivy_file="helix-common-"$new_version".ivy"
+# echo "$ivy_file"
+if [ -f helix-common/$ivy_file ]; then
+  echo "bump up helix-common/$ivy_file"
+  git mv "helix-common/$ivy_file" "helix-common/$new_ivy_file"
+  sed -i "s/${version}/${new_version}/g" "helix-common/$new_ivy_file"
+  grep -C 1 "$new_version" "helix-common/$new_ivy_file"
+else
+  echo "helix-common/$ivy_file not exist"
+fi
+
+echo "bump up zookeeper-api/pom.xml"
+sed -i "s/${version}/${new_version}/g" zookeeper-api/pom.xml
+grep -C 1 "$new_version" zookeeper-api/pom.xml
+# git diff zookeeper-api/pom.xml
+
+ivy_file="zookeeper-api-"$version".ivy"
+new_ivy_file="zookeeper-api-"$new_version".ivy"
+# echo "$ivy_file"
+if [ -f zookeeper-api/$ivy_file ]; then
+  echo "bump up zookeeper-api/$ivy_file"
+  git mv "zookeeper-api/$ivy_file" "zookeeper-api/$new_ivy_file"
+  sed -i "s/${version}/${new_version}/g" "zookeeper-api/$new_ivy_file"
+  grep -C 1 "$new_version" "zookeeper-api/$new_ivy_file"
+else
+  echo "zookeeper-api/$ivy_file not exist"
+fi
+
 echo "bump up helix-core/pom.xml"
 sed -i "s/${version}/${new_version}/g" helix-core/pom.xml
 grep -C 1 "$new_version" helix-core/pom.xml
@@ -109,6 +143,7 @@
   echo "helix-agent/$ivy_file not exist"
 fi
 
+
 for POM in helix-agent/pom.xml recipes/task-execution/pom.xml recipes/pom.xml recipes/distributed-lock-manager/pom.xml recipes/rsync-replicated-file-system/pom.xml recipes/rabbitmq-consumer-group/pom.xml recipes/service-discovery/pom.xml
 do
   echo "bump up $POM"
diff --git a/helix-admin-webapp/pom.xml b/helix-admin-webapp/pom.xml
index 87e7d8e..bd89133 100644
--- a/helix-admin-webapp/pom.xml
+++ b/helix-admin-webapp/pom.xml
@@ -34,8 +34,11 @@
       org.apache.helix*,
       org.codehaus.jackson*,
       org.apache.commons.cli*,
+      org.apache.commons.cli;version="[1.2,2)",
+      org.apache.commons.io*;version="[1.4,2)",
       org.restlet*,
       org.slf4j*;version="[1.6,2)",
+      org.apache.zookeeper*;version="[3.4,4)",
       *
     </osgi.import>
     <osgi.export>org.apache.helix.webapp*;version="${project.version};-noimport:=true</osgi.export>
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/HelixAdminWebApp.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/HelixAdminWebApp.java
index 9785537..a68c85d 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/HelixAdminWebApp.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/HelixAdminWebApp.java
@@ -21,7 +21,7 @@
 
 import org.apache.helix.manager.zk.ByteArraySerializer;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.resources.ResourceUtil;
 import org.restlet.Component;
 import org.restlet.Context;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterRepresentationUtil.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterRepresentationUtil.java
index 2710684..3adc6af 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterRepresentationUtil.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterRepresentationUtil.java
@@ -34,11 +34,11 @@
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.LiveInstance.LiveInstanceProperty;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.JsonParseException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java
index e168903..f4d1de3 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java
@@ -25,8 +25,8 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.tools.ClusterSetup;
 import org.codehaus.jackson.JsonGenerationException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java
index fe6a6d5..05a2e8f 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java
@@ -23,8 +23,8 @@
 import java.util.List;

 

 import org.apache.helix.HelixException;

-import org.apache.helix.ZNRecord;

-import org.apache.helix.manager.zk.ZkClient;

+import org.apache.helix.zookeeper.datamodel.ZNRecord;

+import org.apache.helix.zookeeper.impl.client.ZkClient;

 import org.apache.helix.tools.ClusterSetup;

 import org.codehaus.jackson.JsonGenerationException;

 import org.codehaus.jackson.map.JsonMappingException;

diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java
index c7a58b5..65f468d 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java
@@ -25,8 +25,8 @@
 
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.HelixConfigScope;
 import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
index dc16c18..56fc1cf 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
@@ -22,9 +22,9 @@
 import java.util.Map;
 
 import org.apache.helix.HelixAdmin;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
 import org.apache.helix.tools.ClusterSetup;
 import org.restlet.data.MediaType;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java
index fede1b6..2004992 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java
@@ -30,10 +30,10 @@
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.util.StatusUpdateUtil.Level;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerStatusUpdateResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerStatusUpdateResource.java
index 6a6a7b7..54875c1 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerStatusUpdateResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerStatusUpdateResource.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/CurrentStateResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/CurrentStateResource.java
index 424ce92..6a84ddd 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/CurrentStateResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/CurrentStateResource.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/CurrentStatesResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/CurrentStatesResource.java
index 02582a3..5787075 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/CurrentStatesResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/CurrentStatesResource.java
@@ -22,7 +22,7 @@
 import java.io.IOException;
 
 import org.apache.helix.PropertyType;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ErrorResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ErrorResource.java
index 0c12705..5952bc2 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ErrorResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ErrorResource.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ErrorsResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ErrorsResource.java
index 671c528..efb3d97 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ErrorsResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ErrorsResource.java
@@ -22,7 +22,7 @@
 import java.io.IOException;
 
 import org.apache.helix.PropertyType;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ExternalViewResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ExternalViewResource.java
index 801a86f..48d0967 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ExternalViewResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ExternalViewResource.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
 import org.restlet.data.MediaType;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java
index b927a35..ae1a0ab 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java
@@ -26,8 +26,8 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.tools.ClusterSetup;
 import org.codehaus.jackson.JsonGenerationException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/InstanceResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/InstanceResource.java
index 23d6303..f251cbb 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/InstanceResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/InstanceResource.java
@@ -25,7 +25,7 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.tools.ClusterSetup;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/InstancesResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/InstancesResource.java
index 5909d72..9c03400 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/InstancesResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/InstancesResource.java
@@ -28,8 +28,8 @@
 import com.google.common.collect.Lists;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.tools.ClusterSetup;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java
index 10c603d..bacd0ea 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java
@@ -24,8 +24,8 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.task.JobConfig;
 import org.apache.helix.task.TaskDriver;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java
index 2937026..debdb49 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java
@@ -29,8 +29,8 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.task.JobQueue;
 import org.apache.helix.task.TaskDriver;
 import org.apache.helix.task.Workflow;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java
index 8389e5d..aab37ed 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java
@@ -22,8 +22,8 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.task.JobContext;
 import org.apache.helix.task.TaskDriver;
 import org.apache.helix.task.TaskUtil;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java
index 41d9a77..69e4de0 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JsonParameters.java
@@ -29,7 +29,7 @@
 import java.util.TreeMap;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.tools.ClusterSetup;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.restlet.data.Form;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceGroupResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceGroupResource.java
index a8c7634..860199e 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceGroupResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceGroupResource.java
@@ -25,7 +25,7 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.tools.ClusterSetup;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceGroupsResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceGroupsResource.java
index 75f8fb5..d974987 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceGroupsResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceGroupsResource.java
@@ -27,8 +27,8 @@
 import com.google.common.collect.Maps;
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.tools.ClusterSetup;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java
index 78a760a..053b542 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java
@@ -30,7 +30,7 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/SchedulerTasksResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/SchedulerTasksResource.java
index 9e4b849..d38c8e4 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/SchedulerTasksResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/SchedulerTasksResource.java
@@ -31,7 +31,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.manager.zk.DefaultSchedulerMessageHandlerFactory;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.Message.MessageType;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StateModelResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StateModelResource.java
index efb5776..ea451eb 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StateModelResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StateModelResource.java
@@ -25,8 +25,8 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.webapp.RestAdminApplication;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StateModelsResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StateModelsResource.java
index 326fa54..5340067 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StateModelsResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StateModelsResource.java
@@ -24,8 +24,8 @@
 
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.webapp.RestAdminApplication;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StatusUpdateResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StatusUpdateResource.java
index 2d81a22..2e9aafe 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StatusUpdateResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StatusUpdateResource.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StatusUpdatesResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StatusUpdatesResource.java
index 0838a85..4867365 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StatusUpdatesResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/StatusUpdatesResource.java
@@ -22,7 +22,7 @@
 import java.io.IOException;
 
 import org.apache.helix.PropertyType;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/WorkflowsResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/WorkflowsResource.java
index a695c36..658e98d 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/WorkflowsResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/WorkflowsResource.java
@@ -32,8 +32,8 @@
 import org.apache.helix.HelixProperty;
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.task.TaskDriver;
 import org.apache.helix.task.Workflow;
 import org.apache.helix.task.WorkflowConfig;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ZkChildResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ZkChildResource.java
index d321e38..2c81de2 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ZkChildResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ZkChildResource.java
@@ -21,8 +21,8 @@
 
 import java.util.List;
 
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.apache.zookeeper.data.Stat;
 import org.restlet.data.MediaType;
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ZkPathResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ZkPathResource.java
index 3e49284..99294e4 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ZkPathResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ZkPathResource.java
@@ -23,8 +23,8 @@
 import java.util.List;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.apache.zookeeper.data.Stat;
 import org.restlet.data.MediaType;
diff --git a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/AdminTestBase.java b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/AdminTestBase.java
index 0037a5b..6af49de 100644
--- a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/AdminTestBase.java
+++ b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/AdminTestBase.java
@@ -21,13 +21,13 @@
 
 import java.util.logging.Level;
 
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.TestHelper;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.util.ZKClientPool;
 import org.apache.helix.webapp.AdminTestHelper.AdminThread;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
 import org.restlet.Client;
 import org.restlet.data.Protocol;
 import org.slf4j.Logger;
diff --git a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/AdminTestHelper.java b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/AdminTestHelper.java
index f5f05c9..acc15ef 100644
--- a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/AdminTestHelper.java
+++ b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/AdminTestHelper.java
@@ -24,7 +24,7 @@
 import java.io.StringWriter;
 import java.util.concurrent.CountDownLatch;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.restlet.Client;
 import org.restlet.Request;
diff --git a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestClusterManagementWebapp.java b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestClusterManagementWebapp.java
index 486af07..0e45ac3 100644
--- a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestClusterManagementWebapp.java
+++ b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestClusterManagementWebapp.java
@@ -27,7 +27,7 @@
 import java.util.Map;
 
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.InstanceConfig.InstanceConfigProperty;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.webapp.resources.ClusterRepresentationUtil;
diff --git a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestDisableResource.java b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestDisableResource.java
index 9800179..e85fa87 100644
--- a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestDisableResource.java
+++ b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestDisableResource.java
@@ -27,7 +27,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.model.IdealState;
diff --git a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestHelixAdminScenariosRest.java b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestHelixAdminScenariosRest.java
index 0b49096..e78e074 100644
--- a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestHelixAdminScenariosRest.java
+++ b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestHelixAdminScenariosRest.java
@@ -30,7 +30,7 @@
 
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixDataAccessor;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.ClusterDistributedController;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestResetPartitionState.java b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestResetPartitionState.java
index 8d994e8..42bcbad 100644
--- a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestResetPartitionState.java
+++ b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/TestResetPartitionState.java
@@ -28,7 +28,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
diff --git a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/resources/TestJobQueuesResource.java b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/resources/TestJobQueuesResource.java
index 6d5d5e2..ca34d52 100644
--- a/helix-admin-webapp/src/test/java/org/apache/helix/webapp/resources/TestJobQueuesResource.java
+++ b/helix-admin-webapp/src/test/java/org/apache/helix/webapp/resources/TestJobQueuesResource.java
@@ -25,7 +25,7 @@
 
 import com.google.common.collect.Lists;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.integration.task.MockTask;
diff --git a/helix-common/LICENSE b/helix-common/LICENSE
new file mode 100644
index 0000000..d78ae52
--- /dev/null
+++ b/helix-common/LICENSE
@@ -0,0 +1,270 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
+
+
+For xstream:
+
+Copyright (c) 2003-2006, Joe Walnes
+Copyright (c) 2006-2009, 2011 XStream Committers
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other materials provided
+with the distribution.
+
+3. Neither the name of XStream nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+for jline:
+
+Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/helix-common/NOTICE b/helix-common/NOTICE
new file mode 100644
index 0000000..ff5a745
--- /dev/null
+++ b/helix-common/NOTICE
@@ -0,0 +1,37 @@
+Apache Helix
+Copyright 2014 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Codehaus (http://www.codehaus.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+jline (http://jline.sourceforge.net/).
+Licensed under the BSD License.
+
+This product includes software developed at
+restlet (http://www.restlet.org/about/legal).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Google (http://www.google.com/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+snakeyaml (http://www.snakeyaml.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+zkclient (https://github.com/sgroschupf/zkclient).
+Licensed under the Apache License 2.0.
+
+II. License Summary
+- Apache License 2.0
+- BSD License
\ No newline at end of file
diff --git a/helix-common/helix-common-0.9.2-SNAPSHOT.ivy b/helix-common/helix-common-0.9.2-SNAPSHOT.ivy
new file mode 100644
index 0000000..e1aa2f5
--- /dev/null
+++ b/helix-common/helix-common-0.9.2-SNAPSHOT.ivy
@@ -0,0 +1,51 @@
+<?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.
+-->
+<ivy-module version="1.0">
+	<info organisation="org.apache.helix"
+		module="helix-common"
+		revision="0.9.2-SNAPSHOT"
+		status="integration"
+		publication="20170128141623"
+	/>
+	<configurations>
+		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
+		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
+		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
+		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
+		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
+		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases."/>
+		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
+	</configurations>
+	<publications>
+		<artifact name="helix-common" type="jar" ext="jar" conf="master"/>
+	</publications>
+	<dependencies>
+	  <dependency org="org.slf4j" name="slf4j-api" rev="1.7.25" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)">
+        <artifact name="slf4j-api" ext="jar"/>
+    </dependency>
+    <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.14" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)">
+        <artifact name="slf4j-log4j12" ext="jar"/>
+    </dependency>
+    <dependency org="org.yaml" name="snakeyaml" rev="1.12" conf="compile->compile(default);runtime->runtime(default);default->default"/>
+		<dependency org="org.codehaus.jackson" name="jackson-core-asl" rev="1.8.5" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>
+		<dependency org="org.codehaus.jackson" name="jackson-mapper-asl" rev="1.8.5" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>
+		<dependency org="commons-cli" name="commons-cli" rev="1.2" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>
+	</dependencies>
+</ivy-module>
diff --git a/helix-common/pom.xml b/helix-common/pom.xml
new file mode 100644
index 0000000..64a5f88
--- /dev/null
+++ b/helix-common/pom.xml
@@ -0,0 +1,97 @@
+<?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>
+    <groupId>org.apache.helix</groupId>
+    <artifactId>helix</artifactId>
+    <version>0.9.2-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>helix-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Apache Helix :: Helix Common</name>
+
+  <properties>
+    <osgi.import>
+      org.codehaus.jackson*,
+      org.slf4j*;version="[1.6,2)",
+      *
+    </osgi.import>
+    <osgi.export>org.apache.helix*;version="${project.version};-noimport:=true</osgi.export>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.helix</groupId>
+      <artifactId>metrics-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.helix</groupId>
+      <artifactId>zookeeper-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.25</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>1.7.14</version>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <resources>
+      <resource>
+        <directory>${basedir}</directory>
+        <includes>
+          <include>DISCLAIMER</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/assemble/assembly.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/helix-common/src/assemble/assembly.xml b/helix-common/src/assemble/assembly.xml
new file mode 100644
index 0000000..79e2f5c
--- /dev/null
+++ b/helix-common/src/assemble/assembly.xml
@@ -0,0 +1,60 @@
+<?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.
+-->
+<assembly>
+  <id>pkg</id>
+  <formats>
+    <format>tar</format>
+  </formats>
+  <fileSets>
+    <fileSet>
+      <directory>target/helix-common-pkg/bin</directory>
+      <outputDirectory>bin</outputDirectory>
+      <lineEnding>unix</lineEnding>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>target/helix-common-pkg/repo/</directory>
+      <outputDirectory>repo</outputDirectory>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+      <excludes>
+        <exclude>**/*.xml</exclude>
+      </excludes>
+    </fileSet>
+   <fileSet>
+      <directory>target/helix-common-pkg/conf</directory>
+      <outputDirectory>conf</outputDirectory>
+      <lineEnding>unix</lineEnding>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}</directory>
+      <outputDirectory>/</outputDirectory>
+      <includes>
+        <include>LICENSE</include>
+        <include>NOTICE</include>
+        <include>DISCLAIMER</include>
+      </includes>
+      <fileMode>0755</fileMode>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/helix-core/src/main/java/org/apache/helix/HelixException.java b/helix-common/src/main/java/org/apache/helix/HelixException.java
similarity index 100%
rename from helix-core/src/main/java/org/apache/helix/HelixException.java
rename to helix-common/src/main/java/org/apache/helix/HelixException.java
diff --git a/helix-common/src/main/java/org/apache/helix/SystemPropertyKeys.java b/helix-common/src/main/java/org/apache/helix/SystemPropertyKeys.java
new file mode 100644
index 0000000..bcb8405
--- /dev/null
+++ b/helix-common/src/main/java/org/apache/helix/SystemPropertyKeys.java
@@ -0,0 +1,60 @@
+package org.apache.helix;
+
+/*
+ * 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.
+ */
+
+public class SystemPropertyKeys {
+  // Task Driver
+  public static final String TASK_CONFIG_LIMITATION = "helixTask.configsLimitation";
+
+  // ZKHelixManager
+  public static final String CLUSTER_MANAGER_VERSION = "cluster-manager-version.properties";
+
+  public static final String FLAPPING_TIME_WINDOW = "helixmanager.flappingTimeWindow";
+
+  // max disconnect count during the flapping time window to trigger HelixManager flapping handling
+  public static final String MAX_DISCONNECT_THRESHOLD = "helixmanager.maxDisconnectThreshold";
+
+  public static final String ZK_SESSION_TIMEOUT = "zk.session.timeout";
+
+  public static final String ZK_CONNECTION_TIMEOUT = "zk.connection.timeout";
+
+  @Deprecated
+  public static final String ZK_REESTABLISHMENT_CONNECTION_TIMEOUT =
+      "zk.connectionReEstablishment.timeout";
+
+  public static final String ZK_WAIT_CONNECTED_TIMEOUT = "helixmanager.waitForConnectedTimeout";
+
+  public static final String PARTICIPANT_HEALTH_REPORT_LATENCY =
+      "helixmanager.participantHealthReport.reportLatency";
+
+  // Indicate monitoring level of the HelixManager metrics
+  public static final String MONITOR_LEVEL = "helixmanager.monitorLevel";
+
+  // CallbackHandler
+  public static final String ASYNC_BATCH_MODE_ENABLED = "helix.callbackhandler.isAsyncBatchModeEnabled";
+
+  public static final String LEGACY_ASYNC_BATCH_MODE_ENABLED = "isAsyncBatchModeEnabled";
+
+  // Controller
+  public static final String CONTROLLER_MESSAGE_PURGE_DELAY = "helix.controller.stages.MessageGenerationPhase.messagePurgeDelay";
+
+  // MBean monitor for helix.
+  public static final String HELIX_MONITOR_TIME_WINDOW_LENGTH_MS = "helix.monitor.slidingTimeWindow.ms";
+}
diff --git a/helix-common/src/main/java/org/apache/helix/ZNRecord.java b/helix-common/src/main/java/org/apache/helix/ZNRecord.java
new file mode 100644
index 0000000..a8018fd
--- /dev/null
+++ b/helix-common/src/main/java/org/apache/helix/ZNRecord.java
@@ -0,0 +1,48 @@
+package org.apache.helix;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
+
+
+/**
+ * Deprecated; please use ZNRecord in zookeeper-api instead.
+ *
+ * Generic Record Format to store data at a Node This can be used to store
+ * simpleFields mapFields listFields
+ */
+@Deprecated
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonSerialize(include = Inclusion.NON_NULL)
+public class ZNRecord extends org.apache.helix.zookeeper.datamodel.ZNRecord {
+  public ZNRecord(String id) {
+    super(id);
+  }
+
+  public ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord record) {
+    super(record);
+  }
+
+  public ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord record, String id) {
+    super(record, id);
+  }
+}
diff --git a/helix-common/src/main/java/org/apache/helix/ZNRecordDelta.java b/helix-common/src/main/java/org/apache/helix/ZNRecordDelta.java
new file mode 100644
index 0000000..2529d94
--- /dev/null
+++ b/helix-common/src/main/java/org/apache/helix/ZNRecordDelta.java
@@ -0,0 +1,29 @@
+package org.apache.helix;
+
+/*
+ * 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.
+ */
+
+/**
+ * Deprecated; please use ZNRecordDelta in zookeeper-api instead.
+ *
+ * A ZNRecord container that specifies how it should be merged with another ZNRecord
+ */
+@Deprecated
+public class ZNRecordDelta {
+}
diff --git a/helix-common/src/main/java/org/apache/helix/manager/zk/serializer/JacksonPayloadSerializer.java b/helix-common/src/main/java/org/apache/helix/manager/zk/serializer/JacksonPayloadSerializer.java
new file mode 100644
index 0000000..651836b
--- /dev/null
+++ b/helix-common/src/main/java/org/apache/helix/manager/zk/serializer/JacksonPayloadSerializer.java
@@ -0,0 +1,29 @@
+package org.apache.helix.manager.zk.serializer;
+
+/*
+ * 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.
+ */
+
+/**
+ * Deprecated; please use JacksonPayloadSerializer in zookeeper-api instead.
+ *
+ * Serializes and deserializes data of a generic type using Jackson
+ */
+@Deprecated
+public class JacksonPayloadSerializer extends org.apache.helix.zookeeper.datamodel.serializer.JacksonPayloadSerializer {
+}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java b/helix-common/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java
similarity index 62%
copy from helix-core/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java
copy to helix-common/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java
index a9531bd..a26e7fb 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java
+++ b/helix-common/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java
@@ -20,22 +20,10 @@
  */
 
 /**
+ * Deprecated; please use PayloadSerializer in zookeeper-api instead.
+ *
  * Interface for converting back and forth between raw bytes and generic objects
  */
-public interface PayloadSerializer {
-
-  /**
-   * Convert a generic object instance to raw bytes
-   * @param data instance of the generic type
-   * @return byte array representing the object
-   */
-  public <T> byte[] serialize(final T data);
-
-  /**
-   * Convert raw bytes to a generic object instance
-   * @param clazz The class represented by the deserialized bytes
-   * @param bytes byte array representing the object
-   * @return instance of the generic type or null if the conversion failed
-   */
-  public <T> T deserialize(final Class<T> clazz, final byte[] bytes);
+@Deprecated
+public interface PayloadSerializer extends org.apache.helix.zookeeper.datamodel.serializer.PayloadSerializer {
 }
diff --git a/helix-common/src/test/conf/testng.xml b/helix-common/src/test/conf/testng.xml
new file mode 100644
index 0000000..46911d2
--- /dev/null
+++ b/helix-common/src/test/conf/testng.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Suite" parallel="false">
+  <test name="Test" preserve-order="true">
+    <packages>
+      <package name="org.apache.helix.helix.common.*"/>
+    </packages>
+  </test>
+</suite>
diff --git a/helix-common/src/test/resources/log4j.properties b/helix-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..24b6d10
--- /dev/null
+++ b/helix-common/src/test/resources/log4j.properties
@@ -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.
+#
+# Set root logger level to DEBUG and its only appender to R.
+log4j.rootLogger=ERROR, C
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.C=org.apache.log4j.ConsoleAppender
+log4j.appender.C.layout=org.apache.log4j.PatternLayout
+log4j.appender.C.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+
+log4j.appender.R=org.apache.log4j.RollingFileAppender
+log4j.appender.R.layout=org.apache.log4j.PatternLayout
+log4j.appender.R.layout.ConversionPattern=%5p [%C:%M] (%F:%L) - %m%n
+log4j.appender.R.File=target/ClusterManagerLogs/log.txt
+
+log4j.appender.STATUSDUMP=org.apache.log4j.RollingFileAppender
+log4j.appender.STATUSDUMP.layout=org.apache.log4j.SimpleLayout
+log4j.appender.STATUSDUMP.File=target/ClusterManagerLogs/statusUpdates.log
+
+log4j.logger.org.I0Itec=ERROR
+log4j.logger.org.apache=ERROR
+log4j.logger.com.noelios=ERROR
+log4j.logger.org.restlet=ERROR
+
+log4j.logger.org.apache.helix.monitoring.ZKPathDataDumpTask=ERROR,STATUSDUMP
diff --git a/helix-core/helix-core-0.9.2-SNAPSHOT.ivy b/helix-core/helix-core-0.9.2-SNAPSHOT.ivy
index 07dd266..6a1cdee 100644
--- a/helix-core/helix-core-0.9.2-SNAPSHOT.ivy
+++ b/helix-core/helix-core-0.9.2-SNAPSHOT.ivy
@@ -57,9 +57,8 @@
     <dependency org="org.codehaus.jackson" name="jackson-mapper-asl" rev="1.8.5" conf="compile->compile(default);runtime->runtime(default);default->default"/>
     <dependency org="commons-io" name="commons-io" rev="1.4" conf="compile->compile(default);runtime->runtime(default);default->default"/>
     <dependency org="commons-cli" name="commons-cli" rev="1.2" conf="compile->compile(default);runtime->runtime(default);default->default"/>
-    <dependency org="org.apache.commons" name="commons-math" rev="2.1" conf="compile->compile(default);runtime->runtime(default);default->default"/>
-    <dependency org="org.apache.commons" name="commons-math3" rev="3.6.1" conf="compile->compile(default);runtime->runtime(default);default->default"/>
-    <dependency org="com.101tec" name="zkclient" rev="0.5" conf="compile->compile(default);runtime->runtime(default);default->default"/>
+    <dependency org="commons-math" name="commons-math" rev="2.1" conf="compile->compile(default);runtime->runtime(default);default->default"/>
+    <dependency org="commons-math" name="commons-math3" rev="3.6.1" conf="compile->compile(default);runtime->runtime(default);default->default"/>
     <dependency org="com.google.guava" name="guava" rev="15.0" conf="compile->compile(default);runtime->runtime(default);default->default"/>
     <dependency org="org.yaml" name="snakeyaml" rev="1.12" conf="compile->compile(default);runtime->runtime(default);default->default"/>
     <dependency org="commons-logging" name="commons-logging-api" rev="1.1" conf="compile->compile(*),master(*);runtime->runtime(*)"/>
diff --git a/helix-core/pom.xml b/helix-core/pom.xml
index 1077cc0..18113e1 100644
--- a/helix-core/pom.xml
+++ b/helix-core/pom.xml
@@ -34,7 +34,6 @@
     <osgi.import>
       javax.management*,
       javax.xml.bind*,
-      org.I0Itec.zkclient*,
       org.apache.commons.cli*;version="[1.2,2)",
       org.apache.commons.io*;version="[1.4,2)",
       org.apache.commons.math*;version="[2.1,4)",
@@ -55,6 +54,11 @@
 
   <dependencies>
     <dependency>
+      <groupId>org.apache.helix</groupId>
+      <artifactId>helix-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
       <version>1.7.25</version>
@@ -114,11 +118,6 @@
       <version>1.2</version>
     </dependency>
     <dependency>
-      <groupId>com.101tec</groupId>
-      <artifactId>zkclient</artifactId>
-      <version>0.5</version>
-    </dependency>
-    <dependency>
       <groupId>org.testng</groupId>
       <artifactId>testng</artifactId>
       <scope>test</scope>
diff --git a/helix-core/src/main/java/org/apache/helix/BaseDataAccessor.java b/helix-core/src/main/java/org/apache/helix/BaseDataAccessor.java
index 34a20ea..ba64664 100644
--- a/helix-core/src/main/java/org/apache/helix/BaseDataAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/BaseDataAccessor.java
@@ -21,9 +21,9 @@
 
 import java.util.List;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
 import org.apache.zookeeper.data.Stat;
 
 
diff --git a/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java b/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java
index 018743d..b5f06ef 100644
--- a/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java
@@ -29,8 +29,6 @@
 
 import org.apache.helix.manager.zk.ZKUtil;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ConfigScope;
 import org.apache.helix.model.HelixConfigScope;
@@ -40,6 +38,9 @@
 import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.util.StringTemplate;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/GroupCommit.java b/helix-core/src/main/java/org/apache/helix/GroupCommit.java
index b23fc2e..7146e37 100644
--- a/helix-core/src/main/java/org/apache/helix/GroupCommit.java
+++ b/helix-core/src/main/java/org/apache/helix/GroupCommit.java
@@ -25,11 +25,13 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 // TODO: move to mananger.zk
+
 /**
  * Support committing updates to data such that they are ordered for each key
  */
@@ -94,8 +96,9 @@
       if (queue._running.compareAndSet(null, Thread.currentThread())) {
         ArrayList<Entry> processed = new ArrayList<>();
         try {
-          if (queue._pending.peek() == null)
+          if (queue._pending.peek() == null) {
             return true;
+          }
 
           // remove from queue
           Entry first = queue._pending.poll();
@@ -123,8 +126,9 @@
           Iterator<Entry> it = queue._pending.iterator();
           while (it.hasNext()) {
             Entry ent = it.next();
-            if (!ent._key.equals(mergedKey))
+            if (!ent._key.equals(mergedKey)) {
               continue;
+            }
             processed.add(ent);
             merged.merge(ent._record);
             // System.out.println("After merging:" + merged);
@@ -162,7 +166,8 @@
           try {
             entry.wait(10);
           } catch (InterruptedException e) {
-            LOG.error("Interrupted while committing change, key: " + key + ", record: " + record, e);
+            LOG.error("Interrupted while committing change, key: " + key + ", record: " + record,
+                e);
             // Restore interrupt status
             Thread.currentThread().interrupt();
             return false;
@@ -172,5 +177,4 @@
     }
     return success;
   }
-
 }
diff --git a/helix-core/src/main/java/org/apache/helix/HelixDataAccessor.java b/helix-core/src/main/java/org/apache/helix/HelixDataAccessor.java
index d8dde1e..2a763df 100644
--- a/helix-core/src/main/java/org/apache/helix/HelixDataAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/HelixDataAccessor.java
@@ -22,12 +22,14 @@
 import java.util.List;
 import java.util.Map;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.model.MaintenanceSignal;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.PauseSignal;
 import org.apache.helix.model.StateModelDefinition;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 /**
  * Interface used to interact with Helix Data Types like IdealState, Config,
@@ -36,9 +38,13 @@
  */
 public interface HelixDataAccessor {
   boolean createStateModelDef(StateModelDefinition stateModelDef);
+
   boolean createControllerMessage(Message message);
+
   boolean createControllerLeader(LiveInstance leader);
+
   boolean createPause(PauseSignal pauseSignal);
+
   boolean createMaintenance(MaintenanceSignal maintenanceSignal);
 
   /**
@@ -67,7 +73,8 @@
    * @param value
    * @return true if the update was successful
    */
-  <T extends HelixProperty> boolean updateProperty(PropertyKey key, DataUpdater<ZNRecord> updater, T value);
+  <T extends HelixProperty> boolean updateProperty(PropertyKey key, DataUpdater<ZNRecord> updater,
+      T value);
 
   /**
    * Return the property value, it must be refer to a single Helix Property. i.e
diff --git a/helix-core/src/main/java/org/apache/helix/HelixManager.java b/helix-core/src/main/java/org/apache/helix/HelixManager.java
index 67d77ec..2191153 100644
--- a/helix-core/src/main/java/org/apache/helix/HelixManager.java
+++ b/helix-core/src/main/java/org/apache/helix/HelixManager.java
@@ -43,6 +43,8 @@
 import org.apache.helix.participant.StateMachineEngine;
 import org.apache.helix.spectator.RoutingTableProvider;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 /**
  * Class that represents the Helix Agent.
diff --git a/helix-core/src/main/java/org/apache/helix/HelixProperty.java b/helix-core/src/main/java/org/apache/helix/HelixProperty.java
index 540e4c6..a9db500 100644
--- a/helix-core/src/main/java/org/apache/helix/HelixProperty.java
+++ b/helix-core/src/main/java/org/apache/helix/HelixProperty.java
@@ -27,7 +27,9 @@
 import java.util.List;
 import java.util.Map;
 
-import org.I0Itec.zkclient.serialize.ZkSerializer;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordDelta;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,8 +41,7 @@
   private static Logger LOG = LoggerFactory.getLogger(HelixProperty.class);
 
   public enum HelixPropertyAttribute {
-    BUCKET_SIZE,
-    BATCH_MESSAGE_MODE
+    BUCKET_SIZE, BATCH_MESSAGE_MODE
   }
 
   protected final ZNRecord _record;
@@ -172,7 +173,8 @@
    */
   public HelixProperty(ZNRecord record, String id) {
     _record = new ZNRecord(record, id);
-    _stat = new Stat(_record.getVersion(), _record.getCreationTime(), _record.getModifiedTime(), _record.getEphemeralOwner());
+    _stat = new Stat(_record.getVersion(), _record.getCreationTime(), _record.getModifiedTime(),
+        _record.getEphemeralOwner());
   }
 
   /**
@@ -201,7 +203,7 @@
 
   @Override
   public String toString() {
-    return "ZnRecord=" + _record.toString() + ", Stat=" + _stat.toString() ;
+    return "ZnRecord=" + _record.toString() + ", Stat=" + _stat.toString();
   }
 
   /**
@@ -226,8 +228,9 @@
    * @param bucketSize the bucket size (will default to 0 if negative)
    */
   public void setBucketSize(int bucketSize) {
-    if (bucketSize <= 0)
+    if (bucketSize <= 0) {
       bucketSize = 0;
+    }
 
     _record.setSimpleField(HelixPropertyAttribute.BUCKET_SIZE.toString(), "" + bucketSize);
   }
@@ -238,7 +241,8 @@
    * @param record the ZNRecord describing the property
    * @return typed instance corresponding to the record, or null if conversion fails
    */
-  public static <T extends HelixProperty> T convertToTypedInstance(Class<T> clazz, ZNRecord record) {
+  public static <T extends HelixProperty> T convertToTypedInstance(Class<T> clazz,
+      ZNRecord record) {
     if (record == null) {
       return null;
     }
@@ -302,7 +306,7 @@
       return Collections.emptyList();
     }
 
-    List<ZNRecord> records = new ArrayList<ZNRecord>();
+    List<ZNRecord> records = new ArrayList<>();
     for (T typedInstance : typedInstances) {
       records.add(typedInstance.getRecord());
     }
diff --git a/helix-core/src/main/java/org/apache/helix/LiveInstanceInfoProvider.java b/helix-core/src/main/java/org/apache/helix/LiveInstanceInfoProvider.java
index 91d6194..827d434 100644
--- a/helix-core/src/main/java/org/apache/helix/LiveInstanceInfoProvider.java
+++ b/helix-core/src/main/java/org/apache/helix/LiveInstanceInfoProvider.java
@@ -19,6 +19,9 @@
  * under the License.
  */
 
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
+
 /**
  * Interface to provide additional information about a live instance at creation time
  */
diff --git a/helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java b/helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java
index d316986..2d824cb 100644
--- a/helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java
+++ b/helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java
@@ -1,5 +1,24 @@
 package org.apache.helix;
 
+/*
+ * 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.
+ */
+
 public class SystemPropertyKeys {
   // Task Driver
   public static final String TASK_CONFIG_LIMITATION = "helixTask.configsLimitation";
diff --git a/helix-core/src/main/java/org/apache/helix/ZNRecordAssembler.java b/helix-core/src/main/java/org/apache/helix/ZNRecordAssembler.java
index 87231cd..8028007 100644
--- a/helix-core/src/main/java/org/apache/helix/ZNRecordAssembler.java
+++ b/helix-core/src/main/java/org/apache/helix/ZNRecordAssembler.java
@@ -19,33 +19,10 @@
  * under the License.
  */
 
-import java.util.List;
-
 /**
+ * Deprecated - use ZNRecordAssembler in zookeeper-api instead.
  * Constructs ZNRecords from collections of ZNRecords
  */
-public class ZNRecordAssembler {
-  /**
-   * Merge a list of ZNRecords into a single ZNRecord
-   * @param records
-   * @return {@link ZNRecord}
-   */
-  public ZNRecord assemble(List<ZNRecord> records) {
-    ZNRecord assembledRecord = null;
-    if (records != null && records.size() > 0) {
-      for (ZNRecord record : records) {
-        if (record == null) {
-          continue;
-        }
-
-        if (assembledRecord == null) {
-          assembledRecord = new ZNRecord(record.getId());
-        }
-
-        assembledRecord.merge(record);
-      }
-    }
-    return assembledRecord;
-  }
-
+@Deprecated
+public class ZNRecordAssembler extends org.apache.helix.zookeeper.datamodel.ZNRecordAssembler {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/ZNRecordBucketizer.java b/helix-core/src/main/java/org/apache/helix/ZNRecordBucketizer.java
index 79b56cb..a490572 100644
--- a/helix-core/src/main/java/org/apache/helix/ZNRecordBucketizer.java
+++ b/helix-core/src/main/java/org/apache/helix/ZNRecordBucketizer.java
@@ -19,106 +19,17 @@
  * under the License.
  */
 
-import java.util.HashMap;
-import java.util.Map;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 /**
+ * Deprecated - use ZNRecordBucketizer in zookeeper-api instead.
  * Operations to divide a ZNRecord into specified buckets
  */
-public class ZNRecordBucketizer {
-  private static Logger LOG = LoggerFactory.getLogger(ZNRecordBucketizer.class);
-  final int _bucketSize;
-
+@Deprecated
+public class ZNRecordBucketizer extends org.apache.helix.zookeeper.datamodel.ZNRecordBucketizer {
   /**
    * Instantiate a bucketizer with the number of buckets
    * @param bucketSize
    */
   public ZNRecordBucketizer(int bucketSize) {
-    if (bucketSize <= 0) {
-      LOG.debug("bucketSize <= 0 (was " + bucketSize
-          + "). Set to 0 to use non-bucketized HelixProperty.");
-      bucketSize = 0;
-    }
-
-    _bucketSize = bucketSize;
-  }
-
-  /**
-   * Calculate bucketName in form of "resourceName_p{startPartition}-p{endPartition}
-   * @param partitionName
-   * @return the bucket name
-   */
-  public String getBucketName(String key) {
-    if (_bucketSize == 0) {
-      // no bucketize
-      return null;
-    }
-
-    int idx = key.lastIndexOf('_');
-    if (idx < 0) {
-      throw new IllegalArgumentException("Could NOT find partition# in " + key
-          + ". partitionName should be in format of resourceName_partition#");
-    }
-
-    try {
-      int partitionNb = Integer.parseInt(key.substring(idx + 1));
-      int bucketNb = partitionNb / _bucketSize;
-      int startPartition = bucketNb * _bucketSize;
-      int endPartition = bucketNb * _bucketSize + (_bucketSize - 1);
-      return key.substring(0, idx) + "_p" + startPartition + "-p" + endPartition;
-    } catch (NumberFormatException e) {
-      throw new IllegalArgumentException("Could NOT parse partition# (" + key.substring(idx + 1)
-          + ") in " + key);
-    }
-  }
-
-  /**
-   * Bucketize a ZNRecord
-   * @param record
-   * @return A map of bucket names to bucketized records
-   */
-  public Map<String, ZNRecord> bucketize(ZNRecord record) {
-    Map<String, ZNRecord> map = new HashMap<String, ZNRecord>();
-    if (_bucketSize == 0) {
-      map.put(record.getId(), record);
-      return map;
-    }
-
-    // bucketize list field
-    for (String partitionName : record.getListFields().keySet()) {
-      String bucketName = getBucketName(partitionName);
-      if (bucketName != null) {
-        if (!map.containsKey(bucketName)) {
-          map.put(bucketName, new ZNRecord(bucketName));
-        }
-        ZNRecord bucketizedRecord = map.get(bucketName);
-        bucketizedRecord.setListField(partitionName, record.getListField(partitionName));
-      } else {
-        LOG.error("Can't bucketize " + partitionName + " in list field");
-      }
-    }
-
-    // bucketize map field
-    for (String partitionName : record.getMapFields().keySet()) {
-      String bucketName = getBucketName(partitionName);
-      if (bucketName != null) {
-        if (!map.containsKey(bucketName)) {
-          map.put(bucketName, new ZNRecord(bucketName));
-        }
-        ZNRecord bucketizedRecord = map.get(bucketName);
-        bucketizedRecord.setMapField(partitionName, record.getMapField(partitionName));
-      } else {
-        LOG.error("Can't bucketize " + partitionName + " in map field");
-      }
-    }
-
-    // copy all simple fields
-    for (ZNRecord bucketizedRecord : map.values()) {
-      bucketizedRecord.setSimpleFields(record.getSimpleFields());
-    }
-    return map;
+    super(bucketSize);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/ZNRecordUpdater.java b/helix-core/src/main/java/org/apache/helix/ZNRecordUpdater.java
index 6b74dd1..bedb0e4 100644
--- a/helix-core/src/main/java/org/apache/helix/ZNRecordUpdater.java
+++ b/helix-core/src/main/java/org/apache/helix/ZNRecordUpdater.java
@@ -19,28 +19,20 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 /**
+ * Deprecated - Use ZNRecordUpdater in zookeeper-api intstead.
  * Class that specifies how a ZNRecord should be updated with another ZNRecord
  */
-public class ZNRecordUpdater implements DataUpdater<ZNRecord> {
-  final ZNRecord _record;
-
+@Deprecated
+public class ZNRecordUpdater extends org.apache.helix.zookeeper.datamodel.ZNRecordUpdater {
   /**
    * Initialize with the record that will be updated
    * @param record
    */
   public ZNRecordUpdater(ZNRecord record) {
-    _record = record;
-  }
-
-  @Override
-  public ZNRecord update(ZNRecord current) {
-    if (current != null) {
-      current.merge(_record);
-      return current;
-    }
-    return _record;
+    super(record);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/api/config/RebalanceConfig.java b/helix-core/src/main/java/org/apache/helix/api/config/RebalanceConfig.java
index 3750554..8ea1c22 100644
--- a/helix-core/src/main/java/org/apache/helix/api/config/RebalanceConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/api/config/RebalanceConfig.java
@@ -22,7 +22,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.rebalancer.Rebalancer;
 import org.apache.helix.task.TaskRebalancer;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/api/config/StateTransitionTimeoutConfig.java b/helix-core/src/main/java/org/apache/helix/api/config/StateTransitionTimeoutConfig.java
index d39f466..b846b00 100644
--- a/helix-core/src/main/java/org/apache/helix/api/config/StateTransitionTimeoutConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/api/config/StateTransitionTimeoutConfig.java
@@ -22,7 +22,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 public class StateTransitionTimeoutConfig {
   public enum StateTransitionTimeoutProperty {
diff --git a/helix-core/src/main/java/org/apache/helix/api/listeners/PreFetch.java b/helix-core/src/main/java/org/apache/helix/api/listeners/PreFetch.java
index b601680..594f533 100644
--- a/helix-core/src/main/java/org/apache/helix/api/listeners/PreFetch.java
+++ b/helix-core/src/main/java/org/apache/helix/api/listeners/PreFetch.java
@@ -22,7 +22,10 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-
+/**
+ * This annotation has been deprecated. Use PreFetch in zookeeper-api module instead.
+ */
+@Deprecated
 @Retention(RetentionPolicy.RUNTIME)
 public @interface PreFetch {
   boolean enabled() default true;
diff --git a/helix-core/src/main/java/org/apache/helix/common/DedupEventProcessor.java b/helix-core/src/main/java/org/apache/helix/common/DedupEventProcessor.java
index c26ecd8..c761a30 100644
--- a/helix-core/src/main/java/org/apache/helix/common/DedupEventProcessor.java
+++ b/helix-core/src/main/java/org/apache/helix/common/DedupEventProcessor.java
@@ -1,6 +1,6 @@
 package org.apache.helix.common;
 
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/common/caches/TaskDataCache.java b/helix-core/src/main/java/org/apache/helix/common/caches/TaskDataCache.java
index 58c1220..b16e484 100644
--- a/helix-core/src/main/java/org/apache/helix/common/caches/TaskDataCache.java
+++ b/helix-core/src/main/java/org/apache/helix/common/caches/TaskDataCache.java
@@ -30,7 +30,7 @@
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.controllers.ControlContextProvider;
 import org.apache.helix.controller.LogUtil;
 import org.apache.helix.model.ResourceAssignment;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/ExternalViewGenerator.java b/helix-core/src/main/java/org/apache/helix/controller/ExternalViewGenerator.java
index ab25118..2533b97 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/ExternalViewGenerator.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/ExternalViewGenerator.java
@@ -27,7 +27,7 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.CurrentState.CurrentStateProperty;
 import org.apache.helix.model.Message;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/GenericHelixController.java b/helix-core/src/main/java/org/apache/helix/controller/GenericHelixController.java
index e47c420..8f7da2d 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/GenericHelixController.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/GenericHelixController.java
@@ -38,7 +38,6 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 import com.google.common.collect.Sets;
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
 import org.apache.helix.NotificationContext;
@@ -99,6 +98,7 @@
 import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.monitoring.mbeans.ClusterEventMonitor;
 import org.apache.helix.monitoring.mbeans.ClusterStatusMonitor;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/controller/HelixControllerMain.java b/helix-core/src/main/java/org/apache/helix/controller/HelixControllerMain.java
index d8e5a3f..d446fec 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/HelixControllerMain.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/HelixControllerMain.java
@@ -35,7 +35,6 @@
 
 import java.util.Arrays;
 
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
 import org.apache.commons.cli.GnuParser;
@@ -50,6 +49,7 @@
 import org.apache.helix.manager.zk.HelixManagerShutdownHook;
 import org.apache.helix.participant.DistClusterControllerStateModelFactory;
 import org.apache.helix.participant.StateMachineEngine;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/controller/HierarchicalDataHolder.java b/helix-core/src/main/java/org/apache/helix/controller/HierarchicalDataHolder.java
index 27ecc40..a6e71ee 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/HierarchicalDataHolder.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/HierarchicalDataHolder.java
@@ -26,7 +26,7 @@
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeSnapshot.java b/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeSnapshot.java
index fc8c5c4..4d31ad4 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeSnapshot.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeSnapshot.java
@@ -27,7 +27,7 @@
 import java.util.stream.Collectors;
 
 import org.apache.helix.HelixConstants;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.IdealState;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/dataproviders/ResourceControllerDataProvider.java b/helix-core/src/main/java/org/apache/helix/controller/dataproviders/ResourceControllerDataProvider.java
index 1631d50..74a93c7 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/dataproviders/ResourceControllerDataProvider.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/dataproviders/ResourceControllerDataProvider.java
@@ -29,7 +29,7 @@
 import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.caches.AbstractDataCache;
 import org.apache.helix.common.caches.PropertyCache;
 import org.apache.helix.controller.LogUtil;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/dataproviders/WorkflowControllerDataProvider.java b/helix-core/src/main/java/org/apache/helix/controller/dataproviders/WorkflowControllerDataProvider.java
index 714b659..20308a4 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/dataproviders/WorkflowControllerDataProvider.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/dataproviders/WorkflowControllerDataProvider.java
@@ -26,7 +26,7 @@
 
 import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixDataAccessor;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.caches.AbstractDataCache;
 import org.apache.helix.common.caches.TaskDataCache;
 import org.apache.helix.controller.LogUtil;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/AbstractRebalancer.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/AbstractRebalancer.java
index 4a6eff8..17343e6 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/AbstractRebalancer.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/AbstractRebalancer.java
@@ -32,7 +32,7 @@
 import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixManager;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.BaseControllerDataProvider;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.internal.MappingCalculator;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/AutoRebalancer.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/AutoRebalancer.java
index c0b6f26..f74f1eb 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/AutoRebalancer.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/AutoRebalancer.java
@@ -28,7 +28,7 @@
 import java.util.Set;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.stages.CurrentStateOutput;
 import org.apache.helix.model.IdealState;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/DelayedAutoRebalancer.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/DelayedAutoRebalancer.java
index 63870ec..448cc73 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/DelayedAutoRebalancer.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/DelayedAutoRebalancer.java
@@ -30,7 +30,7 @@
 import java.util.Set;
 
 import org.apache.helix.HelixDefinedState;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.StateTransitionThrottleConfig;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.util.DelayedRebalanceUtil;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/constraint/dataprovider/ZkBasedCapacityProvider.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/constraint/dataprovider/ZkBasedCapacityProvider.java
index b4fefb3..f2ba28c 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/constraint/dataprovider/ZkBasedCapacityProvider.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/constraint/dataprovider/ZkBasedCapacityProvider.java
@@ -26,7 +26,7 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.rebalancer.constraint.dataprovider.CapacityProvider;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/constraint/dataprovider/ZkBasedPartitionWeightProvider.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/constraint/dataprovider/ZkBasedPartitionWeightProvider.java
index 8325682..bea711f 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/constraint/dataprovider/ZkBasedPartitionWeightProvider.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/constraint/dataprovider/ZkBasedPartitionWeightProvider.java
@@ -26,7 +26,7 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.rebalancer.constraint.dataprovider.PartitionWeightProvider;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/AbstractEvenDistributionRebalanceStrategy.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/AbstractEvenDistributionRebalanceStrategy.java
index 267ac1a..71daaad 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/AbstractEvenDistributionRebalanceStrategy.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/AbstractEvenDistributionRebalanceStrategy.java
@@ -32,7 +32,7 @@
 import java.util.stream.Collectors;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.LogUtil;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.strategy.crushMapping.CardDealingAdjustmentAlgorithmV2;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/AutoRebalanceStrategy.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/AutoRebalanceStrategy.java
index bd7e46a..0bdaed8 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/AutoRebalanceStrategy.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/AutoRebalanceStrategy.java
@@ -34,7 +34,7 @@
 import java.util.TreeSet;
 
 import org.apache.helix.HelixManager;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/ConstraintRebalanceStrategy.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/ConstraintRebalanceStrategy.java
index 4d8b41f..a555d34 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/ConstraintRebalanceStrategy.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/ConstraintRebalanceStrategy.java
@@ -28,7 +28,7 @@
 import java.util.Random;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.rebalancer.constraint.AbstractRebalanceHardConstraint;
 import org.apache.helix.api.rebalancer.constraint.AbstractRebalanceSoftConstraint;
 import org.apache.helix.api.rebalancer.constraint.dataprovider.CapacityProvider;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/CrushRebalanceStrategy.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/CrushRebalanceStrategy.java
index 4c1d972..5db5b2a 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/CrushRebalanceStrategy.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/CrushRebalanceStrategy.java
@@ -30,7 +30,7 @@
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.LogUtil;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.strategy.crushMapping.CRUSHPlacementAlgorithm;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/MultiRoundCrushRebalanceStrategy.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/MultiRoundCrushRebalanceStrategy.java
index 04b084b..847109c 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/MultiRoundCrushRebalanceStrategy.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/MultiRoundCrushRebalanceStrategy.java
@@ -31,7 +31,7 @@
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.LogUtil;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.strategy.crushMapping.CRUSHPlacementAlgorithm;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/RebalanceStrategy.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/RebalanceStrategy.java
index 223cf8e..4c885cf 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/RebalanceStrategy.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/strategy/RebalanceStrategy.java
@@ -23,7 +23,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.BaseControllerDataProvider;
 
 /**
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/AssignmentMetadataStore.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/AssignmentMetadataStore.java
index afd0187..381b612 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/AssignmentMetadataStore.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/AssignmentMetadataStore.java
@@ -24,16 +24,15 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.BucketDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordJacksonSerializer;
 import org.apache.helix.manager.zk.ZkBucketDataAccessor;
 import org.apache.helix.model.ResourceAssignment;
-
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 
 /**
  * A placeholder before we have the real assignment metadata store.
@@ -178,8 +177,8 @@
     HelixProperty property = new HelixProperty(name);
     // Add each resource's assignment as a simple field in one ZNRecord
     // Node that don't use Arrays.toString() for the record converting. The deserialize will fail.
-    assignmentMap.forEach((resource, assignment) -> property.getRecord()
-        .setSimpleField(resource, new String(SERIALIZER.serialize(assignment.getRecord()))));
+    assignmentMap.forEach((resource, assignment) -> property.getRecord().setSimpleField(resource,
+        new String(SERIALIZER.serialize(assignment.getRecord()))));
     return property;
   }
 
@@ -192,8 +191,8 @@
     Map<String, ResourceAssignment> assignmentMap = new HashMap<>();
     // Convert each resource's assignment String into a ResourceAssignment object and put it in a
     // map
-    property.getRecord().getSimpleFields().forEach((resource, assignmentStr) -> assignmentMap
-        .put(resource,
+    property.getRecord().getSimpleFields()
+        .forEach((resource, assignmentStr) -> assignmentMap.put(resource,
             new ResourceAssignment((ZNRecord) SERIALIZER.deserialize(assignmentStr.getBytes()))));
     return assignmentMap;
   }
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/ClusterDataCache.java b/helix-core/src/main/java/org/apache/helix/controller/stages/ClusterDataCache.java
index 0f321e1..c64dc94 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/ClusterDataCache.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/ClusterDataCache.java
@@ -25,7 +25,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.helix.HelixDataAccessor;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.caches.CurrentStateCache;
 import org.apache.helix.common.caches.IdealStateCache;
 import org.apache.helix.common.caches.InstanceMessagesCache;
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java
index 0580485..ed5cbc3 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java
@@ -35,9 +35,6 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordDelta;
-import org.apache.helix.ZNRecordDelta.MergeOperation;
 import org.apache.helix.controller.LogUtil;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.pipeline.AbstractAsyncBaseStage;
@@ -54,6 +51,8 @@
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.model.StatusUpdate;
 import org.apache.helix.monitoring.mbeans.ClusterStatusMonitor;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordDelta;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -316,7 +315,7 @@
     }
 
     if (finishedTasks.getListFields().size() > 0) {
-      ZNRecordDelta znDelta = new ZNRecordDelta(finishedTasks, MergeOperation.SUBTRACT);
+      ZNRecordDelta znDelta = new ZNRecordDelta(finishedTasks, ZNRecordDelta.MergeOperation.SUBTRACT);
       List<ZNRecordDelta> deltaList = new LinkedList<ZNRecordDelta>();
       deltaList.add(znDelta);
       IdealState delta = new IdealState(taskQueueIdealState.getResourceName());
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java
index d33cdfc..4959646 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java
@@ -26,12 +26,11 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.LogUtil;
 import org.apache.helix.controller.common.PartitionStateMap;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
@@ -43,6 +42,7 @@
 import org.apache.helix.model.MasterSlaveSMD;
 import org.apache.helix.model.Partition;
 import org.apache.helix.model.Resource;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/controller/strategy/AutoRebalanceStrategy.java b/helix-core/src/main/java/org/apache/helix/controller/strategy/AutoRebalanceStrategy.java
index 270e6de..1c325e1 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/strategy/AutoRebalanceStrategy.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/strategy/AutoRebalanceStrategy.java
@@ -33,7 +33,7 @@
 import java.util.TreeSet;
 
 import org.apache.helix.HelixManager;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/examples/ExampleHelper.java b/helix-core/src/main/java/org/apache/helix/examples/ExampleHelper.java
index 44ee1e5..c1e87b0 100644
--- a/helix-core/src/main/java/org/apache/helix/examples/ExampleHelper.java
+++ b/helix-core/src/main/java/org/apache/helix/examples/ExampleHelper.java
@@ -22,9 +22,11 @@
 import java.io.File;
 import java.io.IOException;
 
-import org.I0Itec.zkclient.IDefaultNameSpace;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.commons.io.FileUtils;
+import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+
 
 public class ExampleHelper {
 
@@ -45,7 +47,7 @@
 
     IDefaultNameSpace defaultNameSpace = new IDefaultNameSpace() {
       @Override
-      public void createDefaultNameSpace(org.I0Itec.zkclient.ZkClient zkClient) {
+      public void createDefaultNameSpace(ZkClient zkClient) {
         // do nothing
       }
     };
diff --git a/helix-core/src/main/java/org/apache/helix/examples/IdealStateBuilderExample.java b/helix-core/src/main/java/org/apache/helix/examples/IdealStateBuilderExample.java
index 71e6662..f413c46 100644
--- a/helix-core/src/main/java/org/apache/helix/examples/IdealStateBuilderExample.java
+++ b/helix-core/src/main/java/org/apache/helix/examples/IdealStateBuilderExample.java
@@ -22,8 +22,6 @@
 import org.apache.helix.controller.HelixControllerMain;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.InstanceConfig;
@@ -32,6 +30,9 @@
 import org.apache.helix.model.builder.FullAutoModeISBuilder;
 import org.apache.helix.model.builder.SemiAutoModeISBuilder;
 import org.apache.helix.tools.StateModelConfigGenerator;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+
 
 public class IdealStateBuilderExample {
 
diff --git a/helix-core/src/main/java/org/apache/helix/examples/IdealStateExample.java b/helix-core/src/main/java/org/apache/helix/examples/IdealStateExample.java
index 723cbbe..988eaa7 100644
--- a/helix-core/src/main/java/org/apache/helix/examples/IdealStateExample.java
+++ b/helix-core/src/main/java/org/apache/helix/examples/IdealStateExample.java
@@ -22,12 +22,13 @@
 import org.apache.helix.controller.HelixControllerMain;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.tools.StateModelConfigGenerator;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+
 
 /**
  * Ideal state json format file used in this example for CUSTOMIZED ideal state mode
diff --git a/helix-core/src/main/java/org/apache/helix/examples/Quickstart.java b/helix-core/src/main/java/org/apache/helix/examples/Quickstart.java
index 6773848..854b649 100644
--- a/helix-core/src/main/java/org/apache/helix/examples/Quickstart.java
+++ b/helix-core/src/main/java/org/apache/helix/examples/Quickstart.java
@@ -25,9 +25,6 @@
 import java.util.Map;
 import java.util.TreeSet;
 
-import org.I0Itec.zkclient.IDefaultNameSpace;
-import org.I0Itec.zkclient.ZkClient;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixManagerFactory;
@@ -38,6 +35,10 @@
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.participant.StateMachineEngine;
+import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+
 
 public class Quickstart {
 
diff --git a/helix-core/src/main/java/org/apache/helix/examples/WeightAwareRebalanceUtilExample.java b/helix-core/src/main/java/org/apache/helix/examples/WeightAwareRebalanceUtilExample.java
index 13d22ba..a9a4cc4 100644
--- a/helix-core/src/main/java/org/apache/helix/examples/WeightAwareRebalanceUtilExample.java
+++ b/helix-core/src/main/java/org/apache/helix/examples/WeightAwareRebalanceUtilExample.java
@@ -1,12 +1,30 @@
 package org.apache.helix.examples;
 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.api.rebalancer.constraint.AbstractRebalanceHardConstraint;
 import org.apache.helix.api.rebalancer.constraint.AbstractRebalanceSoftConstraint;
@@ -22,6 +40,8 @@
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.util.WeightAwareRebalanceUtil;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+
 
 public class WeightAwareRebalanceUtilExample {
   private static String ZK_ADDRESS = "localhost:2199";
diff --git a/helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollector.java b/helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollector.java
index 5ba6cb4..1e92e2a 100644
--- a/helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollector.java
+++ b/helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollector.java
@@ -19,7 +19,7 @@
  * under the License.
  */
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 public interface ParticipantHealthReportCollector {
   void addHealthReportProvider(HealthReportProvider provider);
diff --git a/helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollectorImpl.java b/helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollectorImpl.java
index 2ca9dd2..ca08653 100644
--- a/helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollectorImpl.java
+++ b/helix-core/src/main/java/org/apache/helix/healthcheck/ParticipantHealthReportCollectorImpl.java
@@ -25,7 +25,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.HealthStat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/BasicZkSerializer.java b/helix-core/src/main/java/org/apache/helix/manager/zk/BasicZkSerializer.java
index df165b1..99612a0 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/BasicZkSerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/BasicZkSerializer.java
@@ -19,25 +19,17 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.serialize.ZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+
 
 /**
- * Basic path based serializer which ignores the path and delegates
- * serialization into a regular {@link ZkSerializer}
+ * Use BasicZkSerializer in zookeeper-api module instead.
  */
-public class BasicZkSerializer implements PathBasedZkSerializer {
-  private final ZkSerializer _delegate;
-
-  public BasicZkSerializer(ZkSerializer delegate) {
-    _delegate = delegate;
-  }
-
-  public byte[] serialize(Object data, String path) {
-    return _delegate.serialize(data);
-  }
-
-  @Override
-  public Object deserialize(byte[] bytes, String path) {
-    return _delegate.deserialize(bytes);
+@Deprecated
+public class BasicZkSerializer
+    extends org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer {
+  public BasicZkSerializer(
+      ZkSerializer delegate) {
+    super(delegate);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ByteArraySerializer.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ByteArraySerializer.java
index d054011..a1889ea 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ByteArraySerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ByteArraySerializer.java
@@ -19,8 +19,9 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+
 
 public class ByteArraySerializer implements ZkSerializer {
   @Override
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/CallbackHandler.java b/helix-core/src/main/java/org/apache/helix/manager/zk/CallbackHandler.java
index 0f03ee4..82c9b29 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/CallbackHandler.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/CallbackHandler.java
@@ -29,9 +29,6 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixConstants.ChangeType;
 import org.apache.helix.HelixDataAccessor;
@@ -43,7 +40,6 @@
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyPathConfig;
 import org.apache.helix.SystemPropertyKeys;
-import org.apache.helix.ZNRecord;
 import org.apache.helix.api.listeners.BatchMode;
 import org.apache.helix.api.listeners.ClusterConfigChangeListener;
 import org.apache.helix.api.listeners.ConfigChangeListener;
@@ -58,7 +54,6 @@
 import org.apache.helix.api.listeners.ResourceConfigChangeListener;
 import org.apache.helix.api.listeners.ScopedConfigChangeListener;
 import org.apache.helix.common.DedupEventProcessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.ExternalView;
@@ -68,6 +63,11 @@
 import org.apache.helix.model.Message;
 import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.monitoring.mbeans.HelixCallbackMonitor;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.apache.zookeeper.Watcher.Event.EventType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ChainedPathZkSerializer.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ChainedPathZkSerializer.java
index 6975ea1..bb815c1 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ChainedPathZkSerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ChainedPathZkSerializer.java
@@ -23,8 +23,9 @@
 import java.util.Collections;
 import java.util.List;
 
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+
 
 public class ChainedPathZkSerializer implements PathBasedZkSerializer {
 
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ControllerManagerHelper.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ControllerManagerHelper.java
index eeb2242..3b25f0f 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ControllerManagerHelper.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ControllerManagerHelper.java
@@ -21,13 +21,13 @@
 
 import java.util.List;
 
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixTimerTask;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.controller.GenericHelixController;
 import org.apache.helix.messaging.DefaultMessagingService;
 import org.apache.helix.messaging.handling.MultiTypeMessageHandlerFactory;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/CurStateCarryOverUpdater.java b/helix-core/src/main/java/org/apache/helix/manager/zk/CurStateCarryOverUpdater.java
index f52a669..8fff9d2 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/CurStateCarryOverUpdater.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/CurStateCarryOverUpdater.java
@@ -19,11 +19,11 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.task.TaskConstants;
 import org.apache.helix.task.TaskPartitionState;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 
 
 /**
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/DefaultSchedulerMessageHandlerFactory.java b/helix-core/src/main/java/org/apache/helix/manager/zk/DefaultSchedulerMessageHandlerFactory.java
index 93819f9..defdd6b 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/DefaultSchedulerMessageHandlerFactory.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/DefaultSchedulerMessageHandlerFactory.java
@@ -36,7 +36,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.messaging.AsyncCallback;
 import org.apache.helix.messaging.handling.HelixTaskResult;
 import org.apache.helix.messaging.handling.MessageHandler;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/DistributedLeaderElection.java b/helix-core/src/main/java/org/apache/helix/manager/zk/DistributedLeaderElection.java
index 0a9d72d..ed9027b 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/DistributedLeaderElection.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/DistributedLeaderElection.java
@@ -32,7 +32,7 @@
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.listeners.ControllerChangeListener;
 import org.apache.helix.controller.GenericHelixController;
 import org.apache.helix.model.ControllerHistory;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/HelixGroupCommit.java b/helix-core/src/main/java/org/apache/helix/manager/zk/HelixGroupCommit.java
index 9cada74..79f1a51 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/HelixGroupCommit.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/HelixGroupCommit.java
@@ -25,9 +25,9 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkBadVersionException;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkBadVersionException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ParticipantManager.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ParticipantManager.java
index 7af69c6..411d937 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ParticipantManager.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ParticipantManager.java
@@ -25,8 +25,6 @@
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkNodeExistsException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.ConfigAccessor;
@@ -37,9 +35,6 @@
 import org.apache.helix.LiveInstanceInfoProvider;
 import org.apache.helix.PreConnectCallback;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordBucketizer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
 import org.apache.helix.messaging.DefaultMessagingService;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.HelixConfigScope;
@@ -51,6 +46,12 @@
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.participant.StateMachineEngine;
 import org.apache.helix.participant.statemachine.ScheduledTaskStateModelFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordBucketizer;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNodeExistsException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkSessionMismatchedException;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/PathBasedZkSerializer.java b/helix-core/src/main/java/org/apache/helix/manager/zk/PathBasedZkSerializer.java
index c717298..a248fb8 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/PathBasedZkSerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/PathBasedZkSerializer.java
@@ -19,26 +19,10 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-
-public interface PathBasedZkSerializer {
-
-  /**
-   * Serialize data differently according to different paths
-   * @param data
-   * @param path
-   * @return
-   * @throws ZkMarshallingError
-   */
-  public byte[] serialize(Object data, String path) throws ZkMarshallingError;
-
-  /**
-   * Deserialize data differently according to different paths
-   * @param bytes
-   * @param path
-   * @return
-   * @throws ZkMarshallingError
-   */
-  public Object deserialize(byte[] bytes, String path) throws ZkMarshallingError;
-
+/**
+ * Use PathBasedZkSerializer in zookeeper-api module instead.
+ */
+@Deprecated
+public interface PathBasedZkSerializer
+    extends org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/WriteThroughCache.java b/helix-core/src/main/java/org/apache/helix/manager/zk/WriteThroughCache.java
index 82e9b21..1dd84f7 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/WriteThroughCache.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/WriteThroughCache.java
@@ -21,11 +21,11 @@
 
 import java.util.List;
 
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.store.zk.ZNode;
 import org.apache.helix.util.HelixUtil;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKExceptionHandler.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKExceptionHandler.java
index 3746e31..1f9e73d 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKExceptionHandler.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKExceptionHandler.java
@@ -19,7 +19,7 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
index 61e75b3..77d8103 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
@@ -37,9 +37,6 @@
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkException;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.ConfigAccessor;
@@ -53,16 +50,11 @@
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
 import org.apache.helix.controller.rebalancer.DelayedAutoRebalancer;
 import org.apache.helix.controller.rebalancer.strategy.CrushEdRebalanceStrategy;
 import org.apache.helix.controller.rebalancer.strategy.RebalanceStrategy;
 import org.apache.helix.controller.rebalancer.util.WagedValidationUtil;
 import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ClusterConstraints;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
@@ -85,6 +77,12 @@
 import org.apache.helix.tools.DefaultIdealStateCalculator;
 import org.apache.helix.util.HelixUtil;
 import org.apache.helix.util.RebalanceUtil;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -491,22 +489,20 @@
 
     // Record a MaintenanceSignal history
     if (!accessor.getBaseDataAccessor()
-        .update(keyBuilder.controllerLeaderHistory().getPath(), new DataUpdater<ZNRecord>() {
-          @Override
-          public ZNRecord update(ZNRecord oldRecord) {
-            try {
-              if (oldRecord == null) {
-                oldRecord = new ZNRecord(PropertyType.HISTORY.toString());
+        .update(keyBuilder.controllerLeaderHistory().getPath(),
+            (DataUpdater<ZNRecord>) oldRecord -> {
+              try {
+                if (oldRecord == null) {
+                  oldRecord = new ZNRecord(PropertyType.HISTORY.toString());
+                }
+                return new ControllerHistory(oldRecord)
+                    .updateMaintenanceHistory(enabled, reason, currentTime, internalReason,
+                        customFields, triggeringEntity);
+              } catch (IOException e) {
+                logger.error("Failed to update maintenance history! Exception: {}", e);
+                return oldRecord;
               }
-              return new ControllerHistory(oldRecord)
-                  .updateMaintenanceHistory(enabled, reason, currentTime, internalReason,
-                      customFields, triggeringEntity);
-            } catch (IOException e) {
-              logger.error("Failed to update maintenance history! Exception: {}", e);
-              return oldRecord;
-            }
-          }
-        }, AccessOption.PERSISTENT)) {
+            }, AccessOption.PERSISTENT)) {
       logger.error("Failed to write maintenance history to ZK!");
     }
   }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixDataAccessor.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixDataAccessor.java
index 8d3eafa..9f9ddc9 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixDataAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixDataAccessor.java
@@ -25,8 +25,6 @@
 import java.util.List;
 import java.util.Map;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.GroupCommit;
@@ -37,16 +35,18 @@
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordAssembler;
-import org.apache.helix.ZNRecordBucketizer;
-import org.apache.helix.ZNRecordUpdater;
 import org.apache.helix.api.exceptions.HelixMetaDataAccessException;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.model.MaintenanceSignal;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.PauseSignal;
 import org.apache.helix.model.StateModelDefinition;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordAssembler;
+import org.apache.helix.zookeeper.datamodel.ZNRecordBucketizer;
+import org.apache.helix.zookeeper.datamodel.ZNRecordUpdater;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixManager.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixManager.java
index ef0308e..b4368f9 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixManager.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixManager.java
@@ -31,7 +31,7 @@
 import javax.management.JMException;
 
 import com.google.common.collect.Sets;
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
+
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.ClusterMessagingService;
 import org.apache.helix.ConfigAccessor;
@@ -50,7 +50,6 @@
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.PropertyType;
 import org.apache.helix.SystemPropertyKeys;
-import org.apache.helix.ZNRecord;
 import org.apache.helix.api.listeners.ClusterConfigChangeListener;
 import org.apache.helix.api.listeners.ConfigChangeListener;
 import org.apache.helix.api.listeners.ControllerChangeListener;
@@ -67,10 +66,6 @@
 import org.apache.helix.healthcheck.ParticipantHealthReportCollector;
 import org.apache.helix.healthcheck.ParticipantHealthReportCollectorImpl;
 import org.apache.helix.healthcheck.ParticipantHealthReportTask;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
-import org.apache.helix.manager.zk.zookeeper.IZkStateListener;
 import org.apache.helix.messaging.DefaultMessagingService;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
 import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
@@ -83,6 +78,13 @@
 import org.apache.helix.store.zk.AutoFallbackPropertyStore;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
 import org.apache.helix.util.HelixUtil;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+import org.apache.helix.zookeeper.zkclient.IZkStateListener;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
+import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer;
 import org.apache.zookeeper.Watcher.Event.EventType;
 import org.apache.zookeeper.Watcher.Event.KeeperState;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java
index 821af52..126ae38 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKUtil.java
@@ -23,14 +23,14 @@
 import java.util.Collections;
 import java.util.List;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordJacksonSerializer.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordJacksonSerializer.java
index b375e80..4f139ce 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordJacksonSerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordJacksonSerializer.java
@@ -19,49 +19,12 @@
  * under the License.
  */
 
-import java.io.IOException;
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.codehaus.jackson.map.ObjectMapper;
-
 /**
+ * Use ZNRecordJacksonSerializer in zookeeper-api instead.
+ *
  * ZNRecordJacksonSerializer serializes ZNRecord objects into a byte array using Jackson. Note that
  * this serializer doesn't check for the size of the resulting binary.
  */
-public class ZNRecordJacksonSerializer implements ZkSerializer {
-  private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
-
-  @Override
-  public byte[] serialize(Object record) throws ZkMarshallingError {
-    if (!(record instanceof ZNRecord)) {
-      // null is NOT an instance of any class
-      throw new HelixException("Input object is not of type ZNRecord (was " + record + ")");
-    }
-    ZNRecord znRecord = (ZNRecord) record;
-
-    try {
-      return OBJECT_MAPPER.writeValueAsBytes(znRecord);
-    } catch (IOException e) {
-      throw new HelixException(
-          String.format("Exception during serialization. ZNRecord id: %s", znRecord.getId()), e);
-    }
-  }
-
-  @Override
-  public Object deserialize(byte[] bytes) throws ZkMarshallingError {
-    if (bytes == null || bytes.length == 0) {
-      // reading a parent/null node
-      return null;
-    }
-
-    ZNRecord record;
-    try {
-      record = OBJECT_MAPPER.readValue(bytes, ZNRecord.class);
-    } catch (IOException e) {
-      throw new HelixException("Exception during deserialization!", e);
-    }
-    return record;
-  }
+@Deprecated
+public class ZNRecordJacksonSerializer extends org.apache.helix.zookeeper.datamodel.serializer.ZNRecordJacksonSerializer {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordSerializer.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordSerializer.java
index df9acaa..ba1892f 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordSerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordSerializer.java
@@ -19,115 +19,9 @@
  * under the License.
  */
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.List;
-import java.util.Map;
-
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.util.GZipCompressionUtil;
-import org.codehaus.jackson.map.DeserializationConfig;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.codehaus.jackson.map.SerializationConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ZNRecordSerializer implements ZkSerializer {
-  private static Logger logger = LoggerFactory.getLogger(ZNRecordSerializer.class);
-
-  private static int getListFieldBound(ZNRecord record) {
-    int max = Integer.MAX_VALUE;
-    if (record.getSimpleFields().containsKey(ZNRecord.LIST_FIELD_BOUND)) {
-      String maxStr = record.getSimpleField(ZNRecord.LIST_FIELD_BOUND);
-      try {
-        max = Integer.parseInt(maxStr);
-      } catch (Exception e) {
-        logger.error("IllegalNumberFormat for list field bound: " + maxStr);
-      }
-    }
-    return max;
-  }
-
-  @Override
-  public byte[] serialize(Object data) {
-    if (!(data instanceof ZNRecord)) {
-      // null is NOT an instance of any class
-      logger.error("Input object must be of type ZNRecord but it is " + data
-          + ". Will not write to zk");
-      throw new HelixException("Input object is not of type ZNRecord (was " + data + ")");
-    }
-
-    ZNRecord record = (ZNRecord) data;
-
-    // apply retention policy
-    int max = getListFieldBound(record);
-    if (max < Integer.MAX_VALUE) {
-      Map<String, List<String>> listMap = record.getListFields();
-      for (String key : listMap.keySet()) {
-        List<String> list = listMap.get(key);
-        if (list.size() > max) {
-          listMap.put(key, list.subList(0, max));
-        }
-      }
-    }
-
-    // do serialization
-    ObjectMapper mapper = new ObjectMapper();
-    SerializationConfig serializationConfig = mapper.getSerializationConfig();
-    serializationConfig.set(SerializationConfig.Feature.INDENT_OUTPUT, true);
-    serializationConfig.set(SerializationConfig.Feature.AUTO_DETECT_FIELDS, true);
-    serializationConfig.set(SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, true);
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    byte[] serializedBytes;
-    try {
-      mapper.writeValue(baos, data);
-      serializedBytes = baos.toByteArray();
-      // apply compression if needed
-      if (record.getBooleanField("enableCompression", false) || serializedBytes.length > ZNRecord.SIZE_LIMIT) {
-        serializedBytes = GZipCompressionUtil.compress(serializedBytes);
-      }
-    } catch (Exception e) {
-      logger.error("Exception during data serialization. Will not write to zk. Data (first 1k): "
-          + new String(baos.toByteArray()).substring(0, 1024), e);
-      throw new HelixException(e);
-    }
-    if (serializedBytes.length > ZNRecord.SIZE_LIMIT) {
-      logger.error("Data size larger than 1M, ZNRecord.id: " + record.getId()
-          + ". Will not write to zk. Data (first 1k): "
-          + new String(serializedBytes).substring(0, 1024));
-      throw new HelixException("Data size larger than 1M, ZNRecord.id: " + record.getId());
-    }
-    return serializedBytes;
-  }
-
-  @Override
-  public Object deserialize(byte[] bytes) {
-    if (bytes == null || bytes.length == 0) {
-      // reading a parent/null node
-      return null;
-    }
-
-    ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
-
-    ObjectMapper mapper = new ObjectMapper();
-    DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
-    deserializationConfig.set(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, true);
-    deserializationConfig.set(DeserializationConfig.Feature.AUTO_DETECT_SETTERS, true);
-    deserializationConfig.set(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, true);
-    try {
-      //decompress the data if its already compressed
-      if (GZipCompressionUtil.isCompressed(bytes)) {
-        byte[] uncompressedBytes = GZipCompressionUtil.uncompress(bais);
-        bais = new ByteArrayInputStream(uncompressedBytes);
-      }
-      ZNRecord zn = mapper.readValue(bais, ZNRecord.class);
-
-      return zn;
-    } catch (Exception e) {
-      logger.error("Exception during deserialization of bytes: " + new String(bytes), e);
-      return null;
-    }
-  }
+/**
+ * Use ZNRecordSerializer in zookeeper-api instead.
+ */
+@Deprecated
+public class ZNRecordSerializer extends org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordStreamingSerializer.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordStreamingSerializer.java
index 769baa0..bd18eff 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordStreamingSerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordStreamingSerializer.java
@@ -19,292 +19,9 @@
  * under the License.
  */
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import com.google.common.collect.Maps;
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.commons.codec.binary.Base64;
-import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.util.GZipCompressionUtil;
-import org.codehaus.jackson.JsonFactory;
-import org.codehaus.jackson.JsonGenerator;
-import org.codehaus.jackson.JsonParser;
-import org.codehaus.jackson.JsonToken;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ZNRecordStreamingSerializer implements ZkSerializer {
-  private static Logger LOG = LoggerFactory.getLogger(ZNRecordStreamingSerializer.class);
-
-  private static int getListFieldBound(ZNRecord record) {
-    int max = Integer.MAX_VALUE;
-    if (record.getSimpleFields().containsKey(ZNRecord.LIST_FIELD_BOUND)) {
-      String maxStr = record.getSimpleField(ZNRecord.LIST_FIELD_BOUND);
-      try {
-        max = Integer.parseInt(maxStr);
-      } catch (Exception e) {
-        LOG.error("IllegalNumberFormat for list field bound: " + maxStr);
-      }
-    }
-    return max;
-  }
-
-  @Override
-  public byte[] serialize(Object data) throws ZkMarshallingError {
-    if (!(data instanceof ZNRecord)) {
-      // null is NOT an instance of any class
-      LOG.error("Input object must be of type ZNRecord but it is " + data
-          + ". Will not write to zk");
-      throw new HelixException("Input object is not of type ZNRecord (was " + data + ")");
-    }
-
-    // apply retention policy on list field
-    ZNRecord record = (ZNRecord) data;
-    int max = getListFieldBound(record);
-    if (max < Integer.MAX_VALUE) {
-      Map<String, List<String>> listMap = record.getListFields();
-      for (String key : listMap.keySet()) {
-        List<String> list = listMap.get(key);
-        if (list.size() > max) {
-          listMap.put(key, list.subList(0, max));
-        }
-      }
-    }
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    byte[] serializedBytes = null;
-    try {
-      JsonFactory f = new JsonFactory();
-      JsonGenerator g = f.createJsonGenerator(baos);
-
-      g.writeStartObject();
-
-      // write id field
-      g.writeRaw("\n  ");
-      g.writeStringField("id", record.getId());
-
-      // write simepleFields
-      g.writeRaw("\n  ");
-      g.writeObjectFieldStart("simpleFields");
-      for (String key : record.getSimpleFields().keySet()) {
-        g.writeRaw("\n    ");
-        g.writeStringField(key, record.getSimpleField(key));
-      }
-      g.writeRaw("\n  ");
-      g.writeEndObject(); // for simpleFields
-
-      // write listFields
-      g.writeRaw("\n  ");
-      g.writeObjectFieldStart("listFields");
-      for (String key : record.getListFields().keySet()) {
-        // g.writeStringField(key, record.getListField(key).toString());
-
-        // g.writeObjectFieldStart(key);
-        g.writeRaw("\n    ");
-        g.writeArrayFieldStart(key);
-        List<String> list = record.getListField(key);
-        for (String listValue : list) {
-          g.writeString(listValue);
-        }
-        // g.writeEndObject();
-        g.writeEndArray();
-
-      }
-      g.writeRaw("\n  ");
-      g.writeEndObject(); // for listFields
-
-      // write mapFields
-      g.writeRaw("\n  ");
-      g.writeObjectFieldStart("mapFields");
-      for (String key : record.getMapFields().keySet()) {
-        // g.writeStringField(key, record.getMapField(key).toString());
-        g.writeRaw("\n    ");
-        g.writeObjectFieldStart(key);
-        Map<String, String> map = record.getMapField(key);
-        for (String mapKey : map.keySet()) {
-          g.writeRaw("\n      ");
-          g.writeStringField(mapKey, map.get(mapKey));
-        }
-        g.writeRaw("\n    ");
-        g.writeEndObject();
-
-      }
-      g.writeRaw("\n  ");
-      g.writeEndObject(); // for mapFields
-
-      byte[] rawPayload = record.getRawPayload();
-      if (rawPayload != null && rawPayload.length > 0) {
-        // write rawPayload
-        g.writeRaw("\n  ");
-        g.writeStringField("rawPayload", new String(Base64.encodeBase64(rawPayload), "UTF-8"));
-      }
-
-      g.writeRaw("\n");
-      g.writeEndObject(); // for whole znrecord
-
-      // important: will force flushing of output, close underlying output
-      // stream
-      g.close();
-      serializedBytes = baos.toByteArray();
-      // apply compression if needed
-      if (record.getBooleanField("enableCompression", false) || serializedBytes.length > ZNRecord.SIZE_LIMIT) {
-        serializedBytes = GZipCompressionUtil.compress(serializedBytes);
-      }
-    } catch (Exception e) {
-      LOG.error("Exception during data serialization. Will not write to zk. Data (first 1k): "
-          + new String(baos.toByteArray()).substring(0, 1024), e);
-      throw new HelixException(e);
-    }
-    // check size
-    if (serializedBytes.length > ZNRecord.SIZE_LIMIT) {
-      LOG.error("Data size larger than 1M, ZNRecord.id: " + record.getId()
-          + ". Will not write to zk. Data (first 1k): "
-          + new String(serializedBytes).substring(0, 1024));
-      throw new HelixException("Data size larger than 1M, ZNRecord.id: " + record.getId());
-    }
-
-    return serializedBytes;
-  }
-
-  @Override
-  public Object deserialize(byte[] bytes) throws ZkMarshallingError {
-    if (bytes == null || bytes.length == 0) {
-      LOG.error("ZNode is empty.");
-      return null;
-    }
-
-    ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
-
-    ZNRecord record = null;
-    String id = null;
-    Map<String, String> simpleFields = Maps.newHashMap();
-    Map<String, List<String>> listFields = Maps.newHashMap();
-    Map<String, Map<String, String>> mapFields = Maps.newHashMap();
-    byte[] rawPayload = null;
-
-    try {
-      // decompress the data if its already compressed
-      if (GZipCompressionUtil.isCompressed(bytes)) {
-        byte[] uncompressedBytes = GZipCompressionUtil.uncompress(bais);
-        bais = new ByteArrayInputStream(uncompressedBytes);
-      }
-      JsonFactory f = new JsonFactory();
-      JsonParser jp = f.createJsonParser(bais);
-
-      jp.nextToken(); // will return JsonToken.START_OBJECT (verify?)
-      while (jp.nextToken() != JsonToken.END_OBJECT) {
-        String fieldname = jp.getCurrentName();
-        jp.nextToken(); // move to value, or START_OBJECT/START_ARRAY
-        if ("id".equals(fieldname)) {
-          // contains an object
-          id = jp.getText();
-        } else if ("simpleFields".equals(fieldname)) {
-          while (jp.nextToken() != JsonToken.END_OBJECT) {
-            String key = jp.getCurrentName();
-            jp.nextToken(); // move to value
-            simpleFields.put(key, jp.getText());
-          }
-        } else if ("mapFields".equals(fieldname)) {
-          // user.setVerified(jp.getCurrentToken() == JsonToken.VALUE_TRUE);
-          while (jp.nextToken() != JsonToken.END_OBJECT) {
-            String key = jp.getCurrentName();
-            mapFields.put(key, new TreeMap<String, String>());
-            jp.nextToken(); // move to value
-
-            while (jp.nextToken() != JsonToken.END_OBJECT) {
-              String mapKey = jp.getCurrentName();
-              jp.nextToken(); // move to value
-              mapFields.get(key).put(mapKey, jp.getText());
-            }
-          }
-
-        } else if ("listFields".equals(fieldname)) {
-          // user.setUserImage(jp.getBinaryValue());
-          while (jp.nextToken() != JsonToken.END_OBJECT) {
-            String key = jp.getCurrentName();
-            listFields.put(key, new ArrayList<String>());
-            jp.nextToken(); // move to value
-            while (jp.nextToken() != JsonToken.END_ARRAY) {
-              listFields.get(key).add(jp.getText());
-            }
-
-          }
-
-        } else if ("rawPayload".equals(fieldname)) {
-          rawPayload = Base64.decodeBase64(jp.getText());
-        } else {
-          throw new IllegalStateException("Unrecognized field '" + fieldname + "'!");
-        }
-      }
-      jp.close(); // ensure resources get cleaned up timely and properly
-
-      if (id == null) {
-        throw new IllegalStateException("ZNRecord id field is required!");
-      }
-      record = new ZNRecord(id);
-      record.setSimpleFields(simpleFields);
-      record.setListFields(listFields);
-      record.setMapFields(mapFields);
-      record.setRawPayload(rawPayload);
-    } catch (Exception e) {
-      LOG.error("Exception during deserialization of bytes: " + new String(bytes), e);
-    }
-    return record;
-  }
-
-  public static void main(String[] args) {
-    ZNRecord record = new ZNRecord("record");
-    final int recordSize = 10;
-    for (int i = 0; i < recordSize; i++) {
-      record.setSimpleField("" + i, "" + i);
-      record.setListField("" + i, new ArrayList<String>());
-      for (int j = 0; j < recordSize; j++) {
-        record.getListField("" + i).add("" + j);
-      }
-
-      record.setMapField("" + i, new TreeMap<String, String>());
-      for (int j = 0; j < recordSize; j++) {
-        record.getMapField("" + i).put("" + j, "" + j);
-      }
-    }
-
-    ZNRecordStreamingSerializer serializer = new ZNRecordStreamingSerializer();
-    byte[] bytes = serializer.serialize(record);
-    System.out.println(new String(bytes));
-    ZNRecord record2 = (ZNRecord) serializer.deserialize(bytes);
-    System.out.println(record2);
-
-    long start = System.currentTimeMillis();
-    for (int i = 0; i < 100; i++) {
-      bytes = serializer.serialize(record);
-      // System.out.println(new String(bytes));
-      record2 = (ZNRecord) serializer.deserialize(bytes);
-      // System.out.println(record2);
-    }
-    long end = System.currentTimeMillis();
-    System.out.println("ZNRecordStreamingSerializer time used: " + (end - start));
-
-    ZNRecordSerializer serializer2 = new ZNRecordSerializer();
-    bytes = serializer2.serialize(record);
-    // System.out.println(new String(bytes));
-    record2 = (ZNRecord) serializer2.deserialize(bytes);
-    // System.out.println(record2);
-
-    start = System.currentTimeMillis();
-    for (int i = 0; i < 100; i++) {
-      bytes = serializer2.serialize(record);
-      // System.out.println(new String(bytes));
-      record2 = (ZNRecord) serializer2.deserialize(bytes);
-      // System.out.println(record2);
-    }
-    end = System.currentTimeMillis();
-    System.out.println("ZNRecordSerializer time used: " + (end - start));
-
-  }
+/**
+ * Use ZNRecordStreamingSerializer in zookeeper-api module instead.
+ */
+@Deprecated
+public class ZNRecordStreamingSerializer extends org.apache.helix.zookeeper.datamodel.serializer.ZNRecordStreamingSerializer {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkAsyncCallbacks.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkAsyncCallbacks.java
index 6b51b47..566a6e0 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkAsyncCallbacks.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkAsyncCallbacks.java
@@ -19,174 +19,10 @@
  * under the License.
  */
 
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.helix.monitoring.mbeans.ZkClientMonitor;
-import org.apache.zookeeper.AsyncCallback.DataCallback;
-import org.apache.zookeeper.AsyncCallback.StatCallback;
-import org.apache.zookeeper.AsyncCallback.StringCallback;
-import org.apache.zookeeper.AsyncCallback.VoidCallback;
-import org.apache.zookeeper.KeeperException.Code;
-import org.apache.zookeeper.data.Stat;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ZkAsyncCallbacks {
-  private static Logger LOG = LoggerFactory.getLogger(ZkAsyncCallbacks.class);
-
-  public static class GetDataCallbackHandler extends DefaultCallback implements DataCallback {
-    byte[] _data;
-    Stat _stat;
-
-    @Override
-    public void handle() {
-      // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
-      if (rc == 0) {
-        _data = data;
-        _stat = stat;
-        // update ctx with data size
-        if (_data != null && ctx != null && ctx instanceof ZkAsyncCallContext) {
-          ZkAsyncCallContext zkCtx = (ZkAsyncCallContext) ctx;
-          zkCtx._bytes = _data.length;
-        }
-      }
-      callback(rc, path, ctx);
-    }
-  }
-
-  public static class SetDataCallbackHandler extends DefaultCallback implements StatCallback {
-    Stat _stat;
-
-    @Override
-    public void handle() {
-      // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void processResult(int rc, String path, Object ctx, Stat stat) {
-      if (rc == 0) {
-        _stat = stat;
-      }
-      callback(rc, path, ctx);
-    }
-
-    public Stat getStat() {
-      return _stat;
-    }
-  }
-
-  public static class ExistsCallbackHandler extends DefaultCallback implements StatCallback {
-    Stat _stat;
-
-    @Override
-    public void handle() {
-      // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void processResult(int rc, String path, Object ctx, Stat stat) {
-      if (rc == 0) {
-        _stat = stat;
-      }
-      callback(rc, path, ctx);
-    }
-  }
-
-  public static class CreateCallbackHandler extends DefaultCallback implements StringCallback {
-    @Override
-    public void processResult(int rc, String path, Object ctx, String name) {
-      callback(rc, path, ctx);
-    }
-
-    @Override
-    public void handle() {
-      // TODO Auto-generated method stub
-    }
-  }
-
-  public static class DeleteCallbackHandler extends DefaultCallback implements VoidCallback {
-    @Override
-    public void processResult(int rc, String path, Object ctx) {
-      callback(rc, path, ctx);
-    }
-
-    @Override
-    public void handle() {
-      // TODO Auto-generated method stub
-    }
-  }
-
-  /**
-   * Default callback for zookeeper async api
-   */
-  public static abstract class DefaultCallback {
-    AtomicBoolean _lock = new AtomicBoolean(false);
-    int _rc = -1;
-
-    public void callback(int rc, String path, Object ctx) {
-      if (rc != 0 && LOG.isDebugEnabled()) {
-        LOG.debug(this + ", rc:" + Code.get(rc) + ", path: " + path);
-      }
-
-      if (ctx != null && ctx instanceof ZkAsyncCallContext) {
-        ZkAsyncCallContext zkCtx = (ZkAsyncCallContext) ctx;
-        if (zkCtx._monitor != null) {
-          if (zkCtx._isRead) {
-            zkCtx._monitor.record(path, zkCtx._bytes, zkCtx._startTimeMilliSec,
-                ZkClientMonitor.AccessType.READ);
-          } else {
-            zkCtx._monitor.record(path, zkCtx._bytes, zkCtx._startTimeMilliSec,
-                ZkClientMonitor.AccessType.WRITE);
-          }
-        }
-      }
-
-      _rc = rc;
-      handle();
-
-      synchronized (_lock) {
-        _lock.set(true);
-        _lock.notify();
-      }
-    }
-
-    public boolean waitForSuccess() {
-      try {
-        synchronized (_lock) {
-          while (!_lock.get()) {
-            _lock.wait();
-          }
-        }
-      } catch (InterruptedException e) {
-        LOG.error("Interrupted waiting for success", e);
-      }
-      return true;
-    }
-
-    public int getRc() {
-      return _rc;
-    }
-
-    abstract public void handle();
-  }
-
-  public static class ZkAsyncCallContext {
-    private long _startTimeMilliSec;
-    private int _bytes;
-    private ZkClientMonitor _monitor;
-    private boolean _isRead;
-
-    public ZkAsyncCallContext(final ZkClientMonitor monitor, long startTimeMilliSec, int bytes,
-        boolean isRead) {
-      _monitor = monitor;
-      _startTimeMilliSec = startTimeMilliSec;
-      _bytes = bytes;
-      _isRead = isRead;
-    }
-  }
-
+/**
+ * This class has been deprecated. Please use ZkAsyncCallbacks in zookeeper-api module instead.
+ */
+@Deprecated
+public class ZkAsyncCallbacks
+    extends org.apache.helix.zookeeper.zkclient.callback.ZkAsyncCallbacks {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBaseDataAccessor.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBaseDataAccessor.java
index c1abccb..bc84a1d 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBaseDataAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBaseDataAccessor.java
@@ -27,28 +27,25 @@
 import java.util.List;
 import java.util.Map;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.exception.ZkBadVersionException;
-import org.I0Itec.zkclient.exception.ZkException;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
-import org.I0Itec.zkclient.exception.ZkNodeExistsException;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.api.exceptions.HelixMetaDataAccessException;
-import org.apache.helix.manager.zk.ZkAsyncCallbacks.CreateCallbackHandler;
-import org.apache.helix.manager.zk.ZkAsyncCallbacks.DeleteCallbackHandler;
-import org.apache.helix.manager.zk.ZkAsyncCallbacks.ExistsCallbackHandler;
-import org.apache.helix.manager.zk.ZkAsyncCallbacks.GetDataCallbackHandler;
-import org.apache.helix.manager.zk.ZkAsyncCallbacks.SetDataCallbackHandler;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
 import org.apache.helix.store.zk.ZNode;
 import org.apache.helix.util.HelixUtil;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.callback.ZkAsyncCallbacks;
+import org.apache.helix.zookeeper.zkclient.exception.ZkBadVersionException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNodeExistsException;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException.Code;
 import org.apache.zookeeper.data.Stat;
@@ -143,7 +140,7 @@
   }
 
   /**
-   * Creates a ZkBaseDataAccessor with {@link org.apache.helix.ZNRecord} as the data model.
+   * Creates a ZkBaseDataAccessor with {@link ZNRecord} as the data model.
    * Uses a shared ZkConnection resource.
    * Does NOT support ephemeral node creation, callbacks, or session management.
    * Uses {@link ZNRecordSerializer} serializer
@@ -154,7 +151,7 @@
   }
 
   /**
-   * Creates a ZkBaseDataAccessor with {@link org.apache.helix.ZNRecord} as the data model.
+   * Creates a ZkBaseDataAccessor with {@link ZNRecord} as the data model.
    * If DEDICATED, it will use a dedicated ZkConnection, which allows ephemeral
    * node creation, callbacks, and session management.
    * If SHARED, it will use a shared ZkConnection, which only allows simple
@@ -492,13 +489,13 @@
 
     try {
       // issue asyn get requests
-      GetDataCallbackHandler[] cbList = new GetDataCallbackHandler[paths.size()];
+      ZkAsyncCallbacks.GetDataCallbackHandler[] cbList = new ZkAsyncCallbacks.GetDataCallbackHandler[paths.size()];
       for (int i = 0; i < paths.size(); i++) {
         if (!needRead[i])
           continue;
 
         String path = paths.get(i);
-        cbList[i] = new GetDataCallbackHandler();
+        cbList[i] = new ZkAsyncCallbacks.GetDataCallbackHandler();
         _zkClient.asyncGetData(path, cbList[i]);
       }
 
@@ -507,7 +504,7 @@
         if (!needRead[i])
           continue;
 
-        GetDataCallbackHandler cb = cbList[i];
+        ZkAsyncCallbacks.GetDataCallbackHandler cb = cbList[i];
         cb.waitForSuccess();
       }
 
@@ -518,7 +515,7 @@
         if (!needRead[i])
           continue;
 
-        GetDataCallbackHandler cb = cbList[i];
+        ZkAsyncCallbacks.GetDataCallbackHandler cb = cbList[i];
         if (Code.get(cb.getRc()) == Code.OK) {
           @SuppressWarnings("unchecked")
           T record = (T) _zkClient.deserialize(cb._data, paths.get(i));
@@ -684,7 +681,7 @@
   /**
    * async create. give up on error other than NONODE
    */
-  CreateCallbackHandler[] create(List<String> paths, List<T> records, boolean[] needCreate,
+  ZkAsyncCallbacks.CreateCallbackHandler[] create(List<String> paths, List<T> records, boolean[] needCreate,
       List<List<String>> pathsCreated, int options) {
     if ((records != null && records.size() != paths.size()) || needCreate.length != paths.size()
         || (pathsCreated != null && pathsCreated.size() != paths.size())) {
@@ -692,7 +689,7 @@
           "paths, records, needCreate, and pathsCreated should be of same size");
     }
 
-    CreateCallbackHandler[] cbList = new CreateCallbackHandler[paths.size()];
+    ZkAsyncCallbacks.CreateCallbackHandler[] cbList = new ZkAsyncCallbacks.CreateCallbackHandler[paths.size()];
 
     CreateMode mode = AccessOption.getMode(options);
     if (mode == null) {
@@ -710,7 +707,7 @@
 
         String path = paths.get(i);
         T record = records == null ? null : records.get(i);
-        cbList[i] = new CreateCallbackHandler();
+        cbList[i] = new ZkAsyncCallbacks.CreateCallbackHandler();
         _zkClient.asyncCreate(path, record, mode, cbList[i]);
       }
 
@@ -721,7 +718,7 @@
         if (!needCreate[i])
           continue;
 
-        CreateCallbackHandler cb = cbList[i];
+        ZkAsyncCallbacks.CreateCallbackHandler cb = cbList[i];
         cb.waitForSuccess();
         String path = paths.get(i);
 
@@ -747,10 +744,10 @@
       if (failOnNoNode) {
         boolean[] needCreateParent = Arrays.copyOf(needCreate, needCreate.length);
 
-        CreateCallbackHandler[] parentCbList =
+        ZkAsyncCallbacks.CreateCallbackHandler[] parentCbList =
             create(parentPaths, null, needCreateParent, pathsCreated, AccessOption.PERSISTENT);
         for (int i = 0; i < parentCbList.length; i++) {
-          CreateCallbackHandler parentCb = parentCbList[i];
+          ZkAsyncCallbacks.CreateCallbackHandler parentCb = parentCbList[i];
           if (parentCb == null)
             continue;
 
@@ -790,10 +787,10 @@
     long startT = System.nanoTime();
     try {
 
-      CreateCallbackHandler[] cbList = create(paths, records, needCreate, pathsCreated, options);
+      ZkAsyncCallbacks.CreateCallbackHandler[] cbList = create(paths, records, needCreate, pathsCreated, options);
 
       for (int i = 0; i < cbList.length; i++) {
-        CreateCallbackHandler cb = cbList[i];
+        ZkAsyncCallbacks.CreateCallbackHandler cb = cbList[i];
         success[i] = (Code.get(cb.getRc()) == Code.OK);
       }
 
@@ -840,8 +837,8 @@
     }
 
     List<Stat> setStats = new ArrayList<>(Collections.<Stat> nCopies(paths.size(), null));
-    SetDataCallbackHandler[] cbList = new SetDataCallbackHandler[paths.size()];
-    CreateCallbackHandler[] createCbList = null;
+    ZkAsyncCallbacks.SetDataCallbackHandler[] cbList = new ZkAsyncCallbacks.SetDataCallbackHandler[paths.size()];
+    ZkAsyncCallbacks.CreateCallbackHandler[] createCbList = null;
     boolean[] needSet = new boolean[paths.size()];
     Arrays.fill(needSet, true);
 
@@ -858,7 +855,7 @@
 
           String path = paths.get(i);
           T record = records.get(i);
-          cbList[i] = new SetDataCallbackHandler();
+          cbList[i] = new ZkAsyncCallbacks.SetDataCallbackHandler();
           _zkClient.asyncSetData(path, record, -1, cbList[i]);
 
         }
@@ -866,7 +863,7 @@
         boolean failOnNoNode = false;
 
         for (int i = 0; i < cbList.length; i++) {
-          SetDataCallbackHandler cb = cbList[i];
+          ZkAsyncCallbacks.SetDataCallbackHandler cb = cbList[i];
           cb.waitForSuccess();
           Code rc = Code.get(cb.getRc());
           switch (rc) {
@@ -890,7 +887,7 @@
           boolean[] needCreate = Arrays.copyOf(needSet, needSet.length);
           createCbList = create(paths, records, needCreate, pathsCreated, options);
           for (int i = 0; i < createCbList.length; i++) {
-            CreateCallbackHandler createCb = createCbList[i];
+            ZkAsyncCallbacks.CreateCallbackHandler createCb = createCbList[i];
             if (createCb == null) {
               continue;
             }
@@ -916,13 +913,13 @@
 
       // construct return results
       for (int i = 0; i < cbList.length; i++) {
-        SetDataCallbackHandler cb = cbList[i];
+        ZkAsyncCallbacks.SetDataCallbackHandler cb = cbList[i];
 
         Code rc = Code.get(cb.getRc());
         if (rc == Code.OK) {
           success[i] = true;
         } else if (rc == Code.NONODE) {
-          CreateCallbackHandler createCb = createCbList[i];
+          ZkAsyncCallbacks.CreateCallbackHandler createCb = createCbList[i];
           if (Code.get(createCb.getRc()) == Code.OK) {
             success[i] = true;
           }
@@ -986,8 +983,8 @@
       return updateData;
     }
 
-    SetDataCallbackHandler[] cbList = new SetDataCallbackHandler[paths.size()];
-    CreateCallbackHandler[] createCbList = null;
+    ZkAsyncCallbacks.SetDataCallbackHandler[] cbList = new ZkAsyncCallbacks.SetDataCallbackHandler[paths.size()];
+    ZkAsyncCallbacks.CreateCallbackHandler[] createCbList = null;
     boolean[] needUpdate = new boolean[paths.size()];
     Arrays.fill(needUpdate, true);
 
@@ -1026,7 +1023,7 @@
             failOnNoNode = true;
             needCreate[i] = true;
           } else {
-            cbList[i] = new SetDataCallbackHandler();
+            cbList[i] = new ZkAsyncCallbacks.SetDataCallbackHandler();
             _zkClient.asyncSetData(path, newData, curStat.getVersion(), cbList[i]);
           }
         }
@@ -1035,7 +1032,7 @@
         boolean failOnBadVersion = false;
 
         for (int i = 0; i < paths.size(); i++) {
-          SetDataCallbackHandler cb = cbList[i];
+          ZkAsyncCallbacks.SetDataCallbackHandler cb = cbList[i];
           if (cb == null)
             continue;
 
@@ -1066,7 +1063,7 @@
         if (failOnNoNode) {
           createCbList = create(paths, newDataList, needCreate, pathsCreated, options);
           for (int i = 0; i < paths.size(); i++) {
-            CreateCallbackHandler createCb = createCbList[i];
+            ZkAsyncCallbacks.CreateCallbackHandler createCb = createCbList[i];
             if (createCb == null) {
               continue;
             }
@@ -1141,15 +1138,15 @@
     long startT = System.nanoTime();
 
     try {
-      ExistsCallbackHandler[] cbList = new ExistsCallbackHandler[paths.size()];
+      ZkAsyncCallbacks.ExistsCallbackHandler[] cbList = new ZkAsyncCallbacks.ExistsCallbackHandler[paths.size()];
       for (int i = 0; i < paths.size(); i++) {
         String path = paths.get(i);
-        cbList[i] = new ExistsCallbackHandler();
+        cbList[i] = new ZkAsyncCallbacks.ExistsCallbackHandler();
         _zkClient.asyncExists(path, cbList[i]);
       }
 
       for (int i = 0; i < cbList.length; i++) {
-        ExistsCallbackHandler cb = cbList[i];
+        ZkAsyncCallbacks.ExistsCallbackHandler cb = cbList[i];
         cb.waitForSuccess();
         stats[i] = cb._stat;
       }
@@ -1175,19 +1172,19 @@
 
     boolean[] success = new boolean[paths.size()];
 
-    DeleteCallbackHandler[] cbList = new DeleteCallbackHandler[paths.size()];
+    ZkAsyncCallbacks.DeleteCallbackHandler[] cbList = new ZkAsyncCallbacks.DeleteCallbackHandler[paths.size()];
 
     long startT = System.nanoTime();
 
     try {
       for (int i = 0; i < paths.size(); i++) {
         String path = paths.get(i);
-        cbList[i] = new DeleteCallbackHandler();
+        cbList[i] = new ZkAsyncCallbacks.DeleteCallbackHandler();
         _zkClient.asyncDelete(path, cbList[i]);
       }
 
       for (int i = 0; i < cbList.length; i++) {
-        DeleteCallbackHandler cb = cbList[i];
+        ZkAsyncCallbacks.DeleteCallbackHandler cb = cbList[i];
         cb.waitForSuccess();
         success[i] = (cb.getRc() == 0);
       }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBucketDataAccessor.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBucketDataAccessor.java
index bc13471..ffed9f3 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBucketDataAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBucketDataAccessor.java
@@ -30,18 +30,18 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BucketDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
 import org.apache.helix.util.GZipCompressionUtil;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheBaseDataAccessor.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheBaseDataAccessor.java
index b230827..6d2c5cf 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheBaseDataAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheBaseDataAccessor.java
@@ -29,22 +29,22 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixException;
-import org.apache.helix.manager.zk.ZkAsyncCallbacks.CreateCallbackHandler;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor.RetCode;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
 import org.apache.helix.store.HelixPropertyListener;
 import org.apache.helix.store.HelixPropertyStore;
 import org.apache.helix.store.zk.ZNode;
 import org.apache.helix.util.PathUtils;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.callback.ZkAsyncCallbacks;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.apache.zookeeper.KeeperException.Code;
 import org.apache.zookeeper.data.Stat;
 import org.apache.zookeeper.server.DataTree;
@@ -451,12 +451,12 @@
         Arrays.fill(needCreate, true);
         List<List<String>> pathsCreatedList =
             new ArrayList<List<String>>(Collections.<List<String>>nCopies(size, null));
-        CreateCallbackHandler[] createCbList =
+        ZkAsyncCallbacks.CreateCallbackHandler[] createCbList =
             _baseAccessor.create(serverPaths, records, needCreate, pathsCreatedList, options);
 
         boolean[] success = new boolean[size];
         for (int i = 0; i < size; i++) {
-          CreateCallbackHandler cb = createCbList[i];
+          ZkAsyncCallbacks.CreateCallbackHandler cb = createCbList[i];
           success[i] = (Code.get(cb.getRc()) == Code.OK);
 
           updateCache(cache, pathsCreatedList.get(i), success[i], serverPaths.get(i),
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheEventThread.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheEventThread.java
index 9c208d3..5dc1ebb 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheEventThread.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCacheEventThread.java
@@ -23,7 +23,7 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCallbackCache.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCallbackCache.java
index 49037eb..4b08d27 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCallbackCache.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkCallbackCache.java
@@ -25,9 +25,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
 
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.manager.zk.ZkCacheEventThread.ZkCacheEvent;
@@ -35,6 +32,9 @@
 import org.apache.helix.store.HelixPropertyListener;
 import org.apache.helix.store.zk.ZNode;
 import org.apache.helix.util.HelixUtil;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.apache.zookeeper.Watcher.Event.EventType;
 import org.apache.zookeeper.Watcher.Event.KeeperState;
 import org.apache.zookeeper.data.Stat;
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkClient.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkClient.java
index 55c7048..4e38f26 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkClient.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkClient.java
@@ -19,12 +19,14 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.IZkConnection;
-import org.I0Itec.zkclient.serialize.SerializableSerializer;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.HelixException;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.zookeeper.ZkConnection;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.zkclient.IZkConnection;
+import org.apache.helix.zookeeper.zkclient.ZkConnection;
+import org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.SerializableSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -189,7 +191,8 @@
       return this;
     }
 
-    public Builder setZkSerializer(PathBasedZkSerializer zkSerializer) {
+    public Builder setZkSerializer(
+        PathBasedZkSerializer zkSerializer) {
       this._zkSerializer = zkSerializer;
       return this;
     }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkSessionMismatchedException.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkSessionMismatchedException.java
deleted file mode 100644
index e336bb6..0000000
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkSessionMismatchedException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.apache.helix.manager.zk;
-
-import org.I0Itec.zkclient.exception.ZkException;
-import org.apache.zookeeper.KeeperException;
-
-
-/**
- * Exception thrown when an action is taken by an expected zk session which
- * does not match the actual zk session.
- */
-public class ZkSessionMismatchedException extends ZkException {
-
-    private static final long serialVersionUID = 1L;
-
-    public ZkSessionMismatchedException(String message) {
-        super(message);
-    }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/client/DedicatedZkClientFactory.java b/helix-core/src/main/java/org/apache/helix/manager/zk/client/DedicatedZkClientFactory.java
index edeb978..c82c1fc 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/client/DedicatedZkClientFactory.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/client/DedicatedZkClientFactory.java
@@ -1,35 +1,29 @@
 package org.apache.helix.manager.zk.client;
 
-import org.apache.helix.manager.zk.ZkClient;
+/*
+ * 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.
+ */
 
 /**
+ * Deprecated; please use DedicatedZkClientFactory in zookeeper-api instead.
+ *
  * Singleton factory that build dedicated clients using the raw ZkClient.
  */
-public class DedicatedZkClientFactory extends HelixZkClientFactory {
-
-  protected DedicatedZkClientFactory() {}
-
-  private static class SingletonHelper{
-    private static final DedicatedZkClientFactory INSTANCE = new DedicatedZkClientFactory();
-  }
-
-  public static DedicatedZkClientFactory getInstance(){
-    return SingletonHelper.INSTANCE;
-  }
-
-  /**
-   * Build a Dedicated ZkClient based on connection config and client config
-   *
-   * @param connectionConfig
-   * @param clientConfig
-   * @return
-   */
-  @Override
-  public HelixZkClient buildZkClient(HelixZkClient.ZkConnectionConfig connectionConfig,
-      HelixZkClient.ZkClientConfig clientConfig) {
-    return new ZkClient(createZkConnection(connectionConfig),
-        (int) clientConfig.getConnectInitTimeout(), clientConfig.getOperationRetryTimeout(),
-        clientConfig.getZkSerializer(), clientConfig.getMonitorType(), clientConfig.getMonitorKey(),
-        clientConfig.getMonitorInstanceName(), clientConfig.isMonitorRootPathOnly());
-  }
+@Deprecated
+public class DedicatedZkClientFactory extends org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/client/HelixZkClient.java b/helix-core/src/main/java/org/apache/helix/manager/zk/client/HelixZkClient.java
index 5f58b69..a2aa114 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/client/HelixZkClient.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/client/HelixZkClient.java
@@ -1,442 +1,9 @@
 package org.apache.helix.manager.zk.client;
 
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.exception.ZkTimeoutException;
-import org.I0Itec.zkclient.serialize.SerializableSerializer;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.helix.manager.zk.BasicZkSerializer;
-import org.apache.helix.manager.zk.PathBasedZkSerializer;
-import org.apache.helix.manager.zk.ZkAsyncCallbacks;
-import org.apache.helix.manager.zk.zookeeper.IZkStateListener;
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.Op;
-import org.apache.zookeeper.OpResult;
-import org.apache.zookeeper.Watcher.Event.KeeperState;
-import org.apache.zookeeper.data.ACL;
-import org.apache.zookeeper.data.Stat;
-
 /**
- * Helix ZkClient interface.
+ * NOTE: this interface has been deprecated. Please use HelixZkClient or RealmAwareZkClient in zookeeper-api instead.
+ * HelixZkClient interface.
  */
-public interface HelixZkClient {
-  int DEFAULT_OPERATION_TIMEOUT = Integer.MAX_VALUE;
-  int DEFAULT_CONNECTION_TIMEOUT = 60 * 1000;
-  int DEFAULT_SESSION_TIMEOUT = 30 * 1000;
-
-  // listener subscription
-  List<String> subscribeChildChanges(String path, IZkChildListener listener);
-
-  void unsubscribeChildChanges(String path, IZkChildListener listener);
-
-  void subscribeDataChanges(String path, IZkDataListener listener);
-
-  void unsubscribeDataChanges(String path, IZkDataListener listener);
-
-  /*
-   * This is for backwards compatibility.
-   *
-   * TODO: remove below default implementation when getting rid of I0Itec in the new zk client.
-   */
-  default void subscribeStateChanges(final IZkStateListener listener) {
-    subscribeStateChanges(new I0ItecIZkStateListenerHelixImpl(listener));
-  }
-
-  /*
-   * This is for backwards compatibility.
-   *
-   * TODO: remove below default implementation when getting rid of I0Itec in the new zk client.
-   */
-  default void unsubscribeStateChanges(IZkStateListener listener) {
-    unsubscribeStateChanges(new I0ItecIZkStateListenerHelixImpl(listener));
-  }
-
-  /**
-   * Subscribes state changes for a {@link org.I0Itec.zkclient.IZkStateListener} listener.
-   *
-   * @deprecated
-   * This is deprecated. It is kept for backwards compatibility. Please use
-   * {@link #subscribeStateChanges(org.apache.helix.manager.zk.zookeeper.IZkStateListener)}.
-   *
-   * @param listener {@link org.I0Itec.zkclient.IZkStateListener} listener
-   */
-  @Deprecated
-  void subscribeStateChanges(final org.I0Itec.zkclient.IZkStateListener listener);
-
-  /**
-   * Unsubscribes state changes for a {@link org.I0Itec.zkclient.IZkStateListener} listener.
-   *
-   * @deprecated
-   * This is deprecated. It is kept for backwards compatibility. Please use
-   * {@link #unsubscribeStateChanges(org.apache.helix.manager.zk.zookeeper.IZkStateListener)}.
-   *
-   * @param listener {@link org.I0Itec.zkclient.IZkStateListener} listener
-   */
-  @Deprecated
-  void unsubscribeStateChanges(org.I0Itec.zkclient.IZkStateListener listener);
-
-  void unsubscribeAll();
-
-  // data access
-  void createPersistent(String path);
-
-  void createPersistent(String path, boolean createParents);
-
-  void createPersistent(String path, boolean createParents, List<ACL> acl);
-
-  void createPersistent(String path, Object data);
-
-  void createPersistent(String path, Object data, List<ACL> acl);
-
-  String createPersistentSequential(String path, Object data);
-
-  String createPersistentSequential(String path, Object data, List<ACL> acl);
-
-  void createEphemeral(final String path);
-
-  void createEphemeral(final String path, final String sessionId);
-
-  void createEphemeral(final String path, final List<ACL> acl);
-
-  void createEphemeral(final String path, final List<ACL> acl, final String sessionId);
-
-  String create(final String path, Object data, final CreateMode mode);
-
-  String create(final String path, Object datat, final List<ACL> acl, final CreateMode mode);
-
-  void createEphemeral(final String path, final Object data);
-
-  void createEphemeral(final String path, final Object data, final String sessionId);
-
-  void createEphemeral(final String path, final Object data, final List<ACL> acl);
-
-  void createEphemeral(final String path, final Object data, final List<ACL> acl,
-      final String sessionId);
-
-  String createEphemeralSequential(final String path, final Object data);
-
-  String createEphemeralSequential(final String path, final Object data, final List<ACL> acl);
-
-  String createEphemeralSequential(final String path, final Object data, final String sessionId);
-
-  String createEphemeralSequential(final String path, final Object data, final List<ACL> acl,
-      final String sessionId);
-
-  List<String> getChildren(String path);
-
-  int countChildren(String path);
-
-  boolean exists(final String path);
-
-  Stat getStat(final String path);
-
-  boolean waitUntilExists(String path, TimeUnit timeUnit, long time);
-
-  void deleteRecursively(String path);
-
-  boolean delete(final String path);
-
-  <T extends Object> T readData(String path);
-
-  <T extends Object> T readData(String path, boolean returnNullIfPathNotExists);
-
-  <T extends Object> T readData(String path, Stat stat);
-
-  <T extends Object> T readData(final String path, final Stat stat, final boolean watch);
-
-  <T extends Object> T readDataAndStat(String path, Stat stat, boolean returnNullIfPathNotExists);
-
-  void writeData(String path, Object object);
-
-  <T extends Object> void updateDataSerialized(String path, DataUpdater<T> updater);
-
-  void writeData(final String path, Object datat, final int expectedVersion);
-
-  Stat writeDataReturnStat(final String path, Object datat, final int expectedVersion);
-
-  Stat writeDataGetStat(final String path, Object datat, final int expectedVersion);
-
-  void asyncCreate(final String path, Object datat, final CreateMode mode,
-      final ZkAsyncCallbacks.CreateCallbackHandler cb);
-
-  void asyncSetData(final String path, Object datat, final int version,
-      final ZkAsyncCallbacks.SetDataCallbackHandler cb);
-
-  void asyncGetData(final String path, final ZkAsyncCallbacks.GetDataCallbackHandler cb);
-
-  void asyncExists(final String path, final ZkAsyncCallbacks.ExistsCallbackHandler cb);
-
-  void asyncDelete(final String path, final ZkAsyncCallbacks.DeleteCallbackHandler cb);
-
-  void watchForData(final String path);
-
-  List<String> watchForChilds(final String path);
-
-  long getCreationTime(String path);
-
-  List<OpResult> multi(final Iterable<Op> ops);
-
-  // ZK state control
-  boolean waitUntilConnected(long time, TimeUnit timeUnit);
-
-  /**
-   * Waits for SyncConnected state and returns a valid session ID(non-zero). The implementation of
-   * this method should wait for SyncConnected state and ZK session to be established, and should
-   * guarantee the established session's ID is returned before keeper state changes.
-   *
-   * Please note: this default implementation may have race condition issue and return an unexpected
-   * session ID that is zero or another new session's ID. The default implementation is for backward
-   * compatibility purpose.
-   *
-   * @param timeout Max waiting time for connecting to ZK server.
-   * @param timeUnit Time unit for the timeout.
-   * @return A valid ZK session ID which is non-zero.
-   */
-  default long waitForEstablishedSession(long timeout, TimeUnit timeUnit) {
-    if (!waitUntilConnected(timeout, timeUnit)) {
-      throw new ZkTimeoutException(
-          "Failed to get established session because connecting to ZK server has timed out in "
-              + timeout + " " + timeUnit);
-    }
-    return getSessionId();
-  }
-
-  String getServers();
-
-  long getSessionId();
-
-  void close();
-
-  boolean isClosed();
-
-  // other
-  byte[] serialize(Object data, String path);
-
-  <T extends Object> T deserialize(byte[] data, String path);
-
-  void setZkSerializer(ZkSerializer zkSerializer);
-
-  void setZkSerializer(PathBasedZkSerializer zkSerializer);
-
-  PathBasedZkSerializer getZkSerializer();
-
-  /**
-   * A class that wraps a default implementation of
-   * {@link org.apache.helix.manager.zk.zookeeper.IZkStateListener}, which means this listener
-   * runs the methods of {@link org.apache.helix.manager.zk.zookeeper.IZkStateListener}.
-   * This is for backward compatibility and to avoid breaking the original implementation of
-   * {@link org.I0Itec.zkclient.IZkStateListener}.
-   */
-  class I0ItecIZkStateListenerHelixImpl implements org.I0Itec.zkclient.IZkStateListener {
-    private IZkStateListener _listener;
-
-    I0ItecIZkStateListenerHelixImpl(IZkStateListener listener) {
-      _listener = listener;
-    }
-
-    @Override
-    public void handleStateChanged(KeeperState keeperState) throws Exception {
-      _listener.handleStateChanged(keeperState);
-    }
-
-    @Override
-    public void handleNewSession() throws Exception {
-      /*
-       * org.apache.helix.manager.zk.zookeeper.IZkStateListener does not have handleNewSession(),
-       * so null is passed into handleNewSession(sessionId).
-       */
-      _listener.handleNewSession(null);
-    }
-
-    @Override
-    public void handleSessionEstablishmentError(Throwable error) throws Exception {
-      _listener.handleSessionEstablishmentError(error);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (obj == this) {
-        return true;
-      }
-      if (!(obj instanceof I0ItecIZkStateListenerHelixImpl)) {
-        return false;
-      }
-      if (_listener == null) {
-        return false;
-      }
-
-      I0ItecIZkStateListenerHelixImpl defaultListener = (I0ItecIZkStateListenerHelixImpl) obj;
-
-      return _listener.equals(defaultListener._listener);
-    }
-
-    @Override
-    public int hashCode() {
-      /*
-       * The original listener's hashcode helps find the wrapped listener with the same original
-       * listener. This is helpful in unsubscribeStateChanges(listener).
-       */
-      return _listener.hashCode();
-    }
-  }
-
-  /**
-   * Configuration for creating a new ZkConnection.
-   */
-  class ZkConnectionConfig {
-    // Connection configs
-    private final String _zkServers;
-    private int _sessionTimeout = HelixZkClient.DEFAULT_SESSION_TIMEOUT;
-
-    public ZkConnectionConfig(String zkServers) {
-      _zkServers = zkServers;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (obj == this) {
-        return true;
-      }
-      if (!(obj instanceof ZkConnectionConfig)) {
-        return false;
-      }
-      ZkConnectionConfig configObj = (ZkConnectionConfig) obj;
-      return (_zkServers == null && configObj._zkServers == null ||
-          _zkServers != null && _zkServers.equals(configObj._zkServers)) &&
-          _sessionTimeout == configObj._sessionTimeout;
-    }
-
-    @Override
-    public int hashCode() {
-      return _sessionTimeout * 31 + _zkServers.hashCode();
-    }
-
-    @Override
-    public String toString() {
-      return (_zkServers + "_" + _sessionTimeout).replaceAll("[\\W]", "_");
-    }
-
-    public ZkConnectionConfig setSessionTimeout(Integer sessionTimeout) {
-      this._sessionTimeout = sessionTimeout;
-      return this;
-    }
-
-    public String getZkServers() {
-      return _zkServers;
-    }
-
-    public int getSessionTimeout() {
-      return _sessionTimeout;
-    }
-  }
-
-  /**
-   * Configuration for creating a new ZkClient with serializer and monitor.
-   */
-  class ZkClientConfig {
-    // For client to init the connection
-    private long _connectInitTimeout = HelixZkClient.DEFAULT_CONNECTION_TIMEOUT;
-
-    // Data access configs
-    private long _operationRetryTimeout = HelixZkClient.DEFAULT_OPERATION_TIMEOUT;
-
-    // Others
-    private PathBasedZkSerializer _zkSerializer;
-
-    // Monitoring
-    private String _monitorType;
-    private String _monitorKey;
-    private String _monitorInstanceName = null;
-    private boolean _monitorRootPathOnly = true;
-
-    public ZkClientConfig setZkSerializer(PathBasedZkSerializer zkSerializer) {
-      this._zkSerializer = zkSerializer;
-      return this;
-    }
-
-    public ZkClientConfig setZkSerializer(ZkSerializer zkSerializer) {
-      this._zkSerializer = new BasicZkSerializer(zkSerializer);
-      return this;
-    }
-
-    /**
-     * Used as part of the MBean ObjectName. This item is required for enabling monitoring.
-     *
-     * @param monitorType
-     */
-    public ZkClientConfig setMonitorType(String monitorType) {
-      this._monitorType = monitorType;
-      return this;
-    }
-
-    /**
-     * Used as part of the MBean ObjectName. This item is required for enabling monitoring.
-     *
-     * @param monitorKey
-     */
-    public ZkClientConfig setMonitorKey(String monitorKey) {
-      this._monitorKey = monitorKey;
-      return this;
-    }
-
-    /**
-     * Used as part of the MBean ObjectName. This item is optional.
-     *
-     * @param instanceName
-     */
-    public ZkClientConfig setMonitorInstanceName(String instanceName) {
-      this._monitorInstanceName = instanceName;
-      return this;
-    }
-
-    public ZkClientConfig setMonitorRootPathOnly(Boolean monitorRootPathOnly) {
-      this._monitorRootPathOnly = monitorRootPathOnly;
-      return this;
-    }
-
-    public ZkClientConfig setOperationRetryTimeout(Long operationRetryTimeout) {
-      this._operationRetryTimeout = operationRetryTimeout;
-      return this;
-    }
-
-    public ZkClientConfig setConnectInitTimeout(long _connectInitTimeout) {
-      this._connectInitTimeout = _connectInitTimeout;
-      return this;
-    }
-
-    public PathBasedZkSerializer getZkSerializer() {
-      if (_zkSerializer == null) {
-        _zkSerializer = new BasicZkSerializer(new SerializableSerializer());
-      }
-      return _zkSerializer;
-    }
-
-    public long getOperationRetryTimeout() {
-      return _operationRetryTimeout;
-    }
-
-    public String getMonitorType() {
-      return _monitorType;
-    }
-
-    public String getMonitorKey() {
-      return _monitorKey;
-    }
-
-    public String getMonitorInstanceName() {
-      return _monitorInstanceName;
-    }
-
-    public boolean isMonitorRootPathOnly() {
-      return _monitorRootPathOnly;
-    }
-
-    public long getConnectInitTimeout() {
-      return _connectInitTimeout;
-    }
-  }
+@Deprecated
+public interface HelixZkClient extends org.apache.helix.zookeeper.api.client.HelixZkClient {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/client/HelixZkClientFactory.java b/helix-core/src/main/java/org/apache/helix/manager/zk/client/HelixZkClientFactory.java
deleted file mode 100644
index e6dc90e..0000000
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/client/HelixZkClientFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.apache.helix.manager.zk.client;
-
-import org.I0Itec.zkclient.IZkConnection;
-import org.apache.helix.HelixException;
-import org.apache.helix.manager.zk.zookeeper.ZkConnection;
-
-/**
- * Abstract class of the ZkClient factory.
- */
-abstract class HelixZkClientFactory {
-
-  /**
-   * Build a ZkClient using specified connection config and client config
-   *
-   * @param connectionConfig
-   * @param clientConfig
-   * @return HelixZkClient
-   */
-  public abstract HelixZkClient buildZkClient(HelixZkClient.ZkConnectionConfig connectionConfig,
-      HelixZkClient.ZkClientConfig clientConfig);
-
-  /**
-   * Build a ZkClient using specified connection config and default client config
-   *
-   * @param connectionConfig
-   * @return HelixZkClient
-   */
-  public HelixZkClient buildZkClient(HelixZkClient.ZkConnectionConfig connectionConfig) {
-    return buildZkClient(connectionConfig, new HelixZkClient.ZkClientConfig());
-  }
-
-  /**
-   * Construct a new ZkConnection instance based on connection configuration.
-   * Note that the connection is not really made until someone calls zkConnection.connect().
-   * @param connectionConfig
-   * @return
-   */
-  protected IZkConnection createZkConnection(HelixZkClient.ZkConnectionConfig connectionConfig) {
-    if (connectionConfig.getZkServers() == null) {
-      throw new HelixException(
-          "Failed to build ZkClient since no connection or ZK server address is specified.");
-    } else {
-      return new ZkConnection(connectionConfig.getZkServers(), connectionConfig.getSessionTimeout());
-    }
-  }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/client/SharedZkClient.java b/helix-core/src/main/java/org/apache/helix/manager/zk/client/SharedZkClient.java
index 5c6ade0..52a2c17 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/client/SharedZkClient.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/client/SharedZkClient.java
@@ -1,92 +1,37 @@
 package org.apache.helix.manager.zk.client;
 
-import java.util.List;
-
-import org.I0Itec.zkclient.IZkConnection;
-import org.apache.helix.HelixException;
-import org.apache.helix.manager.zk.zookeeper.ZkConnection;
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.data.ACL;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+/*
+ * 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.
+ */
 
 /**
- * ZkClient that uses shared ZkConnection.
- * A SharedZkClient won't manipulate the shared ZkConnection directly.
+ * Deprecated; use SharedZkClient in zookeeper-api instead.
  */
-class SharedZkClient extends org.apache.helix.manager.zk.ZkClient implements HelixZkClient {
-  private static Logger LOG = LoggerFactory.getLogger(SharedZkClient.class);
-  /*
-   * Since we cannot really disconnect the ZkConnection, we need a dummy ZkConnection placeholder.
-   * This is to ensure connection field is never null even the shared ZkClient instance is closed so as to avoid NPE.
-   */
-  private final static ZkConnection IDLE_CONNECTION = new ZkConnection("Dummy_ZkServers");
-  private final OnCloseCallback _onCloseCallback;
-  private final ZkConnectionManager _connectionManager;
-
-  interface OnCloseCallback {
-    /**
-     * Triggered after the ZkClient is closed.
-     */
-    void onClose();
-  }
-
+@Deprecated
+class SharedZkClient extends org.apache.helix.zookeeper.impl.client.SharedZkClient {
   /**
-   * Construct a shared ZkClient that uses a shared ZkConnection.
-   *
-   * @param connectionManager     The manager of the shared ZkConnection.
-   * @param clientConfig          ZkClientConfig details to create the shared ZkClient.
-   * @param callback              Clean up logic when the shared ZkClient is closed.
+   * Construct a shared RealmAwareZkClient that uses a shared ZkConnection.
+   *  @param connectionManager     The manager of the shared ZkConnection.
+   * @param clientConfig          ZkClientConfig details to create the shared RealmAwareZkClient.
+   * @param callback              Clean up logic when the shared RealmAwareZkClient is closed.
    */
   protected SharedZkClient(ZkConnectionManager connectionManager, ZkClientConfig clientConfig,
-      OnCloseCallback callback) {
-    super(connectionManager.getConnection(), 0, clientConfig.getOperationRetryTimeout(),
-        clientConfig.getZkSerializer(), clientConfig.getMonitorType(), clientConfig.getMonitorKey(),
-        clientConfig.getMonitorInstanceName(), clientConfig.isMonitorRootPathOnly());
-    _connectionManager = connectionManager;
-    // Register to the base dedicated ZkClient
-    _connectionManager.registerWatcher(this);
-    _onCloseCallback = callback;
-  }
-
-  @Override
-  public void close() {
-    super.close();
-    if (isClosed()) {
-      // Note that if register is not done while constructing, these private fields may not be init yet.
-      if (_connectionManager != null) {
-        _connectionManager.unregisterWatcher(this);
-      }
-      if (_onCloseCallback != null) {
-        _onCloseCallback.onClose();
-      }
-    }
-  }
-
-  @Override
-  public IZkConnection getConnection() {
-    if (isClosed()) {
-      return IDLE_CONNECTION;
-    }
-    return super.getConnection();
-  }
-
-  /**
-   * Since ZkConnection session is shared in this ZkClient, do not create ephemeral node using a SharedZKClient.
-   */
-  @Override
-  public String create(final String path, Object datat, final List<ACL> acl,
-      final CreateMode mode) {
-    if (mode.isEphemeral()) {
-      throw new HelixException(
-          "Create ephemeral nodes using a " + SharedZkClient.class.getSimpleName()
-              + " ZkClient is not supported.");
-    }
-    return super.create(path, datat, acl, mode);
-  }
-
-  @Override
-  protected boolean isManagingZkConnection() {
-    return false;
+      org.apache.helix.zookeeper.impl.client.SharedZkClient.OnCloseCallback callback) {
+    super(connectionManager, clientConfig, callback);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/client/SharedZkClientFactory.java b/helix-core/src/main/java/org/apache/helix/manager/zk/client/SharedZkClientFactory.java
index ed4b5de..0a00336 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/client/SharedZkClientFactory.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/client/SharedZkClientFactory.java
@@ -1,87 +1,29 @@
 package org.apache.helix.manager.zk.client;
 
-import java.util.HashMap;
-
-import org.apache.helix.HelixException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+/*
+ * 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.
+ */
 
 /**
+ * Deprecated: Please use SharedZkClientFactory in zookeeper-api module instead.
+ *
  * Singleton factory that build shared ZkClient which use a shared ZkConnection.
  */
-public class SharedZkClientFactory extends HelixZkClientFactory {
-  private static Logger LOG = LoggerFactory.getLogger(SharedZkClient.class);
-  // The connection pool to track all created connections.
-  private final HashMap<HelixZkClient.ZkConnectionConfig, ZkConnectionManager>
-      _connectionManagerPool = new HashMap<>();
-
-  protected SharedZkClientFactory() {}
-
-  private static class SingletonHelper {
-    private static final SharedZkClientFactory INSTANCE = new SharedZkClientFactory();
-  }
-
-  public static SharedZkClientFactory getInstance() {
-    return SingletonHelper.INSTANCE;
-  }
-
-  /**
-   * Build a Shared ZkClient that uses sharing ZkConnection that is created based on the specified connection config.
-   *
-   * @param connectionConfig The connection configuration that is used to search for a shared connection. Or create new connection if necessary.
-   * @param clientConfig
-   * @return Shared ZkClient
-   */
-  @Override
-  public HelixZkClient buildZkClient(HelixZkClient.ZkConnectionConfig connectionConfig,
-      HelixZkClient.ZkClientConfig clientConfig) {
-    synchronized (_connectionManagerPool) {
-      final ZkConnectionManager zkConnectionManager =
-          getOrCreateZkConnectionNamanger(connectionConfig, clientConfig.getConnectInitTimeout());
-      if (zkConnectionManager == null) {
-        throw new HelixException("Failed to create a connection manager in the pool to share.");
-      }
-      LOG.info("Sharing ZkConnection {} to a new SharedZkClient.", connectionConfig.toString());
-      return new SharedZkClient(zkConnectionManager, clientConfig,
-          new SharedZkClient.OnCloseCallback() {
-            @Override
-            public void onClose() {
-              cleanupConnectionManager(zkConnectionManager);
-            }
-          });
-    }
-  }
-
-  private ZkConnectionManager getOrCreateZkConnectionNamanger(
-      HelixZkClient.ZkConnectionConfig connectionConfig, long connectInitTimeout) {
-    ZkConnectionManager connectionManager = _connectionManagerPool.get(connectionConfig);
-    if (connectionManager == null || connectionManager.isClosed()) {
-      connectionManager = new ZkConnectionManager(createZkConnection(connectionConfig), connectInitTimeout,
-          connectionConfig.toString());
-      _connectionManagerPool.put(connectionConfig, connectionManager);
-    }
-    return connectionManager;
-  }
-
-  // Close the ZkConnectionManager if no other shared client is referring to it.
-  // Note the close operation of connection manager needs to be synchronized with the pool operation
-  // to avoid race condition.
-  private void cleanupConnectionManager(ZkConnectionManager zkConnectionManager) {
-    synchronized (_connectionManagerPool) {
-      zkConnectionManager.close(true);
-    }
-  }
-
-  // For test only
-  protected int getActiveConnectionCount() {
-    int count = 0;
-    synchronized (_connectionManagerPool) {
-      for (ZkConnectionManager manager : _connectionManagerPool.values()) {
-        if (!manager.isClosed()) {
-          count++;
-        }
-      }
-    }
-    return count;
-  }
+@Deprecated
+public class SharedZkClientFactory extends org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/client/ZkConnectionManager.java b/helix-core/src/main/java/org/apache/helix/manager/zk/client/ZkConnectionManager.java
index 0a9ddc1..c3ccfaa 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/client/ZkConnectionManager.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/client/ZkConnectionManager.java
@@ -1,113 +1,53 @@
 package org.apache.helix.manager.zk.client;
 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
 import java.util.HashSet;
 import java.util.Set;
 
-import org.I0Itec.zkclient.IZkConnection;
-import org.I0Itec.zkclient.serialize.SerializableSerializer;
-import org.apache.helix.HelixException;
-import org.apache.helix.manager.zk.BasicZkSerializer;
-import org.apache.zookeeper.WatchedEvent;
+import org.apache.helix.zookeeper.zkclient.IZkConnection;
 import org.apache.zookeeper.Watcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
 
 /**
- * A ZkConnection manager that maintain connection status and allows additional watchers to be registered.
- * It will forward events to those watchers.
- *
- * TODO Separate connection management logic from the raw ZkClient class.
- * So this manager is a peer to the ZkClient. Connection Manager for maintaining the connection and
- * ZkClient to handle user request.
- * After this is done, Dedicated ZkClient hires one manager for it's connection.
- * While multiple Shared ZkClients can use single connection manager if possible.
+ * Deprecated - use ZkConnectionManager in zookeeper-api instead.
  */
-class ZkConnectionManager extends org.apache.helix.manager.zk.ZkClient {
-  private static Logger LOG = LoggerFactory.getLogger(ZkConnectionManager.class);
-  // Client type that is used in monitor, and metrics.
-  private final static String MONITOR_TYPE = "ZkConnectionManager";
-  private final String _monitorKey;
-  // Set of all registered watchers
-  private final Set<Watcher> _sharedWatchers = new HashSet<>();
-
+@Deprecated
+class ZkConnectionManager extends org.apache.helix.zookeeper.impl.factory.ZkConnectionManager {
   /**
    * Construct and init a ZkConnection Manager.
-   *
-   * @param zkConnection
+   *  @param zkConnection
    * @param connectionTimeout
+   * @param monitorKey
    */
   protected ZkConnectionManager(IZkConnection zkConnection, long connectionTimeout,
       String monitorKey) {
-    super(zkConnection, (int) connectionTimeout, HelixZkClient.DEFAULT_OPERATION_TIMEOUT,
-        new BasicZkSerializer(new SerializableSerializer()), MONITOR_TYPE, monitorKey, null, true);
-    _monitorKey = monitorKey;
-    LOG.info("ZkConnection {} was created for sharing.", _monitorKey);
+    super(zkConnection, connectionTimeout, monitorKey);
   }
 
   /**
-   * Register event watcher.
-   *
-   * @param watcher
-   * @return true if the watcher is newly added. false if it is already in record.
+   * Need to override because of the "instanceof" usage doesn't cover subclasses.
    */
-  protected synchronized boolean registerWatcher(Watcher watcher) {
-    if (isClosed()) {
-      throw new HelixException("Cannot add watcher to a closed client.");
-    }
-    return _sharedWatchers.add(watcher);
-  }
-
-  /**
-   * Unregister the event watcher.
-   *
-   * @param watcher
-   * @return number of the remaining event watchers
-   */
-  protected synchronized int unregisterWatcher(Watcher watcher) {
-    _sharedWatchers.remove(watcher);
-    return _sharedWatchers.size();
-  }
-
   @Override
-  public void process(final WatchedEvent event) {
-    super.process(event);
-    forwardingEvent(event);
-  }
-
-  private synchronized void forwardingEvent(final WatchedEvent event) {
-    // note that process (then forwardingEvent) could be triggered during construction, when sharedWatchers is still null.
-    if (_sharedWatchers == null || _sharedWatchers.isEmpty()) {
-      return;
-    }
-    // forward event to all the watchers' event queue
-    for (final Watcher watcher : _sharedWatchers) {
-      watcher.process(event);
-    }
-  }
-
-  @Override
-  public void close() {
-    // Enforce closing, if any watcher exists, throw Exception.
-    close(false);
-  }
-
-  protected synchronized void close(boolean skipIfWatched) {
-    cleanupInactiveWatchers();
-    if (_sharedWatchers.size() > 0) {
-      if (skipIfWatched) {
-        LOG.debug("Skip closing ZkConnection due to existing watchers. Watcher count {}.",
-            _sharedWatchers.size());
-        return;
-      } else {
-        throw new HelixException(
-            "Cannot close the connection when there are still shared watchers listen on the event.");
-      }
-    }
-    super.close();
-    LOG.info("ZkConnection {} was closed.", _monitorKey);
-  }
-
-  private void cleanupInactiveWatchers() {
+  protected void cleanupInactiveWatchers() {
+    super.cleanupInactiveWatchers();
     Set<Watcher> closedWatchers = new HashSet<>();
     for (Watcher watcher : _sharedWatchers) {
       // TODO ideally, we shall have a ClosableWatcher interface so as to check accordingly. -- JJ
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/IZkStateListener.java b/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/IZkStateListener.java
index aa1fdd6..32e203d 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/IZkStateListener.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/IZkStateListener.java
@@ -19,43 +19,10 @@
  * under the License.
  */
 
-import org.apache.zookeeper.Watcher.Event.KeeperState;
+/**
+ * Use IZkStateListener in zookeeper-api module instead.
+ */
+@Deprecated
+public interface IZkStateListener extends org.apache.helix.zookeeper.zkclient.IZkStateListener {
 
-
-public interface IZkStateListener {
-
-  /**
-   * Called when the zookeeper connection state has changed.
-   *
-   * @param state the new zookeeper state.
-   * @throws Exception if any error occurs.
-   */
-  void handleStateChanged(KeeperState state) throws Exception;
-
-  /**
-   * Called after the zookeeper session has expired and a new session has been created. The new
-   * session id has to be passed in as the parameter.
-   * And you would have to re-create any ephemeral nodes here. This is a session aware operation.
-   * The ephemeral nodes have to be created within the expected session id, which means passed-in
-   * session id has to be checked with current zookeeper's session id. If the passed-in session id
-   * does not match current zookeeper's session id, ephemeral nodes should not be created.
-   * Otherwise, session race condition may occur and the newly created ephemeral nodes may not be in
-   * the expected session.
-   *
-   * @param sessionId the new session's id. The ephemeral nodes are expected to be created in this
-   *                  session. If this session id is expired, ephemeral nodes should not be created.
-   * @throws Exception if any error occurs.
-   */
-  void handleNewSession(final String sessionId) throws Exception;
-
-  /**
-   * Called when a session cannot be re-established. This should be used to implement connection
-   * failure handling e.g. retry to connect or pass the error up
-   *
-   * @param error
-   *            The error that prevents a session from being established
-   * @throws Exception
-   *             On any error.
-   */
-  void handleSessionEstablishmentError(final Throwable error) throws Exception;
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkClient.java b/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkClient.java
index 453c8c3..39e80ba 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkClient.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkClient.java
@@ -10,2154 +10,19 @@
  */
 package org.apache.helix.manager.zk.zookeeper;
 
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.OptionalLong;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.TimeUnit;
-import javax.management.JMException;
-
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.ExceptionUtil;
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkConnection;
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.ZkLock;
-import org.I0Itec.zkclient.exception.ZkBadVersionException;
-import org.I0Itec.zkclient.exception.ZkException;
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
-import org.I0Itec.zkclient.exception.ZkNodeExistsException;
-import org.I0Itec.zkclient.exception.ZkTimeoutException;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.api.listeners.PreFetch;
-import org.apache.helix.manager.zk.BasicZkSerializer;
-import org.apache.helix.manager.zk.PathBasedZkSerializer;
-import org.apache.helix.manager.zk.ZKUtil;
-import org.apache.helix.manager.zk.ZkAsyncCallbacks;
-import org.apache.helix.manager.zk.ZkSessionMismatchedException;
-import org.apache.helix.manager.zk.zookeeper.ZkEventThread.ZkEvent;
-import org.apache.helix.monitoring.mbeans.ZkClientMonitor;
-import org.apache.helix.util.ExponentialBackoffStrategy;
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.KeeperException.ConnectionLossException;
-import org.apache.zookeeper.KeeperException.SessionExpiredException;
-import org.apache.zookeeper.Op;
-import org.apache.zookeeper.OpResult;
-import org.apache.zookeeper.WatchedEvent;
-import org.apache.zookeeper.Watcher;
-import org.apache.zookeeper.Watcher.Event.EventType;
-import org.apache.zookeeper.Watcher.Event.KeeperState;
-import org.apache.zookeeper.ZooDefs;
-import org.apache.zookeeper.ZooKeeper;
-import org.apache.zookeeper.data.ACL;
-import org.apache.zookeeper.data.Stat;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.helix.zookeeper.zkclient.IZkConnection;
+import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer;
 
 
 /**
- * Abstracts the interaction with zookeeper and allows permanent (not just one time) watches on
- * nodes in ZooKeeper.
- * WARN: Do not use this class directly, use {@link org.apache.helix.manager.zk.ZkClient} instead.
+ * Use ZkClient in zookeeper-api module instead.
  */
-public class ZkClient implements Watcher {
-  private static Logger LOG = LoggerFactory.getLogger(ZkClient.class);
-  private static long MAX_RECONNECT_INTERVAL_MS = 30000; // 30 seconds
-
-  private final IZkConnection _connection;
-  private final long _operationRetryTimeoutInMillis;
-  private final Map<String, Set<IZkChildListener>> _childListener = new ConcurrentHashMap<>();
-  private final ConcurrentHashMap<String, Set<IZkDataListenerEntry>> _dataListener =
-      new ConcurrentHashMap<>();
-  private final Set<IZkStateListener> _stateListener = new CopyOnWriteArraySet<>();
-  private KeeperState _currentState;
-  private final ZkLock _zkEventLock = new ZkLock();
-
-  // When a new zookeeper instance is created in reconnect, its session id is not yet valid before
-  // the zookeeper session is established(SyncConnected). To avoid session race condition in
-  // handling new session, the new session event is only fired after SyncConnected. Meanwhile,
-  // SyncConnected state is also received when re-opening the zk connection. So to avoid firing
-  // new session event more than once, this flag is used to check.
-  // It is set to false right after the new zookeeper instance is created in reconnect before the
-  // session is established. And set it to true once the new session event is fired the first time.
-  private boolean _isNewSessionEventFired;
-
-  private boolean _shutdownTriggered;
-  private ZkEventThread _eventThread;
-  // TODO PVo remove this later
-  private Thread _zookeeperEventThread;
-  private volatile boolean _closed;
-  private PathBasedZkSerializer _pathBasedZkSerializer;
-  private ZkClientMonitor _monitor;
-
-  private class IZkDataListenerEntry {
-    final IZkDataListener _dataListener;
-    final boolean _prefetchData;
-
-    public IZkDataListenerEntry(IZkDataListener dataListener, boolean prefetchData) {
-      _dataListener = dataListener;
-      _prefetchData = prefetchData;
-    }
-
-    public IZkDataListenerEntry(IZkDataListener dataListener) {
-      _dataListener = dataListener;
-      _prefetchData = false;
-    }
-
-    public IZkDataListener getDataListener() {
-      return _dataListener;
-    }
-
-    public boolean isPrefetchData() {
-      return _prefetchData;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (this == o) {
-        return true;
-      }
-      if (!(o instanceof IZkDataListenerEntry)) {
-        return false;
-      }
-
-      IZkDataListenerEntry that = (IZkDataListenerEntry) o;
-
-      return _dataListener.equals(that._dataListener);
-    }
-
-    @Override
-    public int hashCode() {
-      return _dataListener.hashCode();
-    }
-  }
-
-  private class ZkPathStatRecord {
-    private final String _path;
-    private Stat _stat = null;
-    private boolean _checked = false;
-
-    public ZkPathStatRecord(String path) {
-      _path = path;
-    }
-
-    public boolean pathExists() {
-      return _stat != null;
-    }
-
-    public boolean pathChecked() {
-      return _checked;
-    }
-
-    /*
-     * Note this method is not thread safe.
-     */
-    public void recordPathStat(Stat stat, OptionalLong notificationTime) {
-      _checked = true;
-      _stat = stat;
-
-      if (_monitor != null && stat != null && notificationTime.isPresent()) {
-        long updateTime = Math.max(stat.getCtime(), stat.getMtime());
-        if (notificationTime.getAsLong() > updateTime) {
-          _monitor.recordDataPropagationLatency(_path, notificationTime.getAsLong() - updateTime);
-        } // else, the node was updated again after the notification. Propagation latency is
-          // unavailable.
-      }
-    }
-  }
-
+@Deprecated
+public class ZkClient extends org.apache.helix.zookeeper.zkclient.ZkClient {
   protected ZkClient(IZkConnection zkConnection, int connectionTimeout, long operationRetryTimeout,
       PathBasedZkSerializer zkSerializer, String monitorType, String monitorKey,
       String monitorInstanceName, boolean monitorRootPathOnly) {
-    if (zkConnection == null) {
-      throw new NullPointerException("Zookeeper connection is null!");
-    }
-    _connection = zkConnection;
-    _pathBasedZkSerializer = zkSerializer;
-    _operationRetryTimeoutInMillis = operationRetryTimeout;
-    _isNewSessionEventFired = false;
-
-    connect(connectionTimeout, this);
-
-    // initiate monitor
-    try {
-      if (monitorKey != null && !monitorKey.isEmpty() && monitorType != null
-          && !monitorType.isEmpty()) {
-        _monitor = new ZkClientMonitor(monitorType, monitorKey, monitorInstanceName,
-            monitorRootPathOnly, _eventThread);
-        _monitor.register();
-      } else {
-        LOG.info("ZkClient monitor key or type is not provided. Skip monitoring.");
-      }
-    } catch (JMException e) {
-      LOG.error("Error in creating ZkClientMonitor", e);
-    }
-  }
-
-  public List<String> subscribeChildChanges(String path, IZkChildListener listener) {
-    synchronized (_childListener) {
-      Set<IZkChildListener> listeners = _childListener.get(path);
-      if (listeners == null) {
-        listeners = new CopyOnWriteArraySet<>();
-        _childListener.put(path, listeners);
-      }
-      listeners.add(listener);
-    }
-    return watchForChilds(path);
-  }
-
-  public void unsubscribeChildChanges(String path, IZkChildListener childListener) {
-    synchronized (_childListener) {
-      final Set<IZkChildListener> listeners = _childListener.get(path);
-      if (listeners != null) {
-        listeners.remove(childListener);
-      }
-    }
-  }
-
-  /**
-   * Subscribe the path and the listener will handle data events of the path
-   * WARNING: if the path is created after deletion, users need to re-subscribe the path
-   * @param path The zookeeper path
-   * @param listener Instance of {@link IZkDataListener}
-   */
-  public void subscribeDataChanges(String path, IZkDataListener listener) {
-    Set<IZkDataListenerEntry> listenerEntries;
-    synchronized (_dataListener) {
-      listenerEntries = _dataListener.get(path);
-      if (listenerEntries == null) {
-        listenerEntries = new CopyOnWriteArraySet<>();
-        _dataListener.put(path, listenerEntries);
-      }
-
-      boolean prefetchEnabled = isPrefetchEnabled(listener);
-      IZkDataListenerEntry listenerEntry = new IZkDataListenerEntry(listener, prefetchEnabled);
-      listenerEntries.add(listenerEntry);
-      if (prefetchEnabled) {
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Subscribed data changes for " + path + ", listener: " + listener
-              + ", prefetch data: " + prefetchEnabled);
-        }
-      }
-    }
-    watchForData(path);
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("Subscribed data changes for " + path);
-    }
-  }
-
-  private boolean isPrefetchEnabled(IZkDataListener dataListener) {
-    PreFetch preFetch = dataListener.getClass().getAnnotation(PreFetch.class);
-    if (preFetch != null) {
-      return preFetch.enabled();
-    }
-
-    Method callbackMethod = IZkDataListener.class.getMethods()[0];
-    try {
-      Method method = dataListener.getClass().getMethod(callbackMethod.getName(),
-          callbackMethod.getParameterTypes());
-      PreFetch preFetchInMethod = method.getAnnotation(PreFetch.class);
-      if (preFetchInMethod != null) {
-        return preFetchInMethod.enabled();
-      }
-    } catch (NoSuchMethodException e) {
-      LOG.warn("No method " + callbackMethod.getName() + " defined in listener "
-          + dataListener.getClass().getCanonicalName());
-    }
-
-    return true;
-  }
-
-  public void unsubscribeDataChanges(String path, IZkDataListener dataListener) {
-    synchronized (_dataListener) {
-      final Set<IZkDataListenerEntry> listeners = _dataListener.get(path);
-      if (listeners != null) {
-        IZkDataListenerEntry listenerEntry = new IZkDataListenerEntry(dataListener);
-        listeners.remove(listenerEntry);
-      }
-      if (listeners == null || listeners.isEmpty()) {
-        _dataListener.remove(path);
-      }
-    }
-  }
-
-  public void subscribeStateChanges(final IZkStateListener listener) {
-    synchronized (_stateListener) {
-      _stateListener.add(listener);
-    }
-  }
-
-  /**
-   * Subscribes state changes for a {@link org.I0Itec.zkclient.IZkStateListener} listener.
-   *
-   * @deprecated
-   * This is deprecated. It is kept for backwards compatibility. Please use
-   * {@link #subscribeStateChanges(IZkStateListener)}.
-   *
-   * @param listener {@link org.I0Itec.zkclient.IZkStateListener} listener
-   */
-  @Deprecated
-  public void subscribeStateChanges(final org.I0Itec.zkclient.IZkStateListener listener) {
-    subscribeStateChanges(new IZkStateListenerI0ItecImpl(listener));
-  }
-
-  public void unsubscribeStateChanges(IZkStateListener stateListener) {
-    synchronized (_stateListener) {
-      _stateListener.remove(stateListener);
-    }
-  }
-
-  /**
-   * Unsubscribes state changes for a {@link org.I0Itec.zkclient.IZkStateListener} listener.
-   *
-   * @deprecated
-   * This is deprecated. It is kept for backwards compatibility. Please use
-   * {@link #unsubscribeStateChanges(IZkStateListener)}.
-   *
-   * @param stateListener {@link org.I0Itec.zkclient.IZkStateListener} listener
-   */
-  @Deprecated
-  public void unsubscribeStateChanges(org.I0Itec.zkclient.IZkStateListener stateListener) {
-    unsubscribeStateChanges(new IZkStateListenerI0ItecImpl(stateListener));
-  }
-
-  public void unsubscribeAll() {
-    synchronized (_childListener) {
-      _childListener.clear();
-    }
-    synchronized (_dataListener) {
-      _dataListener.clear();
-    }
-    synchronized (_stateListener) {
-      _stateListener.clear();
-    }
-  }
-
-  // </listeners>
-
-  /**
-   * Create a persistent node.
-   * @param path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createPersistent(String path)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    createPersistent(path, false);
-  }
-
-  /**
-   * Create a persistent node and set its ACLs.
-   * @param path
-   * @param createParents
-   *          if true all parent dirs are created as well and no {@link ZkNodeExistsException} is
-   *          thrown in case the
-   *          path already exists
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createPersistent(String path, boolean createParents)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    createPersistent(path, createParents, ZooDefs.Ids.OPEN_ACL_UNSAFE);
-  }
-
-  /**
-   * Create a persistent node and set its ACLs.
-   * @param path
-   * @param acl
-   *          List of ACL permissions to assign to the node
-   * @param createParents
-   *          if true all parent dirs are created as well and no {@link ZkNodeExistsException} is
-   *          thrown in case the
-   *          path already exists
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createPersistent(String path, boolean createParents, List<ACL> acl)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    try {
-      create(path, null, acl, CreateMode.PERSISTENT);
-    } catch (ZkNodeExistsException e) {
-      if (!createParents) {
-        throw e;
-      }
-    } catch (ZkNoNodeException e) {
-      if (!createParents) {
-        throw e;
-      }
-      String parentDir = path.substring(0, path.lastIndexOf('/'));
-      createPersistent(parentDir, createParents, acl);
-      createPersistent(path, createParents, acl);
-    }
-  }
-
-  /**
-   * Create a persistent node.
-   * @param path
-   * @param data
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createPersistent(String path, Object data)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    create(path, data, CreateMode.PERSISTENT);
-  }
-
-  /**
-   * Create a persistent node.
-   * @param path
-   * @param data
-   * @param acl
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createPersistent(String path, Object data, List<ACL> acl) {
-    create(path, data, acl, CreateMode.PERSISTENT);
-  }
-
-  /**
-   * Create a persistent, sequental node.
-   * @param path
-   * @param data
-   * @return create node's path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public String createPersistentSequential(String path, Object data)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    return create(path, data, CreateMode.PERSISTENT_SEQUENTIAL);
-  }
-
-  /**
-   * Create a persistent, sequential node and set its ACL.
-   * @param path
-   * @param acl
-   * @param data
-   * @return create node's path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public String createPersistentSequential(String path, Object data, List<ACL> acl)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    return create(path, data, acl, CreateMode.PERSISTENT_SEQUENTIAL);
-  }
-
-  /**
-   * Create an ephemeral node.
-   * @param path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createEphemeral(final String path)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    create(path, null, CreateMode.EPHEMERAL);
-  }
-
-  /**
-   * Creates an ephemeral node. This ephemeral node is created by the expected(passed-in) ZK session.
-   * If the expected session does not match the current ZK session, the node will not be created.
-   *
-   * @param path path of the node
-   * @param sessionId expected session id of the ZK connection. If the session id of current ZK
-   *                  connection does not match the expected session id, ephemeral creation will
-   *                  fail
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createEphemeral(final String path, final String sessionId)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    createEphemeral(path, null, sessionId);
-  }
-
-  /**
-   * Create an ephemeral node and set its ACL.
-   * @param path
-   * @param acl
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createEphemeral(final String path, final List<ACL> acl)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    create(path, null, acl, CreateMode.EPHEMERAL);
-  }
-
-  /**
-   * Creates an ephemeral node and set its ACL. This ephemeral node is created by the
-   * expected(passed-in) ZK session. If the expected session does not match the current ZK session,
-   * the node will not be created.
-   *
-   * @param path path of the ephemeral node
-   * @param acl a list of ACL for the ephemeral node.
-   * @param sessionId expected session id of the ZK connection. If the session id of current ZK
-   *                  connection does not match the expected session id, ephemeral creation will
-   *                  fail.
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createEphemeral(final String path, final List<ACL> acl, final String sessionId)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    create(path, null, acl, CreateMode.EPHEMERAL, sessionId);
-  }
-
-  /**
-   * Create a node.
-   * @param path
-   * @param data
-   * @param mode
-   * @return create node's path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public String create(final String path, Object data, final CreateMode mode)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    return create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, mode);
-  }
-
-  /**
-   * Create a node with ACL.
-   * @param path
-   * @param datat
-   * @param acl
-   * @param mode
-   * @return create node's path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public String create(final String path, Object datat, final List<ACL> acl, final CreateMode mode)
-      throws IllegalArgumentException, ZkException {
-    return create(path, datat, acl, mode, null);
-  }
-
-  /**
-   * Creates a node and returns the actual path of the created node.
-   *
-   * Given an expected non-null session id, if the node is successfully created, it is guaranteed to
-   * be created in the expected(passed-in) session.
-   *
-   * If the expected session is expired, which means the expected session does not match the current
-   * session of ZK connection, the node will not be created.
-   *
-   * @param path the path where you want the node to be created
-   * @param dataObject data of the node
-   * @param acl list of ACL for the node
-   * @param mode {@link CreateMode} of the node
-   * @param expectedSessionId the expected session ID of the ZK connection. It is not necessarily the
-   *                  session ID of current ZK Connection. If the expected session ID is NOT null,
-   *                  the node is guaranteed to be created in the expected session, or creation is
-   *                  failed if the expected session id doesn't match current connected zk session.
-   *                  If the session id is null, it means the create operation is NOT session aware.
-   * @return path of the node created
-   * @throws IllegalArgumentException if called from anything else except the ZooKeeper event thread
-   * @throws ZkException if any zookeeper exception occurs
-   */
-  private String create(final String path, final Object dataObject, final List<ACL> acl,
-      final CreateMode mode, final String expectedSessionId)
-      throws IllegalArgumentException, ZkException {
-    if (path == null) {
-      throw new NullPointerException("Path must not be null.");
-    }
-    if (acl == null || acl.size() == 0) {
-      throw new NullPointerException("Missing value for ACL");
-    }
-    long startT = System.currentTimeMillis();
-    try {
-      final byte[] dataBytes = dataObject == null ? null : serialize(dataObject, path);
-      checkDataSizeLimit(dataBytes);
-
-      final String actualPath = retryUntilConnected(() -> {
-        ZooKeeper zooKeeper = ((ZkConnection) getConnection()).getZookeeper();
-
-        /*
-         * 1. If operation is session aware, we have to check whether or not the
-         * passed-in(expected) session id matches actual session's id.
-         * If not, ephemeral node creation is failed. This validation is
-         * critical to guarantee the ephemeral node created by the expected ZK session.
-         *
-         * 2. Otherwise, the operation is NOT session aware.
-         * In this case, we will use the actual zookeeper session to create the node.
-         */
-        if (isSessionAwareOperation(expectedSessionId, mode)) {
-          acquireEventLock();
-          try {
-            final String actualSessionId = ZKUtil.toHexSessionId(zooKeeper.getSessionId());
-            if (!actualSessionId.equals(expectedSessionId)) {
-              throw new ZkSessionMismatchedException(
-                  "Failed to create ephemeral node! There is a session id mismatch. Expected: "
-                      + expectedSessionId + ". Actual: " + actualSessionId);
-            }
-
-            /*
-             * Cache the zookeeper reference and make sure later zooKeeper.create() is being run
-             * under this zookeeper connection. This is to avoid locking zooKeeper.create() which
-             * may cause potential performance issue.
-             */
-            zooKeeper = ((ZkConnection) getConnection()).getZookeeper();
-          } finally {
-            getEventLock().unlock();
-          }
-        }
-
-        return zooKeeper.create(path, dataBytes, acl, mode);
-      });
-
-      record(path, dataBytes, startT, ZkClientMonitor.AccessType.WRITE);
-      return actualPath;
-    } catch (Exception e) {
-      recordFailure(path, ZkClientMonitor.AccessType.WRITE);
-      throw e;
-    } finally {
-      long endT = System.currentTimeMillis();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("create, path: " + path + ", time: " + (endT - startT) + " ms");
-      }
-    }
-  }
-
-  /**
-   * Create an ephemeral node.
-   * @param path
-   * @param data
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createEphemeral(final String path, final Object data)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    create(path, data, CreateMode.EPHEMERAL);
-  }
-
-  /**
-   * Creates an ephemeral node. Given an expected non-null session id, if the ephemeral
-   * node is successfully created, it is guaranteed to be in the expected(passed-in) session.
-   *
-   * If the expected session is expired, which means the expected session does not match the session
-   * of current ZK connection, the ephemeral node will not be created.
-   * If connection is timed out or interrupted, exception is thrown.
-   *
-   * @param path path of the ephemeral node being created
-   * @param data data of the ephemeral node being created
-   * @param sessionId the expected session ID of the ZK connection. It is not necessarily the
-   *                  session ID of current ZK Connection. If the expected session ID is NOT null,
-   *                  the node is guaranteed to be created in the expected session, or creation is
-   *                  failed if the expected session id doesn't match current connected zk session.
-   *                  If the session id is null, it means the operation is NOT session aware
-   *                  and the node will be created by current ZK session.
-   * @throws ZkInterruptedException if operation is interrupted, or a required reconnection gets
-   *         interrupted
-   * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread
-   * @throws ZkException if any ZooKeeper exception occurs
-   * @throws RuntimeException if any other exception occurs
-   */
-  public void createEphemeral(final String path, final Object data, final String sessionId)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, sessionId);
-  }
-
-  /**
-   * Create an ephemeral node.
-   * @param path
-   * @param data
-   * @param acl
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createEphemeral(final String path, final Object data, final List<ACL> acl)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    create(path, data, acl, CreateMode.EPHEMERAL);
-  }
-
-  /**
-   * Creates an ephemeral node in an expected ZK session. Given an expected non-null session id,
-   * if the ephemeral node is successfully created, it is guaranteed to be in the expected session.
-   * If the expected session is expired, which means the expected session does not match the session
-   * of current ZK connection, the ephemeral node will not be created.
-   * If connection is timed out or interrupted, exception is thrown.
-   *
-   * @param path path of the ephemeral node being created
-   * @param data data of the ephemeral node being created
-   * @param acl list of ACL for the ephemeral node
-   * @param sessionId the expected session ID of the ZK connection. It is not necessarily the
-   *                  session ID of current ZK Connection. If the expected session ID is NOT null,
-   *                  the node is guaranteed to be created in the expected session, or creation is
-   *                  failed if the expected session id doesn't match current connected zk session.
-   *                  If the session id is null, it means the create operation is NOT session aware
-   *                  and the node will be created by current ZK session.
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public void createEphemeral(final String path, final Object data, final List<ACL> acl,
-      final String sessionId)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    create(path, data, acl, CreateMode.EPHEMERAL, sessionId);
-  }
-
-  /**
-   * Create an ephemeral, sequential node.
-   * @param path
-   * @param data
-   * @return created path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public String createEphemeralSequential(final String path, final Object data)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    return create(path, data, CreateMode.EPHEMERAL_SEQUENTIAL);
-  }
-
-  /**
-   * Creates an ephemeral, sequential node with ACL in an expected ZK session.
-   * Given an expected non-null session id, if the ephemeral node is successfully created,
-   * it is guaranteed to be in the expected session.
-   * If the expected session is expired, which means the expected session does not match the session
-   * of current ZK connection, the ephemeral node will not be created.
-   * If connection is timed out or interrupted, exception is thrown.
-   *
-   * @param path path of the node
-   * @param data data of the node
-   * @param acl list of ACL for the node
-   * @return created path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public String createEphemeralSequential(final String path, final Object data, final List<ACL> acl,
-      final String sessionId)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    return create(path, data, acl, CreateMode.EPHEMERAL_SEQUENTIAL, sessionId);
-  }
-
-  /**
-   * Creates an ephemeral, sequential node. Given an expected non-null session id,
-   * if the ephemeral node is successfully created, it is guaranteed to be in the expected session.
-   * If the expected session is expired, which means the expected session does not match the session
-   * of current ZK connection, the ephemeral node will not be created.
-   * If connection is timed out or interrupted, exception is thrown.
-   *
-   * @param path path of the node
-   * @param data data of the node
-   * @param sessionId the expected session ID of the ZK connection. It is not necessarily the
-   *                  session ID of current ZK Connection. If the expected session ID is NOT null,
-   *                  the node is guaranteed to be created in the expected session, or creation is
-   *                  failed if the expected session id doesn't match current connected zk session.
-   *                  If the session id is null, it means the create operation is NOT session aware
-   *                  and the node will be created by current ZK session.
-   * @return created path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public String createEphemeralSequential(final String path, final Object data,
-      final String sessionId)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    return create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL,
-        sessionId);
-  }
-
-  /**
-   * Create an ephemeral, sequential node with ACL.
-   * @param path
-   * @param data
-   * @param acl
-   * @return created path
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs
-   */
-  public String createEphemeralSequential(final String path, final Object data, final List<ACL> acl)
-      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
-    return create(path, data, acl, CreateMode.EPHEMERAL_SEQUENTIAL);
-  }
-
-  @Override
-  public void process(WatchedEvent event) {
-    long notificationTime = System.currentTimeMillis();
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("Received event: " + event);
-    }
-    _zookeeperEventThread = Thread.currentThread();
-
-    boolean stateChanged = event.getPath() == null;
-    boolean znodeChanged = event.getPath() != null;
-    boolean dataChanged = event.getType() == Event.EventType.NodeDataChanged
-        || event.getType() == Event.EventType.NodeDeleted
-        || event.getType() == Event.EventType.NodeCreated
-        || event.getType() == Event.EventType.NodeChildrenChanged;
-    if (event.getType() == EventType.NodeDeleted) {
-      LOG.debug("Path {} is deleted", event.getPath());
-    }
-
-    getEventLock().lock();
-    try {
-      // We might have to install child change event listener if a new node was created
-      if (getShutdownTrigger()) {
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("ignoring event '{" + event.getType() + " | " + event.getPath()
-              + "}' since shutdown triggered");
-        }
-        return;
-      }
-      if (stateChanged) {
-        processStateChanged(event);
-      }
-      if (dataChanged) {
-        processDataOrChildChange(event, notificationTime);
-      }
-    } finally {
-      if (stateChanged) {
-        getEventLock().getStateChangedCondition().signalAll();
-
-        // If the session expired we have to signal all conditions, because watches might have been
-        // removed and
-        // there is no guarantee that those
-        // conditions will be signaled at all after an Expired event
-        // TODO PVo write a test for this
-        if (event.getState() == KeeperState.Expired) {
-          getEventLock().getZNodeEventCondition().signalAll();
-          getEventLock().getDataChangedCondition().signalAll();
-        }
-      }
-      if (znodeChanged) {
-        getEventLock().getZNodeEventCondition().signalAll();
-      }
-      if (dataChanged) {
-        getEventLock().getDataChangedCondition().signalAll();
-      }
-      getEventLock().unlock();
-
-      // update state change counter.
-      recordStateChange(stateChanged, dataChanged);
-
-      if (LOG.isDebugEnabled()) {
-        LOG.debug("Leaving process event");
-      }
-    }
-  }
-
-  private void fireAllEvents() {
-    //TODO: During handling new session, if the path is deleted, watcher leakage could still happen
-    for (Entry<String, Set<IZkChildListener>> entry : _childListener.entrySet()) {
-      fireChildChangedEvents(entry.getKey(), entry.getValue(), true);
-    }
-    for (Entry<String, Set<IZkDataListenerEntry>> entry : _dataListener.entrySet()) {
-      fireDataChangedEvents(entry.getKey(), entry.getValue(), OptionalLong.empty(), true);
-    }
-  }
-
-  public List<String> getChildren(String path) {
-    return getChildren(path, hasListeners(path));
-  }
-
-  protected List<String> getChildren(final String path, final boolean watch) {
-    long startT = System.currentTimeMillis();
-    try {
-      List<String> children = retryUntilConnected(new Callable<List<String>>() {
-        @Override
-        public List<String> call() throws Exception {
-          return getConnection().getChildren(path, watch);
-        }
-      });
-      record(path, null, startT, ZkClientMonitor.AccessType.READ);
-      return children;
-    } catch (ZkNoNodeException e) {
-      record(path, null, startT, ZkClientMonitor.AccessType.READ);
-      throw e;
-    } catch (Exception e) {
-      recordFailure(path, ZkClientMonitor.AccessType.READ);
-      throw e;
-    } finally {
-      long endT = System.currentTimeMillis();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("getChildren, path: " + path + ", time: " + (endT - startT) + " ms");
-      }
-    }
-  }
-
-  /**
-   * Counts number of children for the given path.
-   * @param path
-   * @return number of children or 0 if path does not exist.
-   */
-  public int countChildren(String path) {
-    try {
-      return getChildren(path).size();
-    } catch (ZkNoNodeException e) {
-      return 0;
-    }
-  }
-
-  public boolean exists(final String path) {
-    return exists(path, hasListeners(path));
-  }
-
-  protected boolean exists(final String path, final boolean watch) {
-    long startT = System.currentTimeMillis();
-    try {
-      boolean exists = retryUntilConnected(new Callable<Boolean>() {
-        @Override
-        public Boolean call() throws Exception {
-          return getConnection().exists(path, watch);
-        }
-      });
-      record(path, null, startT, ZkClientMonitor.AccessType.READ);
-      return exists;
-    } catch (ZkNoNodeException e) {
-      record(path, null, startT, ZkClientMonitor.AccessType.READ);
-      throw e;
-    } catch (Exception e) {
-      recordFailure(path, ZkClientMonitor.AccessType.READ);
-      throw e;
-    } finally {
-      long endT = System.currentTimeMillis();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("exists, path: " + path + ", time: " + (endT - startT) + " ms");
-      }
-    }
-  }
-
-  public Stat getStat(final String path) {
-    return getStat(path, false);
-  }
-
-  private Stat getStat(final String path, final boolean watch) {
-    long startT = System.currentTimeMillis();
-    try {
-      Stat stat = retryUntilConnected(
-          () -> ((ZkConnection) getConnection()).getZookeeper().exists(path, watch));
-      record(path, null, startT, ZkClientMonitor.AccessType.READ);
-      return stat;
-    } catch (ZkNoNodeException e) {
-      record(path, null, startT, ZkClientMonitor.AccessType.READ);
-      throw e;
-    } catch (Exception e) {
-      recordFailure(path, ZkClientMonitor.AccessType.READ);
-      throw e;
-    } finally {
-      long endT = System.currentTimeMillis();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("exists, path: " + path + ", time: " + (endT - startT) + " ms");
-      }
-    }
-  }
-
-  protected void processStateChanged(WatchedEvent event) {
-    LOG.info("zookeeper state changed (" + event.getState() + ")");
-    setCurrentState(event.getState());
-    if (getShutdownTrigger()) {
-      return;
-    }
-
-    fireStateChangedEvent(event.getState());
-
-    if (!isManagingZkConnection()) {
-      return;
-    }
-
-    if (event.getState() == KeeperState.SyncConnected) {
-      if (!_isNewSessionEventFired && !"0".equals(getHexSessionId())) {
-        /*
-         * Before the new zookeeper instance is connected to the zookeeper service and its session
-         * is established, its session id is 0.
-         * New session event is not fired until the new zookeeper session receives the first
-         * SyncConnected state(the zookeeper session is established).
-         * Now the session id is available and non-zero, and we can fire new session events.
-         */
-        fireNewSessionEvents();
-        /*
-         * Set it true to avoid firing events again for the same session next time
-         * when SyncConnected events are received.
-         */
-        _isNewSessionEventFired = true;
-
-        /*
-         * With this first SyncConnected state, we just get connected to zookeeper service after
-         * reconnecting when the session expired. Because previous session expired, we also have to
-         * notify all listeners that something might have changed.
-         */
-        fireAllEvents();
-      }
-    } else if (event.getState() == KeeperState.Expired) {
-      reconnectOnExpiring();
-    }
-  }
-
-  private void reconnectOnExpiring() {
-    int retryCount = 0;
-    ExponentialBackoffStrategy retryStrategy =
-        new ExponentialBackoffStrategy(MAX_RECONNECT_INTERVAL_MS, true);
-
-    Exception reconnectException = new ZkException("Shutdown triggered.");
-    while (!isClosed()) {
-      try {
-        reconnect();
-        return;
-      } catch (ZkInterruptedException interrupt) {
-        reconnectException = interrupt;
-        break;
-      } catch (Exception e) {
-        reconnectException = e;
-        long waitInterval = retryStrategy.getNextWaitInterval(retryCount++);
-        LOG.warn("ZkClient reconnect on expiring failed. Will retry after {} ms", waitInterval, e);
-        try {
-          Thread.sleep(waitInterval);
-        } catch (InterruptedException ex) {
-          reconnectException = ex;
-          break;
-        }
-      }
-    }
-
-    LOG.info("Unable to re-establish connection. Notifying consumer of the following exception: ",
-        reconnectException);
-    fireSessionEstablishmentError(reconnectException);
-  }
-
-  private void reconnect() {
-    getEventLock().lock();
-    try {
-      ZkConnection connection = ((ZkConnection) getConnection());
-      connection.reconnect(this);
-      _isNewSessionEventFired = false;
-    } catch (InterruptedException e) {
-      throw new ZkInterruptedException(e);
-    } finally {
-      getEventLock().unlock();
-    }
-  }
-
-  private void fireNewSessionEvents() {
-    final String sessionId = getHexSessionId();
-    for (final IZkStateListener stateListener : _stateListener) {
-      _eventThread.send(new ZkEvent("New session event sent to " + stateListener, sessionId) {
-
-        @Override
-        public void run() throws Exception {
-          stateListener.handleNewSession(sessionId);
-        }
-      });
-    }
-  }
-
-  protected void fireStateChangedEvent(final KeeperState state) {
-    final String sessionId = getHexSessionId();
-    for (final IZkStateListener stateListener : _stateListener) {
-      final String description = "State changed to " + state + " sent to " + stateListener;
-      _eventThread.send(new ZkEvent(description, sessionId) {
-
-        @Override
-        public void run() throws Exception {
-          stateListener.handleStateChanged(state);
-        }
-      });
-    }
-  }
-
-  private void fireSessionEstablishmentError(final Throwable error) {
-    for (final IZkStateListener stateListener : _stateListener) {
-      _eventThread
-          .send(new ZkEvent("Session establishment error(" + error + ") sent to " + stateListener) {
-
-            @Override
-            public void run() throws Exception {
-              stateListener.handleSessionEstablishmentError(error);
-            }
-          });
-    }
-  }
-
-  private boolean hasListeners(String path) {
-    Set<IZkDataListenerEntry> dataListeners = _dataListener.get(path);
-    if (dataListeners != null && dataListeners.size() > 0) {
-      return true;
-    }
-    Set<IZkChildListener> childListeners = _childListener.get(path);
-    if (childListeners != null && childListeners.size() > 0) {
-      return true;
-    }
-    return false;
-  }
-
-  /**
-   * Delete the path as well as all its children.
-   * This method is deprecated, please use {@link #deleteRecursively(String)}} instead
-   * @param path ZK path
-   * @return true if successfully deleted all children, and the given path, else false
-   */
-  @Deprecated
-  public boolean deleteRecursive(String path) {
-    try {
-      deleteRecursively(path);
-      return true;
-    } catch (HelixException e) {
-      LOG.error("Failed to recursively delete path " + path, e);
-      return false;
-    }
-  }
-
-  /**
-   * Delete the path as well as all its children.
-   * @param path
-   * @throws HelixException
-   */
-  public void deleteRecursively(String path) throws HelixException {
-    List<String> children;
-    try {
-      children = getChildren(path, false);
-    } catch (ZkNoNodeException e) {
-      // if the node to be deleted does not exist, treat it as success.
-      return;
-    }
-
-    for (String subPath : children) {
-      deleteRecursively(path + "/" + subPath);
-    }
-
-    // delete() function call will return true if successful, false if the path does not
-    // exist (in this context, it should be treated as successful), and throw exception
-    // if there is any other failure case.
-    try {
-      delete(path);
-    } catch (Exception e) {
-      LOG.error("Failed to delete " + path, e);
-      throw new HelixException("Failed to delete " + path, e);
-    }
-  }
-
-  private void processDataOrChildChange(WatchedEvent event, long notificationTime) {
-    final String path = event.getPath();
-    final boolean pathExists = event.getType() != EventType.NodeDeleted;
-
-    if (event.getType() == EventType.NodeChildrenChanged || event.getType() == EventType.NodeCreated
-        || event.getType() == EventType.NodeDeleted) {
-      Set<IZkChildListener> childListeners = _childListener.get(path);
-      if (childListeners != null && !childListeners.isEmpty()) {
-        // TODO recording child changed event propagation latency as well. Note this change will
-        // introduce additional ZK access.
-        fireChildChangedEvents(path, childListeners, pathExists);
-      }
-    }
-
-    if (event.getType() == EventType.NodeDataChanged || event.getType() == EventType.NodeDeleted
-        || event.getType() == EventType.NodeCreated) {
-      Set<IZkDataListenerEntry> listeners = _dataListener.get(path);
-      if (listeners != null && !listeners.isEmpty()) {
-        fireDataChangedEvents(event.getPath(), listeners, OptionalLong.of(notificationTime), pathExists);
-      }
-    }
-  }
-
-  private void fireDataChangedEvents(final String path, Set<IZkDataListenerEntry> listeners,
-      final OptionalLong notificationTime, boolean pathExists) {
-    try {
-      final ZkPathStatRecord pathStatRecord = new ZkPathStatRecord(path);
-      // Trigger listener callbacks
-      for (final IZkDataListenerEntry listener : listeners) {
-        _eventThread.send(new ZkEvent("Data of " + path + " changed sent to "
-            + listener.getDataListener() + " prefetch data: " + listener.isPrefetchData()) {
-          @Override
-          public void run() throws Exception {
-            if (!pathStatRecord.pathChecked()) {
-              // getStat will re-install watcher only when the path exists
-              pathStatRecord.recordPathStat(getStat(path, pathExists), notificationTime);
-            }
-            if (!pathStatRecord.pathExists()) {
-              listener.getDataListener().handleDataDeleted(path);
-            } else {
-              Object data = null;
-              if (listener.isPrefetchData()) {
-                if (LOG.isDebugEnabled()) {
-                  LOG.debug("Prefetch data for path: {}", path);
-                }
-                try {
-                  // TODO: the data is redundantly read multiple times when multiple listeners exist
-                  data = readData(path, null, true);
-                } catch (ZkNoNodeException e) {
-                  LOG.warn("Prefetch data for path: {} failed.", path, e);
-                  listener.getDataListener().handleDataDeleted(path);
-                  return;
-                }
-              }
-              listener.getDataListener().handleDataChange(path, data);
-            }
-          }
-        });
-      }
-    } catch (Exception e) {
-      LOG.error("Failed to fire data changed event for path: {}", path, e);
-    }
-  }
-
-  private void fireChildChangedEvents(final String path, Set<IZkChildListener> childListeners, boolean pathExists) {
-    try {
-      final ZkPathStatRecord pathStatRecord = new ZkPathStatRecord(path);
-      for (final IZkChildListener listener : childListeners) {
-        _eventThread.send(new ZkEvent("Children of " + path + " changed sent to " + listener) {
-          @Override
-          public void run() throws Exception {
-            if (!pathStatRecord.pathChecked()) {
-              pathStatRecord.recordPathStat(getStat(path, hasListeners(path) && pathExists),
-                  OptionalLong.empty());
-            }
-            List<String> children = null;
-            if (pathStatRecord.pathExists()) {
-              try {
-                //TODO: duplicate reads when multiple child listener exists
-                children = getChildren(path);
-              } catch (ZkNoNodeException e) {
-                LOG.warn("Get children under path: {} failed.", path, e);
-                // Continue trigger the change handler
-              }
-            }
-            listener.handleChildChange(path, children);
-          }
-        });
-      }
-    } catch (Exception e) {
-      LOG.error("Failed to fire child changed event. Unable to getChildren.", e);
-    }
-  }
-
-  public boolean waitUntilExists(String path, TimeUnit timeUnit, long time)
-      throws ZkInterruptedException {
-    Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time));
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("Waiting until znode '" + path + "' becomes available.");
-    }
-    if (exists(path)) {
-      return true;
-    }
-    acquireEventLock();
-    try {
-      while (!exists(path, true)) {
-        boolean gotSignal = getEventLock().getZNodeEventCondition().awaitUntil(timeout);
-        if (!gotSignal) {
-          return false;
-        }
-      }
-      return true;
-    } catch (InterruptedException e) {
-      throw new ZkInterruptedException(e);
-    } finally {
-      getEventLock().unlock();
-    }
-  }
-
-  public IZkConnection getConnection() {
-    return _connection;
-  }
-
-  public long waitForEstablishedSession(long timeout, TimeUnit timeUnit) {
-    validateCurrentThread();
-
-    acquireEventLock();
-    try {
-      if (!waitForKeeperState(KeeperState.SyncConnected, timeout, timeUnit)) {
-        throw new ZkTimeoutException("Waiting to be connected to ZK server has timed out.");
-      }
-      // Reading session ID before unlocking event lock is critical to guarantee the established
-      // session's ID won't change.
-      return getSessionId();
-    } finally {
-      getEventLock().unlock();
-    }
-  }
-
-  public boolean waitUntilConnected(long time, TimeUnit timeUnit) throws ZkInterruptedException {
-    return waitForKeeperState(KeeperState.SyncConnected, time, timeUnit);
-  }
-
-  public boolean waitForKeeperState(KeeperState keeperState, long time, TimeUnit timeUnit)
-      throws ZkInterruptedException {
-    validateCurrentThread();
-    Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time));
-
-    LOG.debug("Waiting for keeper state " + keeperState);
-    acquireEventLock();
-    try {
-      boolean stillWaiting = true;
-      while (_currentState != keeperState) {
-        if (!stillWaiting) {
-          return false;
-        }
-        stillWaiting = getEventLock().getStateChangedCondition().awaitUntil(timeout);
-      }
-      LOG.debug("State is " + (_currentState == null ? "CLOSED" : _currentState));
-      return true;
-    } catch (InterruptedException e) {
-      throw new ZkInterruptedException(e);
-    } finally {
-      getEventLock().unlock();
-    }
-  }
-
-  private void acquireEventLock() {
-    try {
-      getEventLock().lockInterruptibly();
-    } catch (InterruptedException e) {
-      throw new ZkInterruptedException(e);
-    }
-  }
-
-  /**
-   * @param <T>
-   * @param callable
-   * @return result of Callable
-   * @throws ZkInterruptedException
-   *           if operation was interrupted, or a required reconnection got interrupted
-   * @throws IllegalArgumentException
-   *           if called from anything except the ZooKeeper event thread
-   * @throws ZkException
-   *           if any ZooKeeper exception occurred
-   * @throws RuntimeException
-   *           if any other exception occurs from invoking the Callable
-   */
-  public <T> T retryUntilConnected(final Callable<T> callable)
-      throws IllegalArgumentException, ZkException {
-    if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {
-      throw new IllegalArgumentException("Must not be done in the zookeeper event thread.");
-    }
-    final long operationStartTime = System.currentTimeMillis();
-    if (_monitor != null) {
-      _monitor.increaseOutstandingRequestGauge();
-    }
-    try {
-      while (true) {
-        // Because ConnectionLossException and SessionExpiredException are caught but not thrown,
-        // we don't know what causes retry. This is used to record which one of the two exceptions
-        // causes retry in ZkTimeoutException.
-        // This also helps the test testConnectionLossWhileCreateEphemeral.
-        KeeperException.Code retryCauseCode;
-
-        if (isClosed()) {
-          throw new IllegalStateException("ZkClient already closed!");
-        }
-        try {
-          final ZkConnection zkConnection = (ZkConnection) getConnection();
-          // Validate that the connection is not null before trigger callback
-          if (zkConnection == null || zkConnection.getZookeeper() == null) {
-            throw new IllegalStateException(
-                "ZkConnection is in invalid state! Please close this ZkClient and create new client.");
-          }
-          return callable.call();
-        } catch (ConnectionLossException e) {
-          retryCauseCode = e.code();
-          // we give the event thread some time to update the status to 'Disconnected'
-          Thread.yield();
-          waitForRetry();
-        } catch (SessionExpiredException e) {
-          retryCauseCode = e.code();
-          // we give the event thread some time to update the status to 'Expired'
-          Thread.yield();
-          waitForRetry();
-        } catch (ZkSessionMismatchedException e) {
-          throw e;
-        } catch (KeeperException e) {
-          throw ZkException.create(e);
-        } catch (InterruptedException e) {
-          throw new ZkInterruptedException(e);
-        } catch (Exception e) {
-          throw ExceptionUtil.convertToRuntimeException(e);
-        }
-        // before attempting a retry, check whether retry timeout has elapsed
-        if (System.currentTimeMillis() - operationStartTime > _operationRetryTimeoutInMillis) {
-          throw new ZkTimeoutException("Operation cannot be retried because of retry timeout ("
-              + _operationRetryTimeoutInMillis + " milli seconds). Retry was caused by "
-              + retryCauseCode);
-        }
-      }
-    } finally {
-      if (_monitor != null) {
-        _monitor.decreaseOutstandingRequestGauge();
-      }
-    }
-  }
-
-  private void waitForRetry() {
-    waitUntilConnected(_operationRetryTimeoutInMillis, TimeUnit.MILLISECONDS);
-  }
-
-  public void setCurrentState(KeeperState currentState) {
-    getEventLock().lock();
-    try {
-      _currentState = currentState;
-    } finally {
-      getEventLock().unlock();
-    }
-  }
-
-  /**
-   * Returns a mutex all zookeeper events are synchronized aginst. So in case you need to do
-   * something without getting
-   * any zookeeper event interruption synchronize against this mutex. Also all threads waiting on
-   * this mutex object
-   * will be notified on an event.
-   * @return the mutex.
-   */
-  public ZkLock getEventLock() {
-    return _zkEventLock;
-  }
-
-  /**
-   * Delete the given path. Path should not have any children or the deletion will fail.
-   * This function will throw exception if we fail to delete an existing path
-   * @param path
-   * @return true if path is successfully deleted, false if path does not exist
-   */
-  public boolean delete(final String path) {
-    long startT = System.currentTimeMillis();
-    boolean success;
-    try {
-      try {
-        retryUntilConnected(new Callable<Object>() {
-
-          @Override
-          public Object call() throws Exception {
-            getConnection().delete(path);
-            return null;
-          }
-        });
-        success = true;
-      } catch (ZkNoNodeException e) {
-        success = false;
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Failed to delete path " + path + ", znode does not exist!");
-        }
-      }
-      record(path, null, startT, ZkClientMonitor.AccessType.WRITE);
-    } catch (Exception e) {
-      recordFailure(path, ZkClientMonitor.AccessType.WRITE);
-      LOG.warn("Failed to delete path " + path + "! " + e);
-      throw e;
-    } finally {
-      long endT = System.currentTimeMillis();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("delete, path: " + path + ", time: " + (endT - startT) + " ms");
-      }
-    }
-    return success;
-  }
-
-  public void setZkSerializer(ZkSerializer zkSerializer) {
-    _pathBasedZkSerializer = new BasicZkSerializer(zkSerializer);
-  }
-
-  public void setZkSerializer(PathBasedZkSerializer zkSerializer) {
-    _pathBasedZkSerializer = zkSerializer;
-  }
-
-  public PathBasedZkSerializer getZkSerializer() {
-    return _pathBasedZkSerializer;
-  }
-
-  public byte[] serialize(Object data, String path) {
-    return _pathBasedZkSerializer.serialize(data, path);
-  }
-
-  @SuppressWarnings("unchecked")
-  public <T extends Object> T deserialize(byte[] data, String path) {
-    if (data == null) {
-      return null;
-    }
-    return (T) _pathBasedZkSerializer.deserialize(data, path);
-  }
-
-  @SuppressWarnings("unchecked")
-  public <T extends Object> T readData(String path) {
-    return (T) readData(path, false);
-  }
-
-  @SuppressWarnings("unchecked")
-  public <T extends Object> T readData(String path, boolean returnNullIfPathNotExists) {
-    T data = null;
-    try {
-      data = (T) readData(path, null);
-    } catch (ZkNoNodeException e) {
-      if (!returnNullIfPathNotExists) {
-        throw e;
-      }
-    }
-    return data;
-  }
-
-  @SuppressWarnings("unchecked")
-  public <T extends Object> T readData(String path, Stat stat) {
-    return (T) readData(path, stat, hasListeners(path));
-  }
-
-  @SuppressWarnings("unchecked")
-  public <T extends Object> T readData(final String path, final Stat stat, final boolean watch) {
-    long startT = System.currentTimeMillis();
-    byte[] data = null;
-    try {
-      data = retryUntilConnected(new Callable<byte[]>() {
-
-        @Override
-        public byte[] call() throws Exception {
-          return getConnection().readData(path, stat, watch);
-        }
-      });
-      record(path, data, startT, ZkClientMonitor.AccessType.READ);
-      return (T) deserialize(data, path);
-    } catch (ZkNoNodeException e) {
-      record(path, data, startT, ZkClientMonitor.AccessType.READ);
-      throw e;
-    } catch (Exception e) {
-      recordFailure(path, ZkClientMonitor.AccessType.READ);
-      throw e;
-    } finally {
-      long endT = System.currentTimeMillis();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("getData, path: " + path + ", time: " + (endT - startT) + " ms");
-      }
-    }
-  }
-
-  @SuppressWarnings("unchecked")
-  public <T extends Object> T readDataAndStat(String path, Stat stat,
-      boolean returnNullIfPathNotExists) {
-    T data = null;
-    try {
-      data = readData(path, stat);
-    } catch (ZkNoNodeException e) {
-      if (!returnNullIfPathNotExists) {
-        throw e;
-      }
-    }
-    return data;
-  }
-
-  public void writeData(String path, Object object) {
-    writeData(path, object, -1);
-  }
-
-  /**
-   * Updates data of an existing znode. The current content of the znode is passed to the
-   * {@link DataUpdater} that is
-   * passed into this method, which returns the new content. The new content is only written back to
-   * ZooKeeper if
-   * nobody has modified the given znode in between. If a concurrent change has been detected the
-   * new data of the
-   * znode is passed to the updater once again until the new contents can be successfully written
-   * back to ZooKeeper.
-   * @param <T>
-   * @param path
-   *          The path of the znode.
-   * @param updater
-   *          Updater that creates the new contents.
-   */
-  @SuppressWarnings("unchecked")
-  public <T extends Object> void updateDataSerialized(String path, DataUpdater<T> updater) {
-    Stat stat = new Stat();
-    boolean retry;
-    do {
-      retry = false;
-      try {
-        T oldData = (T) readData(path, stat);
-        T newData = updater.update(oldData);
-        writeData(path, newData, stat.getVersion());
-      } catch (ZkBadVersionException e) {
-        retry = true;
-      }
-    } while (retry);
-  }
-
-  public void writeData(final String path, Object datat, final int expectedVersion) {
-    writeDataReturnStat(path, datat, expectedVersion);
-  }
-
-  public Stat writeDataReturnStat(final String path, Object datat, final int expectedVersion) {
-    long startT = System.currentTimeMillis();
-    try {
-      final byte[] data = serialize(datat, path);
-      checkDataSizeLimit(data);
-      final Stat stat = (Stat) retryUntilConnected(new Callable<Object>() {
-        @Override
-        public Object call() throws Exception {
-          return getConnection().writeDataReturnStat(path, data, expectedVersion);
-        }
-      });
-      record(path, data, startT, ZkClientMonitor.AccessType.WRITE);
-      return stat;
-    } catch (Exception e) {
-      recordFailure(path, ZkClientMonitor.AccessType.WRITE);
-      throw e;
-    } finally {
-      long endT = System.currentTimeMillis();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("setData, path: " + path + ", time: " + (endT - startT) + " ms");
-      }
-    }
-  }
-
-  public Stat writeDataGetStat(final String path, Object datat, final int expectedVersion) {
-    return writeDataReturnStat(path, datat, expectedVersion);
-  }
-
-  public void asyncCreate(final String path, Object datat, final CreateMode mode,
-      final ZkAsyncCallbacks.CreateCallbackHandler cb) {
-    final long startT = System.currentTimeMillis();
-    final byte[] data = (datat == null ? null : serialize(datat, path));
-    retryUntilConnected(new Callable<Object>() {
-      @Override
-      public Object call() throws Exception {
-        ((ZkConnection) getConnection()).getZookeeper().create(path, data,
-            ZooDefs.Ids.OPEN_ACL_UNSAFE,
-            // Arrays.asList(DEFAULT_ACL),
-            mode, cb, new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT,
-                data == null ? 0 : data.length, false));
-        return null;
-      }
-    });
-  }
-
-  // Async Data Accessors
-  public void asyncSetData(final String path, Object datat, final int version,
-      final ZkAsyncCallbacks.SetDataCallbackHandler cb) {
-    final long startT = System.currentTimeMillis();
-    final byte[] data = serialize(datat, path);
-    retryUntilConnected(new Callable<Object>() {
-      @Override
-      public Object call() throws Exception {
-        ((ZkConnection) getConnection()).getZookeeper().setData(path, data, version, cb,
-            new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT,
-                data == null ? 0 : data.length, false));
-        return null;
-      }
-    });
-  }
-
-  public void asyncGetData(final String path, final ZkAsyncCallbacks.GetDataCallbackHandler cb) {
-    final long startT = System.currentTimeMillis();
-    retryUntilConnected(new Callable<Object>() {
-      @Override
-      public Object call() throws Exception {
-        ((ZkConnection) getConnection()).getZookeeper().getData(path, null, cb,
-            new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT, 0, true));
-        return null;
-      }
-    });
-  }
-
-  public void asyncExists(final String path, final ZkAsyncCallbacks.ExistsCallbackHandler cb) {
-    final long startT = System.currentTimeMillis();
-    retryUntilConnected(new Callable<Object>() {
-      @Override
-      public Object call() throws Exception {
-        ((ZkConnection) getConnection()).getZookeeper().exists(path, null, cb,
-            new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT, 0, true));
-        return null;
-      }
-    });
-  }
-
-  public void asyncDelete(final String path, final ZkAsyncCallbacks.DeleteCallbackHandler cb) {
-    final long startT = System.currentTimeMillis();
-    retryUntilConnected(new Callable<Object>() {
-      @Override
-      public Object call() throws Exception {
-        ((ZkConnection) getConnection()).getZookeeper().delete(path, -1, cb,
-            new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT, 0, false));
-        return null;
-      }
-    });
-  }
-
-  private void checkDataSizeLimit(byte[] data) {
-    if (data != null && data.length > ZNRecord.SIZE_LIMIT) {
-      LOG.error("Data size larger than 1M, will not write to zk. Data (first 1k): "
-          + new String(data).substring(0, 1024));
-      throw new HelixException("Data size larger than 1M");
-    }
-  }
-
-  public void watchForData(final String path) {
-    retryUntilConnected(new Callable<Object>() {
-      @Override
-      public Object call() throws Exception {
-        getConnection().exists(path, true);
-        return null;
-      }
-    });
-  }
-
-  /**
-   * Installs a child watch for the given path.
-   * @param path
-   * @return the current children of the path or null if the zk node with the given path doesn't
-   *         exist.
-   */
-  public List<String> watchForChilds(final String path) {
-    if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {
-      throw new IllegalArgumentException("Must not be done in the zookeeper event thread.");
-    }
-    return retryUntilConnected(new Callable<List<String>>() {
-      @Override
-      public List<String> call() throws Exception {
-        exists(path, true);
-        try {
-          return getChildren(path, true);
-        } catch (ZkNoNodeException e) {
-          // ignore, the "exists" watch will listen for the parent node to appear
-        }
-        return null;
-      }
-    });
-  }
-
-  /**
-   * Add authentication information to the connection. This will be used to identify the user and
-   * check access to
-   * nodes protected by ACLs
-   * @param scheme
-   * @param auth
-   */
-  public void addAuthInfo(final String scheme, final byte[] auth) {
-    retryUntilConnected(new Callable<Object>() {
-      @Override
-      public Object call() throws Exception {
-        getConnection().addAuthInfo(scheme, auth);
-        return null;
-      }
-    });
-  }
-
-  /**
-   * Connect to ZooKeeper.
-   * @param maxMsToWaitUntilConnected
-   * @param watcher
-   * @throws ZkInterruptedException
-   *           if the connection timed out due to thread interruption
-   * @throws ZkTimeoutException
-   *           if the connection timed out
-   * @throws IllegalStateException
-   *           if the connection timed out due to thread interruption
-   */
-  public void connect(final long maxMsToWaitUntilConnected, Watcher watcher)
-      throws ZkInterruptedException, ZkTimeoutException, IllegalStateException {
-    if (isClosed()) {
-      throw new IllegalStateException("ZkClient already closed!");
-    }
-    boolean started = false;
-    acquireEventLock();
-    try {
-      setShutdownTrigger(false);
-
-      IZkConnection zkConnection = getConnection();
-      _eventThread = new ZkEventThread(zkConnection.getServers());
-      _eventThread.start();
-
-      if (isManagingZkConnection()) {
-        zkConnection.connect(watcher);
-        LOG.debug("Awaiting connection to Zookeeper server");
-        if (!waitUntilConnected(maxMsToWaitUntilConnected, TimeUnit.MILLISECONDS)) {
-          throw new ZkTimeoutException(
-              "Unable to connect to zookeeper server within timeout: " + maxMsToWaitUntilConnected);
-        }
-      } else {
-        // if the client is not managing connection, the input connection is supposed to connect.
-        if (isConnectionClosed()) {
-          throw new HelixException(
-              "Unable to connect to zookeeper server with the specified ZkConnection");
-        }
-        // TODO Refine the init state here. Here we pre-config it to be connected. This may not be
-        // the case, if the connection is connecting or recovering. -- JJ
-        // For shared client, the event notification will not be forwarded before wather add to the
-        // connection manager.
-        setCurrentState(KeeperState.SyncConnected);
-      }
-
-      started = true;
-    } finally {
-      getEventLock().unlock();
-
-      // we should close the zookeeper instance, otherwise it would keep
-      // on trying to connect
-      if (!started) {
-        close();
-      }
-    }
-  }
-
-  public long getCreationTime(String path) {
-    acquireEventLock();
-    try {
-      return getConnection().getCreateTime(path);
-    } catch (KeeperException e) {
-      throw ZkException.create(e);
-    } catch (InterruptedException e) {
-      throw new ZkInterruptedException(e);
-    } finally {
-      getEventLock().unlock();
-    }
-  }
-
-  public String getServers() {
-    return getConnection().getServers();
-  }
-
-  /**
-   * Close the client.
-   * @throws ZkInterruptedException
-   */
-  public void close() throws ZkInterruptedException {
-    if (LOG.isTraceEnabled()) {
-      StackTraceElement[] calls = Thread.currentThread().getStackTrace();
-      LOG.trace("closing a zkclient. callStack: " + Arrays.asList(calls));
-    }
-    getEventLock().lock();
-    IZkConnection connection = getConnection();
-    try {
-      if (connection == null || _closed) {
-        return;
-      }
-      setShutdownTrigger(true);
-      _eventThread.interrupt();
-      _eventThread.join(2000);
-      if (isManagingZkConnection()) {
-        LOG.info("Closing zkclient: " + ((ZkConnection) connection).getZookeeper());
-        connection.close();
-      }
-      _closed = true;
-
-      // send state change notification to unlock any wait
-      setCurrentState(null);
-      getEventLock().getStateChangedCondition().signalAll();
-
-    } catch (InterruptedException e) {
-      /**
-       * Workaround for HELIX-264: calling ZkClient#close() in its own eventThread context will
-       * throw ZkInterruptedException and skip ZkConnection#close()
-       */
-      if (connection != null) {
-        try {
-          /**
-           * ZkInterruptedException#construct() honors InterruptedException by calling
-           * Thread.currentThread().interrupt(); clear it first, so we can safely close the
-           * zk-connection
-           */
-          Thread.interrupted();
-          if (isManagingZkConnection()) {
-            connection.close();
-          }
-          /**
-           * restore interrupted status of current thread
-           */
-          Thread.currentThread().interrupt();
-        } catch (InterruptedException e1) {
-          throw new ZkInterruptedException(e1);
-        }
-      }
-    } finally {
-      getEventLock().unlock();
-      if (_monitor != null) {
-        _monitor.unregister();
-      }
-      LOG.info("Closed zkclient");
-    }
-  }
-
-  public boolean isClosed() {
-    try {
-      getEventLock().lock();
-      return _closed;
-    } finally {
-      getEventLock().unlock();
-    }
-  }
-
-  public boolean isConnectionClosed() {
-    IZkConnection connection = getConnection();
-    return (connection == null || connection.getZookeeperState() == null
-        || !connection.getZookeeperState().isAlive());
-  }
-
-  public void setShutdownTrigger(boolean triggerState) {
-    _shutdownTriggered = triggerState;
-  }
-
-  public boolean getShutdownTrigger() {
-    return _shutdownTriggered;
-  }
-
-  public int numberOfListeners() {
-    int listeners = 0;
-    for (Set<IZkChildListener> childListeners : _childListener.values()) {
-      listeners += childListeners.size();
-    }
-    for (Set<IZkDataListenerEntry> dataListeners : _dataListener.values()) {
-      listeners += dataListeners.size();
-    }
-    listeners += _stateListener.size();
-
-    return listeners;
-  }
-
-  public List<OpResult> multi(final Iterable<Op> ops) throws ZkException {
-    if (ops == null) {
-      throw new NullPointerException("ops must not be null.");
-    }
-
-    return retryUntilConnected(new Callable<List<OpResult>>() {
-
-      @Override
-      public List<OpResult> call() throws Exception {
-        return getConnection().multi(ops);
-      }
-    });
-  }
-
-  /**
-   * @return true if this ZkClient is managing the ZkConnection.
-   */
-  protected boolean isManagingZkConnection() {
-    return true;
-  }
-
-  public long getSessionId() {
-    ZkConnection zkConnection = ((ZkConnection) getConnection());
-    ZooKeeper zk = zkConnection.getZookeeper();
-    if (zk == null) {
-      throw new HelixException(
-          "ZooKeeper connection information is not available now. ZkClient might be disconnected.");
-    } else {
-      return zkConnection.getZookeeper().getSessionId();
-    }
-  }
-
-  /*
-   * Gets a session id in hexadecimal notation.
-   * Ex. 1000a5ceb930004 is returned.
-   */
-  private String getHexSessionId() {
-    return ZKUtil.toHexSessionId(getSessionId());
-  }
-
-  /*
-   * Session aware operation needs below requirements:
-   * 1. the session id is NOT null or empty
-   * 2. create mode is EPHEMERAL or EPHEMERAL_SEQUENTIAL
-   */
-  private boolean isSessionAwareOperation(String expectedSessionId, CreateMode mode) {
-    return expectedSessionId != null && !expectedSessionId.isEmpty()
-        && (mode == CreateMode.EPHEMERAL || mode == CreateMode.EPHEMERAL_SEQUENTIAL);
-  }
-
-  // operations to update monitor's counters
-  private void record(String path, byte[] data, long startTimeMilliSec,
-      ZkClientMonitor.AccessType accessType) {
-    if (_monitor != null) {
-      int dataSize = (data != null) ? data.length : 0;
-      _monitor.record(path, dataSize, startTimeMilliSec, accessType);
-    }
-  }
-
-  private void recordFailure(String path, ZkClientMonitor.AccessType accessType) {
-    if (_monitor != null) {
-      _monitor.recordFailure(path, accessType);
-    }
-  }
-
-  private void recordStateChange(boolean stateChanged, boolean dataChanged) {
-    // update state change counter.
-    if (_monitor != null) {
-      if (stateChanged) {
-        _monitor.increaseStateChangeEventCounter();
-      }
-      if (dataChanged) {
-        _monitor.increaseDataChangeEventCounter();
-      }
-    }
-  }
-
-  /**
-   * Creates a {@link org.apache.helix.manager.zk.zookeeper.IZkStateListener} that wraps a default
-   * implementation of {@link org.I0Itec.zkclient.IZkStateListener}, which means the returned
-   * listener runs the methods of {@link org.I0Itec.zkclient.IZkStateListener}.
-   * This is for backward compatibility with {@link org.I0Itec.zkclient.IZkStateListener}.
-   */
-  private static class IZkStateListenerI0ItecImpl implements IZkStateListener {
-    private org.I0Itec.zkclient.IZkStateListener _listener;
-
-    IZkStateListenerI0ItecImpl(org.I0Itec.zkclient.IZkStateListener listener) {
-      _listener = listener;
-    }
-
-    @Override
-    public void handleStateChanged(Watcher.Event.KeeperState keeperState) throws Exception {
-      _listener.handleStateChanged(keeperState);
-    }
-
-    @Override
-    public void handleNewSession(final String sessionId) throws Exception {
-      /*
-       * org.I0Itec.zkclient.IZkStateListener does not have handleNewSession(sessionId),
-       * so just call handleNewSession() by default.
-       */
-      _listener.handleNewSession();
-    }
-
-    @Override
-    public void handleSessionEstablishmentError(Throwable error) throws Exception {
-      _listener.handleSessionEstablishmentError(error);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (obj == this) {
-        return true;
-      }
-      if (!(obj instanceof IZkStateListenerI0ItecImpl)) {
-        return false;
-      }
-      if (_listener == null) {
-        return false;
-      }
-
-      IZkStateListenerI0ItecImpl defaultListener = (IZkStateListenerI0ItecImpl) obj;
-
-      return _listener.equals(defaultListener._listener);
-    }
-
-    @Override
-    public int hashCode() {
-      /*
-       * The original listener's hashcode helps find the wrapped listener with the same original
-       * listener. This is helpful in unsubscribeStateChanges(listener) when finding the listener
-       * to remove.
-       */
-      return _listener.hashCode();
-    }
-  }
-
-  private void validateCurrentThread() {
-    if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {
-      throw new IllegalArgumentException("Must not be done in the zookeeper event thread.");
-    }
+    super(zkConnection, connectionTimeout, operationRetryTimeout, zkSerializer, monitorType,
+        monitorKey, monitorInstanceName, monitorRootPathOnly);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkConnection.java b/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkConnection.java
index 07397ed..c623824 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkConnection.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkConnection.java
@@ -11,177 +11,17 @@
 
 package org.apache.helix.manager.zk.zookeeper;
 
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.I0Itec.zkclient.IZkConnection;
-import org.I0Itec.zkclient.exception.ZkException;
-import org.apache.log4j.Logger;
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.Op;
-import org.apache.zookeeper.OpResult;
-import org.apache.zookeeper.Watcher;
-import org.apache.zookeeper.ZooDefs.Ids;
-import org.apache.zookeeper.ZooKeeper;
-import org.apache.zookeeper.ZooKeeper.States;
-import org.apache.zookeeper.data.ACL;
-import org.apache.zookeeper.data.Stat;
-
-public class ZkConnection implements IZkConnection {
-  private static final Logger LOG = Logger.getLogger(ZkConnection.class);
-
-  /** It is recommended to use quite large sessions timeouts for ZooKeeper. */
-  private static final int DEFAULT_SESSION_TIMEOUT = 30000;
-
-  private ZooKeeper _zk = null;
-  private Lock _zookeeperLock = new ReentrantLock();
-
-  private final String _servers;
-  private final int _sessionTimeOut;
+/**
+ * Use ZkConnection in zookeeper-api module instead.
+ */
+@Deprecated
+public class ZkConnection extends org.apache.helix.zookeeper.zkclient.ZkConnection {
 
   public ZkConnection(String zkServers) {
-    this(zkServers, DEFAULT_SESSION_TIMEOUT);
+    super(zkServers);
   }
 
   public ZkConnection(String zkServers, int sessionTimeOut) {
-    _servers = zkServers;
-    _sessionTimeOut = sessionTimeOut;
-  }
-
-  @Override
-  public void connect(Watcher watcher) {
-    _zookeeperLock.lock();
-    try {
-      if (_zk != null) {
-        throw new IllegalStateException("zk client has already been started");
-      }
-      try {
-        LOG.debug("Creating new ZookKeeper instance to connect to " + _servers + ".");
-        _zk = new ZooKeeper(_servers, _sessionTimeOut, watcher);
-      } catch (IOException e) {
-        throw new ZkException("Unable to connect to " + _servers, e);
-      }
-    } finally {
-      _zookeeperLock.unlock();
-    }
-  }
-
-  @Override
-  public void close() throws InterruptedException {
-    _zookeeperLock.lock();
-    try {
-      if (_zk != null) {
-        LOG.debug("Closing ZooKeeper connected to " + _servers);
-        _zk.close();
-        _zk = null;
-      }
-    } finally {
-      _zookeeperLock.unlock();
-    }
-  }
-
-  protected void reconnect(Watcher watcher) throws InterruptedException {
-    _zookeeperLock.lock();
-    try {
-      if (_zk == null) {
-        throw new IllegalStateException("zk client has not been connected or already been closed");
-      }
-      ZooKeeper prevZk = _zk;
-      try {
-        LOG.debug("Creating new ZookKeeper instance to reconnect to " + _servers + ".");
-        _zk = new ZooKeeper(_servers, _sessionTimeOut, watcher);
-        prevZk.close();
-      } catch (IOException e) {
-        throw new ZkException("Unable to connect to " + _servers, e);
-      }
-    } finally {
-      _zookeeperLock.unlock();
-    }
-  }
-
-  @Override
-  public String create(String path, byte[] data, CreateMode mode)
-      throws KeeperException, InterruptedException {
-    return _zk.create(path, data, Ids.OPEN_ACL_UNSAFE, mode);
-  }
-
-  @Override
-  public String create(String path, byte[] data, List<ACL> acl, CreateMode mode)
-      throws KeeperException, InterruptedException {
-    return _zk.create(path, data, acl, mode);
-  }
-
-  @Override
-  public void delete(String path) throws InterruptedException, KeeperException {
-    _zk.delete(path, -1);
-  }
-
-  @Override
-  public boolean exists(String path, boolean watch) throws KeeperException, InterruptedException {
-    return _zk.exists(path, watch) != null;
-  }
-
-  @Override
-  public List<String> getChildren(final String path, final boolean watch)
-      throws KeeperException, InterruptedException {
-    return _zk.getChildren(path, watch);
-  }
-
-  @Override
-  public byte[] readData(String path, Stat stat, boolean watch)
-      throws KeeperException, InterruptedException {
-    return _zk.getData(path, watch, stat);
-  }
-
-  public void writeData(String path, byte[] data) throws KeeperException, InterruptedException {
-    writeData(path, data, -1);
-  }
-
-  @Override
-  public void writeData(String path, byte[] data, int version)
-      throws KeeperException, InterruptedException {
-    _zk.setData(path, data, version);
-  }
-
-  @Override
-  public Stat writeDataReturnStat(String path, byte[] data, int version)
-      throws KeeperException, InterruptedException {
-    return _zk.setData(path, data, version);
-  }
-
-  @Override
-  public States getZookeeperState() {
-    return _zk != null ? _zk.getState() : null;
-  }
-
-  public ZooKeeper getZookeeper() {
-    return _zk;
-  }
-
-  @Override
-  public long getCreateTime(String path) throws KeeperException, InterruptedException {
-    Stat stat = _zk.exists(path, false);
-    if (stat != null) {
-      return stat.getCtime();
-    }
-    return -1;
-  }
-
-  @Override
-  public String getServers() {
-    return _servers;
-  }
-
-  @Override
-  public List<OpResult> multi(Iterable<Op> ops) throws KeeperException, InterruptedException {
-    return _zk.multi(ops);
-  }
-
-  @Override
-  public void addAuthInfo(String scheme, byte[] auth) {
-    _zk.addAuthInfo(scheme, auth);
+    super(zkServers, sessionTimeOut);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/messaging/ZNRecordRow.java b/helix-core/src/main/java/org/apache/helix/messaging/ZNRecordRow.java
index 5a2effd..d1289aa 100644
--- a/helix-core/src/main/java/org/apache/helix/messaging/ZNRecordRow.java
+++ b/helix-core/src/main/java/org/apache/helix/messaging/ZNRecordRow.java
@@ -25,7 +25,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * A Normalized form of ZNRecord
diff --git a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java
index 316284b..7229bc7 100644
--- a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java
+++ b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java
@@ -38,10 +38,10 @@
 import org.apache.helix.NotificationContext.MapKey;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordBucketizer;
-import org.apache.helix.ZNRecordDelta;
-import org.apache.helix.ZNRecordDelta.MergeOperation;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordBucketizer;
+import org.apache.helix.zookeeper.datamodel.ZNRecordDelta;
+import org.apache.helix.zookeeper.datamodel.ZNRecordDelta.MergeOperation;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.Message.Attributes;
diff --git a/helix-core/src/main/java/org/apache/helix/model/AlertHistory.java b/helix-core/src/main/java/org/apache/helix/model/AlertHistory.java
index 58db71d..ccfcb8c 100644
--- a/helix-core/src/main/java/org/apache/helix/model/AlertHistory.java
+++ b/helix-core/src/main/java/org/apache/helix/model/AlertHistory.java
@@ -19,7 +19,7 @@
  * under the License.
  */
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Maintains a history of alerts that have been fired, as well as actions taken, if any.
diff --git a/helix-core/src/main/java/org/apache/helix/model/AlertStatus.java b/helix-core/src/main/java/org/apache/helix/model/AlertStatus.java
index d90ec1a..e6c1e99 100644
--- a/helix-core/src/main/java/org/apache/helix/model/AlertStatus.java
+++ b/helix-core/src/main/java/org/apache/helix/model/AlertStatus.java
@@ -22,7 +22,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.Alerts.AlertsProperty;
 
 /**
diff --git a/helix-core/src/main/java/org/apache/helix/model/Alerts.java b/helix-core/src/main/java/org/apache/helix/model/Alerts.java
index 506e3d5..e32efb3 100644
--- a/helix-core/src/main/java/org/apache/helix/model/Alerts.java
+++ b/helix-core/src/main/java/org/apache/helix/model/Alerts.java
@@ -22,7 +22,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Describe alerts and corresponding metrics. An alert is triggered when cluster health
diff --git a/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java b/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java
index f88d2f5..165919c 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java
@@ -30,7 +30,7 @@
 import com.google.common.collect.Maps;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.HelixConfigProperty;
 import org.apache.helix.api.config.StateTransitionThrottleConfig;
 import org.apache.helix.api.config.StateTransitionTimeoutConfig;
diff --git a/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java b/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java
index 520f17d..12beabe 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java
@@ -26,7 +26,7 @@
 import java.util.TreeMap;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.Message.MessageType;
 import org.apache.helix.model.builder.ConstraintItemBuilder;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/model/ControllerHistory.java b/helix-core/src/main/java/org/apache/helix/model/ControllerHistory.java
index 955a5bf..a76a957 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ControllerHistory.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ControllerHistory.java
@@ -30,7 +30,7 @@
 import java.util.TimeZone;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.codehaus.jackson.map.ObjectMapper;
 
 /**
diff --git a/helix-core/src/main/java/org/apache/helix/model/CurrentState.java b/helix-core/src/main/java/org/apache/helix/model/CurrentState.java
index c227060..0bc9052 100644
--- a/helix-core/src/main/java/org/apache/helix/model/CurrentState.java
+++ b/helix-core/src/main/java/org/apache/helix/model/CurrentState.java
@@ -24,7 +24,7 @@
 import java.util.TreeMap;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/model/Error.java b/helix-core/src/main/java/org/apache/helix/model/Error.java
index 7744b91..a64905e 100644
--- a/helix-core/src/main/java/org/apache/helix/model/Error.java
+++ b/helix-core/src/main/java/org/apache/helix/model/Error.java
@@ -20,7 +20,7 @@
  */
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Defines an error that occurs in computing a valid ideal state or external view
diff --git a/helix-core/src/main/java/org/apache/helix/model/ExternalView.java b/helix-core/src/main/java/org/apache/helix/model/ExternalView.java
index efe6b4d..7ae9e34 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ExternalView.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ExternalView.java
@@ -24,7 +24,7 @@
 import java.util.TreeMap;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * External view is an aggregation (across all instances)
diff --git a/helix-core/src/main/java/org/apache/helix/model/HealthStat.java b/helix-core/src/main/java/org/apache/helix/model/HealthStat.java
index b8ac32f..f1fee8c 100644
--- a/helix-core/src/main/java/org/apache/helix/model/HealthStat.java
+++ b/helix-core/src/main/java/org/apache/helix/model/HealthStat.java
@@ -24,7 +24,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.Message.Attributes;
 
 /**
diff --git a/helix-core/src/main/java/org/apache/helix/model/IdealState.java b/helix-core/src/main/java/org/apache/helix/model/IdealState.java
index 3908a95..f41bc2c 100644
--- a/helix-core/src/main/java/org/apache/helix/model/IdealState.java
+++ b/helix-core/src/main/java/org/apache/helix/model/IdealState.java
@@ -29,7 +29,6 @@
 
 import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
 import org.apache.helix.controller.rebalancer.Rebalancer;
 import org.apache.helix.model.ResourceConfig.ResourceConfigProperty;
 import org.apache.helix.task.FixedTargetTaskRebalancer;
@@ -37,6 +36,7 @@
 import org.apache.helix.task.JobRebalancer;
 import org.apache.helix.task.TaskRebalancer;
 import org.apache.helix.task.WorkflowRebalancer;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
index b55ba83..24e6154 100644
--- a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
@@ -32,8 +32,8 @@
 import com.google.common.base.Splitter;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
 import org.apache.helix.util.HelixUtil;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/model/LeaderStandbySMD.java b/helix-core/src/main/java/org/apache/helix/model/LeaderStandbySMD.java
index 092f8ba..5d98d23 100644
--- a/helix-core/src/main/java/org/apache/helix/model/LeaderStandbySMD.java
+++ b/helix-core/src/main/java/org/apache/helix/model/LeaderStandbySMD.java
@@ -25,7 +25,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixDefinedState;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Helix built-in Leader-standby state model definition
diff --git a/helix-core/src/main/java/org/apache/helix/model/LiveInstance.java b/helix-core/src/main/java/org/apache/helix/model/LiveInstance.java
index f9a5d6a..74260cc 100644
--- a/helix-core/src/main/java/org/apache/helix/model/LiveInstance.java
+++ b/helix-core/src/main/java/org/apache/helix/model/LiveInstance.java
@@ -22,7 +22,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/model/MaintenanceSignal.java b/helix-core/src/main/java/org/apache/helix/model/MaintenanceSignal.java
index 511e6f3..6a30525 100644
--- a/helix-core/src/main/java/org/apache/helix/model/MaintenanceSignal.java
+++ b/helix-core/src/main/java/org/apache/helix/model/MaintenanceSignal.java
@@ -19,7 +19,7 @@
  * under the License.
  */
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * A ZNode that signals that the cluster is in maintenance mode.
diff --git a/helix-core/src/main/java/org/apache/helix/model/MasterSlaveSMD.java b/helix-core/src/main/java/org/apache/helix/model/MasterSlaveSMD.java
index f2eca27..45f63a3 100644
--- a/helix-core/src/main/java/org/apache/helix/model/MasterSlaveSMD.java
+++ b/helix-core/src/main/java/org/apache/helix/model/MasterSlaveSMD.java
@@ -25,7 +25,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixDefinedState;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Helix built-in Master-slave state model definition
diff --git a/helix-core/src/main/java/org/apache/helix/model/Message.java b/helix-core/src/main/java/org/apache/helix/model/Message.java
index 6317af1..eefecec 100644
--- a/helix-core/src/main/java/org/apache/helix/model/Message.java
+++ b/helix-core/src/main/java/org/apache/helix/model/Message.java
@@ -36,7 +36,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Messages sent internally among nodes in the system to respond to changes in state.
diff --git a/helix-core/src/main/java/org/apache/helix/model/OnlineOfflineSMD.java b/helix-core/src/main/java/org/apache/helix/model/OnlineOfflineSMD.java
index 56c477d..4e0d968 100644
--- a/helix-core/src/main/java/org/apache/helix/model/OnlineOfflineSMD.java
+++ b/helix-core/src/main/java/org/apache/helix/model/OnlineOfflineSMD.java
@@ -25,7 +25,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixDefinedState;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Helix built-in Online-offline state model definition
diff --git a/helix-core/src/main/java/org/apache/helix/model/ParticipantHistory.java b/helix-core/src/main/java/org/apache/helix/model/ParticipantHistory.java
index 6e5a8c6..c17d172 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ParticipantHistory.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ParticipantHistory.java
@@ -29,7 +29,7 @@
 import java.util.TimeZone;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/model/PauseSignal.java b/helix-core/src/main/java/org/apache/helix/model/PauseSignal.java
index ed89e9a..80a2aa7 100644
--- a/helix-core/src/main/java/org/apache/helix/model/PauseSignal.java
+++ b/helix-core/src/main/java/org/apache/helix/model/PauseSignal.java
@@ -20,7 +20,7 @@
  */
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Represent a pause in the cluster
diff --git a/helix-core/src/main/java/org/apache/helix/model/PersistentStats.java b/helix-core/src/main/java/org/apache/helix/model/PersistentStats.java
index 8f0cb69..b61e413 100644
--- a/helix-core/src/main/java/org/apache/helix/model/PersistentStats.java
+++ b/helix-core/src/main/java/org/apache/helix/model/PersistentStats.java
@@ -22,7 +22,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/model/RESTConfig.java b/helix-core/src/main/java/org/apache/helix/model/RESTConfig.java
index 7e7cb22..b42b208 100644
--- a/helix-core/src/main/java/org/apache/helix/model/RESTConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/RESTConfig.java
@@ -1,7 +1,7 @@
 package org.apache.helix.model;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 
 /**
diff --git a/helix-core/src/main/java/org/apache/helix/model/ResourceAssignment.java b/helix-core/src/main/java/org/apache/helix/model/ResourceAssignment.java
index b9f6a15..756f373 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ResourceAssignment.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ResourceAssignment.java
@@ -25,7 +25,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Represents the assignments of replicas for an entire resource, keyed on partitions of the
diff --git a/helix-core/src/main/java/org/apache/helix/model/ResourceConfig.java b/helix-core/src/main/java/org/apache/helix/model/ResourceConfig.java
index 9cdb673..f4d8b2b 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ResourceConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ResourceConfig.java
@@ -27,7 +27,7 @@
 import java.util.TreeMap;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.HelixConfigProperty;
 import org.apache.helix.api.config.RebalanceConfig;
 import org.apache.helix.api.config.StateTransitionTimeoutConfig;
diff --git a/helix-core/src/main/java/org/apache/helix/model/ScheduledTaskSMD.java b/helix-core/src/main/java/org/apache/helix/model/ScheduledTaskSMD.java
index c85e5dd..c267f16 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ScheduledTaskSMD.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ScheduledTaskSMD.java
@@ -25,7 +25,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixDefinedState;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.DefaultSchedulerMessageHandlerFactory;
 import org.apache.helix.model.builder.StateTransitionTableBuilder;
 
diff --git a/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java b/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
index 0a40331..9a24816 100644
--- a/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
+++ b/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
@@ -32,9 +32,10 @@
 
 import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
 import org.apache.helix.model.builder.StateTransitionTableBuilder;
 import org.apache.helix.model.util.StateModelDefinitionValidator;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 /**
  * Describe the state model
diff --git a/helix-core/src/main/java/org/apache/helix/model/StatusUpdate.java b/helix-core/src/main/java/org/apache/helix/model/StatusUpdate.java
index f4857ee..8f162e3 100644
--- a/helix-core/src/main/java/org/apache/helix/model/StatusUpdate.java
+++ b/helix-core/src/main/java/org/apache/helix/model/StatusUpdate.java
@@ -20,7 +20,7 @@
  */
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Wraps updates to Helix constructs, e.g. state transitions and controller task statuses
diff --git a/helix-core/src/main/java/org/apache/helix/model/StorageSchemataSMD.java b/helix-core/src/main/java/org/apache/helix/model/StorageSchemataSMD.java
index 46d54a9..d9dad7e 100644
--- a/helix-core/src/main/java/org/apache/helix/model/StorageSchemataSMD.java
+++ b/helix-core/src/main/java/org/apache/helix/model/StorageSchemataSMD.java
@@ -25,7 +25,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixDefinedState;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Helix built-in StorageSchemata state model definition
diff --git a/helix-core/src/main/java/org/apache/helix/model/TaskSMD.java b/helix-core/src/main/java/org/apache/helix/model/TaskSMD.java
index 33b62e7..6f8f1e9 100644
--- a/helix-core/src/main/java/org/apache/helix/model/TaskSMD.java
+++ b/helix-core/src/main/java/org/apache/helix/model/TaskSMD.java
@@ -24,7 +24,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.builder.StateTransitionTableBuilder;
 import org.apache.helix.task.TaskConstants;
 import org.apache.helix.task.TaskPartitionState;
diff --git a/helix-core/src/main/java/org/apache/helix/model/builder/IdealStateBuilder.java b/helix-core/src/main/java/org/apache/helix/model/builder/IdealStateBuilder.java
index 4701181..1fd3916 100644
--- a/helix-core/src/main/java/org/apache/helix/model/builder/IdealStateBuilder.java
+++ b/helix-core/src/main/java/org/apache/helix/model/builder/IdealStateBuilder.java
@@ -21,7 +21,7 @@
 
 import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.IdealState;
 
 public abstract class IdealStateBuilder {
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/ZKPathDataDumpTask.java b/helix-core/src/main/java/org/apache/helix/monitoring/ZKPathDataDumpTask.java
index c0d6285..1028b85 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/ZKPathDataDumpTask.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/ZKPathDataDumpTask.java
@@ -28,7 +28,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ZkClientMonitor.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ZkClientMonitor.java
index 61b574c..f76a459 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ZkClientMonitor.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ZkClientMonitor.java
@@ -19,231 +19,16 @@
  * under the License.
  */
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import javax.management.JMException;
-import javax.management.MBeanAttributeInfo;
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
+import org.apache.helix.zookeeper.zkclient.ZkEventThread;
 
-import org.apache.helix.HelixException;
-import org.apache.helix.manager.zk.zookeeper.ZkEventThread;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMBeanProvider;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.SimpleDynamicMetric;
-
-public class ZkClientMonitor extends DynamicMBeanProvider {
-  public static final String MONITOR_TYPE = "Type";
-  public static final String MONITOR_KEY = "Key";
-  protected static final String MBEAN_DESCRIPTION = "Helix Zookeeper Client Monitor";
-
-  public enum AccessType {
-    READ, WRITE
-  }
-
-  private String _sensorName;
-  private String _monitorType;
-  private String _monitorKey;
-  private String _monitorInstanceName;
-  private boolean _monitorRootOnly;
-
-  private SimpleDynamicMetric<Long> _stateChangeEventCounter;
-  private SimpleDynamicMetric<Long> _dataChangeEventCounter;
-  private SimpleDynamicMetric<Long> _outstandingRequestGauge;
-
-  private ZkThreadMetric _zkEventThreadMetric;
-
-  private Map<ZkClientPathMonitor.PredefinedPath, ZkClientPathMonitor> _zkClientPathMonitorMap =
-      new ConcurrentHashMap<>();
-
+/**
+ * Use ZkClientMonitor in zookeeper-api module instead.
+ */
+@Deprecated
+public class ZkClientMonitor
+    extends org.apache.helix.zookeeper.zkclient.metric.ZkClientMonitor {
   public ZkClientMonitor(String monitorType, String monitorKey, String monitorInstanceName,
       boolean monitorRootOnly, ZkEventThread zkEventThread) {
-    if (monitorKey == null || monitorKey.isEmpty() || monitorType == null || monitorType
-        .isEmpty()) {
-      throw new HelixException("Cannot create ZkClientMonitor without monitor key and type.");
-    }
-
-    _sensorName =
-        String.format("%s.%s.%s", MonitorDomainNames.HelixZkClient.name(), monitorType, monitorKey);
-    _monitorType = monitorType;
-    _monitorKey = monitorKey;
-    _monitorInstanceName = monitorInstanceName;
-    _monitorRootOnly = monitorRootOnly;
-
-    _stateChangeEventCounter = new SimpleDynamicMetric("StateChangeEventCounter", 0l);
-    _dataChangeEventCounter = new SimpleDynamicMetric("DataChangeEventCounter", 0l);
-    _outstandingRequestGauge = new SimpleDynamicMetric("OutstandingRequestGauge", 0l);
-    if (zkEventThread != null) {
-      _zkEventThreadMetric = new ZkThreadMetric(zkEventThread);
-    }
-  }
-
-  protected static ObjectName getObjectName(String monitorType, String monitorKey,
-      String monitorInstanceName) throws MalformedObjectNameException {
-    return MBeanRegistrar
-        .buildObjectName(MonitorDomainNames.HelixZkClient.name(), MONITOR_TYPE, monitorType,
-            MONITOR_KEY,
-            (monitorKey + (monitorInstanceName == null ? "" : "." + monitorInstanceName)));
-  }
-
-  @Override
-  public DynamicMBeanProvider register() throws JMException {
-    List<DynamicMetric<?, ?>> attributeList = new ArrayList<>();
-    attributeList.add(_dataChangeEventCounter);
-    attributeList.add(_outstandingRequestGauge);
-    attributeList.add(_stateChangeEventCounter);
-    if (_zkEventThreadMetric != null) {
-      attributeList.add(_zkEventThreadMetric);
-    }
-    doRegister(attributeList, MBEAN_DESCRIPTION,
-        getObjectName(_monitorType, _monitorKey, _monitorInstanceName));
-    for (ZkClientPathMonitor.PredefinedPath path : ZkClientPathMonitor.PredefinedPath.values()) {
-      // If monitor root path only, check if the current path is Root.
-      // Otherwise, add monitors for every path.
-      if (!_monitorRootOnly || path.equals(ZkClientPathMonitor.PredefinedPath.Root)) {
-        _zkClientPathMonitorMap.put(path,
-            new ZkClientPathMonitor(path, _monitorType, _monitorKey, _monitorInstanceName)
-                .register());
-      }
-    }
-    return this;
-  }
-
-  /**
-   * After unregistered, the MBean can't be registered again, a new monitor has be to created.
-   */
-  public void unregister() {
-    super.unregister();
-    for (ZkClientPathMonitor zkClientPathMonitor : _zkClientPathMonitorMap.values()) {
-      zkClientPathMonitor.unregister();
-    }
-  }
-
-  @Override
-  public String getSensorName() {
-    return _sensorName;
-  }
-
-  public void increaseStateChangeEventCounter() {
-    synchronized (_stateChangeEventCounter) {
-      _stateChangeEventCounter.updateValue(_stateChangeEventCounter.getValue() + 1);
-    }
-  }
-
-  public void increaseDataChangeEventCounter() {
-    synchronized (_dataChangeEventCounter) {
-      _dataChangeEventCounter.updateValue(_dataChangeEventCounter.getValue() + 1);
-    }
-  }
-
-  public void increaseOutstandingRequestGauge() {
-    synchronized (_outstandingRequestGauge) {
-      _outstandingRequestGauge.updateValue(_outstandingRequestGauge.getValue() + 1);
-    }
-  }
-
-  public void decreaseOutstandingRequestGauge() {
-    synchronized (_outstandingRequestGauge) {
-      _outstandingRequestGauge.updateValue(_outstandingRequestGauge.getValue() - 1);
-    }
-  }
-
-  public void recordDataPropagationLatency(String path, long latencyMilliSec) {
-    if (null == path) {
-      return;
-    }
-    Arrays.stream(ZkClientPathMonitor.PredefinedPath.values())
-        .filter(predefinedPath -> predefinedPath.match(path))
-        .forEach(predefinedPath -> {
-      ZkClientPathMonitor zkClientPathMonitor = _zkClientPathMonitorMap.get(predefinedPath);
-      if (zkClientPathMonitor != null) {
-        zkClientPathMonitor.recordDataPropagationLatency(latencyMilliSec);
-      }
-    });
-  }
-
-  private void record(String path, int bytes, long latencyMilliSec, boolean isFailure,
-      boolean isRead) {
-    if (null == path) {
-      return;
-    }
-    Arrays.stream(ZkClientPathMonitor.PredefinedPath.values())
-        .filter(predefinedPath -> predefinedPath.match(path))
-        .forEach(predefinedPath -> {
-      ZkClientPathMonitor zkClientPathMonitor = _zkClientPathMonitorMap.get(predefinedPath);
-      if (zkClientPathMonitor != null) {
-        zkClientPathMonitor.record(bytes, latencyMilliSec, isFailure, isRead);
-      }
-    });
-  }
-
-  public void record(String path, int dataSize, long startTimeMilliSec, AccessType accessType) {
-    switch (accessType) {
-    case READ:
-      record(path, dataSize, System.currentTimeMillis() - startTimeMilliSec, false, true);
-      return;
-    case WRITE:
-      record(path, dataSize, System.currentTimeMillis() - startTimeMilliSec, false, false);
-      return;
-    default:
-      return;
-    }
-  }
-
-  public void recordFailure(String path, AccessType accessType) {
-    switch (accessType) {
-    case READ:
-      record(path, 0, 0, true, true);
-      return;
-    case WRITE:
-      record(path, 0, 0, true, false);
-      return;
-    default:
-      return;
-    }
-  }
-
-  class ZkThreadMetric extends DynamicMetric<ZkEventThread, ZkEventThread> {
-    public ZkThreadMetric(ZkEventThread eventThread) {
-      super("ZkEventThead", eventThread);
-    }
-
-    @Override
-    protected Set<MBeanAttributeInfo> generateAttributeInfos(String metricName,
-        ZkEventThread eventThread) {
-      Set<MBeanAttributeInfo> attributeInfoSet = new HashSet<>();
-      attributeInfoSet.add(new MBeanAttributeInfo("PendingCallbackGauge", Long.TYPE.getName(),
-          DEFAULT_ATTRIBUTE_DESCRIPTION, true, false, false));
-      attributeInfoSet.add(new MBeanAttributeInfo("TotalCallbackCounter", Long.TYPE.getName(),
-          DEFAULT_ATTRIBUTE_DESCRIPTION, true, false, false));
-      attributeInfoSet.add(
-          new MBeanAttributeInfo("TotalCallbackHandledCounter", Long.TYPE.getName(),
-              DEFAULT_ATTRIBUTE_DESCRIPTION, true, false, false));
-      return attributeInfoSet;
-    }
-
-    @Override
-    public Object getAttributeValue(String attributeName) {
-      switch (attributeName) {
-      case "PendingCallbackGauge":
-        return getMetricObject().getPendingEventsCount();
-      case "TotalCallbackCounter":
-        return getMetricObject().getTotalEventCount();
-      case "TotalCallbackHandledCounter":
-        return getMetricObject().getTotalHandledEventCount();
-      default:
-        throw new HelixException("Unknown attribute name: " + attributeName);
-      }
-    }
-
-    @Override
-    public void updateValue(ZkEventThread newEventThread) {
-      setMetricObject(newEventThread);
-    }
+    super(monitorType, monitorKey, monitorInstanceName, monitorRootOnly, zkEventThread);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ZkClientPathMonitor.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ZkClientPathMonitor.java
index 1795243..6b8d642 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ZkClientPathMonitor.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ZkClientPathMonitor.java
@@ -19,228 +19,14 @@
  * under the License.
  */
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import javax.management.JMException;
-import javax.management.ObjectName;
-
-import com.codahale.metrics.Histogram;
-import com.codahale.metrics.SlidingTimeWindowArrayReservoir;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMBeanProvider;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.HistogramDynamicMetric;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.SimpleDynamicMetric;
-
-public class ZkClientPathMonitor extends DynamicMBeanProvider {
-  public static final String MONITOR_PATH = "PATH";
-  private final String _sensorName;
-  private final String _type;
-  private final String _key;
-  private final String _instanceName;
-  private final PredefinedPath _path;
-
-  public enum PredefinedPath {
-    IdealStates(".*/IDEALSTATES/.*"),
-    Instances(".*/INSTANCES/.*"),
-    Configs(".*/CONFIGS/.*"),
-    Controller(".*/CONTROLLER/.*"),
-    ExternalView(".*/EXTERNALVIEW/.*"),
-    LiveInstances(".*/LIVEINSTANCES/.*"),
-    PropertyStore(".*/PROPERTYSTORE/.*"),
-    CurrentStates(".*/CURRENTSTATES/.*"),
-    Messages(".*/MESSAGES/.*"),
-    Root(".*");
-
-    private final String _matchString;
-
-    PredefinedPath(String matchString) {
-      _matchString = matchString;
-    }
-
-    public boolean match(String path) {
-      return path.matches(this._matchString);
-    }
-  }
-
-  public enum PredefinedMetricDomains {
-    WriteTotalLatencyCounter,
-    ReadTotalLatencyCounter,
-    WriteFailureCounter,
-    ReadFailureCounter,
-    WriteBytesCounter,
-    ReadBytesCounter,
-    WriteCounter,
-    ReadCounter,
-    ReadLatencyGauge,
-    WriteLatencyGauge,
-    ReadBytesGauge,
-    WriteBytesGauge,
-    /*
-     * The latency between a ZK data change happening on the server side and the client side.
-     */
-    DataPropagationLatencyGauge,
-    /**
-     * @deprecated
-     * This domain name has a typo. Keep it in case its historical metric data is being used.
-     */
-    @Deprecated
-    DataPropagationLatencyGuage
-  }
-
-  private SimpleDynamicMetric<Long> _readCounter;
-  private SimpleDynamicMetric<Long> _writeCounter;
-  private SimpleDynamicMetric<Long> _readBytesCounter;
-  private SimpleDynamicMetric<Long> _writeBytesCounter;
-  private SimpleDynamicMetric<Long> _readFailureCounter;
-  private SimpleDynamicMetric<Long> _writeFailureCounter;
-  private SimpleDynamicMetric<Long> _readTotalLatencyCounter;
-  private SimpleDynamicMetric<Long> _writeTotalLatencyCounter;
-
-  private HistogramDynamicMetric _readLatencyGauge;
-  private HistogramDynamicMetric _writeLatencyGauge;
-  private HistogramDynamicMetric _readBytesGauge;
-  private HistogramDynamicMetric _writeBytesGauge;
-  private HistogramDynamicMetric _dataPropagationLatencyGauge;
-
-  /**
-   * @deprecated
-   * Keep it for backward-compatibility purpose.
-   */
-  @Deprecated
-  private HistogramDynamicMetric _dataPropagationLatencyGuage;
-
-  @Override
-  public String getSensorName() {
-    return _sensorName;
-  }
-
+/**
+ * Use ZkClientPathMonitor in zookeeper-api module instead.
+ */
+@Deprecated
+public class ZkClientPathMonitor
+    extends org.apache.helix.zookeeper.zkclient.metric.ZkClientPathMonitor {
   public ZkClientPathMonitor(PredefinedPath path, String monitorType, String monitorKey,
       String monitorInstanceName) {
-    _type = monitorType;
-    _key = monitorKey;
-    _instanceName = monitorInstanceName;
-    _path = path;
-    _sensorName = String
-        .format("%s.%s.%s.%s", MonitorDomainNames.HelixZkClient.name(), monitorType, monitorKey,
-            path.name());
-
-    _writeTotalLatencyCounter =
-        new SimpleDynamicMetric(PredefinedMetricDomains.WriteTotalLatencyCounter.name(), 0l);
-    _readTotalLatencyCounter =
-        new SimpleDynamicMetric(PredefinedMetricDomains.ReadTotalLatencyCounter.name(), 0l);
-    _writeFailureCounter =
-        new SimpleDynamicMetric(PredefinedMetricDomains.WriteFailureCounter.name(), 0l);
-    _readFailureCounter =
-        new SimpleDynamicMetric(PredefinedMetricDomains.ReadFailureCounter.name(), 0l);
-    _writeBytesCounter =
-        new SimpleDynamicMetric(PredefinedMetricDomains.WriteBytesCounter.name(), 0l);
-    _readBytesCounter =
-        new SimpleDynamicMetric(PredefinedMetricDomains.ReadBytesCounter.name(), 0l);
-    _writeCounter = new SimpleDynamicMetric(PredefinedMetricDomains.WriteCounter.name(), 0l);
-    _readCounter = new SimpleDynamicMetric(PredefinedMetricDomains.ReadCounter.name(), 0l);
-
-    _readLatencyGauge = new HistogramDynamicMetric(PredefinedMetricDomains.ReadLatencyGauge.name(),
-        new Histogram(
-            new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(), TimeUnit.MILLISECONDS)));
-    _writeLatencyGauge =
-        new HistogramDynamicMetric(PredefinedMetricDomains.WriteLatencyGauge.name(), new Histogram(
-            new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(), TimeUnit.MILLISECONDS)));
-    _readBytesGauge = new HistogramDynamicMetric(PredefinedMetricDomains.ReadBytesGauge.name(),
-        new Histogram(
-            new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(), TimeUnit.MILLISECONDS)));
-    _writeBytesGauge = new HistogramDynamicMetric(PredefinedMetricDomains.WriteBytesGauge.name(),
-        new Histogram(
-            new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(), TimeUnit.MILLISECONDS)));
-    _dataPropagationLatencyGauge =
-        new HistogramDynamicMetric(PredefinedMetricDomains.DataPropagationLatencyGauge.name(),
-            new Histogram(new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(),
-                TimeUnit.MILLISECONDS)));
-
-    // This is deprecated and keep it for backward-compatibility purpose.
-    _dataPropagationLatencyGuage =
-        new HistogramDynamicMetric(PredefinedMetricDomains.DataPropagationLatencyGuage.name(),
-            new Histogram(new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(),
-                TimeUnit.MILLISECONDS)));
-  }
-
-  public ZkClientPathMonitor register() throws JMException {
-    List<DynamicMetric<?, ?>> attributeList = new ArrayList<>();
-    attributeList.add(_readCounter);
-    attributeList.add(_writeCounter);
-    attributeList.add(_readBytesCounter);
-    attributeList.add(_writeBytesCounter);
-    attributeList.add(_readFailureCounter);
-    attributeList.add(_writeFailureCounter);
-    attributeList.add(_readTotalLatencyCounter);
-    attributeList.add(_writeTotalLatencyCounter);
-    attributeList.add(_readLatencyGauge);
-    attributeList.add(_writeLatencyGauge);
-    attributeList.add(_readBytesGauge);
-    attributeList.add(_writeBytesGauge);
-    attributeList.add(_dataPropagationLatencyGauge);
-    // This is deprecated and keep it for backward-compatibility purpose.
-    attributeList.add(_dataPropagationLatencyGuage);
-
-    ObjectName objectName = new ObjectName(String
-        .format("%s,%s=%s", ZkClientMonitor.getObjectName(_type, _key, _instanceName).toString(),
-            MONITOR_PATH, _path.name()));
-    doRegister(attributeList, ZkClientMonitor.MBEAN_DESCRIPTION, objectName);
-
-    return this;
-  }
-
-  protected synchronized void record(int bytes, long latencyMilliSec, boolean isFailure,
-      boolean isRead) {
-    if (isFailure) {
-      increaseFailureCounter(isRead);
-    } else {
-      increaseCounter(isRead);
-      increaseTotalLatency(isRead, latencyMilliSec);
-      if (bytes > 0) {
-        increaseBytesCounter(isRead, bytes);
-      }
-    }
-  }
-
-  public void recordDataPropagationLatency(long latency) {
-    _dataPropagationLatencyGauge.updateValue(latency);
-    _dataPropagationLatencyGuage.updateValue(latency);
-  }
-
-  private void increaseFailureCounter(boolean isRead) {
-    if (isRead) {
-      _readFailureCounter.updateValue(_readFailureCounter.getValue() + 1);
-    } else {
-      _writeFailureCounter.updateValue(_writeFailureCounter.getValue() + 1);
-    }
-  }
-
-  private void increaseCounter(boolean isRead) {
-    if (isRead) {
-      _readCounter.updateValue(_readCounter.getValue() + 1);
-    } else {
-      _writeCounter.updateValue(_writeCounter.getValue() + 1);
-    }
-  }
-
-  private void increaseBytesCounter(boolean isRead, int bytes) {
-    if (isRead) {
-      _readBytesCounter.updateValue(_readBytesCounter.getValue() + bytes);
-      _readBytesGauge.updateValue((long) bytes);
-    } else {
-      _writeBytesCounter.updateValue(_writeBytesCounter.getValue() + bytes);
-      _writeBytesGauge.updateValue((long) bytes);
-    }
-  }
-
-  private void increaseTotalLatency(boolean isRead, long latencyDelta) {
-    if (isRead) {
-      _readTotalLatencyCounter.updateValue(_readTotalLatencyCounter.getValue() + latencyDelta);
-      _readLatencyGauge.updateValue(latencyDelta);
-    } else {
-      _writeTotalLatencyCounter.updateValue(_writeTotalLatencyCounter.getValue() + latencyDelta);
-      _writeLatencyGauge.updateValue(latencyDelta);
-    }
+    super(path, monitorType, monitorKey, monitorInstanceName);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/participant/HelixCustomCodeRunner.java b/helix-core/src/main/java/org/apache/helix/participant/HelixCustomCodeRunner.java
index b29126c..8d10e8f 100644
--- a/helix-core/src/main/java/org/apache/helix/participant/HelixCustomCodeRunner.java
+++ b/helix-core/src/main/java/org/apache/helix/participant/HelixCustomCodeRunner.java
@@ -23,21 +23,21 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.HelixConstants.ChangeType;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.RebalanceMode;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 /**
  * This provides the ability for users to run a custom code in exactly one
  * process using a LeaderStandBy state model. <br/>
@@ -136,12 +136,11 @@
       // model
       HelixZkClient.ZkClientConfig clientConfig = new HelixZkClient.ZkClientConfig();
       clientConfig.setZkSerializer(new ZNRecordSerializer());
-      zkClient = SharedZkClientFactory
-          .getInstance().buildZkClient(new HelixZkClient.ZkConnectionConfig(_zkAddr), clientConfig);
+      zkClient = SharedZkClientFactory.getInstance()
+          .buildZkClient(new HelixZkClient.ZkConnectionConfig(_zkAddr), clientConfig);
 
       HelixDataAccessor accessor =
-          new ZKHelixDataAccessor(_manager.getClusterName(), new ZkBaseDataAccessor<ZNRecord>(
-              zkClient));
+          new ZKHelixDataAccessor(_manager.getClusterName(), new ZkBaseDataAccessor<>(zkClient));
       Builder keyBuilder = accessor.keyBuilder();
 
       IdealState idealState = new IdealState(_resourceName);
@@ -150,8 +149,8 @@
       idealState.setNumPartitions(1);
       idealState.setStateModelDefRef(LEADER_STANDBY);
       idealState.setStateModelFactoryName(_resourceName);
-      List<String> prefList =
-          new ArrayList<String>(Arrays.asList(IdealState.IdealStateConstants.ANY_LIVEINSTANCE.toString()));
+      List<String> prefList = new ArrayList<String>(
+          Arrays.asList(IdealState.IdealStateConstants.ANY_LIVEINSTANCE.toString()));
       idealState.getRecord().setListField(_resourceName + "_0", prefList);
 
       List<String> idealStates = accessor.getChildNames(keyBuilder.idealStates());
@@ -160,14 +159,13 @@
         idealStates = accessor.getChildNames(keyBuilder.idealStates());
       }
 
-      LOG.info("Set idealState for participantLeader:" + _resourceName + ", idealState:"
-          + idealState);
+      LOG.info(
+          "Set idealState for participantLeader:" + _resourceName + ", idealState:" + idealState);
     } finally {
       if (zkClient != null && !zkClient.isClosed()) {
         zkClient.close();
       }
     }
-
   }
 
   /**
@@ -175,7 +173,7 @@
    */
   public void stop() {
     LOG.info("Removing stateModelFactory for " + _resourceName);
-    _manager.getStateMachineEngine().removeStateModelFactory(LEADER_STANDBY, _stateModelFty,
-        _resourceName);
+    _manager.getStateMachineEngine()
+        .removeStateModelFactory(LEADER_STANDBY, _stateModelFty, _resourceName);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/participant/statemachine/ScheduledTaskStateModel.java b/helix-core/src/main/java/org/apache/helix/participant/statemachine/ScheduledTaskStateModel.java
index 750c7c9..8a54570 100644
--- a/helix-core/src/main/java/org/apache/helix/participant/statemachine/ScheduledTaskStateModel.java
+++ b/helix-core/src/main/java/org/apache/helix/participant/statemachine/ScheduledTaskStateModel.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.HelixException;
 import org.apache.helix.NotificationContext;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.messaging.handling.HelixTaskExecutor;
 import org.apache.helix.messaging.handling.MessageHandler;
 import org.apache.helix.model.Message;
diff --git a/helix-core/src/main/java/org/apache/helix/store/PropertyJsonSerializer.java b/helix-core/src/main/java/org/apache/helix/store/PropertyJsonSerializer.java
index d9bb82d..75d056b 100644
--- a/helix-core/src/main/java/org/apache/helix/store/PropertyJsonSerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/store/PropertyJsonSerializer.java
@@ -23,7 +23,7 @@
 import java.io.StringWriter;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.codehaus.jackson.map.DeserializationConfig;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.SerializationConfig;
diff --git a/helix-core/src/main/java/org/apache/helix/store/ZNRecordJsonSerializer.java b/helix-core/src/main/java/org/apache/helix/store/ZNRecordJsonSerializer.java
index dfb0a0d..64bf74f 100644
--- a/helix-core/src/main/java/org/apache/helix/store/ZNRecordJsonSerializer.java
+++ b/helix-core/src/main/java/org/apache/helix/store/ZNRecordJsonSerializer.java
@@ -19,7 +19,7 @@
  * under the License.
  */
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/store/zk/AutoFallbackPropertyStore.java b/helix-core/src/main/java/org/apache/helix/store/zk/AutoFallbackPropertyStore.java
index dafa36d..47b1dfa 100644
--- a/helix-core/src/main/java/org/apache/helix/store/zk/AutoFallbackPropertyStore.java
+++ b/helix-core/src/main/java/org/apache/helix/store/zk/AutoFallbackPropertyStore.java
@@ -26,9 +26,9 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/store/zk/ZkHelixPropertyStore.java b/helix-core/src/main/java/org/apache/helix/store/zk/ZkHelixPropertyStore.java
index a6e7a9c..1ea9c6f 100644
--- a/helix-core/src/main/java/org/apache/helix/store/zk/ZkHelixPropertyStore.java
+++ b/helix-core/src/main/java/org/apache/helix/store/zk/ZkHelixPropertyStore.java
@@ -21,9 +21,10 @@
 
 import java.util.List;
 
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.manager.zk.ZkCacheBaseDataAccessor;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+
 
 public class ZkHelixPropertyStore<T> extends ZkCacheBaseDataAccessor<T> {
   public static final String MONITOR_TYPE = "HelixPropertyStore";
diff --git a/helix-core/src/main/java/org/apache/helix/task/DeprecatedTaskRebalancer.java b/helix-core/src/main/java/org/apache/helix/task/DeprecatedTaskRebalancer.java
index 115237f..d508d1b 100644
--- a/helix-core/src/main/java/org/apache/helix/task/DeprecatedTaskRebalancer.java
+++ b/helix-core/src/main/java/org/apache/helix/task/DeprecatedTaskRebalancer.java
@@ -44,14 +44,13 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.WorkflowControllerDataProvider;
 import org.apache.helix.controller.rebalancer.Rebalancer;
 import org.apache.helix.controller.rebalancer.internal.MappingCalculator;
@@ -62,6 +61,7 @@
 import org.apache.helix.model.Partition;
 import org.apache.helix.model.Resource;
 import org.apache.helix.model.ResourceAssignment;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/task/JobContext.java b/helix-core/src/main/java/org/apache/helix/task/JobContext.java
index 26628a4..9917c83 100644
--- a/helix-core/src/main/java/org/apache/helix/task/JobContext.java
+++ b/helix-core/src/main/java/org/apache/helix/task/JobContext.java
@@ -29,7 +29,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 /**
  * Provides a typed interface to the context information stored by {@link TaskRebalancer} in the
diff --git a/helix-core/src/main/java/org/apache/helix/task/JobDispatcher.java b/helix-core/src/main/java/org/apache/helix/task/JobDispatcher.java
index 90729d6..479b47c 100644
--- a/helix-core/src/main/java/org/apache/helix/task/JobDispatcher.java
+++ b/helix-core/src/main/java/org/apache/helix/task/JobDispatcher.java
@@ -30,7 +30,7 @@
 import java.util.TreeSet;
 
 import com.google.common.collect.ImmutableMap;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.WorkflowControllerDataProvider;
 import org.apache.helix.controller.stages.CurrentStateOutput;
 import org.apache.helix.model.Message;
diff --git a/helix-core/src/main/java/org/apache/helix/task/TaskDriver.java b/helix-core/src/main/java/org/apache/helix/task/TaskDriver.java
index 9eb46fc..0b8fa17 100644
--- a/helix-core/src/main/java/org/apache/helix/task/TaskDriver.java
+++ b/helix-core/src/main/java/org/apache/helix/task/TaskDriver.java
@@ -28,7 +28,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.ConfigAccessor;
@@ -39,17 +38,18 @@
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.SystemPropertyKeys;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.model.builder.CustomModeISBuilder;
 import org.apache.helix.store.HelixPropertyStore;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
 import org.apache.helix.util.HelixUtil;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/task/TaskUtil.java b/helix-core/src/main/java/org/apache/helix/task/TaskUtil.java
index 5c93469..6b36db4 100644
--- a/helix-core/src/main/java/org/apache/helix/task/TaskUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/task/TaskUtil.java
@@ -29,14 +29,13 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.Sets;
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.WorkflowControllerDataProvider;
 import org.apache.helix.controller.rebalancer.util.RebalanceScheduler;
 import org.apache.helix.model.HelixConfigScope;
@@ -44,6 +43,7 @@
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.store.HelixPropertyStore;
 import org.apache.helix.util.RebalanceUtil;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.type.TypeReference;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/task/WorkflowContext.java b/helix-core/src/main/java/org/apache/helix/task/WorkflowContext.java
index c915e3b..3cbdad8 100644
--- a/helix-core/src/main/java/org/apache/helix/task/WorkflowContext.java
+++ b/helix-core/src/main/java/org/apache/helix/task/WorkflowContext.java
@@ -27,7 +27,7 @@
 import java.util.TreeMap;
 
 import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/task/WorkflowDispatcher.java b/helix-core/src/main/java/org/apache/helix/task/WorkflowDispatcher.java
index 1ed9ab0..11d6687 100644
--- a/helix-core/src/main/java/org/apache/helix/task/WorkflowDispatcher.java
+++ b/helix-core/src/main/java/org/apache/helix/task/WorkflowDispatcher.java
@@ -37,7 +37,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.caches.TaskDataCache;
 import org.apache.helix.controller.LogUtil;
 import org.apache.helix.controller.dataproviders.WorkflowControllerDataProvider;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterExternalViewVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterExternalViewVerifier.java
index a342c2e..d10ba71 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterExternalViewVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterExternalViewVerifier.java
@@ -35,7 +35,7 @@
 import org.apache.helix.controller.stages.ClusterEventType;
 import org.apache.helix.controller.stages.CurrentStateComputationStage;
 import org.apache.helix.controller.stages.ResourceComputationStage;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.Partition;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterLiveNodesVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterLiveNodesVerifier.java
index 164cfcc..c5c1311 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterLiveNodesVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterLiveNodesVerifier.java
@@ -22,7 +22,7 @@
 import java.util.Collections;
 import java.util.List;
 
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 
 /**
  * Please use the class is in tools.ClusterVerifiers.
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
index 5d5f864..7ac20a1 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterSetup.java
@@ -27,7 +27,6 @@
 import java.util.List;
 import java.util.Map;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
 import org.apache.commons.cli.GnuParser;
@@ -41,13 +40,13 @@
 import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ClusterConstraints;
@@ -64,6 +63,7 @@
 import org.apache.helix.model.builder.ConstraintItemBuilder;
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.util.HelixUtil;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java
index a398c24..80ccb68 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java
@@ -29,9 +29,6 @@
 import java.util.concurrent.TimeUnit;
 
 import com.google.common.collect.Sets;
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.exception.ZkNodeExistsException;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
 import org.apache.commons.cli.GnuParser;
@@ -45,7 +42,8 @@
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.listeners.PreFetch;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.pipeline.Stage;
@@ -60,14 +58,16 @@
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.Partition;
 import org.apache.helix.model.Resource;
 import org.apache.helix.task.TaskConstants;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNodeExistsException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifier.java
index bbae075..272d8ae 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifier.java
@@ -23,15 +23,15 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.listeners.PreFetch;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
index 66143fe..0efd187 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
@@ -50,8 +50,8 @@
 import org.apache.helix.controller.stages.CurrentStateOutput;
 import org.apache.helix.controller.stages.ResourceComputationStage;
 import org.apache.helix.manager.zk.ZkBucketDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ClusterLiveNodesVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ClusterLiveNodesVerifier.java
index b4d3862..0c284ff 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ClusterLiveNodesVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ClusterLiveNodesVerifier.java
@@ -24,7 +24,7 @@
 import java.util.List;
 import java.util.Set;
 
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 
 public class ClusterLiveNodesVerifier extends ZkHelixClusterVerifier {
 
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
index 0b3c97e..fcc7261 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
@@ -33,8 +33,8 @@
 import org.apache.helix.PropertyKey;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.AbstractRebalancer;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
index 6efdff5..e82fccf 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
@@ -25,17 +25,17 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.api.listeners.PreFetch;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/tools/DefaultIdealStateCalculator.java b/helix-core/src/main/java/org/apache/helix/tools/DefaultIdealStateCalculator.java
index 886790a..ae68a8f 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/DefaultIdealStateCalculator.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/DefaultIdealStateCalculator.java
@@ -27,7 +27,7 @@
 import java.util.TreeMap;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.IdealState.IdealStateProperty;
 
 /**
diff --git a/helix-core/src/main/java/org/apache/helix/tools/IdealCalculatorByConsistentHashing.java b/helix-core/src/main/java/org/apache/helix/tools/IdealCalculatorByConsistentHashing.java
index 1101a6d..8c1e8d7 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/IdealCalculatorByConsistentHashing.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/IdealCalculatorByConsistentHashing.java
@@ -29,7 +29,7 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.IdealState.IdealStateProperty;
 
 public class IdealCalculatorByConsistentHashing {
diff --git a/helix-core/src/main/java/org/apache/helix/tools/IdealStateCalculatorByRush.java b/helix-core/src/main/java/org/apache/helix/tools/IdealStateCalculatorByRush.java
index 7677b42..d776952 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/IdealStateCalculatorByRush.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/IdealStateCalculatorByRush.java
@@ -26,7 +26,7 @@
 import java.util.Random;
 import java.util.TreeMap;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.IdealState.IdealStateProperty;
 
 public class IdealStateCalculatorByRush {
diff --git a/helix-core/src/main/java/org/apache/helix/tools/IdealStateCalculatorByShuffling.java b/helix-core/src/main/java/org/apache/helix/tools/IdealStateCalculatorByShuffling.java
index d4764ef..57bf276 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/IdealStateCalculatorByShuffling.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/IdealStateCalculatorByShuffling.java
@@ -26,7 +26,7 @@
 import java.util.Random;
 import java.util.TreeMap;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.IdealState.IdealStateProperty;
 
 /*
diff --git a/helix-core/src/main/java/org/apache/helix/tools/MessagePoster.java b/helix-core/src/main/java/org/apache/helix/tools/MessagePoster.java
index 4d096e9..b2b5689 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/MessagePoster.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/MessagePoster.java
@@ -22,10 +22,10 @@
 import java.util.UUID;
 
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.model.LiveInstance.LiveInstanceProperty;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.Message.MessageState;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java b/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java
index 992797f..1cbb0f9 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java
@@ -19,7 +19,7 @@
  * under the License.
  */
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.model.LeaderStandbySMD;
 import org.apache.helix.model.MasterSlaveSMD;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/TestExecutor.java b/helix-core/src/main/java/org/apache/helix/tools/TestExecutor.java
index df14af4..565a860 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/TestExecutor.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/TestExecutor.java
@@ -29,17 +29,17 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
 
-import org.I0Itec.zkclient.exception.ZkBadVersionException;
-import org.I0Itec.zkclient.exception.ZkNodeExistsException;
 import org.apache.helix.HelixManager;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.store.PropertyJsonComparator;
 import org.apache.helix.store.PropertyJsonSerializer;
 import org.apache.helix.store.PropertyStoreException;
 import org.apache.helix.tools.TestCommand.CommandType;
+import org.apache.helix.zookeeper.zkclient.exception.ZkBadVersionException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNodeExistsException;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/TestTrigger.java b/helix-core/src/main/java/org/apache/helix/tools/TestTrigger.java
index 7c5bdf4..96253ee 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/TestTrigger.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/TestTrigger.java
@@ -22,7 +22,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 public class TestTrigger {
   public long _startTime;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ZnodeOpArg.java b/helix-core/src/main/java/org/apache/helix/tools/ZnodeOpArg.java
index e60ce8e..53801f6 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ZnodeOpArg.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ZnodeOpArg.java
@@ -22,7 +22,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.tools.TestExecutor.ZnodePropertyType;
 
 public class ZnodeOpArg {
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ZnodeValue.java b/helix-core/src/main/java/org/apache/helix/tools/ZnodeValue.java
index c975b6f..be40449 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ZnodeValue.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ZnodeValue.java
@@ -22,7 +22,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 
 public class ZnodeValue {
   public String _singleValue;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/commandtools/CurrentStateCleanUp.java b/helix-core/src/main/java/org/apache/helix/tools/commandtools/CurrentStateCleanUp.java
index 29bcdf8..5c276f3 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/commandtools/CurrentStateCleanUp.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/commandtools/CurrentStateCleanUp.java
@@ -4,7 +4,6 @@
 import java.util.List;
 import java.util.Set;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Option;
@@ -18,8 +17,9 @@
 import org.apache.helix.HelixManagerFactory;
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.CurrentState;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/main/java/org/apache/helix/tools/commandtools/IntegrationTestUtil.java b/helix-core/src/main/java/org/apache/helix/tools/commandtools/IntegrationTestUtil.java
index ed7f479..ba9628f 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/commandtools/IntegrationTestUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/commandtools/IntegrationTestUtil.java
@@ -33,10 +33,10 @@
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.tools.ClusterExternalViewVerifier;
 import org.apache.helix.tools.ClusterVerifiers.BestPossibleExternalViewVerifier;
 import org.apache.helix.tools.ClusterVerifiers.ClusterLiveNodesVerifier;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/commandtools/LocalZKServer.java b/helix-core/src/main/java/org/apache/helix/tools/commandtools/LocalZKServer.java
index bb13380..d15d39f 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/commandtools/LocalZKServer.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/commandtools/LocalZKServer.java
@@ -19,9 +19,10 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.IDefaultNameSpace;
-import org.I0Itec.zkclient.ZkClient;
-import org.I0Itec.zkclient.ZkServer;
+import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+
 
 /**
  * Provides ability to start zookeeper locally on a particular port
diff --git a/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZKDumper.java b/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZKDumper.java
index 7d9eeec..bb69e9d 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZKDumper.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZKDumper.java
@@ -28,7 +28,6 @@
 import java.io.OutputStream;
 import java.util.List;
 
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
 import org.apache.commons.cli.HelpFormatter;
@@ -38,8 +37,10 @@
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.PosixParser;
 import org.apache.helix.manager.zk.ByteArraySerializer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+
 
 /**
  * Dumps the Zookeeper file structure on to Disk
diff --git a/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZkCopy.java b/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZkCopy.java
index 805847c..2ea48a3 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZkCopy.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZkCopy.java
@@ -37,8 +37,8 @@
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.manager.zk.ByteArraySerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.zookeeper.common.PathUtils;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
diff --git a/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZkLogCSVFormatter.java b/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZkLogCSVFormatter.java
index d9d4f98..3052441 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZkLogCSVFormatter.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/commandtools/ZkLogCSVFormatter.java
@@ -32,7 +32,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.model.IdealState.IdealStateProperty;
 import org.apache.helix.util.HelixUtil;
diff --git a/helix-core/src/main/java/org/apache/helix/util/ExponentialBackoffStrategy.java b/helix-core/src/main/java/org/apache/helix/util/ExponentialBackoffStrategy.java
index b1a66c9..40b81e3 100644
--- a/helix-core/src/main/java/org/apache/helix/util/ExponentialBackoffStrategy.java
+++ b/helix-core/src/main/java/org/apache/helix/util/ExponentialBackoffStrategy.java
@@ -1,33 +1,31 @@
 package org.apache.helix.util;
 
-import java.util.Random;
+/*
+ * 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.
+ */
 
-public class ExponentialBackoffStrategy {
-  private final long INIT_RETRY_INTERVAL = 500;
-  private final long _maxRetryInterval;
-  private final boolean _addJitter;
-  private final Random _ran;
-
+/**
+ * Use ExponentialBackoffStrategy in zookeeper-api module instead.
+ */
+@Deprecated
+public class ExponentialBackoffStrategy
+    extends org.apache.helix.zookeeper.zkclient.util.ExponentialBackoffStrategy {
   public ExponentialBackoffStrategy(long maxRetryInterval, boolean addJitter) {
-    _maxRetryInterval = maxRetryInterval;
-    _addJitter = addJitter;
-    _ran = new Random(System.currentTimeMillis());
-  }
-
-  public long getNextWaitInterval(int numberOfTriesFailed) {
-    double exponentialMultiplier = Math.pow(2.0, numberOfTriesFailed - 1);
-    double result = exponentialMultiplier * INIT_RETRY_INTERVAL;
-
-    if (_maxRetryInterval > 0 && result > _maxRetryInterval) {
-      result = _maxRetryInterval;
-    }
-
-    if (_addJitter) {
-      // Adding jitter so the real result would be 75% to 100% of the original result.
-      // Don't directly add jitter here, since it may exceed the max retry interval setup
-      result = result * (0.75 + _ran.nextDouble() % 0.25);
-    }
-
-    return (long) result;
+    super(maxRetryInterval, addJitter);
   }
 }
diff --git a/helix-core/src/main/java/org/apache/helix/util/GZipCompressionUtil.java b/helix-core/src/main/java/org/apache/helix/util/GZipCompressionUtil.java
index f29a301..358c969 100644
--- a/helix-core/src/main/java/org/apache/helix/util/GZipCompressionUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/util/GZipCompressionUtil.java
@@ -19,55 +19,9 @@
  * under the License.
  */
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.GZIPOutputStream;
-
-public class GZipCompressionUtil {
-  /**
-   * Compresses a byte array by applying GZIP compression
-   * @param serializedBytes
-   * @return
-   * @throws IOException
-   */
-  public static byte[] compress(byte[] buffer) throws IOException {
-    ByteArrayOutputStream gzipByteArrayOutputStream = new ByteArrayOutputStream();
-    GZIPOutputStream gzipOutputStream = null;
-    gzipOutputStream = new GZIPOutputStream(gzipByteArrayOutputStream);
-    gzipOutputStream.write(buffer, 0, buffer.length);
-    gzipOutputStream.close();
-    byte[] compressedBytes = gzipByteArrayOutputStream.toByteArray();
-    return compressedBytes;
-  }
-
-  public static byte[] uncompress(ByteArrayInputStream bais) throws IOException {
-    GZIPInputStream gzipInputStream = new GZIPInputStream(bais);
-    byte[] buffer = new byte[1024];
-    int length;
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    while ((length = gzipInputStream.read(buffer)) != -1) {
-      baos.write(buffer, 0, length);
-    }
-    gzipInputStream.close();
-    baos.close();
-    byte[] uncompressedBytes = baos.toByteArray();
-    return uncompressedBytes;
-  }
-
-  /*
-   * Determines if a byte array is compressed. The java.util.zip GZip
-   * implementaiton does not expose the GZip header so it is difficult to determine
-   * if a string is compressed.
-   * @param bytes an array of bytes
-   * @return true if the array is compressed or false otherwise
-   */
-  public static boolean isCompressed(byte[] bytes) {
-    if ((bytes == null) || (bytes.length < 2)) {
-      return false;
-    } else {
-      return ((bytes[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (bytes[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)));
-    }
-  }
+/**
+ * Deprecated - please use GZipCompressionUtil in zookeeper-api.
+ */
+@Deprecated
+public class GZipCompressionUtil extends org.apache.helix.zookeeper.util.GZipCompressionUtil {
 }
diff --git a/helix-core/src/main/java/org/apache/helix/util/StatusUpdateUtil.java b/helix-core/src/main/java/org/apache/helix/util/StatusUpdateUtil.java
index 1249fec..7a82347 100644
--- a/helix-core/src/main/java/org/apache/helix/util/StatusUpdateUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/util/StatusUpdateUtil.java
@@ -39,7 +39,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.Error;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.Message.MessageType;
diff --git a/helix-core/src/main/java/org/apache/helix/util/WeightAwareRebalanceUtil.java b/helix-core/src/main/java/org/apache/helix/util/WeightAwareRebalanceUtil.java
index 7bef7b7..f6f692a 100644
--- a/helix-core/src/main/java/org/apache/helix/util/WeightAwareRebalanceUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/util/WeightAwareRebalanceUtil.java
@@ -7,7 +7,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.RebalanceConfig;
 import org.apache.helix.api.rebalancer.constraint.AbstractRebalanceHardConstraint;
 import org.apache.helix.api.rebalancer.constraint.AbstractRebalanceSoftConstraint;
diff --git a/helix-core/src/main/java/org/apache/helix/util/ZKClientPool.java b/helix-core/src/main/java/org/apache/helix/util/ZKClientPool.java
index 3350d57..51b1ab9 100644
--- a/helix-core/src/main/java/org/apache/helix/util/ZKClientPool.java
+++ b/helix-core/src/main/java/org/apache/helix/util/ZKClientPool.java
@@ -23,7 +23,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.zookeeper.ZooKeeper.States;
 
 public class ZKClientPool {
diff --git a/helix-core/src/main/java/org/apache/helix/util/ZNRecordUtil.java b/helix-core/src/main/java/org/apache/helix/util/ZNRecordUtil.java
index 989b54a..d2127b9 100644
--- a/helix-core/src/main/java/org/apache/helix/util/ZNRecordUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/util/ZNRecordUtil.java
@@ -25,12 +25,13 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 //TODO find a proper place for these methods
-public final class ZNRecordUtil {
+public class ZNRecordUtil {
   private static final Logger logger = LoggerFactory.getLogger(ZNRecordUtil.class.getName());
 
   private ZNRecordUtil() {
diff --git a/helix-core/src/test/java/org/apache/helix/MockAccessor.java b/helix-core/src/test/java/org/apache/helix/MockAccessor.java
index c0a0e46..4e9a014 100644
--- a/helix-core/src/test/java/org/apache/helix/MockAccessor.java
+++ b/helix-core/src/test/java/org/apache/helix/MockAccessor.java
@@ -24,15 +24,18 @@
 import java.util.List;
 import java.util.Map;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.mock.MockBaseDataAccessor;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.model.MaintenanceSignal;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.PauseSignal;
 import org.apache.helix.model.StateModelDefinition;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.apache.zookeeper.data.Stat;
+import org.apache.helix.zookeeper.datamodel.ZNRecordUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 public class MockAccessor implements HelixDataAccessor {
   private final String _clusterName;
@@ -69,11 +72,13 @@
     return false;
   }
 
-  @Override public boolean createMaintenance(MaintenanceSignal maintenanceSignal) {
+  @Override
+  public boolean createMaintenance(MaintenanceSignal maintenanceSignal) {
     return false;
   }
 
-  @Override public boolean setProperty(PropertyKey key, HelixProperty value) {
+  @Override
+  public boolean setProperty(PropertyKey key, HelixProperty value) {
     String path = key.getPath();
     _baseDataAccessor.set(path, value.getRecord(), AccessOption.PERSISTENT);
     return true;
@@ -81,11 +86,12 @@
 
   @Override
   public <T extends HelixProperty> boolean updateProperty(PropertyKey key, T value) {
-    return updateProperty(key, new ZNRecordUpdater(value.getRecord()) , value);
+    return updateProperty(key, new ZNRecordUpdater(value.getRecord()), value);
   }
 
   @Override
-  public <T extends HelixProperty> boolean updateProperty(PropertyKey key, DataUpdater<ZNRecord> updater, T value) {
+  public <T extends HelixProperty> boolean updateProperty(PropertyKey key,
+      DataUpdater<ZNRecord> updater, T value) {
     String path = key.getPath();
     PropertyType type = key.getType();
     if (type.updateOnlyOnExists) {
@@ -143,7 +149,8 @@
     try {
       Stat stat = _baseDataAccessor.getStat(path, 0);
       if (stat != null) {
-        return new HelixProperty.Stat(stat.getVersion(), stat.getCtime(), stat.getMtime(), stat.getEphemeralOwner());
+        return new HelixProperty.Stat(stat.getVersion(), stat.getCtime(), stat.getMtime(),
+            stat.getEphemeralOwner());
       }
     } catch (ZkNoNodeException e) {
 
@@ -170,7 +177,8 @@
       HelixProperty.Stat propertyStat = null;
       if (zkStat != null) {
         propertyStat =
-            new HelixProperty.Stat(zkStat.getVersion(), zkStat.getCtime(), zkStat.getMtime(), zkStat.getEphemeralOwner());
+            new HelixProperty.Stat(zkStat.getVersion(), zkStat.getCtime(), zkStat.getMtime(),
+                zkStat.getEphemeralOwner());
       }
       propertyStats.add(propertyStat);
     }
@@ -185,14 +193,15 @@
   }
 
   @SuppressWarnings("unchecked")
-  @Override public <T extends HelixProperty> List<T> getChildValues(PropertyKey propertyKey) {
+  @Override
+  public <T extends HelixProperty> List<T> getChildValues(PropertyKey propertyKey) {
     String path = propertyKey.getPath(); // PropertyPathConfig.getPath(type,
     List<ZNRecord> children = _baseDataAccessor.getChildren(path, null, 0);
     return (List<T>) HelixProperty.convertToTypedList(propertyKey.getTypeClass(), children);
   }
 
-  @Override public <T extends HelixProperty> List<T> getChildValues(PropertyKey key,
-      boolean throwException) {
+  @Override
+  public <T extends HelixProperty> List<T> getChildValues(PropertyKey key, boolean throwException) {
     return getChildValues(key);
   }
 
@@ -202,7 +211,8 @@
     return HelixProperty.convertListToMap(list);
   }
 
-  @Override public <T extends HelixProperty> Map<String, T> getChildValuesMap(PropertyKey key,
+  @Override
+  public <T extends HelixProperty> Map<String, T> getChildValuesMap(PropertyKey key,
       boolean throwException) {
     return getChildValuesMap(key);
   }
@@ -250,7 +260,8 @@
     return list;
   }
 
-  @Override public <T extends HelixProperty> List<T> getProperty(List<PropertyKey> keys,
+  @Override
+  public <T extends HelixProperty> List<T> getProperty(List<PropertyKey> keys,
       boolean throwException) {
     return getProperty(keys);
   }
diff --git a/helix-core/src/test/java/org/apache/helix/TestEspressoStorageClusterIdealState.java b/helix-core/src/test/java/org/apache/helix/TestEspressoStorageClusterIdealState.java
index 6cad5af..18cc84b 100644
--- a/helix-core/src/test/java/org/apache/helix/TestEspressoStorageClusterIdealState.java
+++ b/helix-core/src/test/java/org/apache/helix/TestEspressoStorageClusterIdealState.java
@@ -33,6 +33,8 @@
 import org.testng.Assert;
 import org.testng.AssertJUnit;
 import org.testng.annotations.Test;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 public class TestEspressoStorageClusterIdealState {
   @Test()
@@ -82,7 +84,6 @@
     slaveKeepRatio = result[1];
     Assert.assertTrue(0.66 < masterKeepRatio && 0.67 > masterKeepRatio);
     Assert.assertTrue(0.49 < slaveKeepRatio && 0.51 > slaveKeepRatio);
-
   }
 
   @Test
@@ -209,7 +210,6 @@
         AssertJUnit.assertTrue(slaveCountMap.get(masterPartitionId) == replicas);
       }
     }
-
   }
 
   public void printStat(Map<String, Object> result) {
@@ -259,8 +259,9 @@
     }
 
     result[0] = 1.0 * commonMasters / partitions;
-    System.out.println(commonMasters + " master partitions are kept, "
-        + (partitions - commonMasters) + " moved, keep ratio:" + 1.0 * commonMasters / partitions);
+    System.out.println(
+        commonMasters + " master partitions are kept, " + (partitions - commonMasters)
+            + " moved, keep ratio:" + 1.0 * commonMasters / partitions);
 
     // maps from the partition id to the instance names that holds its slave partition
     Map<Integer, Set<String>> slaveMap1 = new TreeMap<Integer, Set<String>>();
@@ -290,10 +291,9 @@
       }
     }
     result[1] = 1.0 * commonSlaves / partitions / replicas;
-    System.out.println(commonSlaves + " slave partitions are kept, "
-        + (partitions * replicas - commonSlaves) + " moved. keep ratio:" + 1.0 * commonSlaves
-        / partitions / replicas);
+    System.out.println(
+        commonSlaves + " slave partitions are kept, " + (partitions * replicas - commonSlaves)
+            + " moved. keep ratio:" + 1.0 * commonSlaves / partitions / replicas);
     return result;
   }
-
 }
diff --git a/helix-core/src/test/java/org/apache/helix/TestGroupCommit.java b/helix-core/src/test/java/org/apache/helix/TestGroupCommit.java
index 8465033..cd8d598 100644
--- a/helix-core/src/test/java/org/apache/helix/TestGroupCommit.java
+++ b/helix-core/src/test/java/org/apache/helix/TestGroupCommit.java
@@ -23,6 +23,8 @@
 import java.util.concurrent.Executors;
 
 import org.apache.helix.mock.MockBaseDataAccessor;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 public class TestGroupCommit {
   // @Test
@@ -38,7 +40,6 @@
     System.out.println(accessor.get("test", null, 0));
     System.out.println(accessor.get("test", null, 0).getSimpleFields().size());
   }
-
 }
 
 class MyClass implements Runnable {
@@ -63,5 +64,4 @@
     // System.out.println("END " + System.currentTimeMillis() + " --"
     // + Thread.currentThread().getId());
   }
-
 }
diff --git a/helix-core/src/test/java/org/apache/helix/TestHelper.java b/helix-core/src/test/java/org/apache/helix/TestHelper.java
index fa93a72..9cac992 100644
--- a/helix-core/src/test/java/org/apache/helix/TestHelper.java
+++ b/helix-core/src/test/java/org/apache/helix/TestHelper.java
@@ -38,11 +38,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import org.I0Itec.zkclient.IDefaultNameSpace;
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.ZkServer;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.commons.io.FileUtils;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.integration.manager.ZkTestManager;
@@ -51,8 +46,8 @@
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState.RebalanceMode;
@@ -63,11 +58,19 @@
 import org.apache.helix.store.zk.ZNode;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.util.ZKClientPool;
+import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 
+
 public class TestHelper {
   private static final Logger LOG = LoggerFactory.getLogger(TestHelper.class);
   public static final long WAIT_DURATION = 20 * 1000L; // 20 seconds
@@ -103,8 +106,8 @@
 
   static public ZkServer startZkServer(final String zkAddress, final List<String> rootNamespaces,
       boolean overwrite) throws Exception {
-    System.out.println("Start zookeeper at " + zkAddress + " in thread "
-        + Thread.currentThread().getName());
+    System.out.println(
+        "Start zookeeper at " + zkAddress + " in thread " + Thread.currentThread().getName());
 
     String zkDir = zkAddress.replace(':', '_');
     final String logDir = "/tmp/" + zkDir + "/logs";
@@ -117,7 +120,7 @@
 
     IDefaultNameSpace defaultNameSpace = new IDefaultNameSpace() {
       @Override
-      public void createDefaultNameSpace(org.I0Itec.zkclient.ZkClient zkClient) {
+      public void createDefaultNameSpace(ZkClient zkClient) {
         if (rootNamespaces == null) {
           return;
         }
@@ -142,8 +145,9 @@
   static public void stopZkServer(ZkServer zkServer) {
     if (zkServer != null) {
       zkServer.shutdown();
-      System.out.println("Shut down zookeeper at port " + zkServer.getPort() + " in thread "
-          + Thread.currentThread().getName());
+      System.out.println(
+          "Shut down zookeeper at port " + zkServer.getPort() + " in thread " + Thread
+              .currentThread().getName());
     }
   }
 
@@ -186,8 +190,8 @@
       // debug
       // LOG.info(verifierName + ": wait " + ((i + 1) * 1000) + "ms to verify ("
       // + result + ")");
-      System.err.println(verifierName + ": wait " + ((i + 1) * 1000) + "ms to verify " + " ("
-          + result + ")");
+      System.err.println(
+          verifierName + ": wait " + ((i + 1) * 1000) + "ms to verify " + " (" + result + ")");
       LOG.debug("args:" + Arrays.toString(args));
       // System.err.println("args:" + Arrays.toString(args));
 
@@ -240,7 +244,6 @@
         if (extView != null && extView.getRecord().getMapFields().size() != 0) {
           return false;
         }
-
       }
 
       return true;
@@ -256,9 +259,10 @@
   public static void setupCluster(String clusterName, String zkAddr, int startPort,
       String participantNamePrefix, String resourceNamePrefix, int resourceNb, int partitionNb,
       int nodesNb, int replica, String stateModelDef, boolean doRebalance) throws Exception {
-    TestHelper.setupCluster(clusterName, zkAddr, startPort, participantNamePrefix,
-        resourceNamePrefix, resourceNb, partitionNb, nodesNb, replica, stateModelDef,
-        RebalanceMode.SEMI_AUTO, doRebalance);
+    TestHelper
+        .setupCluster(clusterName, zkAddr, startPort, participantNamePrefix, resourceNamePrefix,
+            resourceNb, partitionNb, nodesNb, replica, stateModelDef, RebalanceMode.SEMI_AUTO,
+            doRebalance);
   }
 
   public static void setupCluster(String clusterName, String zkAddr, int startPort,
@@ -338,12 +342,13 @@
         ExternalView extView = accessor.getProperty(keyBuilder.externalView(resGroup));
         for (String instance : stateMap.get(resGroupPartitionKey)) {
           String actualState = extView.getStateMap(partitionKey).get(instance);
-          Assert.assertNotNull(actualState, "externalView doesn't contain state for " + resGroup
-              + "/" + partitionKey + " on " + instance + " (expect " + state + ")");
+          Assert.assertNotNull(actualState,
+              "externalView doesn't contain state for " + resGroup + "/" + partitionKey + " on "
+                  + instance + " (expect " + state + ")");
 
-          Assert
-              .assertEquals(actualState, state, "externalView for " + resGroup + "/" + partitionKey
-                  + " on " + instance + " is " + actualState + " (expect " + state + ")");
+          Assert.assertEquals(actualState, state,
+              "externalView for " + resGroup + "/" + partitionKey + " on " + instance + " is "
+                  + actualState + " (expect " + state + ")");
         }
       }
     } finally {
@@ -519,8 +524,9 @@
       ZNode node = map.get(key);
       TreeSet<String> childSet = new TreeSet<String>();
       childSet.addAll(node.getChildSet());
-      System.out.print(key + "=" + node.getData() + ", " + childSet + ", "
-          + (node.getStat() == null ? "null\n" : node.getStat()));
+      System.out.print(
+          key + "=" + node.getData() + ", " + childSet + ", " + (node.getStat() == null ? "null\n"
+              : node.getStat()));
     }
     System.out.println("END:Print cache");
   }
@@ -600,8 +606,8 @@
       Map<String, ZNode> cache, Map<String, ZNode> zkMap, boolean needVerifyStat) {
     // equal size
     if (zkMap.size() != cache.size()) {
-      System.err.println("size mismatch: cacheSize: " + cache.size() + ", zkMapSize: "
-          + zkMap.size());
+      System.err
+          .println("size mismatch: cacheSize: " + cache.size() + ", zkMapSize: " + zkMap.size());
       System.out.println("cache: (" + cache.size() + ")");
       TestHelper.printCache(cache);
 
@@ -622,31 +628,33 @@
         return false;
       }
 
-      if ((zkNode.getData() == null && cacheNode.getData() != null)
-          || (zkNode.getData() != null && cacheNode.getData() == null)
-          || (zkNode.getData() != null && cacheNode.getData() != null && !zkNode.getData().equals(
-              cacheNode.getData()))) {
+      if ((zkNode.getData() == null && cacheNode.getData() != null) || (zkNode.getData() != null
+          && cacheNode.getData() == null) || (zkNode.getData() != null
+          && cacheNode.getData() != null && !zkNode.getData().equals(cacheNode.getData()))) {
         // data not equal
-        System.err.println("data mismatch on path: " + path + ", inCache: " + cacheNode.getData()
-            + ", onZk: " + zkNode.getData());
+        System.err.println(
+            "data mismatch on path: " + path + ", inCache: " + cacheNode.getData() + ", onZk: "
+                + zkNode.getData());
         return false;
       }
 
-      if ((zkNode.getChildSet() == null && cacheNode.getChildSet() != null)
-          || (zkNode.getChildSet() != null && cacheNode.getChildSet() == null)
-          || (zkNode.getChildSet() != null && cacheNode.getChildSet() != null && !zkNode
-              .getChildSet().equals(cacheNode.getChildSet()))) {
+      if ((zkNode.getChildSet() == null && cacheNode.getChildSet() != null) || (
+          zkNode.getChildSet() != null && cacheNode.getChildSet() == null) || (
+          zkNode.getChildSet() != null && cacheNode.getChildSet() != null && !zkNode.getChildSet()
+              .equals(cacheNode.getChildSet()))) {
         // childSet not equal
-        System.err.println("childSet mismatch on path: " + path + ", inCache: "
-            + cacheNode.getChildSet() + ", onZk: " + zkNode.getChildSet());
+        System.err.println(
+            "childSet mismatch on path: " + path + ", inCache: " + cacheNode.getChildSet()
+                + ", onZk: " + zkNode.getChildSet());
         return false;
       }
 
       if (needVerifyStat && pathsExcludeForStat != null && !pathsExcludeForStat.contains(path)) {
         if (cacheNode.getStat() == null || !zkNode.getStat().equals(cacheNode.getStat())) {
           // stat not equal
-          System.err.println("Stat mismatch on path: " + path + ", inCache: " + cacheNode.getStat()
-              + ", onZk: " + zkNode.getStat());
+          System.err.println(
+              "Stat mismatch on path: " + path + ", inCache: " + cacheNode.getStat() + ", onZk: "
+                  + zkNode.getStat());
           return false;
         }
       }
@@ -800,8 +808,8 @@
     for (int i = 0; i < handlers.size(); i++) {
       CallbackHandler handler = handlers.get(i);
       String path = handler.getPath();
-      sb.append(path.substring(manager.getClusterName().length() + 1) + ": "
-          + handler.getListener());
+      sb.append(
+          path.substring(manager.getClusterName().length() + 1) + ": " + handler.getListener());
       if (i < (handlers.size() - 1)) {
         sb.append(", ");
       }
diff --git a/helix-core/src/test/java/org/apache/helix/TestHierarchicalDataStore.java b/helix-core/src/test/java/org/apache/helix/TestHierarchicalDataStore.java
index aafcbf1..76ba625 100644
--- a/helix-core/src/test/java/org/apache/helix/TestHierarchicalDataStore.java
+++ b/helix-core/src/test/java/org/apache/helix/TestHierarchicalDataStore.java
@@ -22,8 +22,8 @@
 import java.io.FileFilter;
 
 import org.apache.helix.controller.HierarchicalDataHolder;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.testng.AssertJUnit;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/TestShuffledIdealState.java b/helix-core/src/test/java/org/apache/helix/TestShuffledIdealState.java
index 1297322..b5c9b26 100644
--- a/helix-core/src/test/java/org/apache/helix/TestShuffledIdealState.java
+++ b/helix-core/src/test/java/org/apache/helix/TestShuffledIdealState.java
@@ -34,6 +34,8 @@
 import org.testng.Assert;
 import org.testng.AssertJUnit;
 import org.testng.annotations.Test;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 public class TestShuffledIdealState {
   @Test()
@@ -46,19 +48,17 @@
     instanceNames.add("localhost_1233");
     instanceNames.add("localhost_1234");
 
-    ZNRecord result =
-        IdealStateCalculatorByShuffling.calculateIdealState(instanceNames, partitions, replicas,
-            dbName);
+    ZNRecord result = IdealStateCalculatorByShuffling
+        .calculateIdealState(instanceNames, partitions, replicas, dbName);
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "MASTER");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "SLAVE");
 
-    ZNRecord result2 =
-        IdealStateCalculatorByRush.calculateIdealState(instanceNames, 1, partitions, replicas,
-            dbName);
+    ZNRecord result2 = IdealStateCalculatorByRush
+        .calculateIdealState(instanceNames, 1, partitions, replicas, dbName);
 
-    ZNRecord result3 =
-        IdealCalculatorByConsistentHashing.calculateIdealState(instanceNames, partitions, replicas,
-            dbName, new IdealCalculatorByConsistentHashing.FnvHash());
+    ZNRecord result3 = IdealCalculatorByConsistentHashing
+        .calculateIdealState(instanceNames, partitions, replicas, dbName,
+            new IdealCalculatorByConsistentHashing.FnvHash());
     IdealCalculatorByConsistentHashing.printIdealStateStats(result3, "MASTER");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result3, "SLAVE");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result3, "");
@@ -95,7 +95,6 @@
     System.out.println(zn3.toString());
     AssertJUnit.assertTrue(zn3.toString().equalsIgnoreCase(result3.toString()));
     System.out.println();
-
   }
 
   @Test
@@ -109,9 +108,8 @@
     instanceNames.add("localhost_1233");
     instanceNames.add("localhost_1234");
 
-    ZNRecord result =
-        IdealStateCalculatorByShuffling.calculateIdealState(instanceNames, partitions, replicas,
-            dbName);
+    ZNRecord result = IdealStateCalculatorByShuffling
+        .calculateIdealState(instanceNames, partitions, replicas, dbName);
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "MASTER");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "SLAVE");
     Assert.assertTrue(verify(result));
@@ -125,9 +123,8 @@
     for (int i = 0; i < instances; i++) {
       instanceNames.add("localhost_" + (1231 + i));
     }
-    result =
-        IdealStateCalculatorByShuffling.calculateIdealState(instanceNames, partitions, replicas,
-            dbName);
+    result = IdealStateCalculatorByShuffling
+        .calculateIdealState(instanceNames, partitions, replicas, dbName);
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "MASTER");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "SLAVE");
     Assert.assertTrue(verify(result));
@@ -141,9 +138,8 @@
     for (int i = 0; i < instances; i++) {
       instanceNames.add("localhost_" + (1231 + i));
     }
-    result =
-        IdealStateCalculatorByShuffling.calculateIdealState(instanceNames, partitions, replicas,
-            dbName);
+    result = IdealStateCalculatorByShuffling
+        .calculateIdealState(instanceNames, partitions, replicas, dbName);
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "MASTER");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "SLAVE");
     Assert.assertTrue(verify(result));
@@ -157,9 +153,8 @@
     for (int i = 0; i < instances; i++) {
       instanceNames.add("localhost_" + (1231 + i));
     }
-    result =
-        IdealStateCalculatorByShuffling.calculateIdealState(instanceNames, partitions, replicas,
-            dbName);
+    result = IdealStateCalculatorByShuffling
+        .calculateIdealState(instanceNames, partitions, replicas, dbName);
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "MASTER");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "SLAVE");
     Assert.assertTrue(verify(result));
@@ -173,9 +168,8 @@
     for (int i = 0; i < instances; i++) {
       instanceNames.add("localhost_" + (1231 + i));
     }
-    result =
-        IdealStateCalculatorByShuffling.calculateIdealState(instanceNames, partitions, replicas,
-            dbName);
+    result = IdealStateCalculatorByShuffling
+        .calculateIdealState(instanceNames, partitions, replicas, dbName);
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "MASTER");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "SLAVE");
     Assert.assertTrue(verify(result));
@@ -189,9 +183,8 @@
     for (int i = 0; i < instances; i++) {
       instanceNames.add("localhost_" + (1231 + i));
     }
-    result =
-        IdealStateCalculatorByShuffling.calculateIdealState(instanceNames, partitions, replicas,
-            dbName);
+    result = IdealStateCalculatorByShuffling
+        .calculateIdealState(instanceNames, partitions, replicas, dbName);
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "MASTER");
     IdealCalculatorByConsistentHashing.printIdealStateStats(result, "SLAVE");
     Assert.assertTrue(verify(result));
diff --git a/helix-core/src/test/java/org/apache/helix/TestZKRoutingInfoProvider.java b/helix-core/src/test/java/org/apache/helix/TestZKRoutingInfoProvider.java
index 5addcfe..d1e22f7 100644
--- a/helix-core/src/test/java/org/apache/helix/TestZKRoutingInfoProvider.java
+++ b/helix-core/src/test/java/org/apache/helix/TestZKRoutingInfoProvider.java
@@ -32,6 +32,8 @@
 import org.apache.helix.model.Message;
 import org.testng.AssertJUnit;
 import org.testng.annotations.Test;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 public class TestZKRoutingInfoProvider {
   public Map<String, List<ZNRecord>> createCurrentStates(String[] dbNames, String[] nodeNames,
diff --git a/helix-core/src/test/java/org/apache/helix/TestZkBasis.java b/helix-core/src/test/java/org/apache/helix/TestZkBasis.java
index ef6d054..2b54ec0 100644
--- a/helix-core/src/test/java/org/apache/helix/TestZkBasis.java
+++ b/helix-core/src/test/java/org/apache/helix/TestZkBasis.java
@@ -27,11 +27,11 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/ZkTestHelper.java b/helix-core/src/test/java/org/apache/helix/ZkTestHelper.java
index 5b99fa4..c2b3d35 100644
--- a/helix-core/src/test/java/org/apache/helix/ZkTestHelper.java
+++ b/helix-core/src/test/java/org/apache/helix/ZkTestHelper.java
@@ -38,16 +38,16 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.zookeeper.IZkStateListener;
-import org.apache.helix.manager.zk.zookeeper.ZkConnection;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.IZkStateListener;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkConnection;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
 import org.apache.zookeeper.Watcher.Event.EventType;
@@ -57,6 +57,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+
 
 public class ZkTestHelper {
   private static Logger LOG = LoggerFactory.getLogger(ZkTestHelper.class);
@@ -116,7 +118,7 @@
     };
 
     zkClient.subscribeStateChanges(listener);
-    ZkConnection connection = ((ZkConnection) zkClient.getConnection());
+    ZkConnection connection = (ZkConnection) zkClient.getConnection();
     ZooKeeper curZookeeper = connection.getZookeeper();
     LOG.info("Before expiry. sessionId: " + Long.toHexString(curZookeeper.getSessionId()));
 
@@ -202,8 +204,8 @@
 
     String newSessionId = Long.toHexString(curZookeeper.getSessionId());
     LOG.info("After session expiry. sessionId: " + newSessionId + ", zk: " + curZookeeper);
-    Assert.assertFalse(newSessionId.equals(oldSessionId), "Fail to expire current session, zk: "
-        + curZookeeper);
+    Assert.assertFalse(newSessionId.equals(oldSessionId),
+        "Fail to expire current session, zk: " + curZookeeper);
   }
 
   /**
@@ -270,8 +272,9 @@
             String expectState = expectInstanceStateMap.get(expectInstance);
             boolean equals = expectState.equals(actualState);
             if (op.equals("==") && !equals || op.equals("!=") && equals) {
-              System.out.println(partition + "/" + instance + " state mismatch. actual state: "
-                  + actualState + ", but expect: " + expectState + ", op: " + op);
+              System.out.println(
+                  partition + "/" + instance + " state mismatch. actual state: " + actualState
+                      + ", but expect: " + expectState + ", op: " + op);
               result = false;
             }
           }
@@ -345,8 +348,9 @@
         // so add this retry logic
         retry--;
       } finally {
-        if (sock != null)
+        if (sock != null) {
           sock.close();
+        }
       }
     }
     return listenerMap;
diff --git a/helix-core/src/test/java/org/apache/helix/common/ZkTestBase.java b/helix-core/src/test/java/org/apache/helix/common/ZkTestBase.java
index 61c2544..b9c08a9 100644
--- a/helix-core/src/test/java/org/apache/helix/common/ZkTestBase.java
+++ b/helix-core/src/test/java/org/apache/helix/common/ZkTestBase.java
@@ -33,7 +33,6 @@
 import javax.management.MBeanServerConnection;
 import javax.management.ObjectName;
 
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.ConfigAccessor;
 import org.apache.helix.HelixAdmin;
@@ -45,7 +44,8 @@
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.SystemPropertyKeys;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.HelixConfigProperty;
 import org.apache.helix.controller.pipeline.AbstractAsyncBaseStage;
 import org.apache.helix.controller.pipeline.Pipeline;
@@ -60,11 +60,8 @@
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.zookeeper.IZkStateListener;
-import org.apache.helix.manager.zk.zookeeper.ZkConnection;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ConfigScope;
@@ -81,6 +78,9 @@
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.tools.ClusterStateVerifier;
 import org.apache.helix.tools.StateModelConfigGenerator;
+import org.apache.helix.zookeeper.zkclient.IZkStateListener;
+import org.apache.helix.zookeeper.zkclient.ZkConnection;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
 import org.apache.zookeeper.ZooKeeper;
diff --git a/helix-core/src/test/java/org/apache/helix/common/caches/TestCurrentStateSnapshot.java b/helix-core/src/test/java/org/apache/helix/common/caches/TestCurrentStateSnapshot.java
index 62514a4..b9b6270 100644
--- a/helix-core/src/test/java/org/apache/helix/common/caches/TestCurrentStateSnapshot.java
+++ b/helix-core/src/test/java/org/apache/helix/common/caches/TestCurrentStateSnapshot.java
@@ -7,7 +7,7 @@
 
 import org.apache.helix.MockAccessor;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.LiveInstance;
 import org.testng.Assert;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/changedetector/TestResourceChangeDetector.java b/helix-core/src/test/java/org/apache/helix/controller/changedetector/TestResourceChangeDetector.java
index bac9842..000964b 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/changedetector/TestResourceChangeDetector.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/changedetector/TestResourceChangeDetector.java
@@ -276,11 +276,16 @@
    * Remove an instance completely and see if detector detects.
    */
   @Test(dependsOnMethods = "testDisconnectReconnectInstance")
-  public void testRemoveInstance() {
+  public void testRemoveInstance()
+      throws Exception {
     _participants[0].syncStop();
     InstanceConfig instanceConfig =
         _dataAccessor.getProperty(_keyBuilder.instanceConfig(_participants[0].getInstanceName()));
     _gSetupTool.getClusterManagementTool().dropInstance(CLUSTER_NAME, instanceConfig);
+    // Verify that instance has been removed
+    Assert.assertTrue(TestHelper.verify(() -> _dataAccessor
+        .getProperty(_dataAccessor.keyBuilder().instance(_participants[0].getInstanceName()))
+        == null, TestHelper.WAIT_DURATION));
 
     _dataProvider.notifyDataChange(ChangeType.LIVE_INSTANCE);
     _dataProvider.notifyDataChange(ChangeType.INSTANCE_CONFIG);
diff --git a/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestAutoRebalanceStrategy.java b/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestAutoRebalanceStrategy.java
index 26c8e62..0b1370e 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestAutoRebalanceStrategy.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestAutoRebalanceStrategy.java
@@ -41,7 +41,7 @@
 import org.apache.helix.HelixDefinedState;
 import org.apache.helix.MockAccessor;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.strategy.AutoRebalanceStrategy;
 import org.apache.helix.controller.rebalancer.strategy.RebalanceStrategy;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestAutoRebalanceStrategyImbalanceAssignment.java b/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestAutoRebalanceStrategyImbalanceAssignment.java
index a15077c..dc4f0d4 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestAutoRebalanceStrategyImbalanceAssignment.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestAutoRebalanceStrategyImbalanceAssignment.java
@@ -25,7 +25,7 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.strategy.AutoRebalanceStrategy;
 import org.testng.Assert;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestZeroReplicaAvoidance.java b/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestZeroReplicaAvoidance.java
index 53a8f49..9a5e085 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestZeroReplicaAvoidance.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/rebalancer/TestZeroReplicaAvoidance.java
@@ -32,7 +32,7 @@
 import java.util.Set;
 import java.util.UUID;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.stages.BaseStageTest;
 import org.apache.helix.controller.stages.CurrentStateOutput;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/BaseStageTest.java b/helix-core/src/test/java/org/apache/helix/controller/stages/BaseStageTest.java
index a91d220..3e85643 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/BaseStageTest.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/BaseStageTest.java
@@ -31,7 +31,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.pipeline.Stage;
 import org.apache.helix.controller.pipeline.StageContext;
 import org.apache.helix.mock.MockHelixAdmin;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/DummyClusterManager.java b/helix-core/src/test/java/org/apache/helix/controller/stages/DummyClusterManager.java
index e221f45..05631c8 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/DummyClusterManager.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/DummyClusterManager.java
@@ -31,7 +31,7 @@
 import org.apache.helix.LiveInstanceInfoProvider;
 import org.apache.helix.PreConnectCallback;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.listeners.ClusterConfigChangeListener;
 import org.apache.helix.api.listeners.ConfigChangeListener;
 import org.apache.helix.api.listeners.ControllerChangeListener;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/TestBestPossibleCalcStageCompatibility.java b/helix-core/src/test/java/org/apache/helix/controller/stages/TestBestPossibleCalcStageCompatibility.java
index 2205c23..73f4c88 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/TestBestPossibleCalcStageCompatibility.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/TestBestPossibleCalcStageCompatibility.java
@@ -25,7 +25,7 @@
 import java.util.Map;
 
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.IdealStateModeProperty;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/TestCompatibilityCheckStage.java b/helix-core/src/test/java/org/apache/helix/controller/stages/TestCompatibilityCheckStage.java
index d30ec5e..f910da5 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/TestCompatibilityCheckStage.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/TestCompatibilityCheckStage.java
@@ -23,7 +23,7 @@
 import java.util.List;
 
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.pipeline.StageContext;
 import org.apache.helix.mock.MockManager;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/TestCurrentStateComputationStage.java b/helix-core/src/test/java/org/apache/helix/controller/stages/TestCurrentStateComputationStage.java
index be7c208..1f354d7 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/TestCurrentStateComputationStage.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/TestCurrentStateComputationStage.java
@@ -22,7 +22,7 @@
 import java.util.Map;
 
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.Message;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java b/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java
index 1c84e65..e0433d9 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java
@@ -29,7 +29,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.pipeline.Pipeline;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/TestResourceComputationStage.java b/helix-core/src/test/java/org/apache/helix/controller/stages/TestResourceComputationStage.java
index 8b0119a..b04d7d5 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/TestResourceComputationStage.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/TestResourceComputationStage.java
@@ -26,7 +26,7 @@
 
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.pipeline.StageContext;
 import org.apache.helix.model.CurrentState;
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/TestTaskStage.java b/helix-core/src/test/java/org/apache/helix/controller/stages/TestTaskStage.java
index 0d98ed8..fefc737 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/TestTaskStage.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/TestTaskStage.java
@@ -3,7 +3,7 @@
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.caches.TaskDataCache;
 import org.apache.helix.controller.dataproviders.WorkflowControllerDataProvider;
 import org.apache.helix.controller.stages.task.TaskPersistDataStage;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestAddStateModelFactoryAfterConnect.java b/helix-core/src/test/java/org/apache/helix/integration/TestAddStateModelFactoryAfterConnect.java
index 413c343..9106587 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestAddStateModelFactoryAfterConnect.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestAddStateModelFactoryAfterConnect.java
@@ -24,7 +24,7 @@
 
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestBucketizedResource.java b/helix-core/src/test/java/org/apache/helix/integration/TestBucketizedResource.java
index a9c6053..1590058 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestBucketizedResource.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestBucketizedResource.java
@@ -28,7 +28,7 @@
 import org.apache.helix.NotificationContext.Type;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestCarryOverBadCurState.java b/helix-core/src/test/java/org/apache/helix/integration/TestCarryOverBadCurState.java
index c1da527..d0bfe86 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestCarryOverBadCurState.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestCarryOverBadCurState.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestCleanupExternalView.java b/helix-core/src/test/java/org/apache/helix/integration/TestCleanupExternalView.java
index f5b87c5..9096a86 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestCleanupExternalView.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestCleanupExternalView.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestCorrectnessOnConnectivityLoss.java b/helix-core/src/test/java/org/apache/helix/integration/TestCorrectnessOnConnectivityLoss.java
index b7acfda..f97b10f 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestCorrectnessOnConnectivityLoss.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestCorrectnessOnConnectivityLoss.java
@@ -23,7 +23,6 @@
 import java.util.Map;
 
 import com.google.common.collect.Maps;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixManagerFactory;
 import org.apache.helix.InstanceType;
@@ -40,6 +39,7 @@
 import org.apache.helix.spectator.RoutingTableProvider;
 import org.apache.helix.tools.ClusterStateVerifier;
 import org.apache.helix.tools.ClusterStateVerifier.BestPossAndExtViewZkVerifier;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestDisable.java b/helix-core/src/test/java/org/apache/helix/integration/TestDisable.java
index 5db4f82..9c5861d 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestDisable.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestDisable.java
@@ -25,7 +25,7 @@
 
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestDisableCustomCodeRunner.java b/helix-core/src/test/java/org/apache/helix/integration/TestDisableCustomCodeRunner.java
index 3c84a28..d087026 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestDisableCustomCodeRunner.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestDisableCustomCodeRunner.java
@@ -31,7 +31,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestDisableExternalView.java b/helix-core/src/test/java/org/apache/helix/integration/TestDisableExternalView.java
index a021153..d8c5a48 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestDisableExternalView.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestDisableExternalView.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestDisableResource.java b/helix-core/src/test/java/org/apache/helix/integration/TestDisableResource.java
index 3b75821..d12cc7e 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestDisableResource.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestDisableResource.java
@@ -29,7 +29,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestDistributedCMMain.java b/helix-core/src/test/java/org/apache/helix/integration/TestDistributedCMMain.java
index 5ee5aa9..6237378 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestDistributedCMMain.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestDistributedCMMain.java
@@ -25,7 +25,7 @@
 
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterDistributedController;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestDistributedClusterController.java b/helix-core/src/test/java/org/apache/helix/integration/TestDistributedClusterController.java
index 581759a..b6db274 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestDistributedClusterController.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestDistributedClusterController.java
@@ -25,7 +25,7 @@
 
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterDistributedController;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestDriver.java b/helix-core/src/test/java/org/apache/helix/integration/TestDriver.java
index cc8eef5..e4dc10d 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestDriver.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestDriver.java
@@ -28,13 +28,13 @@
 
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.model.IdealState.IdealStateProperty;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.store.PropertyJsonSerializer;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestEnableCompression.java b/helix-core/src/test/java/org/apache/helix/integration/TestEnableCompression.java
index 2110705..39e412c 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestEnableCompression.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestEnableCompression.java
@@ -4,19 +4,19 @@
 import java.util.Date;
 import java.util.List;
 
-import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.builder.CustomModeISBuilder;
 import org.apache.helix.tools.ClusterStateVerifier;
 import org.apache.helix.tools.ClusterStateVerifier.BestPossAndExtViewZkVerifier;
 import org.apache.helix.util.GZipCompressionUtil;
+import org.apache.helix.zookeeper.zkclient.serialize.BytesPushThroughSerializer;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestEntropyFreeNodeBounce.java b/helix-core/src/test/java/org/apache/helix/integration/TestEntropyFreeNodeBounce.java
index 3bd01ad..8d558f9 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestEntropyFreeNodeBounce.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestEntropyFreeNodeBounce.java
@@ -31,14 +31,14 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZKHelixManager;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.Message;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestExternalViewUpdates.java b/helix-core/src/test/java/org/apache/helix/integration/TestExternalViewUpdates.java
index 46dc4e4..5d01a1d 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestExternalViewUpdates.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestExternalViewUpdates.java
@@ -26,7 +26,7 @@
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestNoThrottleDisabledPartitions.java b/helix-core/src/test/java/org/apache/helix/integration/TestNoThrottleDisabledPartitions.java
index 573aebf..07eaf4e 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestNoThrottleDisabledPartitions.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestNoThrottleDisabledPartitions.java
@@ -26,7 +26,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.StateTransitionThrottleConfig;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestNullReplica.java b/helix-core/src/test/java/org/apache/helix/integration/TestNullReplica.java
index 03cd323..9749ab0 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestNullReplica.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestNullReplica.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestPartitionLevelTransitionConstraint.java b/helix-core/src/test/java/org/apache/helix/integration/TestPartitionLevelTransitionConstraint.java
index d74b4ac..6deb6e1 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestPartitionLevelTransitionConstraint.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestPartitionLevelTransitionConstraint.java
@@ -29,7 +29,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.NotificationContext;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestPersistAssignmentStage.java b/helix-core/src/test/java/org/apache/helix/integration/TestPersistAssignmentStage.java
index b029ee4..94147d2 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestPersistAssignmentStage.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestPersistAssignmentStage.java
@@ -6,7 +6,7 @@
 
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.stages.AttributeName;
 import org.apache.helix.controller.stages.BestPossibleStateOutput;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestPreferenceListAsQueue.java b/helix-core/src/test/java/org/apache/helix/integration/TestPreferenceListAsQueue.java
index 79a26a5..52bb979 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestPreferenceListAsQueue.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestPreferenceListAsQueue.java
@@ -27,7 +27,6 @@
 import java.util.concurrent.TimeUnit;
 
 import com.google.common.collect.Lists;
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixDataAccessor;
@@ -37,7 +36,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
@@ -50,6 +49,7 @@
 import org.apache.helix.participant.statemachine.StateModel;
 import org.apache.helix.participant.statemachine.StateModelFactory;
 import org.apache.helix.tools.ClusterSetup;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestRenamePartition.java b/helix-core/src/test/java/org/apache/helix/integration/TestRenamePartition.java
index a8e89aa..66623f8 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestRenamePartition.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestRenamePartition.java
@@ -27,7 +27,7 @@
 
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestResetPartitionState.java b/helix-core/src/test/java/org/apache/helix/integration/TestResetPartitionState.java
index 3a653f2..5e850ba 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestResetPartitionState.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestResetPartitionState.java
@@ -27,7 +27,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestResourceGroupEndtoEnd.java b/helix-core/src/test/java/org/apache/helix/integration/TestResourceGroupEndtoEnd.java
index 5f82fe6..0aa5787 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestResourceGroupEndtoEnd.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestResourceGroupEndtoEnd.java
@@ -36,7 +36,7 @@
 import org.apache.helix.manager.zk.CallbackHandler;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZKHelixManager;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.mock.participant.DummyProcess;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
 import org.apache.helix.model.IdealState;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestResourceWithSamePartitionKey.java b/helix-core/src/test/java/org/apache/helix/integration/TestResourceWithSamePartitionKey.java
index 23bc801..0d1313a 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestResourceWithSamePartitionKey.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestResourceWithSamePartitionKey.java
@@ -25,7 +25,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestSyncSessionToController.java b/helix-core/src/test/java/org/apache/helix/integration/TestSyncSessionToController.java
index 8b503a5..5eefc32 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestSyncSessionToController.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestSyncSessionToController.java
@@ -27,7 +27,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.api.listeners.MessageListener;
 import org.apache.helix.common.ZkTestBase;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestWeightBasedRebalanceUtil.java b/helix-core/src/test/java/org/apache/helix/integration/TestWeightBasedRebalanceUtil.java
index 9dfda50..ee3a3d0 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestWeightBasedRebalanceUtil.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestWeightBasedRebalanceUtil.java
@@ -28,7 +28,7 @@
 import java.util.Map;
 
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.RebalanceConfig;
 import org.apache.helix.api.rebalancer.constraint.AbstractRebalanceHardConstraint;
 import org.apache.helix.api.rebalancer.constraint.AbstractRebalanceSoftConstraint;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestZkCallbackHandlerLeak.java b/helix-core/src/test/java/org/apache/helix/integration/TestZkCallbackHandlerLeak.java
index b514e3d..4682c52 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestZkCallbackHandlerLeak.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestZkCallbackHandlerLeak.java
@@ -24,8 +24,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
 import org.apache.helix.CurrentStateChangeListener;
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey;
@@ -36,11 +34,13 @@
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.integration.manager.ZkTestManager;
 import org.apache.helix.manager.zk.CallbackHandler;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.tools.ClusterStateVerifier;
 import org.apache.helix.tools.ClusterVerifiers.BestPossibleExternalViewVerifier;
 import org.apache.helix.tools.ClusterVerifiers.ZkHelixClusterVerifier;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestZkConnectionLost.java b/helix-core/src/test/java/org/apache/helix/integration/TestZkConnectionLost.java
index 0bbfb52..c790a45 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestZkConnectionLost.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestZkConnectionLost.java
@@ -27,7 +27,6 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.SystemPropertyKeys;
@@ -40,8 +39,8 @@
 import org.apache.helix.integration.task.WorkflowGenerator;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.task.JobConfig;
 import org.apache.helix.task.JobQueue;
 import org.apache.helix.task.TaskState;
@@ -49,6 +48,7 @@
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.tools.ClusterVerifiers.BestPossibleExternalViewVerifier;
 import org.apache.helix.tools.ClusterVerifiers.ZkHelixClusterVerifier;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerDataProviderSelectiveUpdate.java b/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerDataProviderSelectiveUpdate.java
index a34c064..9115e21 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerDataProviderSelectiveUpdate.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerDataProviderSelectiveUpdate.java
@@ -22,7 +22,7 @@
 import org.apache.helix.HelixConstants;
 import org.apache.helix.PropertyType;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.integration.task.WorkflowGenerator;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerLeadershipChange.java b/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerLeadershipChange.java
index 73eeb55..8a1ff03 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerLeadershipChange.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerLeadershipChange.java
@@ -34,7 +34,7 @@
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.manager.zk.CallbackHandler;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.monitoring.mbeans.MonitorDomainNames;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerLiveLock.java b/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerLiveLock.java
index 0dff764..ce5db71 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerLiveLock.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/controller/TestControllerLiveLock.java
@@ -27,7 +27,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/manager/ClusterControllerManager.java b/helix-core/src/test/java/org/apache/helix/integration/manager/ClusterControllerManager.java
index d4c3283..7d810fb 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/manager/ClusterControllerManager.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/manager/ClusterControllerManager.java
@@ -26,7 +26,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.manager.zk.CallbackHandler;
 import org.apache.helix.manager.zk.ZKHelixManager;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-core/src/test/java/org/apache/helix/integration/manager/ClusterDistributedController.java b/helix-core/src/test/java/org/apache/helix/integration/manager/ClusterDistributedController.java
index d17f96c..8e15928 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/manager/ClusterDistributedController.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/manager/ClusterDistributedController.java
@@ -25,7 +25,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.manager.zk.CallbackHandler;
 import org.apache.helix.manager.zk.ZKHelixManager;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.participant.DistClusterControllerStateModelFactory;
 import org.apache.helix.participant.StateMachineEngine;
 import org.slf4j.Logger;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/manager/MockParticipantManager.java b/helix-core/src/test/java/org/apache/helix/integration/manager/MockParticipantManager.java
index ed4612e..0036e0d 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/manager/MockParticipantManager.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/manager/MockParticipantManager.java
@@ -25,7 +25,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.manager.zk.CallbackHandler;
 import org.apache.helix.manager.zk.ZKHelixManager;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.mock.participant.DummyProcess.DummyLeaderStandbyStateModelFactory;
 import org.apache.helix.mock.participant.DummyProcess.DummyOnlineOfflineStateModelFactory;
 import org.apache.helix.mock.participant.MockMSModelFactory;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/manager/TestConsecutiveZkSessionExpiry.java b/helix-core/src/test/java/org/apache/helix/integration/manager/TestConsecutiveZkSessionExpiry.java
index e64306e..a948496 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/manager/TestConsecutiveZkSessionExpiry.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/manager/TestConsecutiveZkSessionExpiry.java
@@ -27,7 +27,7 @@
 import org.apache.helix.PreConnectCallback;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.manager.zk.CallbackHandler;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/manager/TestHelixDataAccessor.java b/helix-core/src/test/java/org/apache/helix/integration/manager/TestHelixDataAccessor.java
index 6789d1c..5277a52 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/manager/TestHelixDataAccessor.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/manager/TestHelixDataAccessor.java
@@ -28,7 +28,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.exceptions.HelixMetaDataAccessException;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/manager/TestParticipantManager.java b/helix-core/src/test/java/org/apache/helix/integration/manager/TestParticipantManager.java
index 55053a5..aef7ff6 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/manager/TestParticipantManager.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/manager/TestParticipantManager.java
@@ -37,7 +37,7 @@
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.SystemPropertyKeys;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/manager/ZkTestManager.java b/helix-core/src/test/java/org/apache/helix/integration/manager/ZkTestManager.java
index bf4209b..1a6903a 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/manager/ZkTestManager.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/manager/ZkTestManager.java
@@ -22,7 +22,7 @@
 import java.util.List;
 
 import org.apache.helix.manager.zk.CallbackHandler;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 
 public interface ZkTestManager {
   HelixZkClient getZkClient();
diff --git a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestBatchMessage.java b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestBatchMessage.java
index 0b16df2..590c9c9 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestBatchMessage.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestBatchMessage.java
@@ -25,11 +25,10 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.I0Itec.zkclient.IZkChildListener;
 import org.apache.helix.HelixProperty.HelixPropertyAttribute;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
@@ -41,6 +40,7 @@
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.tools.ClusterStateVerifier;
 import org.apache.helix.tools.ClusterStateVerifier.BestPossAndExtViewZkVerifier;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestBatchMessageWrapper.java b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestBatchMessageWrapper.java
index 093c943..72ae3cb 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestBatchMessageWrapper.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestBatchMessageWrapper.java
@@ -24,7 +24,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestMessageThrottle.java b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestMessageThrottle.java
index b4a0b32..0e1c9c3 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestMessageThrottle.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestMessageThrottle.java
@@ -23,11 +23,10 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.I0Itec.zkclient.IZkChildListener;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
@@ -40,6 +39,7 @@
 import org.apache.helix.tools.ClusterStateVerifier;
 import org.apache.helix.tools.ClusterStateVerifier.BestPossAndExtViewZkVerifier;
 import org.apache.helix.tools.ClusterStateVerifier.MasterNbInExtViewVerifier;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestMessageThrottle2.java b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestMessageThrottle2.java
index 7b73643..df7cea5 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestMessageThrottle2.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestMessageThrottle2.java
@@ -41,7 +41,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.controller.HelixControllerMain;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMessage.java b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMessage.java
index dfe6b19..1ca4c2a 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMessage.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMessage.java
@@ -39,7 +39,7 @@
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.manager.zk.DefaultSchedulerMessageHandlerFactory;
 import org.apache.helix.messaging.AsyncCallback;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMessage2.java b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMessage2.java
index 3691384..5cea518 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMessage2.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMessage2.java
@@ -28,7 +28,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.manager.zk.DefaultSchedulerMessageHandlerFactory;
 import org.apache.helix.model.Message;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMsgContraints.java b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMsgContraints.java
index c283301..aedc738 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMsgContraints.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMsgContraints.java
@@ -30,7 +30,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.manager.zk.DefaultSchedulerMessageHandlerFactory;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMsgUsingQueue.java b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMsgUsingQueue.java
index f0068d3..dea9ba8 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMsgUsingQueue.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/messaging/TestSchedulerMsgUsingQueue.java
@@ -28,7 +28,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.manager.zk.DefaultSchedulerMessageHandlerFactory;
 import org.apache.helix.model.Message;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/paticipant/TestStateTransitionTimeoutWithResource.java b/helix-core/src/test/java/org/apache/helix/integration/paticipant/TestStateTransitionTimeoutWithResource.java
index 1d33b6c..52ca2eb 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/paticipant/TestStateTransitionTimeoutWithResource.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/paticipant/TestStateTransitionTimeoutWithResource.java
@@ -32,7 +32,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.NotificationContext;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.RebalanceConfig;
 import org.apache.helix.api.config.StateTransitionTimeoutConfig;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoIsWithEmptyMap.java b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoIsWithEmptyMap.java
index 06d9654..acaf8b1 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoIsWithEmptyMap.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoIsWithEmptyMap.java
@@ -25,7 +25,7 @@
 
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoRebalance.java b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoRebalance.java
index 0a4ec4b..90b09ec 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoRebalance.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoRebalance.java
@@ -28,15 +28,15 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.RebalanceMode;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoRebalancePartitionLimit.java b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoRebalancePartitionLimit.java
index 91d4cb8..c78a4f2 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoRebalancePartitionLimit.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestAutoRebalancePartitionLimit.java
@@ -26,15 +26,15 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.tools.ClusterStateVerifier;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestCustomizedIdealStateRebalancer.java b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestCustomizedIdealStateRebalancer.java
index 8fa87c2..28f02a3 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestCustomizedIdealStateRebalancer.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestCustomizedIdealStateRebalancer.java
@@ -27,15 +27,15 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.controller.rebalancer.Rebalancer;
 import org.apache.helix.controller.stages.CurrentStateOutput;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.IdealStateProperty;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestFullAutoNodeTagging.java b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestFullAutoNodeTagging.java
index 074f5b9..e108bde 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestFullAutoNodeTagging.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/rebalancer/TestFullAutoNodeTagging.java
@@ -32,7 +32,7 @@
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
 import org.apache.helix.TestHelper.Verifier;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
@@ -40,9 +40,9 @@
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.RebalanceMode;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/task/TaskTestUtil.java b/helix-core/src/test/java/org/apache/helix/integration/task/TaskTestUtil.java
index 9c36786..23f99a0 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/task/TaskTestUtil.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/task/TaskTestUtil.java
@@ -33,7 +33,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.DedupEventProcessor;
 import org.apache.helix.controller.dataproviders.WorkflowControllerDataProvider;
 import org.apache.helix.controller.pipeline.AsyncWorkerType;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/task/TestTaskStopQueue.java b/helix-core/src/test/java/org/apache/helix/integration/task/TestTaskStopQueue.java
index 5b85457..43551f7 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/task/TestTaskStopQueue.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/task/TestTaskStopQueue.java
@@ -28,7 +28,7 @@
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.integration.manager.ClusterControllerManager;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.task.JobConfig;
 import org.apache.helix.task.JobQueue;
 import org.apache.helix.task.TaskDriver;
diff --git a/helix-core/src/test/java/org/apache/helix/integration/task/TestWorkflowContextWithoutConfig.java b/helix-core/src/test/java/org/apache/helix/integration/task/TestWorkflowContextWithoutConfig.java
index ad75e15..b7d6016 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/task/TestWorkflowContextWithoutConfig.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/task/TestWorkflowContextWithoutConfig.java
@@ -22,7 +22,7 @@
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.task.JobConfig;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestAddBuiltInStateModelDef.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestAddBuiltInStateModelDef.java
index 3fb5fc9..2ca98da 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestAddBuiltInStateModelDef.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestAddBuiltInStateModelDef.java
@@ -25,7 +25,7 @@
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestHandleNewSession.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestHandleNewSession.java
index 8cae2f7..00bedcf 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestHandleNewSession.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestHandleNewSession.java
@@ -34,15 +34,16 @@
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.controller.GenericHelixController;
 import org.apache.helix.integration.manager.MockParticipantManager;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.LiveInstance;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+
 public class TestHandleNewSession extends ZkTestBase {
   @Test
   public void testHandleNewSession() throws Exception {
-    // Logger.getRootLogger().setLevel(Level.INFO);
     String className = TestHelper.getTestClassName();
     String methodName = TestHelper.getTestMethodName();
     String clusterName = className + "_" + methodName;
@@ -127,8 +128,9 @@
     accessor.removeProperty(keyBuilder.controllerLeader());
     // 3. expire the session and create a new session
     ZkTestHelper.asyncExpireSession(manager._zkclient);
-    Assert.assertTrue(TestHelper.verify(
-        () -> !((ZkClient) manager._zkclient).getConnection().getZookeeperState().isAlive(), 3000));
+    Assert.assertTrue(TestHelper
+        .verify(() -> !((ZkClient) manager._zkclient).getConnection().getZookeeperState().isAlive(),
+            3000));
     // 4. start processing event again
     ((ZkClient) manager._zkclient).getEventLock().unlock();
 
@@ -151,14 +153,17 @@
       // Newly created node should have a new creating time but with old session.
       LiveInstance invalidLeaderNode = accessor.getProperty(keyBuilder.controllerLeader());
       // node exist
-      if (invalidLeaderNode == null)
+      if (invalidLeaderNode == null) {
         return false;
+      }
       // node is newly created
-      if (invalidLeaderNode.getStat().getCreationTime() == originalCreationTime)
+      if (invalidLeaderNode.getStat().getCreationTime() == originalCreationTime) {
         return false;
+      }
       // node has the same session as the old one, so it's invalid
-      if (!invalidLeaderNode.getSessionId().equals(originalSessionId))
+      if (!invalidLeaderNode.getSessionId().equals(originalSessionId)) {
         return false;
+      }
       return true;
     }, 2000));
     Assert.assertFalse(manager.isLeader());
@@ -194,16 +199,14 @@
         new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<>(ZK_ADDR));
     final PropertyKey.Builder keyBuilder = accessor.keyBuilder();
 
-    TestHelper.setupCluster(clusterName, ZK_ADDR,
-        12918, // participant port
+    TestHelper.setupCluster(clusterName, ZK_ADDR, 12918, // participant port
         "localhost", // participant name prefix
         "TestDB", // resource name prefix
         1, // resources
         10, // partitions per resource
         5, // number of nodes
         3, // replicas
-        "MasterSlave",
-        true); // do rebalance
+        "MasterSlave", true); // do rebalance
 
     final String instanceName = "localhost_12918";
     final BlockingHandleNewSessionZkHelixManager manager =
@@ -319,16 +322,14 @@
         new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<>(ZK_ADDR));
     final PropertyKey.Builder keyBuilder = accessor.keyBuilder();
 
-    TestHelper.setupCluster(clusterName, ZK_ADDR,
-        12918, // participant port
+    TestHelper.setupCluster(clusterName, ZK_ADDR, 12918, // participant port
         "localhost", // participant name prefix
         "TestDB", // resource name prefix
         1, // resources
         10, // partitions per resource
         5, // number of nodes
         3, // replicas
-        "MasterSlave",
-        true); // do rebalance
+        "MasterSlave", true); // do rebalance
 
     // 1. Original session S0 initialized
     final String instanceName = "localhost_12918";
@@ -412,7 +413,6 @@
 
       // Notify the main thread that live instance is already created by session S2.
       mainThreadBlocker.countDown();
-
     }).start();
 
     // Lock zk event processing to simulate a long backlog queue.
@@ -452,14 +452,14 @@
       // and have a new creation time.
       LiveInstance newLiveInstance = accessor.getProperty(keyBuilder.liveInstance(instanceName));
       return newLiveInstance != null
-          && newLiveInstance.getStat().getCreationTime() != originalCreationTime
-          && newLiveInstance.getEphemeralOwner().equals(latestSessionId);
+          && newLiveInstance.getStat().getCreationTime() != originalCreationTime && newLiveInstance
+          .getEphemeralOwner().equals(latestSessionId);
     }, 2000L));
 
     // All the callback handlers shall be recovered.
     Assert.assertTrue(TestHelper.verify(() -> manager.getHandlers().size() == handlerCount, 1000L));
-    Assert.assertTrue(TestHelper.verify(
-        () -> manager.getHandlers().stream().allMatch(CallbackHandler::isReady), 3000L));
+    Assert.assertTrue(TestHelper
+        .verify(() -> manager.getHandlers().stream().allMatch(CallbackHandler::isReady), 3000L));
 
     // Clean up.
     manager.disconnect();
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestRawZkClient.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestRawZkClient.java
index 111bd7a..a713996 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestRawZkClient.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestRawZkClient.java
@@ -33,19 +33,20 @@
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
 
-import org.I0Itec.zkclient.IZkDataListener;
-import org.I0Itec.zkclient.ZkServer;
-import org.I0Itec.zkclient.exception.ZkTimeoutException;
 import org.apache.helix.HelixException;
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.ZkUnitTestBase;
-import org.apache.helix.manager.zk.zookeeper.IZkStateListener;
-import org.apache.helix.manager.zk.zookeeper.ZkConnection;
 import org.apache.helix.monitoring.mbeans.MBeanRegistrar;
 import org.apache.helix.monitoring.mbeans.MonitorDomainNames;
 import org.apache.helix.monitoring.mbeans.ZkClientMonitor;
 import org.apache.helix.monitoring.mbeans.ZkClientPathMonitor;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.IZkStateListener;
+import org.apache.helix.zookeeper.zkclient.ZkConnection;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+import org.apache.helix.zookeeper.zkclient.exception.ZkSessionMismatchedException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkTimeoutException;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.WatchedEvent;
@@ -143,7 +144,8 @@
    * Tests session expiry for the helix's IZkStateListener.
    */
   @Test
-  void testSessionExpiry() throws Exception {
+  void testSessionExpiry()
+      throws Exception {
     long lastSessionId = _zkClient.getSessionId();
 
     // Test multiple times to make sure each time the new session id is increasing.
@@ -165,27 +167,29 @@
   @Test
   public void testSubscribeStateChangesForI0ItecIZkStateListener() {
     int numListeners = _zkClient.numberOfListeners();
-    List<org.I0Itec.zkclient.IZkStateListener> listeners = new ArrayList<>();
+    List<org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener> listeners =
+        new ArrayList<>();
 
     // Subscribe multiple listeners to test that listener's hashcode works as expected.
     // Each listener is subscribed and unsubscribed successfully.
     for (int i = 0; i < 3; i++) {
-      org.I0Itec.zkclient.IZkStateListener listener = new org.I0Itec.zkclient.IZkStateListener() {
-        @Override
-        public void handleStateChanged(KeeperState state) {
-          System.out.println("Handle new state: " + state);
-        }
+      org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener listener =
+          new org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener() {
+            @Override
+            public void handleStateChanged(KeeperState state) {
+              System.out.println("Handle new state: " + state);
+            }
 
-        @Override
-        public void handleNewSession() {
-          System.out.println("Handle new session: ");
-        }
+            @Override
+            public void handleNewSession() {
+              System.out.println("Handle new session: ");
+            }
 
-        @Override
-        public void handleSessionEstablishmentError(Throwable error) {
-          System.out.println("Handle session establishment error: " + error);
-        }
-      };
+            @Override
+            public void handleSessionEstablishmentError(Throwable error) {
+              System.out.println("Handle session establishment error: " + error);
+            }
+          };
 
       _zkClient.subscribeStateChanges(listener);
       Assert.assertEquals(_zkClient.numberOfListeners(), ++numListeners);
@@ -198,7 +202,7 @@
       listeners.add(listener);
     }
 
-    for (org.I0Itec.zkclient.IZkStateListener listener : listeners) {
+    for (org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener listener : listeners) {
       _zkClient.unsubscribeStateChanges(listener);
       Assert.assertEquals(_zkClient.numberOfListeners(), --numListeners);
     }
@@ -211,9 +215,10 @@
    * TODO: remove this test when getting rid of I0Itec.
    */
   @Test
-  public void testSessionExpiryForI0IItecZkStateListener() throws Exception {
-    org.I0Itec.zkclient.IZkStateListener listener =
-        new org.I0Itec.zkclient.IZkStateListener() {
+  public void testSessionExpiryForI0IItecZkStateListener()
+      throws Exception {
+    org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener listener =
+        new org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener() {
 
           @Override
           public void handleStateChanged(KeeperState state) {
@@ -236,8 +241,9 @@
     long oldSessionId = zookeeper.getSessionId();
     System.out.println("old sessionId= " + oldSessionId);
     Watcher watcher = event -> System.out.println("In New connection In process event:" + event);
-    ZooKeeper newZookeeper = new ZooKeeper(connection.getServers(), zookeeper.getSessionTimeout(),
-        watcher, zookeeper.getSessionId(), zookeeper.getSessionPasswd());
+    ZooKeeper newZookeeper =
+        new ZooKeeper(connection.getServers(), zookeeper.getSessionTimeout(), watcher,
+            zookeeper.getSessionId(), zookeeper.getSessionPasswd());
     Thread.sleep(3000);
     System.out.println("New sessionId= " + newZookeeper.getSessionId());
     Thread.sleep(3000);
@@ -251,7 +257,8 @@
   }
 
   @Test
-  public void testZkClientMonitor() throws Exception {
+  public void testZkClientMonitor()
+      throws Exception {
     final String TEST_KEY = "testZkClientMonitor";
     ZkClient.Builder builder = new ZkClient.Builder();
     builder.setZkServer(ZK_ADDR).setMonitorKey(TEST_KEY).setMonitorType(TEST_TAG)
@@ -272,14 +279,17 @@
 
     MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
 
-    ObjectName name = MBeanRegistrar.buildObjectName(MonitorDomainNames.HelixZkClient.name(),
-        ZkClientMonitor.MONITOR_TYPE, TEST_TAG, ZkClientMonitor.MONITOR_KEY, TEST_KEY);
-    ObjectName rootname = MBeanRegistrar.buildObjectName(MonitorDomainNames.HelixZkClient.name(),
-        ZkClientMonitor.MONITOR_TYPE, TEST_TAG, ZkClientMonitor.MONITOR_KEY, TEST_KEY,
-        ZkClientPathMonitor.MONITOR_PATH, "Root");
-    ObjectName idealStatename = MBeanRegistrar.buildObjectName(
-        MonitorDomainNames.HelixZkClient.name(), ZkClientMonitor.MONITOR_TYPE, TEST_TAG,
-        ZkClientMonitor.MONITOR_KEY, TEST_KEY, ZkClientPathMonitor.MONITOR_PATH, "IdealStates");
+    ObjectName name = MBeanRegistrar
+        .buildObjectName(MonitorDomainNames.HelixZkClient.name(), ZkClientMonitor.MONITOR_TYPE,
+            TEST_TAG, ZkClientMonitor.MONITOR_KEY, TEST_KEY);
+    ObjectName rootname = MBeanRegistrar
+        .buildObjectName(MonitorDomainNames.HelixZkClient.name(), ZkClientMonitor.MONITOR_TYPE,
+            TEST_TAG, ZkClientMonitor.MONITOR_KEY, TEST_KEY, ZkClientPathMonitor.MONITOR_PATH,
+            "Root");
+    ObjectName idealStatename = MBeanRegistrar
+        .buildObjectName(MonitorDomainNames.HelixZkClient.name(), ZkClientMonitor.MONITOR_TYPE,
+            TEST_TAG, ZkClientMonitor.MONITOR_KEY, TEST_KEY, ZkClientPathMonitor.MONITOR_PATH,
+            "IdealStates");
     Assert.assertTrue(beanServer.isRegistered(rootname));
     Assert.assertTrue(beanServer.isRegistered(idealStatename));
 
@@ -336,27 +346,27 @@
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "ReadLatencyGauge.Max"), 0);
     zkClient.readData(TEST_PATH, new Stat());
     Assert.assertEquals((long) beanServer.getAttribute(rootname, "ReadCounter"), 2);
-    Assert.assertEquals((long) beanServer.getAttribute(rootname, "ReadBytesCounter"),
-        TEST_DATA_SIZE);
+    Assert
+        .assertEquals((long) beanServer.getAttribute(rootname, "ReadBytesCounter"), TEST_DATA_SIZE);
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "ReadCounter"), 1);
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "ReadBytesCounter"),
         TEST_DATA_SIZE);
-    Assert.assertTrue((long) beanServer.getAttribute(rootname,
-        "ReadTotalLatencyCounter") >= origReadTotalLatencyCounter);
-    Assert.assertTrue((long) beanServer.getAttribute(idealStatename,
-        "ReadTotalLatencyCounter") >= origIdealStatesReadTotalLatencyCounter);
+    Assert.assertTrue((long) beanServer.getAttribute(rootname, "ReadTotalLatencyCounter")
+        >= origReadTotalLatencyCounter);
+    Assert.assertTrue((long) beanServer.getAttribute(idealStatename, "ReadTotalLatencyCounter")
+        >= origIdealStatesReadTotalLatencyCounter);
     Assert.assertTrue((long) beanServer.getAttribute(idealStatename, "ReadLatencyGauge.Max") >= 0);
     zkClient.getChildren(TEST_PATH);
     Assert.assertEquals((long) beanServer.getAttribute(rootname, "ReadCounter"), 3);
-    Assert.assertEquals((long) beanServer.getAttribute(rootname, "ReadBytesCounter"),
-        TEST_DATA_SIZE);
+    Assert
+        .assertEquals((long) beanServer.getAttribute(rootname, "ReadBytesCounter"), TEST_DATA_SIZE);
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "ReadCounter"), 2);
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "ReadBytesCounter"),
         TEST_DATA_SIZE);
     zkClient.getStat(TEST_PATH);
     Assert.assertEquals((long) beanServer.getAttribute(rootname, "ReadCounter"), 4);
-    Assert.assertEquals((long) beanServer.getAttribute(rootname, "ReadBytesCounter"),
-        TEST_DATA_SIZE);
+    Assert
+        .assertEquals((long) beanServer.getAttribute(rootname, "ReadBytesCounter"), TEST_DATA_SIZE);
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "ReadCounter"), 3);
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "ReadBytesCounter"),
         TEST_DATA_SIZE);
@@ -377,10 +387,10 @@
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "WriteCounter"), 2);
     Assert.assertEquals((long) beanServer.getAttribute(idealStatename, "WriteBytesCounter"),
         TEST_DATA_SIZE * 2);
-    Assert.assertTrue((long) beanServer.getAttribute(rootname,
-        "WriteTotalLatencyCounter") >= origWriteTotalLatencyCounter);
-    Assert.assertTrue((long) beanServer.getAttribute(idealStatename,
-        "WriteTotalLatencyCounter") >= origIdealStatesWriteTotalLatencyCounter);
+    Assert.assertTrue((long) beanServer.getAttribute(rootname, "WriteTotalLatencyCounter")
+        >= origWriteTotalLatencyCounter);
+    Assert.assertTrue((long) beanServer.getAttribute(idealStatename, "WriteTotalLatencyCounter")
+        >= origIdealStatesWriteTotalLatencyCounter);
 
     // Test data change count
     final Lock lock = new ReentrantLock();
@@ -427,12 +437,14 @@
   }
 
   @Test(dependsOnMethods = "testZkClientMonitor")
-  void testPendingRequestGauge() throws Exception {
+  void testPendingRequestGauge()
+      throws Exception {
     final String TEST_KEY = "testPendingRequestGauge";
 
     final MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
-    final ObjectName name = MBeanRegistrar.buildObjectName(MonitorDomainNames.HelixZkClient.name(),
-        ZkClientMonitor.MONITOR_TYPE, TEST_TAG, ZkClientMonitor.MONITOR_KEY, TEST_KEY);
+    final ObjectName name = MBeanRegistrar
+        .buildObjectName(MonitorDomainNames.HelixZkClient.name(), ZkClientMonitor.MONITOR_TYPE,
+            TEST_TAG, ZkClientMonitor.MONITOR_KEY, TEST_KEY);
 
     final int zkPort = TestHelper.getRandomPort();
     final String zkAddr = String.format("localhost:%d", zkPort);
@@ -455,13 +467,15 @@
       executorService.submit(() -> {
         zkClient.exists(TEST_ROOT);
       });
-      Assert.assertTrue(TestHelper.verify(
-          () -> (long) beanServer.getAttribute(name, "OutstandingRequestGauge") == 1, 1000));
+      Assert.assertTrue(TestHelper
+          .verify(() -> (long) beanServer.getAttribute(name, "OutstandingRequestGauge") == 1,
+              1000));
 
       zkServer.start();
       Assert.assertTrue(zkClient.waitUntilConnected(5000, TimeUnit.MILLISECONDS));
-      Assert.assertTrue(TestHelper.verify(
-          () -> (long) beanServer.getAttribute(name, "OutstandingRequestGauge") == 0, 2000));
+      Assert.assertTrue(TestHelper
+          .verify(() -> (long) beanServer.getAttribute(name, "OutstandingRequestGauge") == 0,
+              2000));
       zkClient.close();
     } finally {
       zkServer.shutdown();
@@ -472,21 +486,20 @@
    * This test checks that a valid session can successfully create an ephemeral node.
    */
   @Test
-  public void testCreateEphemeralWithValidSession() throws Exception {
+  public void testCreateEphemeralWithValidSession()
+      throws Exception {
     final String className = TestHelper.getTestClassName();
     final String methodName = TestHelper.getTestMethodName();
     final String clusterName = className + "_" + methodName;
 
-    TestHelper.setupCluster(clusterName, ZK_ADDR,
-        12918, // participant port
+    TestHelper.setupCluster(clusterName, ZK_ADDR, 12918, // participant port
         "localhost", // participant name prefix
         "TestDB", // resource name prefix
         1, // resources
         10, // partitions per resource
         5, // number of nodes
         3, // replicas
-        "MasterSlave",
-        true); // do rebalance
+        "MasterSlave", true); // do rebalance
 
     final String originalSessionId = ZKUtil.toHexSessionId(_zkClient.getSessionId());
     final String path = "/" + methodName;
@@ -532,21 +545,20 @@
    * 4. ZkSessionMismatchedException is expected for the creation and ephemeral node is not created
    */
   @Test
-  public void testCreateEphemeralWithMismatchedSession() throws Exception {
+  public void testCreateEphemeralWithMismatchedSession()
+      throws Exception {
     final String className = TestHelper.getTestClassName();
     final String methodName = TestHelper.getTestMethodName();
     final String clusterName = className + "_" + methodName;
 
-    TestHelper.setupCluster(clusterName, ZK_ADDR,
-        12918, // participant port
+    TestHelper.setupCluster(clusterName, ZK_ADDR, 12918, // participant port
         "localhost", // participant name prefix
         "TestDB", // resource name prefix
         1, // resources
         10, // partitions per resource
         5, // number of nodes
         3, // replicas
-        "MasterSlave",
-        true); // do rebalance
+        "MasterSlave", true); // do rebalance
 
     final long originalSessionId = _zkClient.getSessionId();
     final String originalHexSessionId = ZKUtil.toHexSessionId(originalSessionId);
@@ -591,13 +603,13 @@
    * see if ConnectionLossException was thrown before retry.
    */
   @Test(timeOut = 5 * 60 * 1000L)
-  public void testConnectionLossWhileCreateEphemeral() throws Exception {
+  public void testConnectionLossWhileCreateEphemeral()
+      throws Exception {
     final String methodName = TestHelper.getTestMethodName();
 
-    final ZkClient zkClient = new ZkClient.Builder()
-        .setZkServer(ZK_ADDR)
-        .setOperationRetryTimeout(3000L) // 3 seconds
-        .build();
+    final ZkClient zkClient =
+        new ZkClient.Builder().setZkServer(ZK_ADDR).setOperationRetryTimeout(3000L) // 3 seconds
+            .build();
 
     final String expectedSessionId = ZKUtil.toHexSessionId(zkClient.getSessionId());
     final String path = "/" + methodName;
@@ -655,7 +667,8 @@
    * 5. zk client reconnects successfully and creates an ephemeral node
    */
   @Test(timeOut = 5 * 60 * 1000L)
-  public void testRetryUntilConnectedAfterConnectionLoss() throws Exception {
+  public void testRetryUntilConnectedAfterConnectionLoss()
+      throws Exception {
     final String methodName = TestHelper.getTestMethodName();
 
     final String expectedSessionId = ZKUtil.toHexSessionId(_zkClient.getSessionId());
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheAsyncOpMultiThread.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheAsyncOpMultiThread.java
index 7b030ce..7339512 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheAsyncOpMultiThread.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheAsyncOpMultiThread.java
@@ -25,13 +25,13 @@
 import java.util.List;
 import java.util.concurrent.Callable;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordUpdater;
 import org.apache.helix.ZkUnitTestBase;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheAsyncOpSingleThread.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheAsyncOpSingleThread.java
index 9eea08c..e84d1b0 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheAsyncOpSingleThread.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheAsyncOpSingleThread.java
@@ -24,13 +24,13 @@
 import java.util.Date;
 import java.util.List;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordUpdater;
 import org.apache.helix.ZkUnitTestBase;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheSyncOpSingleThread.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheSyncOpSingleThread.java
index 9572ae0..b80ff9c 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheSyncOpSingleThread.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestWtCacheSyncOpSingleThread.java
@@ -27,8 +27,8 @@
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordUpdater;
 import org.apache.helix.ZkUnitTestBase;
 import org.testng.Assert;
 import org.testng.annotations.Test;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java
index 9c0c60a..de2c6c8 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKUtil.java
@@ -27,9 +27,9 @@
 
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.AssertJUnit;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKWatch.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKWatch.java
index 9d247d4..53017cb 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKWatch.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZKWatch.java
@@ -23,10 +23,10 @@
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.ZkUnitTestBase;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordSerializer.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordSerializer.java
index d3f5746..496196a 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordSerializer.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordSerializer.java
@@ -14,8 +14,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordSizeLimit.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordSizeLimit.java
index 6130062..447478c 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordSizeLimit.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordSizeLimit.java
@@ -25,10 +25,10 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.InstanceConfig;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordStreamingSerializer.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordStreamingSerializer.java
index 50a84e6..925b8f3 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordStreamingSerializer.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZNRecordStreamingSerializer.java
@@ -6,7 +6,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBaseDataAccessor.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBaseDataAccessor.java
index fb78d1c..3e03125 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBaseDataAccessor.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBaseDataAccessor.java
@@ -26,18 +26,18 @@
 import java.util.stream.Collectors;
 
 import com.google.common.collect.ImmutableList;
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordUpdater;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor.AccessResult;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor.RetCode;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.apache.zookeeper.data.Stat;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBucketDataAccessor.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBucketDataAccessor.java
index c7b5cbf..fdd0c8a 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBucketDataAccessor.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkBucketDataAccessor.java
@@ -27,18 +27,18 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.BucketDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkCacheAsyncOpSingleThread.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkCacheAsyncOpSingleThread.java
index 89d4f18..a003a4b 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkCacheAsyncOpSingleThread.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkCacheAsyncOpSingleThread.java
@@ -24,15 +24,15 @@
 import java.util.Date;
 import java.util.List;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordUpdater;
 import org.apache.helix.ZkUnitTestBase;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkCacheSyncOpSingleThread.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkCacheSyncOpSingleThread.java
index fcace67..f04d53e 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkCacheSyncOpSingleThread.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkCacheSyncOpSingleThread.java
@@ -31,11 +31,11 @@
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.ZNRecordUpdater;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecordUpdater;
 import org.apache.helix.ZkUnitTestBase;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.store.HelixPropertyListener;
 import org.testng.Assert;
 import org.testng.annotations.Test;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkClusterManager.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkClusterManager.java
index 0169c41..9c5b64b 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkClusterManager.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkClusterManager.java
@@ -34,7 +34,7 @@
 import org.apache.helix.LiveInstanceInfoProvider;
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkTestHelper;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkFlapping.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkFlapping.java
index 7a90fe3..3795756 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkFlapping.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkFlapping.java
@@ -33,9 +33,11 @@
 import org.apache.helix.manager.zk.zookeeper.IZkStateListener;
 import org.apache.helix.model.LiveInstance;
 import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+
 public class TestZkFlapping extends ZkUnitTestBase {
   private final int _disconnectThreshold = 5;
 
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
index c391085..ac1cc09 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
@@ -40,7 +40,7 @@
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.PropertyType;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
 import org.apache.helix.examples.MasterSlaveStateModelFactory;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkReconnect.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkReconnect.java
index 45ca347..9fdba9c 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkReconnect.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkReconnect.java
@@ -22,7 +22,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixManagerFactory;
@@ -30,9 +29,10 @@
 import org.apache.helix.SystemPropertyKeys;
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZkTestHelper;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
 import org.apache.helix.tools.ClusterSetup;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/client/TestHelixZkClient.java b/helix-core/src/test/java/org/apache/helix/manager/zk/client/TestHelixZkClient.java
index 796d393..c228c4b 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/client/TestHelixZkClient.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/client/TestHelixZkClient.java
@@ -21,14 +21,16 @@
 
 import java.util.concurrent.TimeUnit;
 
-import org.I0Itec.zkclient.IZkDataListener;
-import org.apache.helix.HelixException;
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZkUnitTestBase;
-import org.apache.helix.manager.zk.zookeeper.ZkConnection;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.ZkConnection;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+
 public class TestHelixZkClient extends ZkUnitTestBase {
   private final String TEST_NODE = "/test_helix_zkclient";
 
@@ -37,8 +39,9 @@
     final String TEST_ROOT = "/testZkConnectionManager/IDEALSTATES";
     final String TEST_PATH = TEST_ROOT + TEST_NODE;
 
-    ZkConnectionManager zkConnectionManager = new ZkConnectionManager(new ZkConnection(ZK_ADDR),
-        HelixZkClient.DEFAULT_CONNECTION_TIMEOUT, null);
+    ZkConnectionManager zkConnectionManager =
+        new ZkConnectionManager(new ZkConnection(ZK_ADDR), HelixZkClient.DEFAULT_CONNECTION_TIMEOUT,
+            null);
     Assert.assertTrue(zkConnectionManager.waitUntilConnected(1, TimeUnit.SECONDS));
 
     // This client can write/read from ZK
@@ -53,7 +56,7 @@
     try {
       zkConnectionManager.close();
       Assert.fail("Dedicated ZkClient cannot be closed while sharing!");
-    } catch (HelixException hex) {
+    } catch (ZkClientException hex) {
       // expected
     }
 
@@ -70,7 +73,7 @@
     try {
       new SharedZkClient(zkConnectionManager, new HelixZkClient.ZkClientConfig(), null);
       Assert.fail("Sharing a closed dedicated ZkClient shall fail.");
-    } catch (HelixException hex) {
+    } catch (ZkClientException hex) {
       // expected
     }
 
@@ -102,9 +105,7 @@
     Assert.assertEquals(sharedZkClientA.getSessionId(), sharedZkClientB.getSessionId());
     long sessionId = sharedZkClientA.getSessionId();
 
-    final int[] notificationCountA = {
-        0, 0
-    };
+    final int[] notificationCountA = {0, 0};
     sharedZkClientA.subscribeDataChanges(TEST_PATH, new IZkDataListener() {
       @Override
       public void handleDataChange(String s, Object o) {
@@ -116,9 +117,7 @@
         notificationCountA[1]++;
       }
     });
-    final int[] notificationCountB = {
-        0, 0
-    };
+    final int[] notificationCountB = {0, 0};
     sharedZkClientB.subscribeDataChanges(TEST_PATH, new IZkDataListener() {
       @Override
       public void handleDataChange(String s, Object o) {
@@ -143,7 +142,7 @@
     try {
       sharedZkClientA.createEphemeral(TEST_PATH, true);
       Assert.fail("Create Ephemeral nodes using shared client should fail.");
-    } catch (HelixException he) {
+    } catch (UnsupportedOperationException e) {
       // expected.
     }
 
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/serializer/TestJacksonPayloadSerializer.java b/helix-core/src/test/java/org/apache/helix/manager/zk/serializer/TestJacksonPayloadSerializer.java
index 85a362f..7a6a54a 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/serializer/TestJacksonPayloadSerializer.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/serializer/TestJacksonPayloadSerializer.java
@@ -23,7 +23,7 @@
 import java.util.LinkedList;
 import java.util.List;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZNRecordStreamingSerializer;
 import org.codehaus.jackson.JsonNode;
diff --git a/helix-core/src/test/java/org/apache/helix/messaging/TestDefaultMessagingService.java b/helix-core/src/test/java/org/apache/helix/messaging/TestDefaultMessagingService.java
index f431ab6..dcd5754 100644
--- a/helix-core/src/test/java/org/apache/helix/messaging/TestDefaultMessagingService.java
+++ b/helix-core/src/test/java/org/apache/helix/messaging/TestDefaultMessagingService.java
@@ -34,7 +34,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.messaging.handling.HelixTaskResult;
 import org.apache.helix.messaging.handling.MessageHandler;
 import org.apache.helix.messaging.handling.MessageHandlerFactory;
diff --git a/helix-core/src/test/java/org/apache/helix/mock/MockBaseDataAccessor.java b/helix-core/src/test/java/org/apache/helix/mock/MockBaseDataAccessor.java
index f50cd83..f9383b0 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/MockBaseDataAccessor.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/MockBaseDataAccessor.java
@@ -25,12 +25,12 @@
 import java.util.List;
 import java.util.Map;
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.IZkChildListener;
-import org.I0Itec.zkclient.IZkDataListener;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
 import org.apache.zookeeper.data.Stat;
 
 public class MockBaseDataAccessor implements BaseDataAccessor<ZNRecord> {
diff --git a/helix-core/src/test/java/org/apache/helix/mock/MockHelixAdmin.java b/helix-core/src/test/java/org/apache/helix/mock/MockHelixAdmin.java
index 8e17d73..b3502eb 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/MockHelixAdmin.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/MockHelixAdmin.java
@@ -30,7 +30,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ClusterConstraints;
 import org.apache.helix.model.ConstraintItem;
diff --git a/helix-core/src/test/java/org/apache/helix/mock/MockManager.java b/helix-core/src/test/java/org/apache/helix/mock/MockManager.java
index bbe7d7b..dfb90ec 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/MockManager.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/MockManager.java
@@ -33,7 +33,7 @@
 import org.apache.helix.MockAccessor;
 import org.apache.helix.PreConnectCallback;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.listeners.ClusterConfigChangeListener;
 import org.apache.helix.api.listeners.ConfigChangeListener;
 import org.apache.helix.api.listeners.ControllerChangeListener;
diff --git a/helix-core/src/test/java/org/apache/helix/mock/MockZkClient.java b/helix-core/src/test/java/org/apache/helix/mock/MockZkClient.java
index b65c2dc..1acff11 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/MockZkClient.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/MockZkClient.java
@@ -6,8 +6,8 @@
 
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkAsyncCallbacks;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 
 public class MockZkClient extends ZkClient implements HelixZkClient {
   Map<String, byte[]> _dataMap;
diff --git a/helix-core/src/test/java/org/apache/helix/mock/MockZkHelixDataAccessor.java b/helix-core/src/test/java/org/apache/helix/mock/MockZkHelixDataAccessor.java
index 9b71ff2..480be70 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/MockZkHelixDataAccessor.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/MockZkHelixDataAccessor.java
@@ -8,7 +8,7 @@
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyType;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 
 public class MockZkHelixDataAccessor extends ZKHelixDataAccessor {
diff --git a/helix-core/src/test/java/org/apache/helix/mock/controller/MockController.java b/helix-core/src/test/java/org/apache/helix/mock/controller/MockController.java
index 55535e3..56710d2 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/controller/MockController.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/controller/MockController.java
@@ -30,12 +30,12 @@
 
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState.IdealStateProperty;
 import org.apache.helix.model.LiveInstance.LiveInstanceProperty;
diff --git a/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java b/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java
index cee2a79..e3d3833 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java
@@ -19,14 +19,15 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixManager;
 import org.apache.helix.NotificationContext;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.Message;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+
 
 // simulate access property store and update different znodes
 public class StoreAccessDiffNodeTransition extends MockTransition {
diff --git a/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java b/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java
index 9e1c487..216fb57 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java
@@ -19,14 +19,15 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixManager;
 import org.apache.helix.NotificationContext;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.Message;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+
 
 // simulate access property store and update one znode
 public class StoreAccessOneNodeTransition extends MockTransition {
diff --git a/helix-core/src/test/java/org/apache/helix/mock/spectator/MockSpectatorProcess.java b/helix-core/src/test/java/org/apache/helix/mock/spectator/MockSpectatorProcess.java
index 60c0003..db00bb7 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/spectator/MockSpectatorProcess.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/spectator/MockSpectatorProcess.java
@@ -21,17 +21,18 @@
 
 import java.util.List;
 
-import org.I0Itec.zkclient.IDefaultNameSpace;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixManagerFactory;
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.spectator.RoutingTableProvider;
 import org.apache.helix.tools.ClusterSetup;
+import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
 
 
 /**
@@ -79,7 +80,7 @@
 
     IDefaultNameSpace defaultNameSpace = new IDefaultNameSpace() {
       @Override
-      public void createDefaultNameSpace(org.I0Itec.zkclient.ZkClient client) {
+      public void createDefaultNameSpace(ZkClient client) {
         client.deleteRecursive("/" + clusterName);
 
       }
diff --git a/helix-core/src/test/java/org/apache/helix/model/TestClusterConfig.java b/helix-core/src/test/java/org/apache/helix/model/TestClusterConfig.java
index d688827..8e4a016 100644
--- a/helix-core/src/test/java/org/apache/helix/model/TestClusterConfig.java
+++ b/helix-core/src/test/java/org/apache/helix/model/TestClusterConfig.java
@@ -26,7 +26,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java b/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java
index 78df6d3..43b9ee2 100644
--- a/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java
+++ b/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java
@@ -27,7 +27,7 @@
 
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
diff --git a/helix-core/src/test/java/org/apache/helix/model/TestInstanceConfig.java b/helix-core/src/test/java/org/apache/helix/model/TestInstanceConfig.java
index f4c7715..9b47677 100644
--- a/helix-core/src/test/java/org/apache/helix/model/TestInstanceConfig.java
+++ b/helix-core/src/test/java/org/apache/helix/model/TestInstanceConfig.java
@@ -20,7 +20,7 @@
  */
 
 import com.google.common.collect.ImmutableMap;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/model/TestResourceConfig.java b/helix-core/src/test/java/org/apache/helix/model/TestResourceConfig.java
index 8099486..2e13c3e 100644
--- a/helix-core/src/test/java/org/apache/helix/model/TestResourceConfig.java
+++ b/helix-core/src/test/java/org/apache/helix/model/TestResourceConfig.java
@@ -20,7 +20,7 @@
  */
 
 import com.google.common.collect.ImmutableMap;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.testng.Assert;
 import org.testng.annotations.Test;
diff --git a/helix-core/src/test/java/org/apache/helix/model/TestStateModelValidity.java b/helix-core/src/test/java/org/apache/helix/model/TestStateModelValidity.java
index c3b8beb..88cbb39 100644
--- a/helix-core/src/test/java/org/apache/helix/model/TestStateModelValidity.java
+++ b/helix-core/src/test/java/org/apache/helix/model/TestStateModelValidity.java
@@ -26,7 +26,7 @@
 
 import com.google.common.collect.Lists;
 import org.apache.helix.HelixDefinedState;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.StateModelDefinition.StateModelDefinitionProperty;
 import org.apache.helix.tools.StateModelConfigGenerator;
 import org.testng.Assert;
diff --git a/helix-core/src/test/java/org/apache/helix/model/TestStateTransitionProperty.java b/helix-core/src/test/java/org/apache/helix/model/TestStateTransitionProperty.java
index 86ab7fb..a5ea1e1 100644
--- a/helix-core/src/test/java/org/apache/helix/model/TestStateTransitionProperty.java
+++ b/helix-core/src/test/java/org/apache/helix/model/TestStateTransitionProperty.java
@@ -19,7 +19,7 @@
  * under the License.
  */
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.config.StateTransitionTimeoutConfig;
 import org.testng.Assert;
 import org.testng.annotations.Test;
diff --git a/helix-core/src/test/java/org/apache/helix/monitoring/TestZKPathDataDumpTask.java b/helix-core/src/test/java/org/apache/helix/monitoring/TestZKPathDataDumpTask.java
index 8085a93..a724d3e 100644
--- a/helix-core/src/test/java/org/apache/helix/monitoring/TestZKPathDataDumpTask.java
+++ b/helix-core/src/test/java/org/apache/helix/monitoring/TestZKPathDataDumpTask.java
@@ -26,7 +26,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
diff --git a/helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestClusterStatusMonitor.java b/helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestClusterStatusMonitor.java
index f4ba01f..dee1f86 100644
--- a/helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestClusterStatusMonitor.java
+++ b/helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestClusterStatusMonitor.java
@@ -42,7 +42,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.stages.BestPossibleStateOutput;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
 import org.apache.helix.model.ExternalView;
diff --git a/helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestResourceMonitor.java b/helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestResourceMonitor.java
index f630124..623ae58 100644
--- a/helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestResourceMonitor.java
+++ b/helix-core/src/test/java/org/apache/helix/monitoring/mbeans/TestResourceMonitor.java
@@ -37,7 +37,7 @@
 
 import com.google.common.collect.ImmutableMap;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
diff --git a/helix-core/src/test/java/org/apache/helix/participant/MockZKHelixManager.java b/helix-core/src/test/java/org/apache/helix/participant/MockZKHelixManager.java
index f22484e..85b376f 100644
--- a/helix-core/src/test/java/org/apache/helix/participant/MockZKHelixManager.java
+++ b/helix-core/src/test/java/org/apache/helix/participant/MockZKHelixManager.java
@@ -32,7 +32,7 @@
 import org.apache.helix.LiveInstanceInfoProvider;
 import org.apache.helix.PreConnectCallback;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.api.listeners.ClusterConfigChangeListener;
 import org.apache.helix.api.listeners.ConfigChangeListener;
 import org.apache.helix.api.listeners.ControllerChangeListener;
@@ -48,7 +48,7 @@
 import org.apache.helix.healthcheck.ParticipantHealthReportCollector;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.messaging.DefaultMessagingService;
 import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
diff --git a/helix-core/src/test/java/org/apache/helix/participant/TestDistControllerStateModel.java b/helix-core/src/test/java/org/apache/helix/participant/TestDistControllerStateModel.java
index 28c0fb0..51bb209 100644
--- a/helix-core/src/test/java/org/apache/helix/participant/TestDistControllerStateModel.java
+++ b/helix-core/src/test/java/org/apache/helix/participant/TestDistControllerStateModel.java
@@ -21,7 +21,7 @@
 
 import org.apache.helix.NotificationContext;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.Message.MessageType;
diff --git a/helix-core/src/test/java/org/apache/helix/participant/TestDistControllerStateModelFactory.java b/helix-core/src/test/java/org/apache/helix/participant/TestDistControllerStateModelFactory.java
index b7aa419..e7d3d76 100644
--- a/helix-core/src/test/java/org/apache/helix/participant/TestDistControllerStateModelFactory.java
+++ b/helix-core/src/test/java/org/apache/helix/participant/TestDistControllerStateModelFactory.java
@@ -19,7 +19,7 @@
  * under the License.
  */
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.model.Message;
 import org.testng.annotations.Test;
diff --git a/helix-core/src/test/java/org/apache/helix/spectator/TestRoutingDataCache.java b/helix-core/src/test/java/org/apache/helix/spectator/TestRoutingDataCache.java
index 2cfc62a..146dd37 100644
--- a/helix-core/src/test/java/org/apache/helix/spectator/TestRoutingDataCache.java
+++ b/helix-core/src/test/java/org/apache/helix/spectator/TestRoutingDataCache.java
@@ -24,7 +24,7 @@
 import org.apache.helix.HelixConstants;
 import org.apache.helix.PropertyType;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.common.ZkStandAloneCMTestBase;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.mock.MockZkHelixDataAccessor;
diff --git a/helix-core/src/test/java/org/apache/helix/store/TestJsonComparator.java b/helix-core/src/test/java/org/apache/helix/store/TestJsonComparator.java
index 5af3d55..fdfbbce 100644
--- a/helix-core/src/test/java/org/apache/helix/store/TestJsonComparator.java
+++ b/helix-core/src/test/java/org/apache/helix/store/TestJsonComparator.java
@@ -21,7 +21,7 @@
 
 import java.util.Date;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.testng.AssertJUnit;
 import org.testng.annotations.Test;
 
diff --git a/helix-core/src/test/java/org/apache/helix/store/zk/TestAutoFallbackPropertyStore.java b/helix-core/src/test/java/org/apache/helix/store/zk/TestAutoFallbackPropertyStore.java
index 4b44cb1..f322397 100644
--- a/helix-core/src/test/java/org/apache/helix/store/zk/TestAutoFallbackPropertyStore.java
+++ b/helix-core/src/test/java/org/apache/helix/store/zk/TestAutoFallbackPropertyStore.java
@@ -23,13 +23,13 @@
 import java.util.Date;
 import java.util.List;
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyType;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.apache.zookeeper.data.Stat;
 import org.testng.Assert;
 import org.testng.annotations.Test;
diff --git a/helix-core/src/test/java/org/apache/helix/store/zk/TestZkHelixPropertyStore.java b/helix-core/src/test/java/org/apache/helix/store/zk/TestZkHelixPropertyStore.java
index 8b882a7..98fa298 100644
--- a/helix-core/src/test/java/org/apache/helix/store/zk/TestZkHelixPropertyStore.java
+++ b/helix-core/src/test/java/org/apache/helix/store/zk/TestZkHelixPropertyStore.java
@@ -31,18 +31,18 @@
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
 
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
-import org.I0Itec.zkclient.serialize.SerializableSerializer;
 import org.apache.helix.AccessOption;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.monitoring.mbeans.MBeanRegistrar;
 import org.apache.helix.monitoring.mbeans.MonitorDomainNames;
 import org.apache.helix.monitoring.mbeans.ZkClientMonitor;
 import org.apache.helix.monitoring.mbeans.ZkClientPathMonitor;
 import org.apache.helix.store.HelixPropertyListener;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.zkclient.serialize.SerializableSerializer;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.Test;
diff --git a/helix-core/src/test/java/org/apache/helix/store/zk/TestZkManagerWithAutoFallbackStore.java b/helix-core/src/test/java/org/apache/helix/store/zk/TestZkManagerWithAutoFallbackStore.java
index ed22835..df78aa9 100644
--- a/helix-core/src/test/java/org/apache/helix/store/zk/TestZkManagerWithAutoFallbackStore.java
+++ b/helix-core/src/test/java/org/apache/helix/store/zk/TestZkManagerWithAutoFallbackStore.java
@@ -24,7 +24,7 @@
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.testng.Assert;
diff --git a/helix-core/src/test/java/org/apache/helix/task/TestAssignableInstanceManager.java b/helix-core/src/test/java/org/apache/helix/task/TestAssignableInstanceManager.java
index 76197f7..08b4166 100644
--- a/helix-core/src/test/java/org/apache/helix/task/TestAssignableInstanceManager.java
+++ b/helix-core/src/test/java/org/apache/helix/task/TestAssignableInstanceManager.java
@@ -27,7 +27,7 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.caches.TaskDataCache;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.InstanceConfig;
diff --git a/helix-core/src/test/java/org/apache/helix/tools/TestClusterSetup.java b/helix-core/src/test/java/org/apache/helix/tools/TestClusterSetup.java
index df3f341..2a3e04a 100644
--- a/helix-core/src/test/java/org/apache/helix/tools/TestClusterSetup.java
+++ b/helix-core/src/test/java/org/apache/helix/tools/TestClusterSetup.java
@@ -29,7 +29,7 @@
 import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
diff --git a/helix-core/src/test/java/org/apache/helix/tools/TestHelixAdminCli.java b/helix-core/src/test/java/org/apache/helix/tools/TestHelixAdminCli.java
index 1008371..660c8de 100644
--- a/helix-core/src/test/java/org/apache/helix/tools/TestHelixAdminCli.java
+++ b/helix-core/src/test/java/org/apache/helix/tools/TestHelixAdminCli.java
@@ -29,7 +29,7 @@
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.common.ZkTestBase;
 import org.apache.helix.integration.manager.ClusterDistributedController;
 import org.apache.helix.integration.manager.MockParticipantManager;
diff --git a/helix-core/src/test/java/org/apache/helix/tools/TestZkCopy.java b/helix-core/src/test/java/org/apache/helix/tools/TestZkCopy.java
index 17d857e..a241719 100644
--- a/helix-core/src/test/java/org/apache/helix/tools/TestZkCopy.java
+++ b/helix-core/src/test/java/org/apache/helix/tools/TestZkCopy.java
@@ -23,7 +23,7 @@
 
 import org.apache.helix.InstanceType;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.ZkUnitTestBase;
 import org.apache.helix.manager.zk.ZKUtil;
 import org.apache.helix.tools.commandtools.ZkCopy;
diff --git a/helix-core/src/test/java/org/apache/helix/util/TestPropertyKeyGetPath.java b/helix-core/src/test/java/org/apache/helix/util/TestPropertyKeyGetPath.java
index fc0a3f3..572c5c6 100644
--- a/helix-core/src/test/java/org/apache/helix/util/TestPropertyKeyGetPath.java
+++ b/helix-core/src/test/java/org/apache/helix/util/TestPropertyKeyGetPath.java
@@ -22,7 +22,7 @@
 import com.google.common.base.Joiner;
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.task.TaskTestBase;
 import org.apache.helix.task.TaskConstants;
 import org.apache.helix.task.WorkflowContext;
diff --git a/helix-core/src/test/java/org/apache/helix/util/TestZKClientPool.java b/helix-core/src/test/java/org/apache/helix/util/TestZKClientPool.java
index 5c5c8ae..d94d2aa 100644
--- a/helix-core/src/test/java/org/apache/helix/util/TestZKClientPool.java
+++ b/helix-core/src/test/java/org/apache/helix/util/TestZKClientPool.java
@@ -21,11 +21,11 @@
 
 import java.util.Date;
 
-import org.I0Itec.zkclient.ZkServer;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/helix-rest/pom.xml b/helix-rest/pom.xml
index b14b902..e3421d5 100644
--- a/helix-rest/pom.xml
+++ b/helix-rest/pom.xml
@@ -36,6 +36,8 @@
       org.apache.commons.cli*,
       org.restlet*,
       org.slf4j*;version="[1.6,2)",
+      org.apache.zookeeper*;version="[3.4,4)",
+      org.apache.commons.io*;version="[1.4,2)",
       *
     </osgi.import>
     <osgi.export>org.apache.helix.rest*;version="${project.version};-noimport:=true</osgi.export>
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/common/HelixDataAccessorWrapper.java b/helix-rest/src/main/java/org/apache/helix/rest/common/HelixDataAccessorWrapper.java
index 6a501f0..e68276c 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/common/HelixDataAccessorWrapper.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/common/HelixDataAccessorWrapper.java
@@ -34,7 +34,7 @@
 
 import org.apache.helix.HelixProperty;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.model.RESTConfig;
 import org.apache.helix.rest.client.CustomRestClient;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/ServerContext.java b/helix-rest/src/main/java/org/apache/helix/rest/server/ServerContext.java
index acacce9..5a0530a 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/ServerContext.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/ServerContext.java
@@ -23,22 +23,22 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.ConfigAccessor;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.InstanceType;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.manager.zk.client.SharedZkClientFactory;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory;
 import org.apache.helix.task.TaskDriver;
 import org.apache.helix.tools.ClusterSetup;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 
 
 public class ServerContext {
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/json/instance/InstanceInfo.java b/helix-rest/src/main/java/org/apache/helix/rest/server/json/instance/InstanceInfo.java
index 6855e44..b37f033 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/json/instance/InstanceInfo.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/json/instance/InstanceInfo.java
@@ -25,7 +25,7 @@
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/AbstractHelixResource.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/AbstractHelixResource.java
index b13e740..f1bb583 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/AbstractHelixResource.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/AbstractHelixResource.java
@@ -24,10 +24,10 @@
 import org.apache.helix.ConfigAccessor;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixDataAccessor;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.ZkClient;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.rest.common.ContextPropertyKeys;
 import org.apache.helix.rest.server.ServerContext;
 import org.apache.helix.rest.server.resources.AbstractResource;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
index f7f9af5..1257cdb 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
@@ -44,9 +44,9 @@
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKUtil;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ControllerHistory;
 import org.apache.helix.model.HelixConfigScope;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/JobAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/JobAccessor.java
index ee84df7..3a6afee 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/JobAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/JobAccessor.java
@@ -34,14 +34,14 @@
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.task.JobConfig;
 import org.apache.helix.task.JobContext;
 import org.apache.helix.task.TaskConfig;
 import org.apache.helix.task.TaskDriver;
 import org.apache.helix.task.WorkflowConfig;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.codehaus.jackson.node.ArrayNode;
 import org.codehaus.jackson.node.JsonNodeFactory;
 import org.codehaus.jackson.node.ObjectNode;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
index 368c730..2f861bc 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
@@ -40,7 +40,7 @@
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.Error;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PropertyStoreAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PropertyStoreAccessor.java
index 95a8723..4ccc610 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PropertyStoreAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PropertyStoreAccessor.java
@@ -27,7 +27,7 @@
 
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.rest.server.resources.zookeeper.ZooKeeperAccessor;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAccessor.java
index c41d024..ca2189e 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAccessor.java
@@ -41,8 +41,8 @@
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.HelixConfigScope;
 import org.apache.helix.model.IdealState;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/TaskAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/TaskAccessor.java
index 22617db..115813d 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/TaskAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/TaskAccessor.java
@@ -29,8 +29,8 @@
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.task.TaskDriver;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.codehaus.jackson.type.TypeReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/WorkflowAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/WorkflowAccessor.java
index 714571e..384b37c 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/WorkflowAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/WorkflowAccessor.java
@@ -36,9 +36,8 @@
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.task.JobConfig;
 import org.apache.helix.task.JobDag;
 import org.apache.helix.task.JobQueue;
@@ -46,6 +45,7 @@
 import org.apache.helix.task.Workflow;
 import org.apache.helix.task.WorkflowConfig;
 import org.apache.helix.task.WorkflowContext;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.codehaus.jackson.JsonNode;
 import org.codehaus.jackson.map.type.TypeFactory;
 import org.codehaus.jackson.node.ArrayNode;
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java
index cae7dc1..0774bc7 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java
@@ -19,8 +19,6 @@
  * under the License.
  */
 
-import java.io.UnsupportedEncodingException;
-import java.util.Base64;
 import java.util.List;
 import java.util.Map;
 import javax.ws.rs.GET;
@@ -159,9 +157,9 @@
    * @return list of child ZNodes
    */
   private Response getChildren(ZkBaseDataAccessor<byte[]> zkBaseDataAccessor, String path) {
-    if (_zkBaseDataAccessor.exists(path, AccessOption.PERSISTENT)) {
+    if (zkBaseDataAccessor.exists(path, AccessOption.PERSISTENT)) {
       Map<String, List<String>> result = ImmutableMap.of(ZooKeeperCommand.getChildren.name(),
-          _zkBaseDataAccessor.getChildNames(path, AccessOption.PERSISTENT));
+          zkBaseDataAccessor.getChildNames(path, AccessOption.PERSISTENT));
       return JSONRepresentation(result);
     } else {
       throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND)
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/common/TestHelixDataAccessorWrapper.java b/helix-rest/src/test/java/org/apache/helix/rest/common/TestHelixDataAccessorWrapper.java
index 5cf4fba..0449aa9 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/common/TestHelixDataAccessorWrapper.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/common/TestHelixDataAccessorWrapper.java
@@ -30,7 +30,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import org.apache.helix.PropertyKey;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.model.HealthStat;
 import org.apache.helix.model.RESTConfig;
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/AbstractTestClass.java b/helix-rest/src/test/java/org/apache/helix/rest/server/AbstractTestClass.java
index e3b33fb..cc04984 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/AbstractTestClass.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/AbstractTestClass.java
@@ -37,22 +37,21 @@
 import javax.ws.rs.core.Response;
 
 import com.google.common.base.Joiner;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.helix.AccessOption;
 import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.ConfigAccessor;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.PropertyType;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.integration.manager.ClusterControllerManager;
 import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.integration.task.MockTask;
 import org.apache.helix.integration.task.TaskTestUtil;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory;
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.InstanceConfig;
@@ -80,6 +79,7 @@
 import org.apache.helix.task.WorkflowContext;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.util.ZKClientPool;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.glassfish.jersey.client.ClientConfig;
 import org.glassfish.jersey.server.ResourceConfig;
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
index 05525c0..e7fda60 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
@@ -36,7 +36,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.rebalancer.DelayedAutoRebalancer;
 import org.apache.helix.controller.rebalancer.strategy.CrushEdRebalanceStrategy;
 import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
index d57bbde..68a4e85 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
@@ -36,7 +36,7 @@
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.InstanceConfig;
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPropertyStoreAccessor.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPropertyStoreAccessor.java
index cfc6461..fc0a701 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPropertyStoreAccessor.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPropertyStoreAccessor.java
@@ -22,13 +22,13 @@
 import java.io.IOException;
 import javax.ws.rs.core.Response;
 
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.AccessOption;
 import org.apache.helix.PropertyPathBuilder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.rest.server.util.JerseyUriRequestBuilder;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.apache.http.HttpStatus;
 import org.codehaus.jackson.JsonNode;
 import org.junit.Assert;
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
index f865674..6d672ed 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
@@ -40,7 +40,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.TestHelper;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.ExternalView;
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestZooKeeperAccessor.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestZooKeeperAccessor.java
index 4d71d56..23337ba 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestZooKeeperAccessor.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestZooKeeperAccessor.java
@@ -27,11 +27,11 @@
 
 import javax.ws.rs.core.Response;
 
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
 import org.apache.helix.AccessOption;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.rest.server.util.JerseyUriRequestBuilder;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
diff --git a/metrics-common/LICENSE b/metrics-common/LICENSE
new file mode 100644
index 0000000..d78ae52
--- /dev/null
+++ b/metrics-common/LICENSE
@@ -0,0 +1,270 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
+
+
+For xstream:
+
+Copyright (c) 2003-2006, Joe Walnes
+Copyright (c) 2006-2009, 2011 XStream Committers
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other materials provided
+with the distribution.
+
+3. Neither the name of XStream nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+for jline:
+
+Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/metrics-common/NOTICE b/metrics-common/NOTICE
new file mode 100644
index 0000000..ff5a745
--- /dev/null
+++ b/metrics-common/NOTICE
@@ -0,0 +1,37 @@
+Apache Helix
+Copyright 2014 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Codehaus (http://www.codehaus.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+jline (http://jline.sourceforge.net/).
+Licensed under the BSD License.
+
+This product includes software developed at
+restlet (http://www.restlet.org/about/legal).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Google (http://www.google.com/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+snakeyaml (http://www.snakeyaml.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+zkclient (https://github.com/sgroschupf/zkclient).
+Licensed under the Apache License 2.0.
+
+II. License Summary
+- Apache License 2.0
+- BSD License
\ No newline at end of file
diff --git a/metrics-common/metrics-common-0.9.2-SNAPSHOT.ivy b/metrics-common/metrics-common-0.9.2-SNAPSHOT.ivy
new file mode 100644
index 0000000..e84dd9a
--- /dev/null
+++ b/metrics-common/metrics-common-0.9.2-SNAPSHOT.ivy
@@ -0,0 +1,47 @@
+<?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.
+-->
+<ivy-module version="1.0">
+	<info organisation="org.apache.helix"
+		module="metrics-common"
+		revision="0.9.2-SNAPSHOT"
+		status="integration"
+		publication="20170128141623"
+	/>
+	<configurations>
+		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
+		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
+		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
+		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
+		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
+		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases."/>
+		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
+	</configurations>
+	<publications>
+		<artifact name="metrics-common" type="jar" ext="jar" conf="master"/>
+	</publications>
+	<dependencies>
+	  <dependency org="org.slf4j" name="slf4j-api" rev="1.7.25" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)">
+        <artifact name="slf4j-api" ext="jar"/>
+    </dependency>
+    <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.14" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)">
+        <artifact name="slf4j-log4j12" ext="jar"/>
+    </dependency>
+	</dependencies>
+</ivy-module>
diff --git a/metrics-common/pom.xml b/metrics-common/pom.xml
new file mode 100644
index 0000000..79a5e1f
--- /dev/null
+++ b/metrics-common/pom.xml
@@ -0,0 +1,91 @@
+<?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>
+    <groupId>org.apache.helix</groupId>
+    <artifactId>helix</artifactId>
+    <version>0.9.2-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>metrics-common</artifactId>
+  <packaging>bundle</packaging>
+  <name>Apache Helix :: Metrics Common</name>
+
+  <properties>
+    <osgi.import>
+      org.slf4j*;version="[1.6,2)",
+      *
+    </osgi.import>
+    <osgi.export>org.apache.helix*;version="${project.version};-noimport:=true</osgi.export>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.dropwizard.metrics</groupId>
+      <artifactId>metrics-core</artifactId>
+      <version>3.2.3</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.25</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>1.7.14</version>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <resources>
+      <resource>
+        <directory>${basedir}</directory>
+        <includes>
+          <include>DISCLAIMER</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/assemble/assembly.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/metrics-common/src/assemble/assembly.xml b/metrics-common/src/assemble/assembly.xml
new file mode 100644
index 0000000..3c9eb4b
--- /dev/null
+++ b/metrics-common/src/assemble/assembly.xml
@@ -0,0 +1,60 @@
+<?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.
+-->
+<assembly>
+  <id>pkg</id>
+  <formats>
+    <format>tar</format>
+  </formats>
+  <fileSets>
+    <fileSet>
+      <directory>target/metrics-common-pkg/bin</directory>
+      <outputDirectory>bin</outputDirectory>
+      <lineEnding>unix</lineEnding>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>target/metrics-common-pkg/repo/</directory>
+      <outputDirectory>repo</outputDirectory>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+      <excludes>
+        <exclude>**/*.xml</exclude>
+      </excludes>
+    </fileSet>
+   <fileSet>
+      <directory>target/metrics-common-pkg/conf</directory>
+      <outputDirectory>conf</outputDirectory>
+      <lineEnding>unix</lineEnding>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}</directory>
+      <outputDirectory>/</outputDirectory>
+      <includes>
+        <include>LICENSE</include>
+        <include>NOTICE</include>
+        <include>DISCLAIMER</include>
+      </includes>
+      <fileMode>0755</fileMode>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/SensorNameProvider.java b/metrics-common/src/main/java/org/apache/helix/monitoring/SensorNameProvider.java
similarity index 100%
rename from helix-core/src/main/java/org/apache/helix/monitoring/SensorNameProvider.java
rename to metrics-common/src/main/java/org/apache/helix/monitoring/SensorNameProvider.java
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MBeanRegistrar.java b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/MBeanRegistrar.java
similarity index 99%
rename from helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MBeanRegistrar.java
rename to metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/MBeanRegistrar.java
index 9cff11b..04ff496 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MBeanRegistrar.java
+++ b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/MBeanRegistrar.java
@@ -29,6 +29,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 public class MBeanRegistrar {
   private static Logger LOG = LoggerFactory.getLogger(MBeanRegistrar.class);
 
diff --git a/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java
new file mode 100644
index 0000000..73bf057
--- /dev/null
+++ b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java
@@ -0,0 +1,32 @@
+package org.apache.helix.monitoring.mbeans;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * This enum defines all of domain names used with various Helix monitor mbeans.
+ */
+public enum MonitorDomainNames {
+  ClusterStatus,
+  HelixZkClient,
+  HelixThreadPoolExecutor,
+  HelixCallback,
+  RoutingTableProvider,
+  CLMParticipantReport
+}
diff --git a/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMBeanProvider.java b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMBeanProvider.java
new file mode 100644
index 0000000..8174326
--- /dev/null
+++ b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMBeanProvider.java
@@ -0,0 +1,260 @@
+package org.apache.helix.monitoring.mbeans.dynamicMBeans;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.apache.helix.monitoring.SensorNameProvider;
+import org.apache.helix.monitoring.mbeans.MBeanRegistrar;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Dynamic MBean provider that reporting DynamicMetric attributes
+ */
+public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNameProvider {
+  protected final Logger _logger = LoggerFactory.getLogger(getClass());
+  protected static final long DEFAULT_RESET_INTERVAL_MS = 60 * 60 * 1000; // Reset time every hour
+  private static final String HELIX_MONITOR_TIME_WINDOW_LENGTH_MS =
+      "helix.monitor.slidingTimeWindow.ms";
+
+  private static String SENSOR_NAME_TAG = "SensorName";
+  private static String DEFAULT_DESCRIPTION =
+      "Information on the management interface of the MBean";
+
+  // Attribute name to the DynamicMetric object mapping
+  private final Map<String, DynamicMetric> _attributeMap = new HashMap<>();
+  private ObjectName _objectName = null;
+  private MBeanInfo _mBeanInfo;
+
+  /**
+   * Instantiates a new Dynamic MBean provider.
+   * @param dynamicMetrics Dynamic Metrics that are exposed by this provider
+   * @param description the MBean description
+   * @param domain the MBean domain name
+   * @param keyValuePairs the MBean object name components
+   */
+  protected synchronized boolean doRegister(Collection<DynamicMetric<?, ?>> dynamicMetrics,
+      String description, String domain, String... keyValuePairs) throws JMException {
+    return doRegister(dynamicMetrics, description,
+        MBeanRegistrar.buildObjectName(domain, keyValuePairs));
+  }
+
+  /**
+   * Instantiates a new Dynamic MBean provider.
+   * @param dynamicMetrics Dynamic Metrics that are exposed by this provider
+   * @param description the MBean description
+   * @param objectName the proposed MBean ObjectName
+   */
+  protected synchronized boolean doRegister(Collection<DynamicMetric<?, ?>> dynamicMetrics,
+      String description, ObjectName objectName) throws JMException {
+    if (_objectName != null) {
+      _logger.debug("Mbean {} has already been registered. Ignore register request.",
+          objectName.getCanonicalName());
+      return false;
+    }
+    updateAttributtInfos(dynamicMetrics, description);
+    _objectName = MBeanRegistrar.register(this, objectName);
+    return true;
+  }
+
+  protected synchronized boolean doRegister(Collection<DynamicMetric<?, ?>> dynamicMetrics,
+      ObjectName objectName) throws JMException {
+    return doRegister(dynamicMetrics, null, objectName);
+  }
+
+  /**
+   * Update the Dynamic MBean provider with new metric list.
+   * @param description description of the MBean
+   * @param dynamicMetrics the DynamicMetrics
+   */
+  private void updateAttributtInfos(Collection<DynamicMetric<?, ?>> dynamicMetrics,
+      String description) {
+    _attributeMap.clear();
+
+    // get all attributes that can be emit by the dynamicMetrics.
+    List<MBeanAttributeInfo> attributeInfoList = new ArrayList<>();
+    if (dynamicMetrics != null) {
+      for (DynamicMetric dynamicMetric : dynamicMetrics) {
+        Iterator<MBeanAttributeInfo> iter = dynamicMetric.getAttributeInfos().iterator();
+        while (iter.hasNext()) {
+          MBeanAttributeInfo attributeInfo = iter.next();
+          // Info list to create MBean info
+          attributeInfoList.add(attributeInfo);
+          // Attribute mapping for getting attribute value when getAttribute() is called
+          _attributeMap.put(attributeInfo.getName(), dynamicMetric);
+        }
+      }
+    }
+
+    // SensorName
+    attributeInfoList.add(new MBeanAttributeInfo(SENSOR_NAME_TAG, String.class.getName(),
+        "The name of the metric sensor", true, false, false));
+
+    MBeanConstructorInfo constructorInfo = new MBeanConstructorInfo(
+        String.format("Default %s Constructor", getClass().getSimpleName()),
+        getClass().getConstructors()[0]);
+
+    MBeanAttributeInfo[] attributeInfos = new MBeanAttributeInfo[attributeInfoList.size()];
+    attributeInfos = attributeInfoList.toArray(attributeInfos);
+
+    if (description == null) {
+      description = DEFAULT_DESCRIPTION;
+    }
+
+    _mBeanInfo = new MBeanInfo(getClass().getName(), description, attributeInfos,
+        new MBeanConstructorInfo[]{constructorInfo}, new MBeanOperationInfo[0],
+        new MBeanNotificationInfo[0]);
+  }
+
+  /**
+   * Call doRegister() to finish registration MBean and the attributes.
+   */
+  public abstract DynamicMBeanProvider register() throws JMException;
+
+  /**
+   * Unregister the MBean and clean up object name record.
+   * Note that all the metric data is kept even after unregister.
+   */
+  public synchronized void unregister() {
+    MBeanRegistrar.unregister(_objectName);
+    _objectName = null;
+  }
+
+  @Override
+  public Object getAttribute(String attribute)
+      throws AttributeNotFoundException, MBeanException, ReflectionException {
+    if (SENSOR_NAME_TAG.equals(attribute)) {
+      return getSensorName();
+    }
+
+    if (!_attributeMap.containsKey(attribute)) {
+      return null;
+    }
+
+    return _attributeMap.get(attribute).getAttributeValue(attribute);
+  }
+
+  @Override
+  public AttributeList getAttributes(String[] attributes) {
+    AttributeList attributeList = new AttributeList();
+    for (String attributeName : attributes) {
+      try {
+        Object value = getAttribute(attributeName);
+        attributeList.add(new Attribute(attributeName, value));
+      } catch (AttributeNotFoundException | MBeanException | ReflectionException ex) {
+        _logger.error("Failed to get attribute: " + attributeName, ex);
+      }
+    }
+    return attributeList;
+  }
+
+  @Override
+  public MBeanInfo getMBeanInfo() {
+    return _mBeanInfo;
+  }
+
+  @Override
+  public void setAttribute(Attribute attribute)
+      throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException,
+             ReflectionException {
+    // All MBeans are readonly
+    return;
+  }
+
+  @Override
+  public AttributeList setAttributes(AttributeList attributes) {
+    // All MBeans are readonly
+    return null;
+  }
+
+  @Override
+  public Object invoke(String actionName, Object[] params, String[] signature)
+      throws MBeanException, ReflectionException {
+    // No operation supported
+    return null;
+  }
+
+  /**
+   * NOTE: This method is not thread-safe nor atomic.
+   * Increment the value of a given SimpleDynamicMetric by 1.
+   */
+  protected void incrementSimpleDynamicMetric(SimpleDynamicMetric<Long> metric) {
+    incrementSimpleDynamicMetric(metric, 1);
+  }
+
+  /**
+   * NOTE: This method is not thread-safe nor atomic.
+   * Increment the value of a given SimpleDynamicMetric with input value.
+   */
+  protected void incrementSimpleDynamicMetric(SimpleDynamicMetric<Long> metric, long value) {
+    metric.updateValue(metric.getValue() + value);
+  }
+
+  /**
+   * Return the interval length for the underlying reservoir used by the MBean metric configured
+   * in the system env variables. If not found, use default value.
+   */
+  protected Long getResetIntervalInMs() {
+    return getSystemPropertyAsLong(HELIX_MONITOR_TIME_WINDOW_LENGTH_MS, DEFAULT_RESET_INTERVAL_MS);
+  }
+
+  /**
+   * Get the value of system property
+   * @param propertyKey
+   * @param propertyDefaultValue
+   * @return
+   */
+  private long getSystemPropertyAsLong(String propertyKey, long propertyDefaultValue) {
+    String valueString = System.getProperty(propertyKey, "" + propertyDefaultValue);
+
+    try {
+      long value = Long.parseLong(valueString);
+      if (value > 0) {
+        return value;
+      }
+    } catch (NumberFormatException e) {
+      _logger.warn("Exception while parsing property: " + propertyKey + ", string: " + valueString
+          + ", using default value: " + propertyDefaultValue);
+    }
+
+    return propertyDefaultValue;
+  }
+}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMetric.java b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMetric.java
similarity index 95%
rename from helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMetric.java
rename to metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMetric.java
index 5ac4cbd..b1736c4 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMetric.java
+++ b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMetric.java
@@ -25,7 +25,8 @@
 import java.util.Set;
 import javax.management.MBeanAttributeInfo;
 
-import org.apache.helix.HelixException;
+import org.apache.helix.monitoring.mbeans.exception.MetricException;
+
 
 /**
  * The abstract class for dynamic metrics that is used to emitting monitor data to DynamicMBean.
@@ -48,7 +49,7 @@
    */
   public DynamicMetric(String metricName, O metricObject) {
     if (metricName == null || metricObject == null) {
-      throw new HelixException("Failed to construct metric due to missing argument.");
+      throw new MetricException("Failed to construct metric due to missing argument.");
     }
     _metricObject = metricObject;
     _attributeInfoSet =
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/HistogramDynamicMetric.java b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/HistogramDynamicMetric.java
similarity index 99%
rename from helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/HistogramDynamicMetric.java
rename to metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/HistogramDynamicMetric.java
index b683043..64f4c0b 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/HistogramDynamicMetric.java
+++ b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/HistogramDynamicMetric.java
@@ -30,6 +30,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 /**
  * The dynamic metric that accept Long monitor data and emits histogram information based on the input
  */
diff --git a/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/SimpleDynamicMetric.java b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/SimpleDynamicMetric.java
new file mode 100644
index 0000000..1be6a21
--- /dev/null
+++ b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/SimpleDynamicMetric.java
@@ -0,0 +1,60 @@
+package org.apache.helix.monitoring.mbeans.dynamicMBeans;
+
+/*
+ * 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.
+ */
+
+/**
+ * The dynamic metric that accept and emits same type of monitor data
+ *
+ * @param <T> the type of the metric value
+ */
+public class SimpleDynamicMetric<T> extends DynamicMetric<T, T> {
+  private final String _metricName;
+
+  /**
+   * Instantiates a new Simple dynamic metric.
+   *
+   * @param metricName   the metric name
+   * @param metricObject the metric object
+   */
+  public SimpleDynamicMetric(String metricName, T metricObject) {
+    super(metricName, metricObject);
+    _metricName = metricName;
+  }
+
+  @Override
+  public T getAttributeValue(String attributeName) {
+    if (!attributeName.equals(_metricName)) {
+      return null;
+    }
+    return getMetricObject();
+  }
+
+  /**
+   * @return current metric value
+   */
+  public T getValue() {
+    return getMetricObject();
+  }
+
+  @Override
+  public void updateValue(T metricObject) {
+    setMetricObject(metricObject);
+  }
+}
diff --git a/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/exception/MetricException.java b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/exception/MetricException.java
new file mode 100644
index 0000000..569893c
--- /dev/null
+++ b/metrics-common/src/main/java/org/apache/helix/monitoring/mbeans/exception/MetricException.java
@@ -0,0 +1,35 @@
+package org.apache.helix.monitoring.mbeans.exception;
+
+/*
+ * 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.
+ */
+
+public class MetricException extends RuntimeException {
+
+  public MetricException(String message) {
+    super(message);
+  }
+
+  public MetricException(Throwable cause) {
+    super(cause);
+  }
+
+  public MetricException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git a/metrics-common/src/test/conf/testng.xml b/metrics-common/src/test/conf/testng.xml
new file mode 100644
index 0000000..125e51c
--- /dev/null
+++ b/metrics-common/src/test/conf/testng.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Suite" parallel="false">
+  <test name="Test" preserve-order="true">
+    <packages>
+      <package name="org.apache.helix.metrics.common.*"/>
+    </packages>
+  </test>
+</suite>
diff --git a/metrics-common/src/test/resources/log4j.properties b/metrics-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..24b6d10
--- /dev/null
+++ b/metrics-common/src/test/resources/log4j.properties
@@ -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.
+#
+# Set root logger level to DEBUG and its only appender to R.
+log4j.rootLogger=ERROR, C
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.C=org.apache.log4j.ConsoleAppender
+log4j.appender.C.layout=org.apache.log4j.PatternLayout
+log4j.appender.C.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+
+log4j.appender.R=org.apache.log4j.RollingFileAppender
+log4j.appender.R.layout=org.apache.log4j.PatternLayout
+log4j.appender.R.layout.ConversionPattern=%5p [%C:%M] (%F:%L) - %m%n
+log4j.appender.R.File=target/ClusterManagerLogs/log.txt
+
+log4j.appender.STATUSDUMP=org.apache.log4j.RollingFileAppender
+log4j.appender.STATUSDUMP.layout=org.apache.log4j.SimpleLayout
+log4j.appender.STATUSDUMP.File=target/ClusterManagerLogs/statusUpdates.log
+
+log4j.logger.org.I0Itec=ERROR
+log4j.logger.org.apache=ERROR
+log4j.logger.com.noelios=ERROR
+log4j.logger.org.restlet=ERROR
+
+log4j.logger.org.apache.helix.monitoring.ZKPathDataDumpTask=ERROR,STATUSDUMP
diff --git a/pom.xml b/pom.xml
index 29308c1..c131ce8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,12 @@
 
 <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">
   <modelVersion>4.0.0</modelVersion>
+  <dependencies>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+  </dependencies>
 
   <parent>
     <groupId>org.apache</groupId>
@@ -240,6 +246,9 @@
     </developer>
   </developers>
   <modules>
+    <module>metrics-common</module>
+    <module>zookeeper-api</module>
+    <module>helix-common</module>
     <module>helix-core</module>
     <module>helix-admin-webapp</module>
     <module>helix-rest</module>
@@ -619,7 +628,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-plugin</artifactId>
-          <version>3.0.0-M3</version>
+          <version>2.18.1</version>
         </plugin>
         <plugin>
           <groupId>org.apache.rat</groupId>
diff --git a/recipes/distributed-lock-manager/src/main/java/org/apache/helix/lockmanager/LockManagerDemo.java b/recipes/distributed-lock-manager/src/main/java/org/apache/helix/lockmanager/LockManagerDemo.java
index b6c54db..cc4c3d3 100644
--- a/recipes/distributed-lock-manager/src/main/java/org/apache/helix/lockmanager/LockManagerDemo.java
+++ b/recipes/distributed-lock-manager/src/main/java/org/apache/helix/lockmanager/LockManagerDemo.java
@@ -23,9 +23,6 @@
 import java.util.Map;
 import java.util.TreeSet;
 
-import org.I0Itec.zkclient.IDefaultNameSpace;
-import org.I0Itec.zkclient.ZkClient;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.commons.io.FileUtils;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixManager;
@@ -35,6 +32,10 @@
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.tools.StateModelConfigGenerator;
+import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+
 
 public class LockManagerDemo {
   /**
diff --git a/recipes/rabbitmq-consumer-group/src/main/java/org/apache/helix/recipes/rabbitmq/Consumer.java b/recipes/rabbitmq-consumer-group/src/main/java/org/apache/helix/recipes/rabbitmq/Consumer.java
index 25b6027..508faca 100644
--- a/recipes/rabbitmq-consumer-group/src/main/java/org/apache/helix/recipes/rabbitmq/Consumer.java
+++ b/recipes/rabbitmq-consumer-group/src/main/java/org/apache/helix/recipes/rabbitmq/Consumer.java
@@ -26,7 +26,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.participant.StateMachineEngine;
 
diff --git a/recipes/rabbitmq-consumer-group/src/main/java/org/apache/helix/recipes/rabbitmq/SetupConsumerCluster.java b/recipes/rabbitmq-consumer-group/src/main/java/org/apache/helix/recipes/rabbitmq/SetupConsumerCluster.java
index b73aaa9..3d81929 100644
--- a/recipes/rabbitmq-consumer-group/src/main/java/org/apache/helix/recipes/rabbitmq/SetupConsumerCluster.java
+++ b/recipes/rabbitmq-consumer-group/src/main/java/org/apache/helix/recipes/rabbitmq/SetupConsumerCluster.java
@@ -21,7 +21,7 @@
 
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.tools.StateModelConfigGenerator;
diff --git a/recipes/rsync-replicated-file-system/pom.xml b/recipes/rsync-replicated-file-system/pom.xml
index eb85b64..b98f9ed 100644
--- a/recipes/rsync-replicated-file-system/pom.xml
+++ b/recipes/rsync-replicated-file-system/pom.xml
@@ -88,11 +88,6 @@
       <artifactId>commons-jci-fam</artifactId>
       <version>1.0</version>
     </dependency>
-    <dependency>
-      <groupId>com.101tec</groupId>
-      <artifactId>zkclient</artifactId>
-      <version>0.5</version>
-    </dependency>
   </dependencies>
   <build>
     <pluginManagement>
diff --git a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/FileStore.java b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/FileStore.java
index 6448411..8ec1a15 100644
--- a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/FileStore.java
+++ b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/FileStore.java
@@ -22,7 +22,7 @@
 import org.apache.helix.HelixManager;
 import org.apache.helix.HelixManagerFactory;
 import org.apache.helix.InstanceType;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.participant.StateMachineEngine;
 
 public class FileStore {
diff --git a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/FileStoreStateModel.java b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/FileStoreStateModel.java
index 49799db..bac9c25 100644
--- a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/FileStoreStateModel.java
+++ b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/FileStoreStateModel.java
@@ -19,17 +19,17 @@
  * under the License.
  */
 
-import org.I0Itec.zkclient.DataUpdater;
 import org.apache.helix.AccessOption;
 import org.apache.helix.HelixManager;
 import org.apache.helix.NotificationContext;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.Message;
 import org.apache.helix.participant.statemachine.StateModel;
 import org.apache.helix.participant.statemachine.StateModelInfo;
 import org.apache.helix.participant.statemachine.Transition;
 import org.apache.helix.store.zk.ZkHelixPropertyStore;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/IntegrationTest.java b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/IntegrationTest.java
index 845afb8..bafa163 100644
--- a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/IntegrationTest.java
+++ b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/IntegrationTest.java
@@ -25,9 +25,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import org.I0Itec.zkclient.IDefaultNameSpace;
-import org.I0Itec.zkclient.ZkClient;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.commons.io.FileUtils;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
@@ -37,12 +34,15 @@
 import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.tools.ClusterSetup;
+import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+
 
 public class IntegrationTest {
 
   public static void main(String[] args) throws InterruptedException {
     ZkServer server = null;
-    ;
 
     try {
       String baseDir = "/tmp/IntegrationTest/";
diff --git a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/Replicator.java b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/Replicator.java
index b4bf5af..0da5a65 100644
--- a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/Replicator.java
+++ b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/Replicator.java
@@ -23,7 +23,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.helix.NotificationContext;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.spectator.RoutingTableProvider;
diff --git a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/SetupCluster.java b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/SetupCluster.java
index 5c756ce..42a6cf7 100644
--- a/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/SetupCluster.java
+++ b/recipes/rsync-replicated-file-system/src/main/java/org/apache/helix/filestore/SetupCluster.java
@@ -21,7 +21,7 @@
 
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.StateModelDefinition;
diff --git a/recipes/service-discovery/src/main/java/org/apache/helix/servicediscovery/ServiceDiscovery.java b/recipes/service-discovery/src/main/java/org/apache/helix/servicediscovery/ServiceDiscovery.java
index 670e563..c2e3a32 100644
--- a/recipes/service-discovery/src/main/java/org/apache/helix/servicediscovery/ServiceDiscovery.java
+++ b/recipes/service-discovery/src/main/java/org/apache/helix/servicediscovery/ServiceDiscovery.java
@@ -36,7 +36,7 @@
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.PropertyKey.Builder;
-import org.apache.helix.ZNRecord;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.manager.zk.ZKHelixManager;
 import org.apache.helix.model.HelixConfigScope;
 import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
diff --git a/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/TaskCluster.java b/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/TaskCluster.java
index 064fc1a..55b7bf1 100644
--- a/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/TaskCluster.java
+++ b/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/TaskCluster.java
@@ -22,7 +22,7 @@
 import org.apache.helix.ConfigAccessor;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.HelixConfigScope;
 import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty;
 import org.apache.helix.model.IdealState.RebalanceMode;
diff --git a/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/TaskExecutionDemo.java b/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/TaskExecutionDemo.java
index 01716d7..04c5b70 100644
--- a/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/TaskExecutionDemo.java
+++ b/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/TaskExecutionDemo.java
@@ -24,13 +24,14 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
-import org.I0Itec.zkclient.IDefaultNameSpace;
-import org.I0Itec.zkclient.ZkClient;
-import org.I0Itec.zkclient.ZkServer;
 import org.apache.commons.io.FileUtils;
 import org.apache.helix.HelixManager;
 import org.apache.helix.controller.HelixControllerMain;
 import org.apache.helix.taskexecution.Dag.Node;
+import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
+import org.apache.helix.zookeeper.zkclient.ZkServer;
+
 
 /**
  * Demo for execution of task Dag using primitives provided by Helix. This demo sets up a Dag of
diff --git a/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/Worker.java b/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/Worker.java
index 4d54b23..dd9ebee 100644
--- a/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/Worker.java
+++ b/recipes/task-execution/src/main/java/org/apache/helix/taskexecution/Worker.java
@@ -26,7 +26,7 @@
 import org.apache.helix.InstanceType;
 import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZNRecordSerializer;
-import org.apache.helix.manager.zk.ZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.participant.StateMachineEngine;
 
diff --git a/zookeeper-api/LICENSE b/zookeeper-api/LICENSE
new file mode 100644
index 0000000..d78ae52
--- /dev/null
+++ b/zookeeper-api/LICENSE
@@ -0,0 +1,270 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
+
+
+For xstream:
+
+Copyright (c) 2003-2006, Joe Walnes
+Copyright (c) 2006-2009, 2011 XStream Committers
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other materials provided
+with the distribution.
+
+3. Neither the name of XStream nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+for jline:
+
+Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/zookeeper-api/NOTICE b/zookeeper-api/NOTICE
new file mode 100644
index 0000000..ff5a745
--- /dev/null
+++ b/zookeeper-api/NOTICE
@@ -0,0 +1,37 @@
+Apache Helix
+Copyright 2014 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Codehaus (http://www.codehaus.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+jline (http://jline.sourceforge.net/).
+Licensed under the BSD License.
+
+This product includes software developed at
+restlet (http://www.restlet.org/about/legal).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Google (http://www.google.com/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+snakeyaml (http://www.snakeyaml.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+zkclient (https://github.com/sgroschupf/zkclient).
+Licensed under the Apache License 2.0.
+
+II. License Summary
+- Apache License 2.0
+- BSD License
\ No newline at end of file
diff --git a/zookeeper-api/pom.xml b/zookeeper-api/pom.xml
new file mode 100644
index 0000000..5ec1e56
--- /dev/null
+++ b/zookeeper-api/pom.xml
@@ -0,0 +1,112 @@
+<?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>
+    <groupId>org.apache.helix</groupId>
+    <artifactId>helix</artifactId>
+    <version>0.9.2-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>zookeeper-api</artifactId>
+  <packaging>bundle</packaging>
+  <name>Apache Helix :: ZooKeeper API</name>
+
+  <properties>
+    <osgi.import>
+      org.slf4j*;version="[1.6,2)",
+      *
+    </osgi.import>
+    <osgi.export>org.apache.helix.zookeeper*;version="${project.version};-noimport:=true</osgi.export>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.helix</groupId>
+      <artifactId>metrics-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.zookeeper</groupId>
+      <artifactId>zookeeper</artifactId>
+      <version>3.4.13</version>
+      <exclusions>
+        <exclusion>
+          <groupId>junit</groupId>
+          <artifactId>junit</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+      <version>1.9.13</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+      <version>1.14</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.25</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>1.7.14</version>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <resources>
+      <resource>
+        <directory>${basedir}</directory>
+        <includes>
+          <include>DISCLAIMER</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <descriptors>
+            <descriptor>src/assemble/assembly.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/zookeeper-api/src/assemble/assembly.xml b/zookeeper-api/src/assemble/assembly.xml
new file mode 100644
index 0000000..61e06f5
--- /dev/null
+++ b/zookeeper-api/src/assemble/assembly.xml
@@ -0,0 +1,60 @@
+<?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.
+-->
+<assembly>
+  <id>pkg</id>
+  <formats>
+    <format>tar</format>
+  </formats>
+  <fileSets>
+    <fileSet>
+      <directory>target/zookeeper-api-pkg/bin</directory>
+      <outputDirectory>bin</outputDirectory>
+      <lineEnding>unix</lineEnding>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>target/zookeeper-api-pkg/repo/</directory>
+      <outputDirectory>repo</outputDirectory>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+      <excludes>
+        <exclude>**/*.xml</exclude>
+      </excludes>
+    </fileSet>
+   <fileSet>
+      <directory>target/zookeeper-api-pkg/conf</directory>
+      <outputDirectory>conf</outputDirectory>
+      <lineEnding>unix</lineEnding>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}</directory>
+      <outputDirectory>/</outputDirectory>
+      <includes>
+        <include>LICENSE</include>
+        <include>NOTICE</include>
+        <include>DISCLAIMER</include>
+      </includes>
+      <fileMode>0755</fileMode>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/zookeeper-api/src/main/config/log4j.properties b/zookeeper-api/src/main/config/log4j.properties
new file mode 100644
index 0000000..375c7ba
--- /dev/null
+++ b/zookeeper-api/src/main/config/log4j.properties
@@ -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.
+#
+
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=WARN,A1
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+
+log4j.logger.org.I0Itec=ERROR
+log4j.logger.org.apache.zookeeper=ERROR
+log4j.logger.org.apache.helix=INFO
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/HelixZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/HelixZkClient.java
new file mode 100644
index 0000000..bbb8a98
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/HelixZkClient.java
@@ -0,0 +1,26 @@
+package org.apache.helix.zookeeper.api.client;
+
+/*
+ * 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.
+ */
+
+/**
+ * HelixZkClient interface that follows the supported API structure of RealmAwareZkClient.
+ */
+public interface HelixZkClient extends RealmAwareZkClient {
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/RealmAwareZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/RealmAwareZkClient.java
new file mode 100644
index 0000000..f2345f6
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/client/RealmAwareZkClient.java
@@ -0,0 +1,463 @@
+package org.apache.helix.zookeeper.api.client;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.IZkStateListener;
+import org.apache.helix.zookeeper.zkclient.callback.ZkAsyncCallbacks;
+import org.apache.helix.zookeeper.zkclient.exception.ZkTimeoutException;
+import org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.SerializableSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.Op;
+import org.apache.zookeeper.OpResult;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+
+public interface RealmAwareZkClient {
+  int DEFAULT_OPERATION_TIMEOUT = Integer.MAX_VALUE;
+  int DEFAULT_CONNECTION_TIMEOUT = 60 * 1000;
+  int DEFAULT_SESSION_TIMEOUT = 30 * 1000;
+
+  // listener subscription
+  List<String> subscribeChildChanges(String path, IZkChildListener listener);
+
+  void unsubscribeChildChanges(String path, IZkChildListener listener);
+
+  void subscribeDataChanges(String path, IZkDataListener listener);
+
+  void unsubscribeDataChanges(String path, IZkDataListener listener);
+
+  /*
+   * This is for backwards compatibility.
+   *
+   * TODO: remove below default implementation when getting rid of I0Itec in the new zk client.
+   */
+  default void subscribeStateChanges(final IZkStateListener listener) {
+    subscribeStateChanges(new HelixZkClient.I0ItecIZkStateListenerHelixImpl(listener));
+  }
+
+  /*
+   * This is for backwards compatibility.
+   *
+   * TODO: remove below default implementation when getting rid of I0Itec in the new zk client.
+   */
+  default void unsubscribeStateChanges(IZkStateListener listener) {
+    unsubscribeStateChanges(new HelixZkClient.I0ItecIZkStateListenerHelixImpl(listener));
+  }
+
+  /**
+   * Subscribes state changes for a
+   * {@link org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener} listener.
+   * @deprecated
+   *             This is deprecated. It is kept for backwards compatibility. Please use
+   *             {@link #subscribeStateChanges(IZkStateListener)}.
+   * @param listener {@link org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener}
+   *          listener
+   */
+  @Deprecated
+  void subscribeStateChanges(
+      final org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener listener);
+
+  /**
+   * Unsubscribes state changes for a
+   * {@link org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener} listener.
+   * @deprecated
+   *             This is deprecated. It is kept for backwards compatibility. Please use
+   *             {@link #unsubscribeStateChanges(IZkStateListener)}.
+   * @param listener {@link org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener}
+   *          listener
+   */
+  @Deprecated
+  void unsubscribeStateChanges(
+      org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener listener);
+
+  void unsubscribeAll();
+
+  // data access
+  void createPersistent(String path);
+
+  void createPersistent(String path, boolean createParents);
+
+  void createPersistent(String path, boolean createParents, List<ACL> acl);
+
+  void createPersistent(String path, Object data);
+
+  void createPersistent(String path, Object data, List<ACL> acl);
+
+  String createPersistentSequential(String path, Object data);
+
+  String createPersistentSequential(String path, Object data, List<ACL> acl);
+
+  void createEphemeral(final String path);
+
+  void createEphemeral(final String path, final String sessionId);
+
+  void createEphemeral(final String path, final List<ACL> acl);
+
+  void createEphemeral(final String path, final List<ACL> acl, final String sessionId);
+
+  String create(final String path, Object data, final CreateMode mode);
+
+  String create(final String path, Object datat, final List<ACL> acl, final CreateMode mode);
+
+  void createEphemeral(final String path, final Object data);
+
+  void createEphemeral(final String path, final Object data, final String sessionId);
+
+  void createEphemeral(final String path, final Object data, final List<ACL> acl);
+
+  void createEphemeral(final String path, final Object data, final List<ACL> acl,
+      final String sessionId);
+
+  String createEphemeralSequential(final String path, final Object data);
+
+  String createEphemeralSequential(final String path, final Object data, final List<ACL> acl);
+
+  String createEphemeralSequential(final String path, final Object data, final String sessionId);
+
+  String createEphemeralSequential(final String path, final Object data, final List<ACL> acl,
+      final String sessionId);
+
+  List<String> getChildren(String path);
+
+  int countChildren(String path);
+
+  boolean exists(final String path);
+
+  Stat getStat(final String path);
+
+  boolean waitUntilExists(String path, TimeUnit timeUnit, long time);
+
+  void deleteRecursively(String path);
+
+  boolean delete(final String path);
+
+  <T extends Object> T readData(String path);
+
+  <T extends Object> T readData(String path, boolean returnNullIfPathNotExists);
+
+  <T extends Object> T readData(String path, Stat stat);
+
+  <T extends Object> T readData(final String path, final Stat stat, final boolean watch);
+
+  <T extends Object> T readDataAndStat(String path, Stat stat, boolean returnNullIfPathNotExists);
+
+  void writeData(String path, Object object);
+
+  <T extends Object> void updateDataSerialized(String path, DataUpdater<T> updater);
+
+  void writeData(final String path, Object datat, final int expectedVersion);
+
+  Stat writeDataReturnStat(final String path, Object datat, final int expectedVersion);
+
+  Stat writeDataGetStat(final String path, Object datat, final int expectedVersion);
+
+  void asyncCreate(final String path, Object datat, final CreateMode mode,
+      final ZkAsyncCallbacks.CreateCallbackHandler cb);
+
+  void asyncSetData(final String path, Object datat, final int version,
+      final ZkAsyncCallbacks.SetDataCallbackHandler cb);
+
+  void asyncGetData(final String path, final ZkAsyncCallbacks.GetDataCallbackHandler cb);
+
+  void asyncExists(final String path, final ZkAsyncCallbacks.ExistsCallbackHandler cb);
+
+  void asyncDelete(final String path, final ZkAsyncCallbacks.DeleteCallbackHandler cb);
+
+  void watchForData(final String path);
+
+  List<String> watchForChilds(final String path);
+
+  long getCreationTime(String path);
+
+  List<OpResult> multi(final Iterable<Op> ops);
+
+  // ZK state control
+  boolean waitUntilConnected(long time, TimeUnit timeUnit);
+
+  /**
+   * Waits for SyncConnected state and returns a valid session ID(non-zero). The implementation of
+   * this method should wait for SyncConnected state and ZK session to be established, and should
+   * guarantee the established session's ID is returned before keeper state changes.
+   *
+   * Please note: this default implementation may have race condition issue and return an unexpected
+   * session ID that is zero or another new session's ID. The default implementation is for backward
+   * compatibility purpose.
+   *
+   * @param timeout Max waiting time for connecting to ZK server.
+   * @param timeUnit Time unit for the timeout.
+   * @return A valid ZK session ID which is non-zero.
+   */
+  default long waitForEstablishedSession(long timeout, TimeUnit timeUnit) {
+    if (!waitUntilConnected(timeout, timeUnit)) {
+      throw new ZkTimeoutException(
+          "Failed to get established session because connecting to ZK server has timed out in "
+              + timeout + " " + timeUnit);
+    }
+    return getSessionId();
+  }
+
+  String getServers();
+
+  long getSessionId();
+
+  void close();
+
+  boolean isClosed();
+
+  // other
+  byte[] serialize(Object data, String path);
+
+  <T extends Object> T deserialize(byte[] data, String path);
+
+  void setZkSerializer(ZkSerializer zkSerializer);
+
+  void setZkSerializer(PathBasedZkSerializer zkSerializer);
+
+  PathBasedZkSerializer getZkSerializer();
+
+  /**
+   * A class that wraps a default implementation of
+   * {@link IZkStateListener}, which means this listener
+   * runs the methods of {@link IZkStateListener}.
+   * This is for backward compatibility and to avoid breaking the original implementation of
+   * {@link org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener}.
+   */
+  class I0ItecIZkStateListenerHelixImpl
+      implements org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener {
+    private IZkStateListener _listener;
+
+    I0ItecIZkStateListenerHelixImpl(IZkStateListener listener) {
+      _listener = listener;
+    }
+
+    @Override
+    public void handleStateChanged(Watcher.Event.KeeperState keeperState) throws Exception {
+      _listener.handleStateChanged(keeperState);
+    }
+
+    @Override
+    public void handleNewSession() throws Exception {
+      /*
+       * org.apache.helix.manager.zk.zookeeper.IZkStateListener does not have handleNewSession(),
+       * so null is passed into handleNewSession(sessionId).
+       */
+      _listener.handleNewSession(null);
+    }
+
+    @Override
+    public void handleSessionEstablishmentError(Throwable error) throws Exception {
+      _listener.handleSessionEstablishmentError(error);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof HelixZkClient.I0ItecIZkStateListenerHelixImpl)) {
+        return false;
+      }
+      if (_listener == null) {
+        return false;
+      }
+
+      HelixZkClient.I0ItecIZkStateListenerHelixImpl
+          defaultListener = (HelixZkClient.I0ItecIZkStateListenerHelixImpl) obj;
+
+      return _listener.equals(defaultListener._listener);
+    }
+
+    @Override
+    public int hashCode() {
+      /*
+       * The original listener's hashcode helps find the wrapped listener with the same original
+       * listener. This is helpful in unsubscribeStateChanges(listener).
+       */
+      return _listener.hashCode();
+    }
+  }
+
+  /**
+   * Configuration for creating a new ZkConnection.
+   */
+  class ZkConnectionConfig {
+    // Connection configs
+    private final String _zkServers;
+    private int _sessionTimeout = HelixZkClient.DEFAULT_SESSION_TIMEOUT;
+
+    public ZkConnectionConfig(String zkServers) {
+      _zkServers = zkServers;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof HelixZkClient.ZkConnectionConfig)) {
+        return false;
+      }
+      HelixZkClient.ZkConnectionConfig configObj = (HelixZkClient.ZkConnectionConfig) obj;
+      return (_zkServers == null && configObj._zkServers == null ||
+          _zkServers != null && _zkServers.equals(configObj._zkServers)) &&
+          _sessionTimeout == configObj._sessionTimeout;
+    }
+
+    @Override
+    public int hashCode() {
+      return _sessionTimeout * 31 + _zkServers.hashCode();
+    }
+
+    @Override
+    public String toString() {
+      return (_zkServers + "_" + _sessionTimeout).replaceAll("[\\W]", "_");
+    }
+
+    public HelixZkClient.ZkConnectionConfig setSessionTimeout(Integer sessionTimeout) {
+      this._sessionTimeout = sessionTimeout;
+      return this;
+    }
+
+    public String getZkServers() {
+      return _zkServers;
+    }
+
+    public int getSessionTimeout() {
+      return _sessionTimeout;
+    }
+  }
+
+  /**
+   * Configuration for creating a new RealmAwareZkClient with serializer and monitor.
+   */
+  class ZkClientConfig {
+    // For client to init the connection
+    private long _connectInitTimeout = HelixZkClient.DEFAULT_CONNECTION_TIMEOUT;
+
+    // Data access configs
+    private long _operationRetryTimeout = HelixZkClient.DEFAULT_OPERATION_TIMEOUT;
+
+    // Others
+    private PathBasedZkSerializer _zkSerializer;
+
+    // Monitoring
+    private String _monitorType;
+    private String _monitorKey;
+    private String _monitorInstanceName = null;
+    private boolean _monitorRootPathOnly = true;
+
+    public HelixZkClient.ZkClientConfig setZkSerializer(PathBasedZkSerializer zkSerializer) {
+      this._zkSerializer = zkSerializer;
+      return this;
+    }
+
+    public HelixZkClient.ZkClientConfig setZkSerializer(ZkSerializer zkSerializer) {
+      this._zkSerializer = new BasicZkSerializer(zkSerializer);
+      return this;
+    }
+
+    /**
+     * Used as part of the MBean ObjectName. This item is required for enabling monitoring.
+     *
+     * @param monitorType
+     */
+    public HelixZkClient.ZkClientConfig setMonitorType(String monitorType) {
+      this._monitorType = monitorType;
+      return this;
+    }
+
+    /**
+     * Used as part of the MBean ObjectName. This item is required for enabling monitoring.
+     *
+     * @param monitorKey
+     */
+    public HelixZkClient.ZkClientConfig setMonitorKey(String monitorKey) {
+      this._monitorKey = monitorKey;
+      return this;
+    }
+
+    /**
+     * Used as part of the MBean ObjectName. This item is optional.
+     *
+     * @param instanceName
+     */
+    public HelixZkClient.ZkClientConfig setMonitorInstanceName(String instanceName) {
+      this._monitorInstanceName = instanceName;
+      return this;
+    }
+
+    public HelixZkClient.ZkClientConfig setMonitorRootPathOnly(Boolean monitorRootPathOnly) {
+      this._monitorRootPathOnly = monitorRootPathOnly;
+      return this;
+    }
+
+    public HelixZkClient.ZkClientConfig setOperationRetryTimeout(Long operationRetryTimeout) {
+      this._operationRetryTimeout = operationRetryTimeout;
+      return this;
+    }
+
+    public HelixZkClient.ZkClientConfig setConnectInitTimeout(long _connectInitTimeout) {
+      this._connectInitTimeout = _connectInitTimeout;
+      return this;
+    }
+
+    public PathBasedZkSerializer getZkSerializer() {
+      if (_zkSerializer == null) {
+        _zkSerializer = new BasicZkSerializer(new SerializableSerializer());
+      }
+      return _zkSerializer;
+    }
+
+    public long getOperationRetryTimeout() {
+      return _operationRetryTimeout;
+    }
+
+    public String getMonitorType() {
+      return _monitorType;
+    }
+
+    public String getMonitorKey() {
+      return _monitorKey;
+    }
+
+    public String getMonitorInstanceName() {
+      return _monitorInstanceName;
+    }
+
+    public boolean isMonitorRootPathOnly() {
+      return _monitorRootPathOnly;
+    }
+
+    public long getConnectInitTimeout() {
+      return _connectInitTimeout;
+    }
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/factory/RealmAwareZkClientFactory.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/factory/RealmAwareZkClientFactory.java
new file mode 100644
index 0000000..9fbf259
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/factory/RealmAwareZkClientFactory.java
@@ -0,0 +1,23 @@
+package org.apache.helix.zookeeper.api.factory;
+
+/*
+ * 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.
+ */
+
+public interface RealmAwareZkClientFactory {
+}
diff --git a/helix-core/src/main/java/org/apache/helix/ZNRecord.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecord.java
similarity index 97%
rename from helix-core/src/main/java/org/apache/helix/ZNRecord.java
rename to zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecord.java
index bd9acbb..6070414 100644
--- a/helix-core/src/main/java/org/apache/helix/ZNRecord.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecord.java
@@ -1,4 +1,4 @@
-package org.apache.helix;
+package org.apache.helix.zookeeper.datamodel;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -24,9 +24,8 @@
 import java.util.Map;
 import java.util.TreeMap;
 
-import org.apache.helix.ZNRecordDelta.MergeOperation;
-import org.apache.helix.manager.zk.serializer.JacksonPayloadSerializer;
-import org.apache.helix.manager.zk.serializer.PayloadSerializer;
+import org.apache.helix.zookeeper.datamodel.serializer.JacksonPayloadSerializer;
+import org.apache.helix.zookeeper.datamodel.serializer.PayloadSerializer;
 import org.codehaus.jackson.annotate.JsonCreator;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
@@ -36,6 +35,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 /**
  * Generic Record Format to store data at a Node This can be used to store
  * simpleFields mapFields listFields
@@ -540,11 +540,11 @@
    * @param delta
    */
   void merge(ZNRecordDelta delta) {
-    if (delta.getMergeOperation() == MergeOperation.ADD) {
+    if (delta.getMergeOperation() == ZNRecordDelta.MergeOperation.ADD) {
       merge(delta.getRecord());
-    } else if (delta.getMergeOperation() == MergeOperation.SUBTRACT) {
+    } else if (delta.getMergeOperation() == ZNRecordDelta.MergeOperation.SUBTRACT) {
       subtract(delta.getRecord());
-    } else if (delta.getMergeOperation() == MergeOperation.UPDATE) {
+    } else if (delta.getMergeOperation() == ZNRecordDelta.MergeOperation.UPDATE) {
       update(delta.getRecord());
     }
   }
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordAssembler.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordAssembler.java
new file mode 100644
index 0000000..925fd17
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordAssembler.java
@@ -0,0 +1,52 @@
+package org.apache.helix.zookeeper.datamodel;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+
+/**
+ * Constructs ZNRecords from collections of ZNRecords
+ */
+public class ZNRecordAssembler {
+  /**
+   * Merge a list of ZNRecords into a single ZNRecord
+   * @param records
+   * @return {@link ZNRecord}
+   */
+  public ZNRecord assemble(List<ZNRecord> records) {
+    ZNRecord assembledRecord = null;
+    if (records != null && records.size() > 0) {
+      for (ZNRecord record : records) {
+        if (record == null) {
+          continue;
+        }
+
+        if (assembledRecord == null) {
+          assembledRecord = new ZNRecord(record.getId());
+        }
+
+        assembledRecord.merge(record);
+      }
+    }
+    return assembledRecord;
+  }
+
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordBucketizer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordBucketizer.java
new file mode 100644
index 0000000..107e12e
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordBucketizer.java
@@ -0,0 +1,125 @@
+package org.apache.helix.zookeeper.datamodel;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Operations to divide a ZNRecord into specified buckets
+ */
+public class ZNRecordBucketizer {
+  private static Logger LOG = LoggerFactory.getLogger(ZNRecordBucketizer.class);
+  final int _bucketSize;
+
+  /**
+   * Instantiate a bucketizer with the number of buckets
+   * @param bucketSize
+   */
+  public ZNRecordBucketizer(int bucketSize) {
+    if (bucketSize <= 0) {
+      LOG.debug("bucketSize <= 0 (was " + bucketSize
+          + "). Set to 0 to use non-bucketized HelixProperty.");
+      bucketSize = 0;
+    }
+
+    _bucketSize = bucketSize;
+  }
+
+  /**
+   * Calculate bucketName in form of "resourceName_p{startPartition}-p{endPartition}
+   * @param partitionName
+   * @return the bucket name
+   */
+  public String getBucketName(String key) {
+    if (_bucketSize == 0) {
+      // no bucketize
+      return null;
+    }
+
+    int idx = key.lastIndexOf('_');
+    if (idx < 0) {
+      throw new IllegalArgumentException("Could NOT find partition# in " + key
+          + ". partitionName should be in format of resourceName_partition#");
+    }
+
+    try {
+      int partitionNb = Integer.parseInt(key.substring(idx + 1));
+      int bucketNb = partitionNb / _bucketSize;
+      int startPartition = bucketNb * _bucketSize;
+      int endPartition = bucketNb * _bucketSize + (_bucketSize - 1);
+      return key.substring(0, idx) + "_p" + startPartition + "-p" + endPartition;
+    } catch (NumberFormatException e) {
+      throw new IllegalArgumentException("Could NOT parse partition# (" + key.substring(idx + 1)
+          + ") in " + key);
+    }
+  }
+
+  /**
+   * Bucketize a ZNRecord
+   * @param record
+   * @return A map of bucket names to bucketized records
+   */
+  public Map<String, ZNRecord> bucketize(ZNRecord record) {
+    Map<String, ZNRecord> map = new HashMap<String, ZNRecord>();
+    if (_bucketSize == 0) {
+      map.put(record.getId(), record);
+      return map;
+    }
+
+    // bucketize list field
+    for (String partitionName : record.getListFields().keySet()) {
+      String bucketName = getBucketName(partitionName);
+      if (bucketName != null) {
+        if (!map.containsKey(bucketName)) {
+          map.put(bucketName, new ZNRecord(bucketName));
+        }
+        ZNRecord bucketizedRecord = map.get(bucketName);
+        bucketizedRecord.setListField(partitionName, record.getListField(partitionName));
+      } else {
+        LOG.error("Can't bucketize " + partitionName + " in list field");
+      }
+    }
+
+    // bucketize map field
+    for (String partitionName : record.getMapFields().keySet()) {
+      String bucketName = getBucketName(partitionName);
+      if (bucketName != null) {
+        if (!map.containsKey(bucketName)) {
+          map.put(bucketName, new ZNRecord(bucketName));
+        }
+        ZNRecord bucketizedRecord = map.get(bucketName);
+        bucketizedRecord.setMapField(partitionName, record.getMapField(partitionName));
+      } else {
+        LOG.error("Can't bucketize " + partitionName + " in map field");
+      }
+    }
+
+    // copy all simple fields
+    for (ZNRecord bucketizedRecord : map.values()) {
+      bucketizedRecord.setSimpleFields(record.getSimpleFields());
+    }
+    return map;
+  }
+}
diff --git a/helix-core/src/main/java/org/apache/helix/ZNRecordDelta.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordDelta.java
similarity index 97%
rename from helix-core/src/main/java/org/apache/helix/ZNRecordDelta.java
rename to zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordDelta.java
index 616e1f5..9ccf0a9 100644
--- a/helix-core/src/main/java/org/apache/helix/ZNRecordDelta.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordDelta.java
@@ -1,4 +1,4 @@
-package org.apache.helix;
+package org.apache.helix.zookeeper.datamodel;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordUpdater.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordUpdater.java
new file mode 100644
index 0000000..27388fa
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/ZNRecordUpdater.java
@@ -0,0 +1,47 @@
+package org.apache.helix.zookeeper.datamodel;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+
+
+/**
+ * Class that specifies how a ZNRecord should be updated with another ZNRecord
+ */
+public class ZNRecordUpdater implements DataUpdater<ZNRecord> {
+  final ZNRecord _record;
+
+  /**
+   * Initialize with the record that will be updated
+   * @param record
+   */
+  public ZNRecordUpdater(ZNRecord record) {
+    _record = record;
+  }
+
+  @Override
+  public ZNRecord update(ZNRecord current) {
+    if (current != null) {
+      current.merge(_record);
+      return current;
+    }
+    return _record;
+  }
+}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/serializer/JacksonPayloadSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/JacksonPayloadSerializer.java
similarity index 94%
rename from helix-core/src/main/java/org/apache/helix/manager/zk/serializer/JacksonPayloadSerializer.java
rename to zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/JacksonPayloadSerializer.java
index 1cc8102..ee16fd1 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/serializer/JacksonPayloadSerializer.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/JacksonPayloadSerializer.java
@@ -1,4 +1,4 @@
-package org.apache.helix.manager.zk.serializer;
+package org.apache.helix.zookeeper.datamodel.serializer;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -22,13 +22,14 @@
 import java.io.ByteArrayInputStream;
 import java.io.StringWriter;
 
-import org.apache.helix.HelixException;
+import org.apache.helix.zookeeper.exception.ZkClientException;
 import org.codehaus.jackson.map.DeserializationConfig;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.SerializationConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 /**
  * Serializes and deserializes data of a generic type using Jackson
  */
@@ -52,7 +53,7 @@
       mapper.writeValue(sw, data);
     } catch (Exception e) {
       logger.error("Exception during payload data serialization.", e);
-      throw new HelixException(e);
+      throw new ZkClientException(e);
     }
     return sw.toString().getBytes();
   }
@@ -78,5 +79,4 @@
       return null;
     }
   }
-
 }
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/PayloadSerializer.java
similarity index 95%
rename from helix-core/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java
rename to zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/PayloadSerializer.java
index a9531bd..54ccf71 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/serializer/PayloadSerializer.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/PayloadSerializer.java
@@ -1,4 +1,4 @@
-package org.apache.helix.manager.zk.serializer;
+package org.apache.helix.zookeeper.datamodel.serializer;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordJacksonSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordJacksonSerializer.java
new file mode 100644
index 0000000..a30829a
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordJacksonSerializer.java
@@ -0,0 +1,69 @@
+package org.apache.helix.zookeeper.datamodel.serializer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+import org.codehaus.jackson.map.ObjectMapper;
+
+
+/**
+ * ZNRecordJacksonSerializer serializes ZNRecord objects into a byte array using Jackson. Note that
+ * this serializer doesn't check for the size of the resulting binary.
+ */
+public class ZNRecordJacksonSerializer implements ZkSerializer {
+  private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+  @Override
+  public byte[] serialize(Object record) throws ZkMarshallingError {
+    if (!(record instanceof ZNRecord)) {
+      // null is NOT an instance of any class
+      throw new ZkClientException("Input object is not of type ZNRecord (was " + record + ")");
+    }
+    ZNRecord znRecord = (ZNRecord) record;
+
+    try {
+      return OBJECT_MAPPER.writeValueAsBytes(znRecord);
+    } catch (IOException e) {
+      throw new ZkClientException(
+          String.format("Exception during serialization. ZNRecord id: %s", znRecord.getId()), e);
+    }
+  }
+
+  @Override
+  public Object deserialize(byte[] bytes) throws ZkMarshallingError {
+    if (bytes == null || bytes.length == 0) {
+      // reading a parent/null node
+      return null;
+    }
+
+    ZNRecord record;
+    try {
+      record = OBJECT_MAPPER.readValue(bytes, ZNRecord.class);
+    } catch (IOException e) {
+      throw new ZkClientException("Exception during deserialization!", e);
+    }
+    return record;
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordSerializer.java
new file mode 100644
index 0000000..89850b0
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordSerializer.java
@@ -0,0 +1,134 @@
+package org.apache.helix.zookeeper.datamodel.serializer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.util.GZipCompressionUtil;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+import org.codehaus.jackson.map.DeserializationConfig;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class ZNRecordSerializer implements ZkSerializer {
+  private static Logger logger = LoggerFactory.getLogger(ZNRecordSerializer.class);
+
+  private static int getListFieldBound(ZNRecord record) {
+    int max = Integer.MAX_VALUE;
+    if (record.getSimpleFields().containsKey(ZNRecord.LIST_FIELD_BOUND)) {
+      String maxStr = record.getSimpleField(ZNRecord.LIST_FIELD_BOUND);
+      try {
+        max = Integer.parseInt(maxStr);
+      } catch (Exception e) {
+        logger.error("IllegalNumberFormat for list field bound: " + maxStr);
+      }
+    }
+    return max;
+  }
+
+  @Override
+  public byte[] serialize(Object data) {
+    if (!(data instanceof ZNRecord)) {
+      // null is NOT an instance of any class
+      logger.error("Input object must be of type ZNRecord but it is " + data
+          + ". Will not write to zk");
+      throw new ZkClientException("Input object is not of type ZNRecord (was " + data + ")");
+    }
+
+    ZNRecord record = (ZNRecord) data;
+
+    // apply retention policy
+    int max = getListFieldBound(record);
+    if (max < Integer.MAX_VALUE) {
+      Map<String, List<String>> listMap = record.getListFields();
+      for (String key : listMap.keySet()) {
+        List<String> list = listMap.get(key);
+        if (list.size() > max) {
+          listMap.put(key, list.subList(0, max));
+        }
+      }
+    }
+
+    // do serialization
+    ObjectMapper mapper = new ObjectMapper();
+    SerializationConfig serializationConfig = mapper.getSerializationConfig();
+    serializationConfig.set(SerializationConfig.Feature.INDENT_OUTPUT, true);
+    serializationConfig.set(SerializationConfig.Feature.AUTO_DETECT_FIELDS, true);
+    serializationConfig.set(SerializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS, true);
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    byte[] serializedBytes;
+    try {
+      mapper.writeValue(baos, data);
+      serializedBytes = baos.toByteArray();
+      // apply compression if needed
+      if (record.getBooleanField("enableCompression", false) || serializedBytes.length > ZNRecord.SIZE_LIMIT) {
+        serializedBytes = GZipCompressionUtil.compress(serializedBytes);
+      }
+    } catch (Exception e) {
+      logger.error("Exception during data serialization. Will not write to zk. Data (first 1k): "
+          + new String(baos.toByteArray()).substring(0, 1024), e);
+      throw new ZkClientException(e);
+    }
+    if (serializedBytes.length > ZNRecord.SIZE_LIMIT) {
+      logger.error("Data size larger than 1M, ZNRecord.id: " + record.getId()
+          + ". Will not write to zk. Data (first 1k): "
+          + new String(serializedBytes).substring(0, 1024));
+      throw new ZkClientException("Data size larger than 1M, ZNRecord.id: " + record.getId());
+    }
+    return serializedBytes;
+  }
+
+  @Override
+  public Object deserialize(byte[] bytes) {
+    if (bytes == null || bytes.length == 0) {
+      // reading a parent/null node
+      return null;
+    }
+
+    ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+
+    ObjectMapper mapper = new ObjectMapper();
+    DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
+    deserializationConfig.set(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, true);
+    deserializationConfig.set(DeserializationConfig.Feature.AUTO_DETECT_SETTERS, true);
+    deserializationConfig.set(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, true);
+    try {
+      //decompress the data if its already compressed
+      if (GZipCompressionUtil.isCompressed(bytes)) {
+        byte[] uncompressedBytes = GZipCompressionUtil.uncompress(bais);
+        bais = new ByteArrayInputStream(uncompressedBytes);
+      }
+      ZNRecord zn = mapper.readValue(bais, ZNRecord.class);
+
+      return zn;
+    } catch (Exception e) {
+      logger.error("Exception during deserialization of bytes: " + new String(bytes), e);
+      return null;
+    }
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordStreamingSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordStreamingSerializer.java
new file mode 100644
index 0000000..c5acdb0
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/datamodel/serializer/ZNRecordStreamingSerializer.java
@@ -0,0 +1,311 @@
+package org.apache.helix.zookeeper.datamodel.serializer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.util.GZipCompressionUtil;
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class ZNRecordStreamingSerializer implements ZkSerializer {
+  private static Logger LOG = LoggerFactory.getLogger(ZNRecordStreamingSerializer.class);
+
+  private static int getListFieldBound(ZNRecord record) {
+    int max = Integer.MAX_VALUE;
+    if (record.getSimpleFields().containsKey(ZNRecord.LIST_FIELD_BOUND)) {
+      String maxStr = record.getSimpleField(ZNRecord.LIST_FIELD_BOUND);
+      try {
+        max = Integer.parseInt(maxStr);
+      } catch (Exception e) {
+        LOG.error("IllegalNumberFormat for list field bound: " + maxStr);
+      }
+    }
+    return max;
+  }
+
+  @Override
+  public byte[] serialize(Object data) throws ZkMarshallingError {
+    if (!(data instanceof ZNRecord)) {
+      // null is NOT an instance of any class
+      LOG.error("Input object must be of type ZNRecord but it is " + data
+          + ". Will not write to zk");
+      throw new ZkClientException("Input object is not of type ZNRecord (was " + data + ")");
+    }
+
+    // apply retention policy on list field
+    ZNRecord record = (ZNRecord) data;
+    int max = getListFieldBound(record);
+    if (max < Integer.MAX_VALUE) {
+      Map<String, List<String>> listMap = record.getListFields();
+      for (String key : listMap.keySet()) {
+        List<String> list = listMap.get(key);
+        if (list.size() > max) {
+          listMap.put(key, list.subList(0, max));
+        }
+      }
+    }
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    byte[] serializedBytes = null;
+    try {
+      JsonFactory f = new JsonFactory();
+      JsonGenerator g = f.createJsonGenerator(baos);
+
+      g.writeStartObject();
+
+      // write id field
+      g.writeRaw("\n  ");
+      g.writeStringField("id", record.getId());
+
+      // write simepleFields
+      g.writeRaw("\n  ");
+      g.writeObjectFieldStart("simpleFields");
+      for (String key : record.getSimpleFields().keySet()) {
+        g.writeRaw("\n    ");
+        g.writeStringField(key, record.getSimpleField(key));
+      }
+      g.writeRaw("\n  ");
+      g.writeEndObject(); // for simpleFields
+
+      // write listFields
+      g.writeRaw("\n  ");
+      g.writeObjectFieldStart("listFields");
+      for (String key : record.getListFields().keySet()) {
+        // g.writeStringField(key, record.getListField(key).toString());
+
+        // g.writeObjectFieldStart(key);
+        g.writeRaw("\n    ");
+        g.writeArrayFieldStart(key);
+        List<String> list = record.getListField(key);
+        for (String listValue : list) {
+          g.writeString(listValue);
+        }
+        // g.writeEndObject();
+        g.writeEndArray();
+
+      }
+      g.writeRaw("\n  ");
+      g.writeEndObject(); // for listFields
+
+      // write mapFields
+      g.writeRaw("\n  ");
+      g.writeObjectFieldStart("mapFields");
+      for (String key : record.getMapFields().keySet()) {
+        // g.writeStringField(key, record.getMapField(key).toString());
+        g.writeRaw("\n    ");
+        g.writeObjectFieldStart(key);
+        Map<String, String> map = record.getMapField(key);
+        for (String mapKey : map.keySet()) {
+          g.writeRaw("\n      ");
+          g.writeStringField(mapKey, map.get(mapKey));
+        }
+        g.writeRaw("\n    ");
+        g.writeEndObject();
+
+      }
+      g.writeRaw("\n  ");
+      g.writeEndObject(); // for mapFields
+
+      byte[] rawPayload = record.getRawPayload();
+      if (rawPayload != null && rawPayload.length > 0) {
+        // write rawPayload
+        g.writeRaw("\n  ");
+        g.writeStringField("rawPayload", new String(Base64.encodeBase64(rawPayload), "UTF-8"));
+      }
+
+      g.writeRaw("\n");
+      g.writeEndObject(); // for whole znrecord
+
+      // important: will force flushing of output, close underlying output
+      // stream
+      g.close();
+      serializedBytes = baos.toByteArray();
+      // apply compression if needed
+      if (record.getBooleanField("enableCompression", false) || serializedBytes.length > ZNRecord.SIZE_LIMIT) {
+        serializedBytes = GZipCompressionUtil.compress(serializedBytes);
+      }
+    } catch (Exception e) {
+      LOG.error("Exception during data serialization. Will not write to zk. Data (first 1k): "
+          + new String(baos.toByteArray()).substring(0, 1024), e);
+      throw new ZkClientException(e);
+    }
+    // check size
+    if (serializedBytes.length > ZNRecord.SIZE_LIMIT) {
+      LOG.error("Data size larger than 1M, ZNRecord.id: " + record.getId()
+          + ". Will not write to zk. Data (first 1k): "
+          + new String(serializedBytes).substring(0, 1024));
+      throw new ZkClientException("Data size larger than 1M, ZNRecord.id: " + record.getId());
+    }
+
+    return serializedBytes;
+  }
+
+  @Override
+  public Object deserialize(byte[] bytes) throws ZkMarshallingError {
+    if (bytes == null || bytes.length == 0) {
+      LOG.error("ZNode is empty.");
+      return null;
+    }
+
+    ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+
+    ZNRecord record = null;
+    String id = null;
+    Map<String, String> simpleFields = new HashMap<>();
+    Map<String, List<String>> listFields = new HashMap<>();
+    Map<String, Map<String, String>> mapFields = new HashMap<>();
+    byte[] rawPayload = null;
+
+    try {
+      // decompress the data if its already compressed
+      if (GZipCompressionUtil.isCompressed(bytes)) {
+        byte[] uncompressedBytes = GZipCompressionUtil.uncompress(bais);
+        bais = new ByteArrayInputStream(uncompressedBytes);
+      }
+      JsonFactory f = new JsonFactory();
+      JsonParser jp = f.createJsonParser(bais);
+
+      jp.nextToken(); // will return JsonToken.START_OBJECT (verify?)
+      while (jp.nextToken() != JsonToken.END_OBJECT) {
+        String fieldname = jp.getCurrentName();
+        jp.nextToken(); // move to value, or START_OBJECT/START_ARRAY
+        if ("id".equals(fieldname)) {
+          // contains an object
+          id = jp.getText();
+        } else if ("simpleFields".equals(fieldname)) {
+          while (jp.nextToken() != JsonToken.END_OBJECT) {
+            String key = jp.getCurrentName();
+            jp.nextToken(); // move to value
+            simpleFields.put(key, jp.getText());
+          }
+        } else if ("mapFields".equals(fieldname)) {
+          // user.setVerified(jp.getCurrentToken() == JsonToken.VALUE_TRUE);
+          while (jp.nextToken() != JsonToken.END_OBJECT) {
+            String key = jp.getCurrentName();
+            mapFields.put(key, new TreeMap<String, String>());
+            jp.nextToken(); // move to value
+
+            while (jp.nextToken() != JsonToken.END_OBJECT) {
+              String mapKey = jp.getCurrentName();
+              jp.nextToken(); // move to value
+              mapFields.get(key).put(mapKey, jp.getText());
+            }
+          }
+
+        } else if ("listFields".equals(fieldname)) {
+          // user.setUserImage(jp.getBinaryValue());
+          while (jp.nextToken() != JsonToken.END_OBJECT) {
+            String key = jp.getCurrentName();
+            listFields.put(key, new ArrayList<String>());
+            jp.nextToken(); // move to value
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+              listFields.get(key).add(jp.getText());
+            }
+
+          }
+
+        } else if ("rawPayload".equals(fieldname)) {
+          rawPayload = Base64.decodeBase64(jp.getText());
+        } else {
+          throw new IllegalStateException("Unrecognized field '" + fieldname + "'!");
+        }
+      }
+      jp.close(); // ensure resources get cleaned up timely and properly
+
+      if (id == null) {
+        throw new IllegalStateException("ZNRecord id field is required!");
+      }
+      record = new ZNRecord(id);
+      record.setSimpleFields(simpleFields);
+      record.setListFields(listFields);
+      record.setMapFields(mapFields);
+      record.setRawPayload(rawPayload);
+    } catch (Exception e) {
+      LOG.error("Exception during deserialization of bytes: " + new String(bytes), e);
+    }
+    return record;
+  }
+
+  public static void main(String[] args) {
+    ZNRecord record = new ZNRecord("record");
+    final int recordSize = 10;
+    for (int i = 0; i < recordSize; i++) {
+      record.setSimpleField("" + i, "" + i);
+      record.setListField("" + i, new ArrayList<String>());
+      for (int j = 0; j < recordSize; j++) {
+        record.getListField("" + i).add("" + j);
+      }
+
+      record.setMapField("" + i, new TreeMap<String, String>());
+      for (int j = 0; j < recordSize; j++) {
+        record.getMapField("" + i).put("" + j, "" + j);
+      }
+    }
+
+    ZNRecordStreamingSerializer serializer = new ZNRecordStreamingSerializer();
+    byte[] bytes = serializer.serialize(record);
+    System.out.println(new String(bytes));
+    ZNRecord record2 = (ZNRecord) serializer.deserialize(bytes);
+    System.out.println(record2);
+
+    long start = System.currentTimeMillis();
+    for (int i = 0; i < 100; i++) {
+      bytes = serializer.serialize(record);
+      // System.out.println(new String(bytes));
+      record2 = (ZNRecord) serializer.deserialize(bytes);
+      // System.out.println(record2);
+    }
+    long end = System.currentTimeMillis();
+    System.out.println("ZNRecordStreamingSerializer time used: " + (end - start));
+
+    ZNRecordSerializer serializer2 = new ZNRecordSerializer();
+    bytes = serializer2.serialize(record);
+    // System.out.println(new String(bytes));
+    record2 = (ZNRecord) serializer2.deserialize(bytes);
+    // System.out.println(record2);
+
+    start = System.currentTimeMillis();
+    for (int i = 0; i < 100; i++) {
+      bytes = serializer2.serialize(record);
+      // System.out.println(new String(bytes));
+      record2 = (ZNRecord) serializer2.deserialize(bytes);
+      // System.out.println(record2);
+    }
+    end = System.currentTimeMillis();
+    System.out.println("ZNRecordSerializer time used: " + (end - start));
+
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/exception/ZkClientException.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/exception/ZkClientException.java
new file mode 100644
index 0000000..3a669f5
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/exception/ZkClientException.java
@@ -0,0 +1,38 @@
+package org.apache.helix.zookeeper.exception;
+
+/*
+ * 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.
+ */
+
+/**
+ * Exceptions for ZkClient-related failures.
+ */
+public class ZkClientException extends RuntimeException {
+
+  public ZkClientException(String message) {
+    super(message);
+  }
+
+  public ZkClientException(Throwable cause) {
+    super(cause);
+  }
+
+  public ZkClientException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java
new file mode 100644
index 0000000..3925a6d
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java
@@ -0,0 +1,363 @@
+package org.apache.helix.zookeeper.impl.client;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
+import org.apache.helix.zookeeper.zkclient.DataUpdater;
+import org.apache.helix.zookeeper.zkclient.IZkChildListener;
+import org.apache.helix.zookeeper.zkclient.IZkDataListener;
+import org.apache.helix.zookeeper.zkclient.callback.ZkAsyncCallbacks;
+import org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener;
+import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.Op;
+import org.apache.zookeeper.OpResult;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+
+public class FederatedZkClient implements RealmAwareZkClient {
+  @Override
+  public List<String> subscribeChildChanges(String path, IZkChildListener listener) {
+    return null;
+  }
+
+  @Override
+  public void unsubscribeChildChanges(String path, IZkChildListener listener) {
+
+  }
+
+  @Override
+  public void subscribeDataChanges(String path, IZkDataListener listener) {
+
+  }
+
+  @Override
+  public void unsubscribeDataChanges(String path, IZkDataListener listener) {
+
+  }
+
+  @Override
+  public void subscribeStateChanges(IZkStateListener listener) {
+
+  }
+
+  @Override
+  public void unsubscribeStateChanges(IZkStateListener listener) {
+
+  }
+
+  @Override
+  public void unsubscribeAll() {
+
+  }
+
+  @Override
+  public void createPersistent(String path) {
+
+  }
+
+  @Override
+  public void createPersistent(String path, boolean createParents) {
+
+  }
+
+  @Override
+  public void createPersistent(String path, boolean createParents, List<ACL> acl) {
+
+  }
+
+  @Override
+  public void createPersistent(String path, Object data) {
+
+  }
+
+  @Override
+  public void createPersistent(String path, Object data, List<ACL> acl) {
+
+  }
+
+  @Override
+  public String createPersistentSequential(String path, Object data) {
+    return null;
+  }
+
+  @Override
+  public String createPersistentSequential(String path, Object data, List<ACL> acl) {
+    return null;
+  }
+
+  @Override
+  public void createEphemeral(String path) {
+
+  }
+
+  @Override
+  public void createEphemeral(String path, String sessionId) {
+
+  }
+
+  @Override
+  public void createEphemeral(String path, List<ACL> acl) {
+
+  }
+
+  @Override
+  public void createEphemeral(String path, List<ACL> acl, String sessionId) {
+
+  }
+
+  @Override
+  public String create(String path, Object data, CreateMode mode) {
+    return null;
+  }
+
+  @Override
+  public String create(String path, Object datat, List<ACL> acl, CreateMode mode) {
+    return null;
+  }
+
+  @Override
+  public void createEphemeral(String path, Object data) {
+
+  }
+
+  @Override
+  public void createEphemeral(String path, Object data, String sessionId) {
+
+  }
+
+  @Override
+  public void createEphemeral(String path, Object data, List<ACL> acl) {
+
+  }
+
+  @Override
+  public void createEphemeral(String path, Object data, List<ACL> acl, String sessionId) {
+
+  }
+
+  @Override
+  public String createEphemeralSequential(String path, Object data) {
+    return null;
+  }
+
+  @Override
+  public String createEphemeralSequential(String path, Object data, List<ACL> acl) {
+    return null;
+  }
+
+  @Override
+  public String createEphemeralSequential(String path, Object data, String sessionId) {
+    return null;
+  }
+
+  @Override
+  public String createEphemeralSequential(String path, Object data, List<ACL> acl,
+      String sessionId) {
+    return null;
+  }
+
+  @Override
+  public List<String> getChildren(String path) {
+    return null;
+  }
+
+  @Override
+  public int countChildren(String path) {
+    return 0;
+  }
+
+  @Override
+  public boolean exists(String path) {
+    return false;
+  }
+
+  @Override
+  public Stat getStat(String path) {
+    return null;
+  }
+
+  @Override
+  public boolean waitUntilExists(String path, TimeUnit timeUnit, long time) {
+    return false;
+  }
+
+  @Override
+  public void deleteRecursively(String path) {
+
+  }
+
+  @Override
+  public boolean delete(String path) {
+    return false;
+  }
+
+  @Override
+  public <T> T readData(String path) {
+    return null;
+  }
+
+  @Override
+  public <T> T readData(String path, boolean returnNullIfPathNotExists) {
+    return null;
+  }
+
+  @Override
+  public <T> T readData(String path, Stat stat) {
+    return null;
+  }
+
+  @Override
+  public <T> T readData(String path, Stat stat, boolean watch) {
+    return null;
+  }
+
+  @Override
+  public <T> T readDataAndStat(String path, Stat stat, boolean returnNullIfPathNotExists) {
+    return null;
+  }
+
+  @Override
+  public void writeData(String path, Object object) {
+
+  }
+
+  @Override
+  public <T> void updateDataSerialized(String path, DataUpdater<T> updater) {
+
+  }
+
+  @Override
+  public void writeData(String path, Object datat, int expectedVersion) {
+
+  }
+
+  @Override
+  public Stat writeDataReturnStat(String path, Object datat, int expectedVersion) {
+    return null;
+  }
+
+  @Override
+  public Stat writeDataGetStat(String path, Object datat, int expectedVersion) {
+    return null;
+  }
+
+  @Override
+  public void asyncCreate(String path, Object datat, CreateMode mode,
+      ZkAsyncCallbacks.CreateCallbackHandler cb) {
+
+  }
+
+  @Override
+  public void asyncSetData(String path, Object datat, int version,
+      ZkAsyncCallbacks.SetDataCallbackHandler cb) {
+
+  }
+
+  @Override
+  public void asyncGetData(String path, ZkAsyncCallbacks.GetDataCallbackHandler cb) {
+
+  }
+
+  @Override
+  public void asyncExists(String path, ZkAsyncCallbacks.ExistsCallbackHandler cb) {
+
+  }
+
+  @Override
+  public void asyncDelete(String path, ZkAsyncCallbacks.DeleteCallbackHandler cb) {
+
+  }
+
+  @Override
+  public void watchForData(String path) {
+
+  }
+
+  @Override
+  public List<String> watchForChilds(String path) {
+    return null;
+  }
+
+  @Override
+  public long getCreationTime(String path) {
+    return 0;
+  }
+
+  @Override
+  public List<OpResult> multi(Iterable<Op> ops) {
+    return null;
+  }
+
+  @Override
+  public boolean waitUntilConnected(long time, TimeUnit timeUnit) {
+    return false;
+  }
+
+  @Override
+  public String getServers() {
+    return null;
+  }
+
+  @Override
+  public long getSessionId() {
+    return 0;
+  }
+
+  @Override
+  public void close() {
+
+  }
+
+  @Override
+  public boolean isClosed() {
+    return false;
+  }
+
+  @Override
+  public byte[] serialize(Object data, String path) {
+    return new byte[0];
+  }
+
+  @Override
+  public <T> T deserialize(byte[] data, String path) {
+    return null;
+  }
+
+  @Override
+  public void setZkSerializer(ZkSerializer zkSerializer) {
+
+  }
+
+  @Override
+  public void setZkSerializer(PathBasedZkSerializer zkSerializer) {
+
+  }
+
+  @Override
+  public PathBasedZkSerializer getZkSerializer() {
+    return null;
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java
new file mode 100644
index 0000000..70d58a8
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java
@@ -0,0 +1,115 @@
+package org.apache.helix.zookeeper.impl.client;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.factory.ZkConnectionManager;
+import org.apache.helix.zookeeper.zkclient.IZkConnection;
+import org.apache.helix.zookeeper.zkclient.ZkConnection;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.data.ACL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * NOTE: DO NOT USE THIS CLASS DIRECTLY. USE SharedZkClientFactory instead.
+ *
+ * HelixZkClient that uses shared ZkConnection.
+ * A SharedZkClient won't manipulate the shared ZkConnection directly.
+ */
+public class SharedZkClient extends ZkClient implements HelixZkClient {
+  private static Logger LOG = LoggerFactory.getLogger(SharedZkClient.class);
+  /*
+   * Since we cannot really disconnect the ZkConnection, we need a dummy ZkConnection placeholder.
+   * This is to ensure connection field is never null even the shared RealmAwareZkClient instance is closed so as to avoid NPE.
+   */
+  private final static ZkConnection IDLE_CONNECTION = new ZkConnection("Dummy_ZkServers");
+  private final OnCloseCallback _onCloseCallback;
+  private final ZkConnectionManager _connectionManager;
+
+  public interface OnCloseCallback {
+    /**
+     * Triggered after the RealmAwareZkClient is closed.
+     */
+    void onClose();
+  }
+
+  /**
+   * Construct a shared RealmAwareZkClient that uses a shared ZkConnection.
+   *
+   * @param connectionManager     The manager of the shared ZkConnection.
+   * @param clientConfig          ZkClientConfig details to create the shared RealmAwareZkClient.
+   * @param callback              Clean up logic when the shared RealmAwareZkClient is closed.
+   */
+  public SharedZkClient(ZkConnectionManager connectionManager, ZkClientConfig clientConfig,
+      OnCloseCallback callback) {
+    super(connectionManager.getConnection(), 0, clientConfig.getOperationRetryTimeout(),
+        clientConfig.getZkSerializer(), clientConfig.getMonitorType(), clientConfig.getMonitorKey(),
+        clientConfig.getMonitorInstanceName(), clientConfig.isMonitorRootPathOnly());
+    _connectionManager = connectionManager;
+    // Register to the base dedicated RealmAwareZkClient
+    _connectionManager.registerWatcher(this);
+    _onCloseCallback = callback;
+  }
+
+  @Override
+  public void close() {
+    super.close();
+    if (isClosed()) {
+      // Note that if register is not done while constructing, these private fields may not be init yet.
+      if (_connectionManager != null) {
+        _connectionManager.unregisterWatcher(this);
+      }
+      if (_onCloseCallback != null) {
+        _onCloseCallback.onClose();
+      }
+    }
+  }
+
+  @Override
+  public IZkConnection getConnection() {
+    if (isClosed()) {
+      return IDLE_CONNECTION;
+    }
+    return super.getConnection();
+  }
+
+  /**
+   * Since ZkConnection session is shared in this RealmAwareZkClient, do not create ephemeral node using a SharedZKClient.
+   */
+  @Override
+  public String create(final String path, Object datat, final List<ACL> acl,
+      final CreateMode mode) {
+    if (mode.isEphemeral()) {
+      throw new UnsupportedOperationException(
+          "Create ephemeral nodes using a " + SharedZkClient.class.getSimpleName()
+              + " is not supported.");
+    }
+    return super.create(path, datat, acl, mode);
+  }
+
+  @Override
+  protected boolean isManagingZkConnection() {
+    return false;
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/ZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/ZkClient.java
new file mode 100644
index 0000000..619e1da
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/ZkClient.java
@@ -0,0 +1,277 @@
+package org.apache.helix.zookeeper.impl.client;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.zkclient.IZkConnection;
+import org.apache.helix.zookeeper.zkclient.ZkConnection;
+import org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.SerializableSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * "Raw ZkClient".
+ *
+ * Raw ZkClient that wraps {@link org.apache.helix.zookeeper.zkclient.ZkClient},
+ * with additional constructors and builder.
+ *
+ * Note that, instead of directly constructing a raw RealmAwareZkClient, applications should always use
+ * HelixZkClientFactory to build shared or dedicated HelixZkClient instances.
+ * Only constructing a raw RealmAwareZkClient when advanced usage is required.
+ * For example, application need to access/manage ZkConnection directly.
+ *
+ * Both SharedZKClient and DedicatedZkClient are built based on the raw RealmAwareZkClient. As shown below.
+ *                ----------------------------
+ *               |                            |
+ *     ---------------------                  |
+ *    |                     |                 | *implements
+ *  SharedZkClient  DedicatedZkClient           ----> HelixZkClient Interface
+ *    |                     |                 |
+ *     ---------------------                  |
+ *               |                            |
+ *           Raw RealmAwareZkClient (this class)--------
+ *               |
+ *         Native RealmAwareZkClient
+ *
+ * TODO Completely replace usage of the raw ZkClient within helix-core. Instead, using HelixZkClient. --JJ
+ */
+
+public class ZkClient extends org.apache.helix.zookeeper.zkclient.ZkClient implements HelixZkClient {
+  private static Logger LOG = LoggerFactory.getLogger(ZkClient.class);
+
+  public static final int DEFAULT_OPERATION_TIMEOUT = Integer.MAX_VALUE;
+  public static final int DEFAULT_CONNECTION_TIMEOUT = 60 * 1000;
+  public static final int DEFAULT_SESSION_TIMEOUT = 30 * 1000;
+
+  /**
+   *
+   * @param zkConnection
+   *            The Zookeeper connection
+   * @param connectionTimeout
+   *            The connection timeout in milli seconds
+   * @param zkSerializer
+   *            The Zookeeper data serializer
+   * @param operationRetryTimeout
+   *            Most operations are retried in cases like connection loss with the Zookeeper servers. During such failures, this
+   *            <code>operationRetryTimeout</code> decides the maximum amount of time, in milli seconds, each
+   *            operation is retried. A value lesser than 0 is considered as
+   *            "retry forever until a connection has been reestablished".
+   * @param monitorType
+   * @param monitorKey
+   * @param monitorInstanceName
+   *            These 3 inputs are used to name JMX monitor bean name for this RealmAwareZkClient.
+   *            The JMX bean name will be: HelixZkClient.monitorType.monitorKey.monitorInstanceName.
+   * @param monitorRootPathOnly
+   *            Should only stat of access to root path be reported to JMX bean or path-specific stat be reported too.
+   */
+  public ZkClient(IZkConnection zkConnection, int connectionTimeout, long operationRetryTimeout,
+      PathBasedZkSerializer zkSerializer,
+      String monitorType, String monitorKey, String monitorInstanceName,
+      boolean monitorRootPathOnly) {
+    super(zkConnection, connectionTimeout, operationRetryTimeout, zkSerializer, monitorType,
+        monitorKey, monitorInstanceName, monitorRootPathOnly);
+  }
+
+  public ZkClient(IZkConnection connection, int connectionTimeout,
+      PathBasedZkSerializer zkSerializer,
+      String monitorType, String monitorKey, long operationRetryTimeout) {
+    this(connection, connectionTimeout, operationRetryTimeout, zkSerializer, monitorType,
+        monitorKey, null, true);
+  }
+
+  public ZkClient(IZkConnection connection, int connectionTimeout,
+      PathBasedZkSerializer zkSerializer,
+      String monitorType, String monitorKey) {
+    this(connection, connectionTimeout, zkSerializer, monitorType, monitorKey,
+        DEFAULT_OPERATION_TIMEOUT);
+  }
+
+  public ZkClient(String zkServers, String monitorType, String monitorKey) {
+    this(new ZkConnection(zkServers, DEFAULT_SESSION_TIMEOUT), Integer.MAX_VALUE,
+        new BasicZkSerializer(new SerializableSerializer()), monitorType, monitorKey);
+  }
+
+  public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout,
+      PathBasedZkSerializer zkSerializer,
+      String monitorType, String monitorKey) {
+    this(new ZkConnection(zkServers, sessionTimeout), connectionTimeout, zkSerializer, monitorType,
+        monitorKey);
+  }
+
+  public ZkClient(IZkConnection connection, int connectionTimeout,
+      PathBasedZkSerializer zkSerializer) {
+    this(connection, connectionTimeout, zkSerializer, null, null);
+  }
+
+  public ZkClient(IZkConnection connection, int connectionTimeout, ZkSerializer zkSerializer) {
+    this(connection, connectionTimeout, new BasicZkSerializer(zkSerializer));
+  }
+
+  public ZkClient(IZkConnection connection, int connectionTimeout) {
+    this(connection, connectionTimeout, new SerializableSerializer());
+  }
+
+  public ZkClient(IZkConnection connection) {
+    this(connection, Integer.MAX_VALUE, new SerializableSerializer());
+  }
+
+  public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout,
+      ZkSerializer zkSerializer) {
+    this(new ZkConnection(zkServers, sessionTimeout), connectionTimeout, zkSerializer);
+  }
+
+  public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout,
+      PathBasedZkSerializer zkSerializer) {
+    this(new ZkConnection(zkServers, sessionTimeout), connectionTimeout, zkSerializer);
+  }
+
+  public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout) {
+    this(new ZkConnection(zkServers, sessionTimeout), connectionTimeout,
+        new SerializableSerializer());
+  }
+
+  public ZkClient(String zkServers, int connectionTimeout) {
+    this(new ZkConnection(zkServers, DEFAULT_SESSION_TIMEOUT), connectionTimeout,
+        new SerializableSerializer());
+  }
+
+  public ZkClient(String zkServers) {
+    this(zkServers, null, null);
+  }
+
+  public ZkClient(final String zkServers, final int sessionTimeout, final int connectionTimeout,
+      final ZkSerializer zkSerializer, final long operationRetryTimeout) {
+    this(new ZkConnection(zkServers, sessionTimeout), connectionTimeout, zkSerializer,
+        operationRetryTimeout);
+  }
+
+  public ZkClient(final IZkConnection zkConnection, final int connectionTimeout,
+      final ZkSerializer zkSerializer, final long operationRetryTimeout) {
+    this(zkConnection, connectionTimeout, operationRetryTimeout,
+        new BasicZkSerializer(zkSerializer), null, null, null, false);
+  }
+
+  public static class Builder {
+    IZkConnection _connection;
+    String _zkServer;
+
+    PathBasedZkSerializer _zkSerializer;
+
+    long _operationRetryTimeout = DEFAULT_OPERATION_TIMEOUT;
+    int _connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
+    int _sessionTimeout = DEFAULT_SESSION_TIMEOUT;
+
+    String _monitorType;
+    String _monitorKey;
+    String _monitorInstanceName = null;
+    boolean _monitorRootPathOnly = true;
+
+    public Builder setConnection(IZkConnection connection) {
+      this._connection = connection;
+      return this;
+    }
+
+    public Builder setConnectionTimeout(Integer connectionTimeout) {
+      this._connectionTimeout = connectionTimeout;
+      return this;
+    }
+
+    public Builder setZkSerializer(
+        PathBasedZkSerializer zkSerializer) {
+      this._zkSerializer = zkSerializer;
+      return this;
+    }
+
+    public Builder setZkSerializer(ZkSerializer zkSerializer) {
+      this._zkSerializer = new BasicZkSerializer(zkSerializer);
+      return this;
+    }
+
+    /**
+     * Used as part of the MBean ObjectName. This item is required for enabling monitoring.
+     * @param monitorType
+     */
+    public Builder setMonitorType(String monitorType) {
+      this._monitorType = monitorType;
+      return this;
+    }
+
+    /**
+     * Used as part of the MBean ObjectName. This item is required for enabling monitoring.
+     * @param monitorKey
+     */
+    public Builder setMonitorKey(String monitorKey) {
+      this._monitorKey = monitorKey;
+      return this;
+    }
+
+    /**
+     * Used as part of the MBean ObjectName. This item is optional.
+     * @param instanceName
+     */
+    public Builder setMonitorInstanceName(String instanceName) {
+      this._monitorInstanceName = instanceName;
+      return this;
+    }
+
+    public Builder setMonitorRootPathOnly(Boolean monitorRootPathOnly) {
+      this._monitorRootPathOnly = monitorRootPathOnly;
+      return this;
+    }
+
+    public Builder setZkServer(String zkServer) {
+      this._zkServer = zkServer;
+      return this;
+    }
+
+    public Builder setSessionTimeout(Integer sessionTimeout) {
+      this._sessionTimeout = sessionTimeout;
+      return this;
+    }
+
+    public Builder setOperationRetryTimeout(Long operationRetryTimeout) {
+      this._operationRetryTimeout = operationRetryTimeout;
+      return this;
+    }
+
+    public ZkClient build() {
+      if (_connection == null) {
+        if (_zkServer == null) {
+          throw new ZkClientException(
+              "Failed to build ZkClient since no connection or ZK server address is specified.");
+        } else {
+          _connection = new ZkConnection(_zkServer, _sessionTimeout);
+        }
+      }
+
+      if (_zkSerializer == null) {
+        _zkSerializer = new BasicZkSerializer(new SerializableSerializer());
+      }
+
+      return new ZkClient(_connection, _connectionTimeout, _operationRetryTimeout, _zkSerializer,
+          _monitorType, _monitorKey, _monitorInstanceName, _monitorRootPathOnly);
+    }
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/DedicatedZkClientFactory.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/DedicatedZkClientFactory.java
new file mode 100644
index 0000000..90ecf9b
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/DedicatedZkClientFactory.java
@@ -0,0 +1,57 @@
+package org.apache.helix.zookeeper.impl.factory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+
+
+/**
+ * Singleton factory that build dedicated clients using the raw ZkClient.
+ */
+public class DedicatedZkClientFactory extends HelixZkClientFactory {
+
+  protected DedicatedZkClientFactory() {
+  }
+
+  private static class SingletonHelper {
+    private static final DedicatedZkClientFactory INSTANCE = new DedicatedZkClientFactory();
+  }
+
+  public static DedicatedZkClientFactory getInstance() {
+    return SingletonHelper.INSTANCE;
+  }
+
+  /**
+   * Build a Dedicated ZkClient based on connection config and client config
+   *
+   * @param connectionConfig
+   * @param clientConfig
+   * @return
+   */
+  @Override
+  public HelixZkClient buildZkClient(HelixZkClient.ZkConnectionConfig connectionConfig,
+      HelixZkClient.ZkClientConfig clientConfig) {
+    return new ZkClient(createZkConnection(connectionConfig),
+        (int) clientConfig.getConnectInitTimeout(), clientConfig.getOperationRetryTimeout(),
+        clientConfig.getZkSerializer(), clientConfig.getMonitorType(), clientConfig.getMonitorKey(),
+        clientConfig.getMonitorInstanceName(), clientConfig.isMonitorRootPathOnly());
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/HelixZkClientFactory.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/HelixZkClientFactory.java
new file mode 100644
index 0000000..9e1ca6e
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/HelixZkClientFactory.java
@@ -0,0 +1,68 @@
+package org.apache.helix.zookeeper.impl.factory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.zkclient.IZkConnection;
+import org.apache.helix.zookeeper.zkclient.ZkConnection;
+
+
+/**
+ * Abstract class of the ZkClient factory.
+ */
+abstract class HelixZkClientFactory {
+
+  /**
+   * Build a ZkClient using specified connection config and client config
+   *
+   * @param connectionConfig
+   * @param clientConfig
+   * @return HelixZkClient
+   */
+  public abstract HelixZkClient buildZkClient(HelixZkClient.ZkConnectionConfig connectionConfig,
+      HelixZkClient.ZkClientConfig clientConfig);
+
+  /**
+   * Build a ZkClient using specified connection config and default client config
+   *
+   * @param connectionConfig
+   * @return HelixZkClient
+   */
+  public HelixZkClient buildZkClient(HelixZkClient.ZkConnectionConfig connectionConfig) {
+    return buildZkClient(connectionConfig, new HelixZkClient.ZkClientConfig());
+  }
+
+  /**
+   * Construct a new ZkConnection instance based on connection configuration.
+   * Note that the connection is not really made until someone calls zkConnection.connect().
+   * @param connectionConfig
+   * @return
+   */
+  protected IZkConnection createZkConnection(HelixZkClient.ZkConnectionConfig connectionConfig) {
+    if (connectionConfig.getZkServers() == null) {
+      throw new ZkClientException(
+          "Failed to build ZkClient since no connection or ZK server address is specified.");
+    } else {
+      return new ZkConnection(connectionConfig.getZkServers(),
+          connectionConfig.getSessionTimeout());
+    }
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/SharedZkClientFactory.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/SharedZkClientFactory.java
new file mode 100644
index 0000000..bf9d9a1
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/SharedZkClientFactory.java
@@ -0,0 +1,111 @@
+package org.apache.helix.zookeeper.impl.factory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.HashMap;
+
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.impl.client.SharedZkClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Singleton factory that build shared ZkClient which use a shared ZkConnection.
+ */
+public class SharedZkClientFactory extends HelixZkClientFactory {
+  private static Logger LOG = LoggerFactory.getLogger(SharedZkClientFactory.class);
+  // The connection pool to track all created connections.
+  private final HashMap<HelixZkClient.ZkConnectionConfig, ZkConnectionManager>
+      _connectionManagerPool = new HashMap<>();
+
+  protected SharedZkClientFactory() {
+  }
+
+  private static class SingletonHelper {
+    private static final SharedZkClientFactory INSTANCE = new SharedZkClientFactory();
+  }
+
+  public static SharedZkClientFactory getInstance() {
+    return SingletonHelper.INSTANCE;
+  }
+
+  /**
+   * Build a Shared ZkClient that uses sharing ZkConnection that is created based on the specified connection config.
+   *
+   * @param connectionConfig The connection configuration that is used to search for a shared connection. Or create new connection if necessary.
+   * @param clientConfig
+   * @return Shared ZkClient
+   */
+  @Override
+  public HelixZkClient buildZkClient(HelixZkClient.ZkConnectionConfig connectionConfig,
+      HelixZkClient.ZkClientConfig clientConfig) {
+    synchronized (_connectionManagerPool) {
+      final ZkConnectionManager zkConnectionManager =
+          getOrCreateZkConnectionManager(connectionConfig, clientConfig.getConnectInitTimeout());
+      if (zkConnectionManager == null) {
+        throw new ZkClientException("Failed to create a connection manager in the pool to share.");
+      }
+      LOG.info("Sharing ZkConnection {} to a new SharedZkClient.", connectionConfig.toString());
+      return new SharedZkClient(zkConnectionManager, clientConfig,
+          new SharedZkClient.OnCloseCallback() {
+            @Override
+            public void onClose() {
+              cleanupConnectionManager(zkConnectionManager);
+            }
+          });
+    }
+  }
+
+  private ZkConnectionManager getOrCreateZkConnectionManager(
+      HelixZkClient.ZkConnectionConfig connectionConfig, long connectInitTimeout) {
+    ZkConnectionManager connectionManager = _connectionManagerPool.get(connectionConfig);
+    if (connectionManager == null || connectionManager.isClosed()) {
+      connectionManager =
+          new ZkConnectionManager(createZkConnection(connectionConfig), connectInitTimeout,
+              connectionConfig.toString());
+      _connectionManagerPool.put(connectionConfig, connectionManager);
+    }
+    return connectionManager;
+  }
+
+  // Close the ZkConnectionManager if no other shared client is referring to it.
+  // Note the close operation of connection manager needs to be synchronized with the pool operation
+  // to avoid race condition.
+  private void cleanupConnectionManager(ZkConnectionManager zkConnectionManager) {
+    synchronized (_connectionManagerPool) {
+      zkConnectionManager.close(true);
+    }
+  }
+
+  // For testing purposes only
+  public int getActiveConnectionCount() {
+    int count = 0;
+    synchronized (_connectionManagerPool) {
+      for (ZkConnectionManager manager : _connectionManagerPool.values()) {
+        if (!manager.isClosed()) {
+          count++;
+        }
+      }
+    }
+    return count;
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/ZkConnectionManager.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/ZkConnectionManager.java
new file mode 100644
index 0000000..1413111
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/ZkConnectionManager.java
@@ -0,0 +1,145 @@
+package org.apache.helix.zookeeper.impl.factory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.helix.zookeeper.api.client.HelixZkClient;
+import org.apache.helix.zookeeper.impl.client.SharedZkClient;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.zkclient.IZkConnection;
+import org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.SerializableSerializer;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * NOTE: DO NOT USE THIS CLASS DIRECTLY. Use ZkClientFactories instead.
+ *
+ * A ZkConnection manager that maintain connection status and allows additional watchers to be registered.
+ * It will forward events to those watchers.
+ *
+ * TODO Separate connection management logic from the raw ZkClient class.
+ * So this manager is a peer to the HelixZkClient. Connection Manager for maintaining the connection and
+ * HelixZkClient to handle user request.
+ * After this is done, Dedicated HelixZkClient hires one manager for it's connection.
+ * While multiple Shared ZkClients can use single connection manager if possible.
+ */
+public class ZkConnectionManager extends ZkClient {
+  private static Logger LOG = LoggerFactory.getLogger(ZkConnectionManager.class);
+  // Client type that is used in monitor, and metrics.
+  private final static String MONITOR_TYPE = "ZkConnectionManager";
+  private final String _monitorKey;
+  // Set of all registered watchers
+  protected final Set<Watcher> _sharedWatchers = new HashSet<>();
+
+  /**
+   * Construct and init a ZkConnection Manager.
+   *
+   * @param zkConnection
+   * @param connectionTimeout
+   */
+  protected ZkConnectionManager(IZkConnection zkConnection, long connectionTimeout,
+      String monitorKey) {
+    super(zkConnection, (int) connectionTimeout, HelixZkClient.DEFAULT_OPERATION_TIMEOUT,
+        new BasicZkSerializer(new SerializableSerializer()), MONITOR_TYPE, monitorKey, null, true);
+    _monitorKey = monitorKey;
+    LOG.info("ZkConnection {} was created for sharing.", _monitorKey);
+  }
+
+  /**
+   * Register event watcher.
+   *
+   * @param watcher
+   * @return true if the watcher is newly added. false if it is already in record.
+   */
+  public synchronized boolean registerWatcher(Watcher watcher) {
+    if (isClosed()) {
+      throw new ZkClientException("Cannot add watcher to a closed client.");
+    }
+    return _sharedWatchers.add(watcher);
+  }
+
+  /**
+   * Unregister the event watcher.
+   *
+   * @param watcher
+   * @return number of the remaining event watchers
+   */
+  public synchronized int unregisterWatcher(Watcher watcher) {
+    _sharedWatchers.remove(watcher);
+    return _sharedWatchers.size();
+  }
+
+  @Override
+  public void process(final WatchedEvent event) {
+    super.process(event);
+    forwardingEvent(event);
+  }
+
+  private synchronized void forwardingEvent(final WatchedEvent event) {
+    // note that process (then forwardingEvent) could be triggered during construction, when sharedWatchers is still null.
+    if (_sharedWatchers == null || _sharedWatchers.isEmpty()) {
+      return;
+    }
+    // forward event to all the watchers' event queue
+    for (final Watcher watcher : _sharedWatchers) {
+      watcher.process(event);
+    }
+  }
+
+  @Override
+  public void close() {
+    // Enforce closing, if any watcher exists, throw Exception.
+    close(false);
+  }
+
+  protected synchronized void close(boolean skipIfWatched) {
+    cleanupInactiveWatchers();
+    if (_sharedWatchers.size() > 0) {
+      if (skipIfWatched) {
+        LOG.debug("Skip closing ZkConnection due to existing watchers. Watcher count {}.",
+            _sharedWatchers.size());
+        return;
+      } else {
+        throw new ZkClientException(
+            "Cannot close the connection when there are still shared watchers listen on the event.");
+      }
+    }
+    super.close();
+    LOG.info("ZkConnection {} was closed.", _monitorKey);
+  }
+
+  protected void cleanupInactiveWatchers() {
+    Set<Watcher> closedWatchers = new HashSet<>();
+    for (Watcher watcher : _sharedWatchers) {
+      // TODO ideally, we shall have a ClosableWatcher interface so as to check accordingly. -- JJ
+      if (watcher instanceof SharedZkClient && ((SharedZkClient) watcher).isClosed()) {
+        closedWatchers.add(watcher);
+      }
+    }
+    _sharedWatchers.removeAll(closedWatchers);
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/GZipCompressionUtil.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/GZipCompressionUtil.java
new file mode 100644
index 0000000..a87ef37
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/GZipCompressionUtil.java
@@ -0,0 +1,74 @@
+package org.apache.helix.zookeeper.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+
+public class GZipCompressionUtil {
+  /**
+   * Compresses a byte array by applying GZIP compression
+   * @param buffer
+   * @return
+   * @throws IOException
+   */
+  public static byte[] compress(byte[] buffer) throws IOException {
+    ByteArrayOutputStream gzipByteArrayOutputStream = new ByteArrayOutputStream();
+    GZIPOutputStream gzipOutputStream = null;
+    gzipOutputStream = new GZIPOutputStream(gzipByteArrayOutputStream);
+    gzipOutputStream.write(buffer, 0, buffer.length);
+    gzipOutputStream.close();
+    byte[] compressedBytes = gzipByteArrayOutputStream.toByteArray();
+    return compressedBytes;
+  }
+
+  public static byte[] uncompress(ByteArrayInputStream bais) throws IOException {
+    GZIPInputStream gzipInputStream = new GZIPInputStream(bais);
+    byte[] buffer = new byte[1024];
+    int length;
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    while ((length = gzipInputStream.read(buffer)) != -1) {
+      baos.write(buffer, 0, length);
+    }
+    gzipInputStream.close();
+    baos.close();
+    byte[] uncompressedBytes = baos.toByteArray();
+    return uncompressedBytes;
+  }
+
+  /*
+   * Determines if a byte array is compressed. The java.util.zip GZip
+   * implementaiton does not expose the GZip header so it is difficult to determine
+   * if a string is compressed.
+   * @param bytes an array of bytes
+   * @return true if the array is compressed or false otherwise
+   */
+  public static boolean isCompressed(byte[] bytes) {
+    if ((bytes == null) || (bytes.length < 2)) {
+      return false;
+    } else {
+      return ((bytes[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (bytes[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)));
+    }
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/DataUpdater.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/DataUpdater.java
new file mode 100644
index 0000000..db9a35b
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/DataUpdater.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+/**
+ * Updates the data of a znode. This is used together with {@link ZkClient#updateDataSerialized(String, DataUpdater)}.
+ *
+ * @param <T>
+ */
+public interface DataUpdater<T extends Object> {
+
+    /**
+     * Updates the current data of a znode.
+     *
+     * @param currentData
+     *            The current contents.
+     * @return the new data that should be written back to ZooKeeper.
+     */
+    public T update(T currentData);
+
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ExceptionUtil.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ExceptionUtil.java
new file mode 100644
index 0000000..9b575d8
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ExceptionUtil.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
+
+
+public class ExceptionUtil {
+
+    public static RuntimeException convertToRuntimeException(Throwable e) {
+        if (e instanceof RuntimeException) {
+            return (RuntimeException) e;
+        }
+        retainInterruptFlag(e);
+        return new RuntimeException(e);
+    }
+
+    /**
+     * This sets the interrupt flag if the catched exception was an {@link InterruptedException}. Catching such an
+     * exception always clears the interrupt flag.
+     *
+     * @param catchedException
+     *            The catched exception.
+     */
+    public static void retainInterruptFlag(Throwable catchedException) {
+        if (catchedException instanceof InterruptedException) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    public static void rethrowInterruptedException(Throwable e) throws InterruptedException {
+        if (e instanceof InterruptedException) {
+            throw (InterruptedException) e;
+        }
+        if (e instanceof ZkInterruptedException) {
+            throw (ZkInterruptedException) e;
+        }
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IDefaultNameSpace.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IDefaultNameSpace.java
new file mode 100644
index 0000000..46a9461
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IDefaultNameSpace.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+public interface IDefaultNameSpace {
+
+    /**
+     * Creates a set of default folder structure within a zookeeper .
+     *
+     * @param zkClient
+     *            The zkclient.
+     */
+    public void createDefaultNameSpace(ZkClient zkClient);
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkChildListener.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkChildListener.java
new file mode 100644
index 0000000..351f305
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkChildListener.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+import java.util.List;
+
+/**
+ * An {@link IZkChildListener} can be registered at a {@link ZkClient} for listening on zk child changes for a given
+ * path.
+ *
+ * Node: Also this listener re-subscribes it watch for the path on each zk event (zk watches are one-timers) is is not
+ * guaranteed that events on the path are missing (see http://zookeeper.wiki.sourceforge.net/ZooKeeperWatches). An
+ * implementation of this class should take that into account.
+ *
+ */
+public interface IZkChildListener {
+
+    /**
+     * Called when the children of the given path changed.
+     *
+     * @param parentPath
+     *            The parent path
+     * @param currentChilds
+     *            The children or null if the root node (parent path) was deleted.
+     * @throws Exception
+     */
+    public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception;
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkConnection.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkConnection.java
new file mode 100644
index 0000000..6370c95
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkConnection.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+import java.util.List;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Op;
+import org.apache.zookeeper.OpResult;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper.States;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+public interface IZkConnection {
+
+    public void connect(Watcher watcher);
+
+    void close() throws InterruptedException;
+
+    public String create(String path, byte[] data, CreateMode mode) throws KeeperException, InterruptedException;
+
+    public String create(String path, byte[] data, List<ACL> acl, CreateMode mode) throws KeeperException, InterruptedException;
+
+    public void delete(String path) throws InterruptedException, KeeperException;
+
+    boolean exists(final String path, final boolean watch) throws KeeperException, InterruptedException;
+
+    List<String> getChildren(final String path, final boolean watch) throws KeeperException, InterruptedException;
+
+    public byte[] readData(String path, Stat stat, boolean watch) throws KeeperException, InterruptedException;
+
+    public void writeData(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException;
+
+    public Stat writeDataReturnStat(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException;
+
+    public States getZookeeperState();
+
+    public long getCreateTime(String path) throws KeeperException, InterruptedException;
+
+    public String getServers();
+
+    public List<OpResult> multi(Iterable<Op> ops) throws KeeperException, InterruptedException;
+
+    public void addAuthInfo(String scheme, byte[] auth);
+}
\ No newline at end of file
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkDataListener.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkDataListener.java
new file mode 100644
index 0000000..c7fb7dd
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkDataListener.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+/**
+ * An {@link IZkDataListener} can be registered at a {@link ZkClient} for listening on zk data changes for a given path.
+ *
+ * Node: Also this listener re-subscribes it watch for the path on each zk event (zk watches are one-timers) is is not
+ * guaranteed that events on the path are missing (see http://zookeeper.wiki.sourceforge.net/ZooKeeperWatches). An
+ * implementation of this class should take that into account.
+ */
+public interface IZkDataListener {
+
+    public void handleDataChange(String dataPath, Object data) throws Exception;
+
+    public void handleDataDeleted(String dataPath) throws Exception;
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkStateListener.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkStateListener.java
new file mode 100644
index 0000000..5970e62
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkStateListener.java
@@ -0,0 +1,61 @@
+package org.apache.helix.zookeeper.zkclient;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+
+
+public interface IZkStateListener {
+
+  /**
+   * Called when the zookeeper connection state has changed.
+   *
+   * @param state the new zookeeper state.
+   * @throws Exception if any error occurs.
+   */
+  void handleStateChanged(KeeperState state) throws Exception;
+
+  /**
+   * Called after the zookeeper session has expired and a new session has been created. The new
+   * session id has to be passed in as the parameter.
+   * And you would have to re-create any ephemeral nodes here. This is a session aware operation.
+   * The ephemeral nodes have to be created within the expected session id, which means passed-in
+   * session id has to be checked with current zookeeper's session id. If the passed-in session id
+   * does not match current zookeeper's session id, ephemeral nodes should not be created.
+   * Otherwise, session race condition may occur and the newly created ephemeral nodes may not be in
+   * the expected session.
+   *
+   * @param sessionId the new session's id. The ephemeral nodes are expected to be created in this
+   *                  session. If this session id is expired, ephemeral nodes should not be created.
+   * @throws Exception if any error occurs.
+   */
+  void handleNewSession(final String sessionId) throws Exception;
+
+  /**
+   * Called when a session cannot be re-established. This should be used to implement connection
+   * failure handling e.g. retry to connect or pass the error up
+   *
+   * @param error
+   *            The error that prevents a session from being established
+   * @throws Exception
+   *             On any error.
+   */
+  void handleSessionEstablishmentError(final Throwable error) throws Exception;
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/NetworkUtil.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/NetworkUtil.java
new file mode 100644
index 0000000..f31a130
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/NetworkUtil.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+public class NetworkUtil {
+
+    public final static String OVERWRITE_HOSTNAME_SYSTEM_PROPERTY = "zkclient.hostname.overwritten";
+
+    public static String[] getLocalHostNames() {
+        final Set<String> hostNames = new HashSet<String>();
+        // we add localhost to this set manually, because if the ip 127.0.0.1 is
+        // configured with more than one name in the /etc/hosts, only the first
+        // name
+        // is returned
+        hostNames.add("localhost");
+        try {
+            final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+            for (final Enumeration<NetworkInterface> ifaces = networkInterfaces; ifaces.hasMoreElements();) {
+                final NetworkInterface iface = ifaces.nextElement();
+                InetAddress ia = null;
+                for (final Enumeration<InetAddress> ips = iface.getInetAddresses(); ips.hasMoreElements();) {
+                    ia = ips.nextElement();
+                    hostNames.add(ia.getCanonicalHostName());
+                    hostNames.add(ipToString(ia.getAddress()));
+                }
+            }
+        } catch (final SocketException e) {
+            throw new RuntimeException("unable to retrieve host names of localhost");
+        }
+        return hostNames.toArray(new String[hostNames.size()]);
+    }
+
+    private static String ipToString(final byte[] bytes) {
+        final StringBuffer addrStr = new StringBuffer();
+        for (int cnt = 0; cnt < bytes.length; cnt++) {
+            final int uByte = bytes[cnt] < 0 ? bytes[cnt] + 256 : bytes[cnt];
+            addrStr.append(uByte);
+            if (cnt < 3)
+                addrStr.append('.');
+        }
+        return addrStr.toString();
+    }
+
+    public static int hostNamesInList(final String serverList, final String[] hostNames) {
+        final String[] serverNames = serverList.split(",");
+        for (int i = 0; i < hostNames.length; i++) {
+            final String hostname = hostNames[i];
+            for (int j = 0; j < serverNames.length; j++) {
+                final String serverNameAndPort = serverNames[j];
+                final String serverName = serverNameAndPort.split(":")[0];
+                if (serverName.equalsIgnoreCase(hostname)) {
+                    return j;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public static boolean hostNameInArray(final String[] hostNames, final String hostName) {
+        for (final String name : hostNames) {
+            if (name.equalsIgnoreCase(hostName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean isPortFree(int port) {
+        try {
+            Socket socket = new Socket("localhost", port);
+            socket.close();
+            return false;
+        } catch (ConnectException e) {
+            return true;
+        } catch (SocketException e) {
+            if (e.getMessage().equals("Connection reset by peer")) {
+                return true;
+            }
+            throw new RuntimeException(e);
+        } catch (UnknownHostException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String getLocalhostName() {
+        String property = System.getProperty(OVERWRITE_HOSTNAME_SYSTEM_PROPERTY);
+        if (property != null && property.trim().length() > 0) {
+            return property;
+        }
+        try {
+            return InetAddress.getLocalHost().getHostName();
+        } catch (final UnknownHostException e) {
+            throw new RuntimeException("unable to retrieve localhost name");
+        }
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java
new file mode 100644
index 0000000..0507c3f
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java
@@ -0,0 +1,2172 @@
+/**
+ * Copyright 2010 the original author or authors.
+ * Licensed 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.helix.zookeeper.zkclient;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.OptionalLong;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.TimeUnit;
+import javax.management.JMException;
+
+import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkBadVersionException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkNodeExistsException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkTimeoutException;
+import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer;
+import org.apache.helix.zookeeper.zkclient.annotation.PreFetch;
+import org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer;
+import org.apache.helix.zookeeper.zkclient.callback.ZkAsyncCallbacks;
+import org.apache.helix.zookeeper.zkclient.exception.ZkSessionMismatchedException;
+import org.apache.helix.zookeeper.zkclient.metric.ZkClientMonitor;
+import org.apache.helix.zookeeper.zkclient.util.ExponentialBackoffStrategy;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.KeeperException.ConnectionLossException;
+import org.apache.zookeeper.KeeperException.SessionExpiredException;
+import org.apache.zookeeper.Op;
+import org.apache.zookeeper.OpResult;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.Watcher.Event.EventType;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * "Native ZkClient": not to be used directly.
+ *
+ * Abstracts the interaction with zookeeper and allows permanent (not just one time) watches on
+ * nodes in ZooKeeper.
+ * WARN: Do not use this class directly, use {@link org.apache.helix.zookeeper.impl.client.ZkClient} instead.
+ */
+public class ZkClient implements Watcher {
+  private static Logger LOG = LoggerFactory.getLogger(ZkClient.class);
+  private static long MAX_RECONNECT_INTERVAL_MS = 30000; // 30 seconds
+
+  private final IZkConnection _connection;
+  private final long _operationRetryTimeoutInMillis;
+  private final Map<String, Set<IZkChildListener>> _childListener = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<String, Set<IZkDataListenerEntry>> _dataListener =
+      new ConcurrentHashMap<>();
+  private final Set<IZkStateListener> _stateListener = new CopyOnWriteArraySet<>();
+  private KeeperState _currentState;
+  private final ZkLock _zkEventLock = new ZkLock();
+
+  // When a new zookeeper instance is created in reconnect, its session id is not yet valid before
+  // the zookeeper session is established(SyncConnected). To avoid session race condition in
+  // handling new session, the new session event is only fired after SyncConnected. Meanwhile,
+  // SyncConnected state is also received when re-opening the zk connection. So to avoid firing
+  // new session event more than once, this flag is used to check.
+  // It is set to false right after the new zookeeper instance is created in reconnect before the
+  // session is established. And set it to true once the new session event is fired the first time.
+  private boolean _isNewSessionEventFired;
+
+  private boolean _shutdownTriggered;
+  private ZkEventThread _eventThread;
+  // TODO PVo remove this later
+  private Thread _zookeeperEventThread;
+  private volatile boolean _closed;
+  private PathBasedZkSerializer _pathBasedZkSerializer;
+  private ZkClientMonitor _monitor;
+
+  private class IZkDataListenerEntry {
+    final IZkDataListener _dataListener;
+    final boolean _prefetchData;
+
+    public IZkDataListenerEntry(IZkDataListener dataListener, boolean prefetchData) {
+      _dataListener = dataListener;
+      _prefetchData = prefetchData;
+    }
+
+    public IZkDataListenerEntry(IZkDataListener dataListener) {
+      _dataListener = dataListener;
+      _prefetchData = false;
+    }
+
+    public IZkDataListener getDataListener() {
+      return _dataListener;
+    }
+
+    public boolean isPrefetchData() {
+      return _prefetchData;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof IZkDataListenerEntry)) {
+        return false;
+      }
+
+      IZkDataListenerEntry that = (IZkDataListenerEntry) o;
+
+      return _dataListener.equals(that._dataListener);
+    }
+
+    @Override
+    public int hashCode() {
+      return _dataListener.hashCode();
+    }
+  }
+
+  private class ZkPathStatRecord {
+    private final String _path;
+    private Stat _stat = null;
+    private boolean _checked = false;
+
+    public ZkPathStatRecord(String path) {
+      _path = path;
+    }
+
+    public boolean pathExists() {
+      return _stat != null;
+    }
+
+    public boolean pathChecked() {
+      return _checked;
+    }
+
+    /*
+     * Note this method is not thread safe.
+     */
+    public void recordPathStat(Stat stat, OptionalLong notificationTime) {
+      _checked = true;
+      _stat = stat;
+
+      if (_monitor != null && stat != null && notificationTime.isPresent()) {
+        long updateTime = Math.max(stat.getCtime(), stat.getMtime());
+        if (notificationTime.getAsLong() > updateTime) {
+          _monitor.recordDataPropagationLatency(_path, notificationTime.getAsLong() - updateTime);
+        } // else, the node was updated again after the notification. Propagation latency is
+        // unavailable.
+      }
+    }
+  }
+
+  protected ZkClient(IZkConnection zkConnection, int connectionTimeout, long operationRetryTimeout,
+      PathBasedZkSerializer zkSerializer, String monitorType, String monitorKey,
+      String monitorInstanceName, boolean monitorRootPathOnly) {
+    if (zkConnection == null) {
+      throw new NullPointerException("Zookeeper connection is null!");
+    }
+    _connection = zkConnection;
+    _pathBasedZkSerializer = zkSerializer;
+    _operationRetryTimeoutInMillis = operationRetryTimeout;
+    _isNewSessionEventFired = false;
+
+    connect(connectionTimeout, this);
+
+    // initiate monitor
+    try {
+      if (monitorKey != null && !monitorKey.isEmpty() && monitorType != null && !monitorType
+          .isEmpty()) {
+        _monitor =
+            new ZkClientMonitor(monitorType, monitorKey, monitorInstanceName, monitorRootPathOnly,
+                _eventThread);
+        _monitor.register();
+      } else {
+        LOG.info("ZkClient monitor key or type is not provided. Skip monitoring.");
+      }
+    } catch (JMException e) {
+      LOG.error("Error in creating ZkClientMonitor", e);
+    }
+  }
+
+  public List<String> subscribeChildChanges(String path, IZkChildListener listener) {
+    synchronized (_childListener) {
+      Set<IZkChildListener> listeners = _childListener.get(path);
+      if (listeners == null) {
+        listeners = new CopyOnWriteArraySet<>();
+        _childListener.put(path, listeners);
+      }
+      listeners.add(listener);
+    }
+    return watchForChilds(path);
+  }
+
+  public void unsubscribeChildChanges(String path, IZkChildListener childListener) {
+    synchronized (_childListener) {
+      final Set<IZkChildListener> listeners = _childListener.get(path);
+      if (listeners != null) {
+        listeners.remove(childListener);
+      }
+    }
+  }
+
+  /**
+   * Subscribe the path and the listener will handle data events of the path
+   * WARNING: if the path is created after deletion, users need to re-subscribe the path
+   * @param path The zookeeper path
+   * @param listener Instance of {@link IZkDataListener}
+   */
+  public void subscribeDataChanges(String path, IZkDataListener listener) {
+    Set<IZkDataListenerEntry> listenerEntries;
+    synchronized (_dataListener) {
+      listenerEntries = _dataListener.get(path);
+      if (listenerEntries == null) {
+        listenerEntries = new CopyOnWriteArraySet<>();
+        _dataListener.put(path, listenerEntries);
+      }
+
+      boolean prefetchEnabled = isPrefetchEnabled(listener);
+      IZkDataListenerEntry listenerEntry = new IZkDataListenerEntry(listener, prefetchEnabled);
+      listenerEntries.add(listenerEntry);
+      if (prefetchEnabled) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Subscribed data changes for " + path + ", listener: " + listener
+              + ", prefetch data: " + prefetchEnabled);
+        }
+      }
+    }
+    watchForData(path);
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Subscribed data changes for " + path);
+    }
+  }
+
+  private boolean isPrefetchEnabled(IZkDataListener dataListener) {
+    PreFetch preFetch = dataListener.getClass().getAnnotation(PreFetch.class);
+    if (preFetch != null) {
+      return preFetch.enabled();
+    }
+
+    Method callbackMethod = IZkDataListener.class.getMethods()[0];
+    try {
+      Method method = dataListener.getClass()
+          .getMethod(callbackMethod.getName(), callbackMethod.getParameterTypes());
+      PreFetch preFetchInMethod = method.getAnnotation(PreFetch.class);
+      if (preFetchInMethod != null) {
+        return preFetchInMethod.enabled();
+      }
+    } catch (NoSuchMethodException e) {
+      LOG.warn("No method " + callbackMethod.getName() + " defined in listener " + dataListener
+          .getClass().getCanonicalName());
+    }
+
+    return true;
+  }
+
+  public void unsubscribeDataChanges(String path, IZkDataListener dataListener) {
+    synchronized (_dataListener) {
+      final Set<IZkDataListenerEntry> listeners = _dataListener.get(path);
+      if (listeners != null) {
+        IZkDataListenerEntry listenerEntry = new IZkDataListenerEntry(dataListener);
+        listeners.remove(listenerEntry);
+      }
+      if (listeners == null || listeners.isEmpty()) {
+        _dataListener.remove(path);
+      }
+    }
+  }
+
+  public void subscribeStateChanges(final IZkStateListener listener) {
+    synchronized (_stateListener) {
+      _stateListener.add(listener);
+    }
+  }
+
+  /**
+   * Subscribes state changes for a {@link IZkStateListener} listener.
+   *
+   * @deprecated
+   * This is deprecated. It is kept for backwards compatibility. Please use
+   * {@link #subscribeStateChanges(IZkStateListener)}.
+   *
+   * @param listener {@link IZkStateListener} listener
+   */
+  @Deprecated
+  public void subscribeStateChanges(
+      final org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener listener) {
+    subscribeStateChanges(new IZkStateListenerI0ItecImpl(listener));
+  }
+
+  public void unsubscribeStateChanges(IZkStateListener stateListener) {
+    synchronized (_stateListener) {
+      _stateListener.remove(stateListener);
+    }
+  }
+
+  /**
+   * Unsubscribes state changes for a {@link IZkStateListener} listener.
+   *
+   * @deprecated
+   * This is deprecated. It is kept for backwards compatibility. Please use
+   * {@link #unsubscribeStateChanges(IZkStateListener)}.
+   *
+   * @param stateListener {@link IZkStateListener} listener
+   */
+  @Deprecated
+  public void unsubscribeStateChanges(
+      org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener stateListener) {
+    unsubscribeStateChanges(new IZkStateListenerI0ItecImpl(stateListener));
+  }
+
+  public void unsubscribeAll() {
+    synchronized (_childListener) {
+      _childListener.clear();
+    }
+    synchronized (_dataListener) {
+      _dataListener.clear();
+    }
+    synchronized (_stateListener) {
+      _stateListener.clear();
+    }
+  }
+
+  // </listeners>
+
+  /**
+   * Create a persistent node.
+   * @param path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createPersistent(String path)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    createPersistent(path, false);
+  }
+
+  /**
+   * Create a persistent node and set its ACLs.
+   * @param path
+   * @param createParents
+   *          if true all parent dirs are created as well and no {@link ZkNodeExistsException} is
+   *          thrown in case the
+   *          path already exists
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createPersistent(String path, boolean createParents)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    createPersistent(path, createParents, ZooDefs.Ids.OPEN_ACL_UNSAFE);
+  }
+
+  /**
+   * Create a persistent node and set its ACLs.
+   * @param path
+   * @param acl
+   *          List of ACL permissions to assign to the node
+   * @param createParents
+   *          if true all parent dirs are created as well and no {@link ZkNodeExistsException} is
+   *          thrown in case the
+   *          path already exists
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createPersistent(String path, boolean createParents, List<ACL> acl)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    try {
+      create(path, null, acl, CreateMode.PERSISTENT);
+    } catch (ZkNodeExistsException e) {
+      if (!createParents) {
+        throw e;
+      }
+    } catch (ZkNoNodeException e) {
+      if (!createParents) {
+        throw e;
+      }
+      String parentDir = path.substring(0, path.lastIndexOf('/'));
+      createPersistent(parentDir, createParents, acl);
+      createPersistent(path, createParents, acl);
+    }
+  }
+
+  /**
+   * Create a persistent node.
+   * @param path
+   * @param data
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createPersistent(String path, Object data)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    create(path, data, CreateMode.PERSISTENT);
+  }
+
+  /**
+   * Create a persistent node.
+   * @param path
+   * @param data
+   * @param acl
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createPersistent(String path, Object data, List<ACL> acl) {
+    create(path, data, acl, CreateMode.PERSISTENT);
+  }
+
+  /**
+   * Create a persistent, sequental node.
+   * @param path
+   * @param data
+   * @return create node's path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public String createPersistentSequential(String path, Object data)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    return create(path, data, CreateMode.PERSISTENT_SEQUENTIAL);
+  }
+
+  /**
+   * Create a persistent, sequential node and set its ACL.
+   * @param path
+   * @param acl
+   * @param data
+   * @return create node's path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public String createPersistentSequential(String path, Object data, List<ACL> acl)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    return create(path, data, acl, CreateMode.PERSISTENT_SEQUENTIAL);
+  }
+
+  /**
+   * Create an ephemeral node.
+   * @param path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createEphemeral(final String path)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    create(path, null, CreateMode.EPHEMERAL);
+  }
+
+  /**
+   * Creates an ephemeral node. This ephemeral node is created by the expected(passed-in) ZK session.
+   * If the expected session does not match the current ZK session, the node will not be created.
+   *
+   * @param path path of the node
+   * @param sessionId expected session id of the ZK connection. If the session id of current ZK
+   *                  connection does not match the expected session id, ephemeral creation will
+   *                  fail
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createEphemeral(final String path, final String sessionId)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    createEphemeral(path, null, sessionId);
+  }
+
+  /**
+   * Create an ephemeral node and set its ACL.
+   * @param path
+   * @param acl
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createEphemeral(final String path, final List<ACL> acl)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    create(path, null, acl, CreateMode.EPHEMERAL);
+  }
+
+  /**
+   * Creates an ephemeral node and set its ACL. This ephemeral node is created by the
+   * expected(passed-in) ZK session. If the expected session does not match the current ZK session,
+   * the node will not be created.
+   *
+   * @param path path of the ephemeral node
+   * @param acl a list of ACL for the ephemeral node.
+   * @param sessionId expected session id of the ZK connection. If the session id of current ZK
+   *                  connection does not match the expected session id, ephemeral creation will
+   *                  fail.
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createEphemeral(final String path, final List<ACL> acl, final String sessionId)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    create(path, null, acl, CreateMode.EPHEMERAL, sessionId);
+  }
+
+  /**
+   * Create a node.
+   * @param path
+   * @param data
+   * @param mode
+   * @return create node's path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public String create(final String path, Object data, final CreateMode mode)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    return create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, mode);
+  }
+
+  /**
+   * Create a node with ACL.
+   * @param path
+   * @param datat
+   * @param acl
+   * @param mode
+   * @return create node's path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public String create(final String path, Object datat, final List<ACL> acl, final CreateMode mode)
+      throws IllegalArgumentException, ZkException {
+    return create(path, datat, acl, mode, null);
+  }
+
+  /**
+   * Creates a node and returns the actual path of the created node.
+   *
+   * Given an expected non-null session id, if the node is successfully created, it is guaranteed to
+   * be created in the expected(passed-in) session.
+   *
+   * If the expected session is expired, which means the expected session does not match the current
+   * session of ZK connection, the node will not be created.
+   *
+   * @param path the path where you want the node to be created
+   * @param dataObject data of the node
+   * @param acl list of ACL for the node
+   * @param mode {@link CreateMode} of the node
+   * @param expectedSessionId the expected session ID of the ZK connection. It is not necessarily the
+   *                  session ID of current ZK Connection. If the expected session ID is NOT null,
+   *                  the node is guaranteed to be created in the expected session, or creation is
+   *                  failed if the expected session id doesn't match current connected zk session.
+   *                  If the session id is null, it means the create operation is NOT session aware.
+   * @return path of the node created
+   * @throws IllegalArgumentException if called from anything else except the ZooKeeper event thread
+   * @throws ZkException if any zookeeper exception occurs
+   */
+  private String create(final String path, final Object dataObject, final List<ACL> acl,
+      final CreateMode mode, final String expectedSessionId)
+      throws IllegalArgumentException, ZkException {
+    if (path == null) {
+      throw new NullPointerException("Path must not be null.");
+    }
+    if (acl == null || acl.size() == 0) {
+      throw new NullPointerException("Missing value for ACL");
+    }
+    long startT = System.currentTimeMillis();
+    try {
+      final byte[] dataBytes = dataObject == null ? null : serialize(dataObject, path);
+      checkDataSizeLimit(dataBytes);
+
+      final String actualPath = retryUntilConnected(() -> {
+        ZooKeeper zooKeeper = ((ZkConnection) getConnection()).getZookeeper();
+
+        /*
+         * 1. If operation is session aware, we have to check whether or not the
+         * passed-in(expected) session id matches actual session's id.
+         * If not, ephemeral node creation is failed. This validation is
+         * critical to guarantee the ephemeral node created by the expected ZK session.
+         *
+         * 2. Otherwise, the operation is NOT session aware.
+         * In this case, we will use the actual zookeeper session to create the node.
+         */
+        if (isSessionAwareOperation(expectedSessionId, mode)) {
+          acquireEventLock();
+          try {
+            final String actualSessionId = toHexSessionId(zooKeeper.getSessionId());
+            if (!actualSessionId.equals(expectedSessionId)) {
+              throw new ZkSessionMismatchedException(
+                  "Failed to create ephemeral node! There is a session id mismatch. Expected: "
+                      + expectedSessionId + ". Actual: " + actualSessionId);
+            }
+
+            /*
+             * Cache the zookeeper reference and make sure later zooKeeper.create() is being run
+             * under this zookeeper connection. This is to avoid locking zooKeeper.create() which
+             * may cause potential performance issue.
+             */
+            zooKeeper = ((ZkConnection) getConnection()).getZookeeper();
+          } finally {
+            getEventLock().unlock();
+          }
+        }
+
+        return zooKeeper.create(path, dataBytes, acl, mode);
+      });
+
+      record(path, dataBytes, startT, ZkClientMonitor.AccessType.WRITE);
+      return actualPath;
+    } catch (Exception e) {
+      recordFailure(path, ZkClientMonitor.AccessType.WRITE);
+      throw e;
+    } finally {
+      long endT = System.currentTimeMillis();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("create, path: " + path + ", time: " + (endT - startT) + " ms");
+      }
+    }
+  }
+
+  /**
+   * Create an ephemeral node.
+   * @param path
+   * @param data
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createEphemeral(final String path, final Object data)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    create(path, data, CreateMode.EPHEMERAL);
+  }
+
+  /**
+   * Creates an ephemeral node. Given an expected non-null session id, if the ephemeral
+   * node is successfully created, it is guaranteed to be in the expected(passed-in) session.
+   *
+   * If the expected session is expired, which means the expected session does not match the session
+   * of current ZK connection, the ephemeral node will not be created.
+   * If connection is timed out or interrupted, exception is thrown.
+   *
+   * @param path path of the ephemeral node being created
+   * @param data data of the ephemeral node being created
+   * @param sessionId the expected session ID of the ZK connection. It is not necessarily the
+   *                  session ID of current ZK Connection. If the expected session ID is NOT null,
+   *                  the node is guaranteed to be created in the expected session, or creation is
+   *                  failed if the expected session id doesn't match current connected zk session.
+   *                  If the session id is null, it means the operation is NOT session aware
+   *                  and the node will be created by current ZK session.
+   * @throws ZkInterruptedException if operation is interrupted, or a required reconnection gets
+   *         interrupted
+   * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread
+   * @throws ZkException if any ZooKeeper exception occurs
+   * @throws RuntimeException if any other exception occurs
+   */
+  public void createEphemeral(final String path, final Object data, final String sessionId)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, sessionId);
+  }
+
+  /**
+   * Create an ephemeral node.
+   * @param path
+   * @param data
+   * @param acl
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createEphemeral(final String path, final Object data, final List<ACL> acl)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    create(path, data, acl, CreateMode.EPHEMERAL);
+  }
+
+  /**
+   * Creates an ephemeral node in an expected ZK session. Given an expected non-null session id,
+   * if the ephemeral node is successfully created, it is guaranteed to be in the expected session.
+   * If the expected session is expired, which means the expected session does not match the session
+   * of current ZK connection, the ephemeral node will not be created.
+   * If connection is timed out or interrupted, exception is thrown.
+   *
+   * @param path path of the ephemeral node being created
+   * @param data data of the ephemeral node being created
+   * @param acl list of ACL for the ephemeral node
+   * @param sessionId the expected session ID of the ZK connection. It is not necessarily the
+   *                  session ID of current ZK Connection. If the expected session ID is NOT null,
+   *                  the node is guaranteed to be created in the expected session, or creation is
+   *                  failed if the expected session id doesn't match current connected zk session.
+   *                  If the session id is null, it means the create operation is NOT session aware
+   *                  and the node will be created by current ZK session.
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public void createEphemeral(final String path, final Object data, final List<ACL> acl,
+      final String sessionId)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    create(path, data, acl, CreateMode.EPHEMERAL, sessionId);
+  }
+
+  /**
+   * Create an ephemeral, sequential node.
+   * @param path
+   * @param data
+   * @return created path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public String createEphemeralSequential(final String path, final Object data)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    return create(path, data, CreateMode.EPHEMERAL_SEQUENTIAL);
+  }
+
+  /**
+   * Creates an ephemeral, sequential node with ACL in an expected ZK session.
+   * Given an expected non-null session id, if the ephemeral node is successfully created,
+   * it is guaranteed to be in the expected session.
+   * If the expected session is expired, which means the expected session does not match the session
+   * of current ZK connection, the ephemeral node will not be created.
+   * If connection is timed out or interrupted, exception is thrown.
+   *
+   * @param path path of the node
+   * @param data data of the node
+   * @param acl list of ACL for the node
+   * @return created path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public String createEphemeralSequential(final String path, final Object data, final List<ACL> acl,
+      final String sessionId)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    return create(path, data, acl, CreateMode.EPHEMERAL_SEQUENTIAL, sessionId);
+  }
+
+  /**
+   * Creates an ephemeral, sequential node. Given an expected non-null session id,
+   * if the ephemeral node is successfully created, it is guaranteed to be in the expected session.
+   * If the expected session is expired, which means the expected session does not match the session
+   * of current ZK connection, the ephemeral node will not be created.
+   * If connection is timed out or interrupted, exception is thrown.
+   *
+   * @param path path of the node
+   * @param data data of the node
+   * @param sessionId the expected session ID of the ZK connection. It is not necessarily the
+   *                  session ID of current ZK Connection. If the expected session ID is NOT null,
+   *                  the node is guaranteed to be created in the expected session, or creation is
+   *                  failed if the expected session id doesn't match current connected zk session.
+   *                  If the session id is null, it means the create operation is NOT session aware
+   *                  and the node will be created by current ZK session.
+   * @return created path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public String createEphemeralSequential(final String path, final Object data,
+      final String sessionId)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    return create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL,
+        sessionId);
+  }
+
+  /**
+   * Create an ephemeral, sequential node with ACL.
+   * @param path
+   * @param data
+   * @param acl
+   * @return created path
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs
+   */
+  public String createEphemeralSequential(final String path, final Object data, final List<ACL> acl)
+      throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException {
+    return create(path, data, acl, CreateMode.EPHEMERAL_SEQUENTIAL);
+  }
+
+  @Override
+  public void process(WatchedEvent event) {
+    long notificationTime = System.currentTimeMillis();
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Received event: " + event);
+    }
+    _zookeeperEventThread = Thread.currentThread();
+
+    boolean stateChanged = event.getPath() == null;
+    boolean znodeChanged = event.getPath() != null;
+    boolean dataChanged =
+        event.getType() == EventType.NodeDataChanged || event.getType() == EventType.NodeDeleted
+            || event.getType() == EventType.NodeCreated
+            || event.getType() == EventType.NodeChildrenChanged;
+    if (event.getType() == EventType.NodeDeleted) {
+      LOG.debug("Path {} is deleted", event.getPath());
+    }
+
+    getEventLock().lock();
+    try {
+      // We might have to install child change event listener if a new node was created
+      if (getShutdownTrigger()) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("ignoring event '{" + event.getType() + " | " + event.getPath()
+              + "}' since shutdown triggered");
+        }
+        return;
+      }
+      if (stateChanged) {
+        processStateChanged(event);
+      }
+      if (dataChanged) {
+        processDataOrChildChange(event, notificationTime);
+      }
+    } finally {
+      if (stateChanged) {
+        getEventLock().getStateChangedCondition().signalAll();
+
+        // If the session expired we have to signal all conditions, because watches might have been
+        // removed and
+        // there is no guarantee that those
+        // conditions will be signaled at all after an Expired event
+        // TODO PVo write a test for this
+        if (event.getState() == KeeperState.Expired) {
+          getEventLock().getZNodeEventCondition().signalAll();
+          getEventLock().getDataChangedCondition().signalAll();
+        }
+      }
+      if (znodeChanged) {
+        getEventLock().getZNodeEventCondition().signalAll();
+      }
+      if (dataChanged) {
+        getEventLock().getDataChangedCondition().signalAll();
+      }
+      getEventLock().unlock();
+
+      // update state change counter.
+      recordStateChange(stateChanged, dataChanged);
+
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Leaving process event");
+      }
+    }
+  }
+
+  private void fireAllEvents() {
+    //TODO: During handling new session, if the path is deleted, watcher leakage could still happen
+    for (Entry<String, Set<IZkChildListener>> entry : _childListener.entrySet()) {
+      fireChildChangedEvents(entry.getKey(), entry.getValue(), true);
+    }
+    for (Entry<String, Set<IZkDataListenerEntry>> entry : _dataListener.entrySet()) {
+      fireDataChangedEvents(entry.getKey(), entry.getValue(), OptionalLong.empty(), true);
+    }
+  }
+
+  public List<String> getChildren(String path) {
+    return getChildren(path, hasListeners(path));
+  }
+
+  protected List<String> getChildren(final String path, final boolean watch) {
+    long startT = System.currentTimeMillis();
+    try {
+      List<String> children = retryUntilConnected(new Callable<List<String>>() {
+        @Override
+        public List<String> call() throws Exception {
+          return getConnection().getChildren(path, watch);
+        }
+      });
+      record(path, null, startT, ZkClientMonitor.AccessType.READ);
+      return children;
+    } catch (ZkNoNodeException e) {
+      record(path, null, startT, ZkClientMonitor.AccessType.READ);
+      throw e;
+    } catch (Exception e) {
+      recordFailure(path, ZkClientMonitor.AccessType.READ);
+      throw e;
+    } finally {
+      long endT = System.currentTimeMillis();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("getChildren, path: " + path + ", time: " + (endT - startT) + " ms");
+      }
+    }
+  }
+
+  /**
+   * Counts number of children for the given path.
+   * @param path
+   * @return number of children or 0 if path does not exist.
+   */
+  public int countChildren(String path) {
+    try {
+      return getChildren(path).size();
+    } catch (ZkNoNodeException e) {
+      return 0;
+    }
+  }
+
+  public boolean exists(final String path) {
+    return exists(path, hasListeners(path));
+  }
+
+  protected boolean exists(final String path, final boolean watch) {
+    long startT = System.currentTimeMillis();
+    try {
+      boolean exists = retryUntilConnected(new Callable<Boolean>() {
+        @Override
+        public Boolean call() throws Exception {
+          return getConnection().exists(path, watch);
+        }
+      });
+      record(path, null, startT, ZkClientMonitor.AccessType.READ);
+      return exists;
+    } catch (ZkNoNodeException e) {
+      record(path, null, startT, ZkClientMonitor.AccessType.READ);
+      throw e;
+    } catch (Exception e) {
+      recordFailure(path, ZkClientMonitor.AccessType.READ);
+      throw e;
+    } finally {
+      long endT = System.currentTimeMillis();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("exists, path: " + path + ", time: " + (endT - startT) + " ms");
+      }
+    }
+  }
+
+  public Stat getStat(final String path) {
+    return getStat(path, false);
+  }
+
+  private Stat getStat(final String path, final boolean watch) {
+    long startT = System.currentTimeMillis();
+    try {
+      Stat stat = retryUntilConnected(
+          () -> ((ZkConnection) getConnection()).getZookeeper().exists(path, watch));
+      record(path, null, startT, ZkClientMonitor.AccessType.READ);
+      return stat;
+    } catch (ZkNoNodeException e) {
+      record(path, null, startT, ZkClientMonitor.AccessType.READ);
+      throw e;
+    } catch (Exception e) {
+      recordFailure(path, ZkClientMonitor.AccessType.READ);
+      throw e;
+    } finally {
+      long endT = System.currentTimeMillis();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("exists, path: " + path + ", time: " + (endT - startT) + " ms");
+      }
+    }
+  }
+
+  protected void processStateChanged(WatchedEvent event) {
+    LOG.info("zookeeper state changed (" + event.getState() + ")");
+    setCurrentState(event.getState());
+    if (getShutdownTrigger()) {
+      return;
+    }
+
+    fireStateChangedEvent(event.getState());
+
+    if (!isManagingZkConnection()) {
+      return;
+    }
+
+    if (event.getState() == KeeperState.SyncConnected) {
+      if (!_isNewSessionEventFired && !"0".equals(getHexSessionId())) {
+        /*
+         * Before the new zookeeper instance is connected to the zookeeper service and its session
+         * is established, its session id is 0.
+         * New session event is not fired until the new zookeeper session receives the first
+         * SyncConnected state(the zookeeper session is established).
+         * Now the session id is available and non-zero, and we can fire new session events.
+         */
+        fireNewSessionEvents();
+        /*
+         * Set it true to avoid firing events again for the same session next time
+         * when SyncConnected events are received.
+         */
+        _isNewSessionEventFired = true;
+
+        /*
+         * With this first SyncConnected state, we just get connected to zookeeper service after
+         * reconnecting when the session expired. Because previous session expired, we also have to
+         * notify all listeners that something might have changed.
+         */
+        fireAllEvents();
+      }
+    } else if (event.getState() == KeeperState.Expired) {
+      reconnectOnExpiring();
+    }
+  }
+
+  private void reconnectOnExpiring() {
+    int retryCount = 0;
+    ExponentialBackoffStrategy retryStrategy =
+        new ExponentialBackoffStrategy(MAX_RECONNECT_INTERVAL_MS, true);
+
+    Exception reconnectException = new ZkException("Shutdown triggered.");
+    while (!isClosed()) {
+      try {
+        reconnect();
+        return;
+      } catch (ZkInterruptedException interrupt) {
+        reconnectException = interrupt;
+        break;
+      } catch (Exception e) {
+        reconnectException = e;
+        long waitInterval = retryStrategy.getNextWaitInterval(retryCount++);
+        LOG.warn("ZkClient reconnect on expiring failed. Will retry after {} ms", waitInterval, e);
+        try {
+          Thread.sleep(waitInterval);
+        } catch (InterruptedException ex) {
+          reconnectException = ex;
+          break;
+        }
+      }
+    }
+
+    LOG.info("Unable to re-establish connection. Notifying consumer of the following exception: ",
+        reconnectException);
+    fireSessionEstablishmentError(reconnectException);
+  }
+
+  private void reconnect() {
+    getEventLock().lock();
+    try {
+      ZkConnection connection = ((ZkConnection) getConnection());
+      connection.reconnect(this);
+      _isNewSessionEventFired = false;
+    } catch (InterruptedException e) {
+      throw new ZkInterruptedException(e);
+    } finally {
+      getEventLock().unlock();
+    }
+  }
+
+  private void fireNewSessionEvents() {
+    final String sessionId = getHexSessionId();
+    for (final IZkStateListener stateListener : _stateListener) {
+      _eventThread.send(new ZkEventThread.ZkEvent("New session event sent to " + stateListener, sessionId) {
+
+        @Override
+        public void run() throws Exception {
+          stateListener.handleNewSession(sessionId);
+        }
+      });
+    }
+  }
+
+  protected void fireStateChangedEvent(final KeeperState state) {
+    final String sessionId = getHexSessionId();
+    for (final IZkStateListener stateListener : _stateListener) {
+      final String description = "State changed to " + state + " sent to " + stateListener;
+      _eventThread.send(new ZkEventThread.ZkEvent(description, sessionId) {
+
+        @Override
+        public void run() throws Exception {
+          stateListener.handleStateChanged(state);
+        }
+      });
+    }
+  }
+
+  private void fireSessionEstablishmentError(final Throwable error) {
+    for (final IZkStateListener stateListener : _stateListener) {
+      _eventThread
+          .send(new ZkEventThread.ZkEvent("Session establishment error(" + error + ") sent to " + stateListener) {
+
+            @Override
+            public void run() throws Exception {
+              stateListener.handleSessionEstablishmentError(error);
+            }
+          });
+    }
+  }
+
+  private boolean hasListeners(String path) {
+    Set<IZkDataListenerEntry> dataListeners = _dataListener.get(path);
+    if (dataListeners != null && dataListeners.size() > 0) {
+      return true;
+    }
+    Set<IZkChildListener> childListeners = _childListener.get(path);
+    if (childListeners != null && childListeners.size() > 0) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Delete the path as well as all its children.
+   * This method is deprecated, please use {@link #deleteRecursively(String)}} instead
+   * @param path ZK path
+   * @return true if successfully deleted all children, and the given path, else false
+   */
+  @Deprecated
+  public boolean deleteRecursive(String path) {
+    try {
+      deleteRecursively(path);
+      return true;
+    } catch (ZkClientException e) {
+      LOG.error("Failed to recursively delete path " + path, e);
+      return false;
+    }
+  }
+
+  /**
+   * Delete the path as well as all its children.
+   * @param path
+   * @throws ZkClientException
+   */
+  public void deleteRecursively(String path) throws ZkClientException {
+    List<String> children;
+    try {
+      children = getChildren(path, false);
+    } catch (ZkNoNodeException e) {
+      // if the node to be deleted does not exist, treat it as success.
+      return;
+    }
+
+    for (String subPath : children) {
+      deleteRecursively(path + "/" + subPath);
+    }
+
+    // delete() function call will return true if successful, false if the path does not
+    // exist (in this context, it should be treated as successful), and throw exception
+    // if there is any other failure case.
+    try {
+      delete(path);
+    } catch (Exception e) {
+      LOG.error("Failed to delete " + path, e);
+      throw new ZkClientException("Failed to delete " + path, e);
+    }
+  }
+
+  private void processDataOrChildChange(WatchedEvent event, long notificationTime) {
+    final String path = event.getPath();
+    final boolean pathExists = event.getType() != EventType.NodeDeleted;
+
+    if (event.getType() == EventType.NodeChildrenChanged || event.getType() == EventType.NodeCreated
+        || event.getType() == EventType.NodeDeleted) {
+      Set<IZkChildListener> childListeners = _childListener.get(path);
+      if (childListeners != null && !childListeners.isEmpty()) {
+        // TODO recording child changed event propagation latency as well. Note this change will
+        // introduce additional ZK access.
+        fireChildChangedEvents(path, childListeners, pathExists);
+      }
+    }
+
+    if (event.getType() == EventType.NodeDataChanged || event.getType() == EventType.NodeDeleted
+        || event.getType() == EventType.NodeCreated) {
+      Set<IZkDataListenerEntry> listeners = _dataListener.get(path);
+      if (listeners != null && !listeners.isEmpty()) {
+        fireDataChangedEvents(event.getPath(), listeners, OptionalLong.of(notificationTime),
+            pathExists);
+      }
+    }
+  }
+
+  private void fireDataChangedEvents(final String path, Set<IZkDataListenerEntry> listeners,
+      final OptionalLong notificationTime, boolean pathExists) {
+    try {
+      final ZkPathStatRecord pathStatRecord = new ZkPathStatRecord(path);
+      // Trigger listener callbacks
+      for (final IZkDataListenerEntry listener : listeners) {
+        _eventThread.send(new ZkEventThread.ZkEvent(
+            "Data of " + path + " changed sent to " + listener.getDataListener()
+                + " prefetch data: " + listener.isPrefetchData()) {
+          @Override
+          public void run() throws Exception {
+            if (!pathStatRecord.pathChecked()) {
+              // getStat will re-install watcher only when the path exists
+              pathStatRecord.recordPathStat(getStat(path, pathExists), notificationTime);
+            }
+            if (!pathStatRecord.pathExists()) {
+              listener.getDataListener().handleDataDeleted(path);
+            } else {
+              Object data = null;
+              if (listener.isPrefetchData()) {
+                if (LOG.isDebugEnabled()) {
+                  LOG.debug("Prefetch data for path: {}", path);
+                }
+                try {
+                  // TODO: the data is redundantly read multiple times when multiple listeners exist
+                  data = readData(path, null, true);
+                } catch (ZkNoNodeException e) {
+                  LOG.warn("Prefetch data for path: {} failed.", path, e);
+                  listener.getDataListener().handleDataDeleted(path);
+                  return;
+                }
+              }
+              listener.getDataListener().handleDataChange(path, data);
+            }
+          }
+        });
+      }
+    } catch (Exception e) {
+      LOG.error("Failed to fire data changed event for path: {}", path, e);
+    }
+  }
+
+  private void fireChildChangedEvents(final String path, Set<IZkChildListener> childListeners, boolean pathExists) {
+    try {
+      final ZkPathStatRecord pathStatRecord = new ZkPathStatRecord(path);
+      for (final IZkChildListener listener : childListeners) {
+        _eventThread.send(new ZkEventThread.ZkEvent("Children of " + path + " changed sent to " + listener) {
+          @Override
+          public void run() throws Exception {
+            if (!pathStatRecord.pathChecked()) {
+              pathStatRecord.recordPathStat(getStat(path, hasListeners(path) && pathExists),
+                  OptionalLong.empty());
+            }
+            List<String> children = null;
+            if (pathStatRecord.pathExists()) {
+              try {
+                children = getChildren(path);
+              } catch (ZkNoNodeException e) {
+                LOG.warn("Get children under path: {} failed.", path, e);
+                // Continue trigger the change handler
+              }
+            }
+            listener.handleChildChange(path, children);
+          }
+        });
+      }
+    } catch (Exception e) {
+      LOG.error("Failed to fire child changed event. Unable to getChildren.", e);
+    }
+  }
+
+  public boolean waitUntilExists(String path, TimeUnit timeUnit, long time)
+      throws ZkInterruptedException {
+    Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time));
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Waiting until znode '" + path + "' becomes available.");
+    }
+    if (exists(path)) {
+      return true;
+    }
+    acquireEventLock();
+    try {
+      while (!exists(path, true)) {
+        boolean gotSignal = getEventLock().getZNodeEventCondition().awaitUntil(timeout);
+        if (!gotSignal) {
+          return false;
+        }
+      }
+      return true;
+    } catch (InterruptedException e) {
+      throw new ZkInterruptedException(e);
+    } finally {
+      getEventLock().unlock();
+    }
+  }
+
+  public IZkConnection getConnection() {
+    return _connection;
+  }
+
+  public long waitForEstablishedSession(long timeout, TimeUnit timeUnit) {
+    validateCurrentThread();
+
+    acquireEventLock();
+    try {
+      if (!waitForKeeperState(KeeperState.SyncConnected, timeout, timeUnit)) {
+        throw new ZkTimeoutException("Waiting to be connected to ZK server has timed out.");
+      }
+      // Reading session ID before unlocking event lock is critical to guarantee the established
+      // session's ID won't change.
+      return getSessionId();
+    } finally {
+      getEventLock().unlock();
+    }
+  }
+
+  public boolean waitUntilConnected(long time, TimeUnit timeUnit) throws ZkInterruptedException {
+    return waitForKeeperState(KeeperState.SyncConnected, time, timeUnit);
+  }
+
+  public boolean waitForKeeperState(KeeperState keeperState, long time, TimeUnit timeUnit)
+      throws ZkInterruptedException {
+    validateCurrentThread();
+    Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time));
+
+    LOG.debug("Waiting for keeper state " + keeperState);
+    acquireEventLock();
+    try {
+      boolean stillWaiting = true;
+      while (_currentState != keeperState) {
+        if (!stillWaiting) {
+          return false;
+        }
+        stillWaiting = getEventLock().getStateChangedCondition().awaitUntil(timeout);
+      }
+      LOG.debug("State is " + (_currentState == null ? "CLOSED" : _currentState));
+      return true;
+    } catch (InterruptedException e) {
+      throw new ZkInterruptedException(e);
+    } finally {
+      getEventLock().unlock();
+    }
+  }
+
+  private void acquireEventLock() {
+    try {
+      getEventLock().lockInterruptibly();
+    } catch (InterruptedException e) {
+      throw new ZkInterruptedException(e);
+    }
+  }
+
+  /**
+   * @param <T>
+   * @param callable
+   * @return result of Callable
+   * @throws ZkInterruptedException
+   *           if operation was interrupted, or a required reconnection got interrupted
+   * @throws IllegalArgumentException
+   *           if called from anything except the ZooKeeper event thread
+   * @throws ZkException
+   *           if any ZooKeeper exception occurred
+   * @throws RuntimeException
+   *           if any other exception occurs from invoking the Callable
+   */
+  public <T> T retryUntilConnected(final Callable<T> callable)
+      throws IllegalArgumentException, ZkException {
+    if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {
+      throw new IllegalArgumentException("Must not be done in the zookeeper event thread.");
+    }
+    final long operationStartTime = System.currentTimeMillis();
+    if (_monitor != null) {
+      _monitor.increaseOutstandingRequestGauge();
+    }
+    try {
+      while (true) {
+        // Because ConnectionLossException and SessionExpiredException are caught but not thrown,
+        // we don't know what causes retry. This is used to record which one of the two exceptions
+        // causes retry in ZkTimeoutException.
+        // This also helps the test testConnectionLossWhileCreateEphemeral.
+        KeeperException.Code retryCauseCode;
+
+        if (isClosed()) {
+          throw new IllegalStateException("ZkClient already closed!");
+        }
+        try {
+          final ZkConnection zkConnection = (ZkConnection) getConnection();
+          // Validate that the connection is not null before trigger callback
+          if (zkConnection == null || zkConnection.getZookeeper() == null) {
+            throw new IllegalStateException(
+                "ZkConnection is in invalid state! Please close this ZkClient and create new client.");
+          }
+          return callable.call();
+        } catch (ConnectionLossException e) {
+          retryCauseCode = e.code();
+          // we give the event thread some time to update the status to 'Disconnected'
+          Thread.yield();
+          waitForRetry();
+        } catch (SessionExpiredException e) {
+          retryCauseCode = e.code();
+          // we give the event thread some time to update the status to 'Expired'
+          Thread.yield();
+          waitForRetry();
+        } catch (ZkSessionMismatchedException e) {
+          throw e;
+        } catch (KeeperException e) {
+          throw ZkException.create(e);
+        } catch (InterruptedException e) {
+          throw new ZkInterruptedException(e);
+        } catch (Exception e) {
+          throw ExceptionUtil.convertToRuntimeException(e);
+        }
+        // before attempting a retry, check whether retry timeout has elapsed
+        if (System.currentTimeMillis() - operationStartTime > _operationRetryTimeoutInMillis) {
+          throw new ZkTimeoutException("Operation cannot be retried because of retry timeout ("
+              + _operationRetryTimeoutInMillis + " milli seconds). Retry was caused by "
+              + retryCauseCode);
+        }
+      }
+    } finally {
+      if (_monitor != null) {
+        _monitor.decreaseOutstandingRequestGauge();
+      }
+    }
+  }
+
+  private void waitForRetry() {
+    waitUntilConnected(_operationRetryTimeoutInMillis, TimeUnit.MILLISECONDS);
+  }
+
+  public void setCurrentState(KeeperState currentState) {
+    getEventLock().lock();
+    try {
+      _currentState = currentState;
+    } finally {
+      getEventLock().unlock();
+    }
+  }
+
+  /**
+   * Returns a mutex all zookeeper events are synchronized aginst. So in case you need to do
+   * something without getting
+   * any zookeeper event interruption synchronize against this mutex. Also all threads waiting on
+   * this mutex object
+   * will be notified on an event.
+   * @return the mutex.
+   */
+  public ZkLock getEventLock() {
+    return _zkEventLock;
+  }
+
+  /**
+   * Delete the given path. Path should not have any children or the deletion will fail.
+   * This function will throw exception if we fail to delete an existing path
+   * @param path
+   * @return true if path is successfully deleted, false if path does not exist
+   */
+  public boolean delete(final String path) {
+    long startT = System.currentTimeMillis();
+    boolean success;
+    try {
+      try {
+        retryUntilConnected(new Callable<Object>() {
+
+          @Override
+          public Object call() throws Exception {
+            getConnection().delete(path);
+            return null;
+          }
+        });
+        success = true;
+      } catch (ZkNoNodeException e) {
+        success = false;
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Failed to delete path " + path + ", znode does not exist!");
+        }
+      }
+      record(path, null, startT, ZkClientMonitor.AccessType.WRITE);
+    } catch (Exception e) {
+      recordFailure(path, ZkClientMonitor.AccessType.WRITE);
+      LOG.warn("Failed to delete path " + path + "! " + e);
+      throw e;
+    } finally {
+      long endT = System.currentTimeMillis();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("delete, path: " + path + ", time: " + (endT - startT) + " ms");
+      }
+    }
+    return success;
+  }
+
+  public void setZkSerializer(ZkSerializer zkSerializer) {
+    _pathBasedZkSerializer = new BasicZkSerializer(zkSerializer);
+  }
+
+  public void setZkSerializer(PathBasedZkSerializer zkSerializer) {
+    _pathBasedZkSerializer = zkSerializer;
+  }
+
+  public PathBasedZkSerializer getZkSerializer() {
+    return _pathBasedZkSerializer;
+  }
+
+  public byte[] serialize(Object data, String path) {
+    return _pathBasedZkSerializer.serialize(data, path);
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T extends Object> T deserialize(byte[] data, String path) {
+    if (data == null) {
+      return null;
+    }
+    return (T) _pathBasedZkSerializer.deserialize(data, path);
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T extends Object> T readData(String path) {
+    return (T) readData(path, false);
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T extends Object> T readData(String path, boolean returnNullIfPathNotExists) {
+    T data = null;
+    try {
+      data = (T) readData(path, null);
+    } catch (ZkNoNodeException e) {
+      if (!returnNullIfPathNotExists) {
+        throw e;
+      }
+    }
+    return data;
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T extends Object> T readData(String path, Stat stat) {
+    return (T) readData(path, stat, hasListeners(path));
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T extends Object> T readData(final String path, final Stat stat, final boolean watch) {
+    long startT = System.currentTimeMillis();
+    byte[] data = null;
+    try {
+      data = retryUntilConnected(new Callable<byte[]>() {
+
+        @Override
+        public byte[] call() throws Exception {
+          return getConnection().readData(path, stat, watch);
+        }
+      });
+      record(path, data, startT, ZkClientMonitor.AccessType.READ);
+      return (T) deserialize(data, path);
+    } catch (ZkNoNodeException e) {
+      record(path, data, startT, ZkClientMonitor.AccessType.READ);
+      throw e;
+    } catch (Exception e) {
+      recordFailure(path, ZkClientMonitor.AccessType.READ);
+      throw e;
+    } finally {
+      long endT = System.currentTimeMillis();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("getData, path: " + path + ", time: " + (endT - startT) + " ms");
+      }
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T extends Object> T readDataAndStat(String path, Stat stat,
+      boolean returnNullIfPathNotExists) {
+    T data = null;
+    try {
+      data = readData(path, stat);
+    } catch (ZkNoNodeException e) {
+      if (!returnNullIfPathNotExists) {
+        throw e;
+      }
+    }
+    return data;
+  }
+
+  public void writeData(String path, Object object) {
+    writeData(path, object, -1);
+  }
+
+  /**
+   * Updates data of an existing znode. The current content of the znode is passed to the
+   * {@link DataUpdater} that is
+   * passed into this method, which returns the new content. The new content is only written back to
+   * ZooKeeper if
+   * nobody has modified the given znode in between. If a concurrent change has been detected the
+   * new data of the
+   * znode is passed to the updater once again until the new contents can be successfully written
+   * back to ZooKeeper.
+   * @param <T>
+   * @param path
+   *          The path of the znode.
+   * @param updater
+   *          Updater that creates the new contents.
+   */
+  @SuppressWarnings("unchecked")
+  public <T extends Object> void updateDataSerialized(String path, DataUpdater<T> updater) {
+    Stat stat = new Stat();
+    boolean retry;
+    do {
+      retry = false;
+      try {
+        T oldData = (T) readData(path, stat);
+        T newData = updater.update(oldData);
+        writeData(path, newData, stat.getVersion());
+      } catch (ZkBadVersionException e) {
+        retry = true;
+      }
+    } while (retry);
+  }
+
+  public void writeData(final String path, Object datat, final int expectedVersion) {
+    writeDataReturnStat(path, datat, expectedVersion);
+  }
+
+  public Stat writeDataReturnStat(final String path, Object datat, final int expectedVersion) {
+    long startT = System.currentTimeMillis();
+    try {
+      final byte[] data = serialize(datat, path);
+      checkDataSizeLimit(data);
+      final Stat stat = (Stat) retryUntilConnected(new Callable<Object>() {
+        @Override
+        public Object call() throws Exception {
+          return getConnection().writeDataReturnStat(path, data, expectedVersion);
+        }
+      });
+      record(path, data, startT, ZkClientMonitor.AccessType.WRITE);
+      return stat;
+    } catch (Exception e) {
+      recordFailure(path, ZkClientMonitor.AccessType.WRITE);
+      throw e;
+    } finally {
+      long endT = System.currentTimeMillis();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("setData, path: " + path + ", time: " + (endT - startT) + " ms");
+      }
+    }
+  }
+
+  public Stat writeDataGetStat(final String path, Object datat, final int expectedVersion) {
+    return writeDataReturnStat(path, datat, expectedVersion);
+  }
+
+  public void asyncCreate(final String path, Object datat, final CreateMode mode,
+      final ZkAsyncCallbacks.CreateCallbackHandler cb) {
+    final long startT = System.currentTimeMillis();
+    final byte[] data = (datat == null ? null : serialize(datat, path));
+    retryUntilConnected(new Callable<Object>() {
+      @Override
+      public Object call() throws Exception {
+        ((ZkConnection) getConnection()).getZookeeper()
+            .create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE,
+                // Arrays.asList(DEFAULT_ACL),
+                mode, cb, new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT,
+                    data == null ? 0 : data.length, false));
+        return null;
+      }
+    });
+  }
+
+  // Async Data Accessors
+  public void asyncSetData(final String path, Object datat, final int version,
+      final ZkAsyncCallbacks.SetDataCallbackHandler cb) {
+    final long startT = System.currentTimeMillis();
+    final byte[] data = serialize(datat, path);
+    retryUntilConnected(new Callable<Object>() {
+      @Override
+      public Object call() throws Exception {
+        ((ZkConnection) getConnection()).getZookeeper().setData(path, data, version, cb,
+            new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT,
+                data == null ? 0 : data.length, false));
+        return null;
+      }
+    });
+  }
+
+  public void asyncGetData(final String path, final ZkAsyncCallbacks.GetDataCallbackHandler cb) {
+    final long startT = System.currentTimeMillis();
+    retryUntilConnected(new Callable<Object>() {
+      @Override
+      public Object call() throws Exception {
+        ((ZkConnection) getConnection()).getZookeeper().getData(path, null, cb,
+            new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT, 0, true));
+        return null;
+      }
+    });
+  }
+
+  public void asyncExists(final String path, final ZkAsyncCallbacks.ExistsCallbackHandler cb) {
+    final long startT = System.currentTimeMillis();
+    retryUntilConnected(new Callable<Object>() {
+      @Override
+      public Object call() throws Exception {
+        ((ZkConnection) getConnection()).getZookeeper().exists(path, null, cb,
+            new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT, 0, true));
+        return null;
+      }
+    });
+  }
+
+  public void asyncDelete(final String path, final ZkAsyncCallbacks.DeleteCallbackHandler cb) {
+    final long startT = System.currentTimeMillis();
+    retryUntilConnected(new Callable<Object>() {
+      @Override
+      public Object call() throws Exception {
+        ((ZkConnection) getConnection()).getZookeeper().delete(path, -1, cb,
+            new ZkAsyncCallbacks.ZkAsyncCallContext(_monitor, startT, 0, false));
+        return null;
+      }
+    });
+  }
+
+  private void checkDataSizeLimit(byte[] data) {
+    if (data != null && data.length > ZNRecord.SIZE_LIMIT) {
+      LOG.error(
+          "Data size larger than 1M, will not write to zk. Data (first 1k): " + new String(data)
+              .substring(0, 1024));
+      throw new ZkClientException("Data size larger than 1M");
+    }
+  }
+
+  public void watchForData(final String path) {
+    retryUntilConnected(new Callable<Object>() {
+      @Override
+      public Object call() throws Exception {
+        getConnection().exists(path, true);
+        return null;
+      }
+    });
+  }
+
+  /**
+   * Installs a child watch for the given path.
+   * @param path
+   * @return the current children of the path or null if the zk node with the given path doesn't
+   *         exist.
+   */
+  public List<String> watchForChilds(final String path) {
+    if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {
+      throw new IllegalArgumentException("Must not be done in the zookeeper event thread.");
+    }
+    return retryUntilConnected(new Callable<List<String>>() {
+      @Override
+      public List<String> call() throws Exception {
+        exists(path, true);
+        try {
+          return getChildren(path, true);
+        } catch (ZkNoNodeException e) {
+          // ignore, the "exists" watch will listen for the parent node to appear
+        }
+        return null;
+      }
+    });
+  }
+
+  /**
+   * Add authentication information to the connection. This will be used to identify the user and
+   * check access to
+   * nodes protected by ACLs
+   * @param scheme
+   * @param auth
+   */
+  public void addAuthInfo(final String scheme, final byte[] auth) {
+    retryUntilConnected(new Callable<Object>() {
+      @Override
+      public Object call() throws Exception {
+        getConnection().addAuthInfo(scheme, auth);
+        return null;
+      }
+    });
+  }
+
+  /**
+   * Connect to ZooKeeper.
+   * @param maxMsToWaitUntilConnected
+   * @param watcher
+   * @throws ZkInterruptedException
+   *           if the connection timed out due to thread interruption
+   * @throws ZkTimeoutException
+   *           if the connection timed out
+   * @throws IllegalStateException
+   *           if the connection timed out due to thread interruption
+   */
+  public void connect(final long maxMsToWaitUntilConnected, Watcher watcher)
+      throws ZkInterruptedException, ZkTimeoutException, IllegalStateException {
+    if (isClosed()) {
+      throw new IllegalStateException("ZkClient already closed!");
+    }
+    boolean started = false;
+    acquireEventLock();
+    try {
+      setShutdownTrigger(false);
+
+      IZkConnection zkConnection = getConnection();
+      _eventThread = new ZkEventThread(zkConnection.getServers());
+      _eventThread.start();
+
+      if (isManagingZkConnection()) {
+        zkConnection.connect(watcher);
+        LOG.debug("Awaiting connection to Zookeeper server");
+        if (!waitUntilConnected(maxMsToWaitUntilConnected, TimeUnit.MILLISECONDS)) {
+          throw new ZkTimeoutException(
+              "Unable to connect to zookeeper server within timeout: " + maxMsToWaitUntilConnected);
+        }
+      } else {
+        // if the client is not managing connection, the input connection is supposed to connect.
+        if (isConnectionClosed()) {
+          throw new ZkClientException(
+              "Unable to connect to zookeeper server with the specified ZkConnection");
+        }
+        // TODO Refine the init state here. Here we pre-config it to be connected. This may not be
+        // the case, if the connection is connecting or recovering. -- JJ
+        // For shared client, the event notification will not be forwarded before wather add to the
+        // connection manager.
+        setCurrentState(KeeperState.SyncConnected);
+      }
+
+      started = true;
+    } finally {
+      getEventLock().unlock();
+
+      // we should close the zookeeper instance, otherwise it would keep
+      // on trying to connect
+      if (!started) {
+        close();
+      }
+    }
+  }
+
+  public long getCreationTime(String path) {
+    acquireEventLock();
+    try {
+      return getConnection().getCreateTime(path);
+    } catch (KeeperException e) {
+      throw ZkException.create(e);
+    } catch (InterruptedException e) {
+      throw new ZkInterruptedException(e);
+    } finally {
+      getEventLock().unlock();
+    }
+  }
+
+  public String getServers() {
+    return getConnection().getServers();
+  }
+
+  /**
+   * Close the client.
+   * @throws ZkInterruptedException
+   */
+  public void close() throws ZkInterruptedException {
+    if (LOG.isTraceEnabled()) {
+      StackTraceElement[] calls = Thread.currentThread().getStackTrace();
+      LOG.trace("closing a zkclient. callStack: " + Arrays.asList(calls));
+    }
+    getEventLock().lock();
+    IZkConnection connection = getConnection();
+    try {
+      if (connection == null || _closed) {
+        return;
+      }
+      setShutdownTrigger(true);
+      _eventThread.interrupt();
+      _eventThread.join(2000);
+      if (isManagingZkConnection()) {
+        LOG.info("Closing zkclient: " + ((ZkConnection) connection).getZookeeper());
+        connection.close();
+      }
+      _closed = true;
+
+      // send state change notification to unlock any wait
+      setCurrentState(null);
+      getEventLock().getStateChangedCondition().signalAll();
+    } catch (InterruptedException e) {
+      /**
+       * Workaround for HELIX-264: calling ZkClient#close() in its own eventThread context will
+       * throw ZkInterruptedException and skip ZkConnection#close()
+       */
+      if (connection != null) {
+        try {
+          /**
+           * ZkInterruptedException#construct() honors InterruptedException by calling
+           * Thread.currentThread().interrupt(); clear it first, so we can safely close the
+           * zk-connection
+           */
+          Thread.interrupted();
+          if (isManagingZkConnection()) {
+            connection.close();
+          }
+          /**
+           * restore interrupted status of current thread
+           */
+          Thread.currentThread().interrupt();
+        } catch (InterruptedException e1) {
+          throw new ZkInterruptedException(e1);
+        }
+      }
+    } finally {
+      getEventLock().unlock();
+      if (_monitor != null) {
+        _monitor.unregister();
+      }
+      LOG.info("Closed zkclient");
+    }
+  }
+
+  public boolean isClosed() {
+    try {
+      getEventLock().lock();
+      return _closed;
+    } finally {
+      getEventLock().unlock();
+    }
+  }
+
+  public boolean isConnectionClosed() {
+    IZkConnection connection = getConnection();
+    return (connection == null || connection.getZookeeperState() == null || !connection
+        .getZookeeperState().isAlive());
+  }
+
+  public void setShutdownTrigger(boolean triggerState) {
+    _shutdownTriggered = triggerState;
+  }
+
+  public boolean getShutdownTrigger() {
+    return _shutdownTriggered;
+  }
+
+  public int numberOfListeners() {
+    int listeners = 0;
+    for (Set<IZkChildListener> childListeners : _childListener.values()) {
+      listeners += childListeners.size();
+    }
+    for (Set<IZkDataListenerEntry> dataListeners : _dataListener.values()) {
+      listeners += dataListeners.size();
+    }
+    listeners += _stateListener.size();
+
+    return listeners;
+  }
+
+  public List<OpResult> multi(final Iterable<Op> ops) throws ZkException {
+    if (ops == null) {
+      throw new NullPointerException("ops must not be null.");
+    }
+
+    return retryUntilConnected(new Callable<List<OpResult>>() {
+
+      @Override
+      public List<OpResult> call() throws Exception {
+        return getConnection().multi(ops);
+      }
+    });
+  }
+
+  /**
+   * @return true if this ZkClient is managing the ZkConnection.
+   */
+  protected boolean isManagingZkConnection() {
+    return true;
+  }
+
+  public long getSessionId() {
+    ZkConnection zkConnection = ((ZkConnection) getConnection());
+    ZooKeeper zk = zkConnection.getZookeeper();
+    if (zk == null) {
+      throw new ZkClientException(
+          "ZooKeeper connection information is not available now. ZkClient might be disconnected.");
+    } else {
+      return zkConnection.getZookeeper().getSessionId();
+    }
+  }
+
+  /*
+   * Gets a session id in hexadecimal notation.
+   * Ex. 1000a5ceb930004 is returned.
+   */
+  private String getHexSessionId() {
+    return toHexSessionId(getSessionId());
+  }
+
+  /*
+   * Session aware operation needs below requirements:
+   * 1. the session id is NOT null or empty
+   * 2. create mode is EPHEMERAL or EPHEMERAL_SEQUENTIAL
+   */
+  private boolean isSessionAwareOperation(String expectedSessionId, CreateMode mode) {
+    return expectedSessionId != null && !expectedSessionId.isEmpty() && (
+        mode == CreateMode.EPHEMERAL || mode == CreateMode.EPHEMERAL_SEQUENTIAL);
+  }
+
+  // operations to update monitor's counters
+  private void record(String path, byte[] data, long startTimeMilliSec,
+      ZkClientMonitor.AccessType accessType) {
+    if (_monitor != null) {
+      int dataSize = (data != null) ? data.length : 0;
+      _monitor.record(path, dataSize, startTimeMilliSec, accessType);
+    }
+  }
+
+  private void recordFailure(String path, ZkClientMonitor.AccessType accessType) {
+    if (_monitor != null) {
+      _monitor.recordFailure(path, accessType);
+    }
+  }
+
+  private void recordStateChange(boolean stateChanged, boolean dataChanged) {
+    // update state change counter.
+    if (_monitor != null) {
+      if (stateChanged) {
+        _monitor.increaseStateChangeEventCounter();
+      }
+      if (dataChanged) {
+        _monitor.increaseDataChangeEventCounter();
+      }
+    }
+  }
+
+  /**
+   * Creates a {@link IZkStateListener} that wraps a default
+   * implementation of {@link org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener}, which means the returned
+   * listener runs the methods of {@link org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener}.
+   * This is for backward compatibility with {@link org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener}.
+   */
+  private static class IZkStateListenerI0ItecImpl implements IZkStateListener {
+    private org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener _listener;
+
+    IZkStateListenerI0ItecImpl(
+        org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener listener) {
+      _listener = listener;
+    }
+
+    @Override
+    public void handleStateChanged(KeeperState keeperState) throws Exception {
+      _listener.handleStateChanged(keeperState);
+    }
+
+    @Override
+    public void handleNewSession(final String sessionId) throws Exception {
+      /*
+       * org.I0Itec.zkclient.IZkStateListener does not have handleNewSession(sessionId),
+       * so just call handleNewSession() by default.
+       */
+      _listener.handleNewSession();
+    }
+
+    @Override
+    public void handleSessionEstablishmentError(Throwable error) throws Exception {
+      _listener.handleSessionEstablishmentError(error);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof IZkStateListenerI0ItecImpl)) {
+        return false;
+      }
+      if (_listener == null) {
+        return false;
+      }
+
+      IZkStateListenerI0ItecImpl defaultListener = (IZkStateListenerI0ItecImpl) obj;
+
+      return _listener.equals(defaultListener._listener);
+    }
+
+    @Override
+    public int hashCode() {
+      /*
+       * The original listener's hashcode helps find the wrapped listener with the same original
+       * listener. This is helpful in unsubscribeStateChanges(listener) when finding the listener
+       * to remove.
+       */
+      return _listener.hashCode();
+    }
+  }
+
+  private void validateCurrentThread() {
+    if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {
+      throw new IllegalArgumentException("Must not be done in the zookeeper event thread.");
+    }
+  }
+
+  /**
+   * Converts a session id in hexadecimal notation from a long type session id.
+   * Ex. 1000a5ceb930004 is returned.
+   *
+   * @return String representation of session id in hexadecimal notation.
+   */
+  private static String toHexSessionId(long sessionId) {
+    return Long.toHexString(sessionId);
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkConnection.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkConnection.java
new file mode 100644
index 0000000..21de122
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkConnection.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright 2010 the original author or authors.
+ * Licensed 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.helix.zookeeper.zkclient;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.helix.zookeeper.zkclient.exception.ZkException;
+import org.apache.log4j.Logger;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Op;
+import org.apache.zookeeper.OpResult;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.ZooKeeper.States;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+
+public class ZkConnection implements IZkConnection {
+  private static final Logger LOG = Logger.getLogger(ZkConnection.class);
+
+  /** It is recommended to use quite large sessions timeouts for ZooKeeper. */
+  private static final int DEFAULT_SESSION_TIMEOUT = 30000;
+
+  private ZooKeeper _zk = null;
+  private Lock _zookeeperLock = new ReentrantLock();
+
+  private final String _servers;
+  private final int _sessionTimeOut;
+
+  public ZkConnection(String zkServers) {
+    this(zkServers, DEFAULT_SESSION_TIMEOUT);
+  }
+
+  public ZkConnection(String zkServers, int sessionTimeOut) {
+    _servers = zkServers;
+    _sessionTimeOut = sessionTimeOut;
+  }
+
+  @Override
+  public void connect(Watcher watcher) {
+    _zookeeperLock.lock();
+    try {
+      if (_zk != null) {
+        throw new IllegalStateException("zk client has already been started");
+      }
+      try {
+        LOG.debug("Creating new ZookKeeper instance to connect to " + _servers + ".");
+        _zk = new ZooKeeper(_servers, _sessionTimeOut, watcher);
+      } catch (IOException e) {
+        throw new ZkException("Unable to connect to " + _servers, e);
+      }
+    } finally {
+      _zookeeperLock.unlock();
+    }
+  }
+
+  @Override
+  public void close() throws InterruptedException {
+    _zookeeperLock.lock();
+    try {
+      if (_zk != null) {
+        LOG.debug("Closing ZooKeeper connected to " + _servers);
+        _zk.close();
+        _zk = null;
+      }
+    } finally {
+      _zookeeperLock.unlock();
+    }
+  }
+
+  protected void reconnect(Watcher watcher) throws InterruptedException {
+    _zookeeperLock.lock();
+    try {
+      if (_zk == null) {
+        throw new IllegalStateException("zk client has not been connected or already been closed");
+      }
+      ZooKeeper prevZk = _zk;
+      try {
+        LOG.debug("Creating new ZookKeeper instance to reconnect to " + _servers + ".");
+        _zk = new ZooKeeper(_servers, _sessionTimeOut, watcher);
+        prevZk.close();
+      } catch (IOException e) {
+        throw new ZkException("Unable to connect to " + _servers, e);
+      }
+    } finally {
+      _zookeeperLock.unlock();
+    }
+  }
+
+  @Override
+  public String create(String path, byte[] data, CreateMode mode)
+      throws KeeperException, InterruptedException {
+    return _zk.create(path, data, Ids.OPEN_ACL_UNSAFE, mode);
+  }
+
+  @Override
+  public String create(String path, byte[] data, List<ACL> acl, CreateMode mode)
+      throws KeeperException, InterruptedException {
+    return _zk.create(path, data, acl, mode);
+  }
+
+  @Override
+  public void delete(String path) throws InterruptedException, KeeperException {
+    _zk.delete(path, -1);
+  }
+
+  @Override
+  public boolean exists(String path, boolean watch) throws KeeperException, InterruptedException {
+    return _zk.exists(path, watch) != null;
+  }
+
+  @Override
+  public List<String> getChildren(final String path, final boolean watch)
+      throws KeeperException, InterruptedException {
+    return _zk.getChildren(path, watch);
+  }
+
+  @Override
+  public byte[] readData(String path, Stat stat, boolean watch)
+      throws KeeperException, InterruptedException {
+    return _zk.getData(path, watch, stat);
+  }
+
+  public void writeData(String path, byte[] data) throws KeeperException, InterruptedException {
+    writeData(path, data, -1);
+  }
+
+  @Override
+  public void writeData(String path, byte[] data, int version)
+      throws KeeperException, InterruptedException {
+    _zk.setData(path, data, version);
+  }
+
+  @Override
+  public Stat writeDataReturnStat(String path, byte[] data, int version)
+      throws KeeperException, InterruptedException {
+    return _zk.setData(path, data, version);
+  }
+
+  @Override
+  public States getZookeeperState() {
+    return _zk != null ? _zk.getState() : null;
+  }
+
+  public ZooKeeper getZookeeper() {
+    return _zk;
+  }
+
+  @Override
+  public long getCreateTime(String path) throws KeeperException, InterruptedException {
+    Stat stat = _zk.exists(path, false);
+    if (stat != null) {
+      return stat.getCtime();
+    }
+    return -1;
+  }
+
+  @Override
+  public String getServers() {
+    return _servers;
+  }
+
+  @Override
+  public List<OpResult> multi(Iterable<Op> ops) throws KeeperException, InterruptedException {
+    return _zk.multi(ops);
+  }
+
+  @Override
+  public void addAuthInfo(String scheme, byte[] auth) {
+    _zk.addAuthInfo(scheme, auth);
+  }
+}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkEventThread.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkEventThread.java
similarity index 96%
rename from helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkEventThread.java
rename to zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkEventThread.java
index f720216..cc1ad74 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/zookeeper/ZkEventThread.java
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkEventThread.java
@@ -8,16 +8,17 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.helix.manager.zk.zookeeper;
+package org.apache.helix.zookeeper.zkclient;
 
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.I0Itec.zkclient.exception.ZkInterruptedException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 /**
  * All listeners registered at the {@link ZkClient} will be notified from this event thread. This is to prevent
  * dead-lock situations. The {@link ZkClient} pulls some information out of the {@link ZooKeeper} events to signal
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkLock.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkLock.java
new file mode 100644
index 0000000..6294c39
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkLock.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class ZkLock extends ReentrantLock {
+
+    private static final long serialVersionUID = 1L;
+
+    private Condition _dataChangedCondition = newCondition();
+    private Condition _stateChangedCondition = newCondition();
+    private Condition _zNodeEventCondition = newCondition();
+
+    /**
+     * This condition will be signaled if a zookeeper event was processed and the event contains a data/child change.
+     *
+     * @return the condition.
+     */
+    public Condition getDataChangedCondition() {
+        return _dataChangedCondition;
+    }
+
+    /**
+     * This condition will be signaled if a zookeeper event was processed and the event contains a state change
+     * (connected, disconnected, session expired, etc ...).
+     *
+     * @return the condition.
+     */
+    public Condition getStateChangedCondition() {
+        return _stateChangedCondition;
+    }
+
+    /**
+     * This condition will be signaled if any znode related zookeeper event was received.
+     *
+     * @return the condition.
+     */
+    public Condition getZNodeEventCondition() {
+        return _zNodeEventCondition;
+    }
+}
\ No newline at end of file
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkServer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkServer.java
new file mode 100644
index 0000000..2ba4d59
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkServer.java
@@ -0,0 +1,175 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.apache.helix.zookeeper.zkclient.exception.ZkException;
+import org.apache.helix.zookeeper.zkclient.exception.ZkInterruptedException;
+import org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.SerializableSerializer;
+import org.apache.log4j.Logger;
+import org.apache.zookeeper.server.NIOServerCnxnFactory;
+import org.apache.zookeeper.server.ZooKeeperServer;
+
+public class ZkServer {
+
+    private final static Logger LOG = Logger.getLogger(ZkServer.class);
+
+    public static final int DEFAULT_PORT = 2181;
+    public static final int DEFAULT_TICK_TIME = 5000;
+    public static final int DEFAULT_MIN_SESSION_TIMEOUT = 2 * DEFAULT_TICK_TIME;
+
+    private String _dataDir;
+    private String _logDir;
+
+    private IDefaultNameSpace _defaultNameSpace;
+
+    private ZooKeeperServer _zk;
+    private NIOServerCnxnFactory _nioFactory;
+    private ZkClient _zkClient;
+    private int _port;
+    private int _tickTime;
+    private int _minSessionTimeout;
+
+    public ZkServer(String dataDir, String logDir, IDefaultNameSpace defaultNameSpace) {
+        this(dataDir, logDir, defaultNameSpace, DEFAULT_PORT);
+    }
+
+    public ZkServer(String dataDir, String logDir, IDefaultNameSpace defaultNameSpace, int port) {
+        this(dataDir, logDir, defaultNameSpace, port, DEFAULT_TICK_TIME);
+    }
+
+    public ZkServer(String dataDir, String logDir, IDefaultNameSpace defaultNameSpace, int port, int tickTime) {
+        this(dataDir, logDir, defaultNameSpace, port, tickTime, DEFAULT_MIN_SESSION_TIMEOUT);
+    }
+
+    public ZkServer(String dataDir, String logDir, IDefaultNameSpace defaultNameSpace, int port, int tickTime, int minSessionTimeout) {
+        _dataDir = dataDir;
+        _logDir = logDir;
+        _defaultNameSpace = defaultNameSpace;
+        _port = port;
+        _tickTime = tickTime;
+        _minSessionTimeout = minSessionTimeout;
+    }
+
+    public int getPort() {
+        return _port;
+    }
+
+    @PostConstruct
+    public void start() {
+        final String[] localHostNames = NetworkUtil.getLocalHostNames();
+        String names = "";
+        for (int i = 0; i < localHostNames.length; i++) {
+            final String name = localHostNames[i];
+            names += " " + name;
+            if (i + 1 != localHostNames.length) {
+                names += ",";
+            }
+        }
+        LOG.info("Starting ZkServer on: [" + names + "] port " + _port + "...");
+        startZooKeeperServer();
+        _zkClient = new ZkClient(new ZkConnection("localhost:" + _port), 10000, -1, new BasicZkSerializer(new SerializableSerializer()), null, null, null, false);
+        _defaultNameSpace.createDefaultNameSpace(_zkClient);
+    }
+
+    private void startZooKeeperServer() {
+        final String[] localhostHostNames = NetworkUtil.getLocalHostNames();
+        final String servers = "localhost:" + _port;
+        // check if this server needs to start a _client server.
+        int pos = -1;
+        LOG.debug("check if hostNames " + servers + " is in list: " + Arrays.asList(localhostHostNames));
+        if ((pos = NetworkUtil.hostNamesInList(servers, localhostHostNames)) != -1) {
+            // yes this server needs to start a zookeeper server
+            final String[] hosts = servers.split(",");
+            final String[] hostSplitted = hosts[pos].split(":");
+            int port = _port;
+            if (hostSplitted.length > 1) {
+                port = Integer.parseInt(hostSplitted[1]);
+            }
+            // check if this machine is already something running..
+            if (NetworkUtil.isPortFree(port)) {
+                final File dataDir = new File(_dataDir);
+                final File dataLogDir = new File(_logDir);
+                dataDir.mkdirs();
+                dataLogDir.mkdirs();
+
+                if (hosts.length > 1) {
+                    // multiple zk servers
+                    LOG.info("Start distributed zookeeper server...");
+                    throw new IllegalArgumentException("Unable to start distributed zookeeper server");
+                }
+                // single zk server
+                LOG.info("Start single zookeeper server...");
+                LOG.info("data dir: " + dataDir.getAbsolutePath());
+                LOG.info("data log dir: " + dataLogDir.getAbsolutePath());
+                startSingleZkServer(_tickTime, dataDir, dataLogDir, port);
+            } else {
+                throw new IllegalStateException("Zookeeper port " + port + " was already in use. Running in single machine mode?");
+            }
+        }
+    }
+
+    private void startSingleZkServer(final int tickTime, final File dataDir, final File dataLogDir, final int port) {
+        try {
+            _zk = new ZooKeeperServer(dataDir, dataLogDir, tickTime);
+            _zk.setMinSessionTimeout(_minSessionTimeout);
+            _nioFactory = new NIOServerCnxnFactory();
+            int maxClientConnections = 0; // 0 means unlimited
+            _nioFactory.configure(new InetSocketAddress(port), maxClientConnections);
+            _nioFactory.startup(_zk);
+        } catch (IOException e) {
+            throw new ZkException("Unable to start single ZooKeeper server.", e);
+        } catch (InterruptedException e) {
+            throw new ZkInterruptedException(e);
+        }
+    }
+
+    @PreDestroy
+    public void shutdown() {
+        LOG.info("Shutting down ZkServer...");
+        try {
+            _zkClient.close();
+        } catch (ZkException e) {
+            LOG.warn("Error on closing zkclient: " + e.getClass().getName());
+        }
+        if (_nioFactory != null) {
+            _nioFactory.shutdown();
+            try {
+                _nioFactory.join();
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+            _nioFactory = null;
+        }
+        if (_zk != null) {
+            _zk.shutdown();
+            _zk = null;
+        }
+        LOG.info("Shutting down ZkServer...done");
+    }
+
+    public ZkClient getZkClient() {
+        return _zkClient;
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/annotation/PreFetch.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/annotation/PreFetch.java
new file mode 100644
index 0000000..f32081d
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/annotation/PreFetch.java
@@ -0,0 +1,29 @@
+package org.apache.helix.zookeeper.zkclient.annotation;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PreFetch {
+  boolean enabled() default true;
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/callback/ZkAsyncCallbacks.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/callback/ZkAsyncCallbacks.java
new file mode 100644
index 0000000..04c4058
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/callback/ZkAsyncCallbacks.java
@@ -0,0 +1,193 @@
+package org.apache.helix.zookeeper.zkclient.callback;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.helix.zookeeper.zkclient.metric.ZkClientMonitor;
+import org.apache.zookeeper.AsyncCallback.DataCallback;
+import org.apache.zookeeper.AsyncCallback.StatCallback;
+import org.apache.zookeeper.AsyncCallback.StringCallback;
+import org.apache.zookeeper.AsyncCallback.VoidCallback;
+import org.apache.zookeeper.KeeperException.Code;
+import org.apache.zookeeper.data.Stat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class ZkAsyncCallbacks {
+  private static Logger LOG = LoggerFactory.getLogger(ZkAsyncCallbacks.class);
+
+  public static class GetDataCallbackHandler extends DefaultCallback implements DataCallback {
+    public byte[] _data;
+    public Stat _stat;
+
+    @Override
+    public void handle() {
+      // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
+      if (rc == 0) {
+        _data = data;
+        _stat = stat;
+        // update ctx with data size
+        if (_data != null && ctx != null && ctx instanceof ZkAsyncCallContext) {
+          ZkAsyncCallContext zkCtx = (ZkAsyncCallContext) ctx;
+          zkCtx._bytes = _data.length;
+        }
+      }
+      callback(rc, path, ctx);
+    }
+  }
+
+  public static class SetDataCallbackHandler extends DefaultCallback implements StatCallback {
+    Stat _stat;
+
+    @Override
+    public void handle() {
+      // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void processResult(int rc, String path, Object ctx, Stat stat) {
+      if (rc == 0) {
+        _stat = stat;
+      }
+      callback(rc, path, ctx);
+    }
+
+    public Stat getStat() {
+      return _stat;
+    }
+  }
+
+  public static class ExistsCallbackHandler extends DefaultCallback implements StatCallback {
+    public Stat _stat;
+
+    @Override
+    public void handle() {
+      // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void processResult(int rc, String path, Object ctx, Stat stat) {
+      if (rc == 0) {
+        _stat = stat;
+      }
+      callback(rc, path, ctx);
+    }
+  }
+
+  public static class CreateCallbackHandler extends DefaultCallback implements StringCallback {
+    @Override
+    public void processResult(int rc, String path, Object ctx, String name) {
+      callback(rc, path, ctx);
+    }
+
+    @Override
+    public void handle() {
+      // TODO Auto-generated method stub
+    }
+  }
+
+  public static class DeleteCallbackHandler extends DefaultCallback implements VoidCallback {
+    @Override
+    public void processResult(int rc, String path, Object ctx) {
+      callback(rc, path, ctx);
+    }
+
+    @Override
+    public void handle() {
+      // TODO Auto-generated method stub
+    }
+  }
+
+  /**
+   * Default callback for zookeeper async api
+   */
+  public static abstract class DefaultCallback {
+    AtomicBoolean _lock = new AtomicBoolean(false);
+    int _rc = -1;
+
+    public void callback(int rc, String path, Object ctx) {
+      if (rc != 0 && LOG.isDebugEnabled()) {
+        LOG.debug(this + ", rc:" + Code.get(rc) + ", path: " + path);
+      }
+
+      if (ctx != null && ctx instanceof ZkAsyncCallContext) {
+        ZkAsyncCallContext zkCtx = (ZkAsyncCallContext) ctx;
+        if (zkCtx._monitor != null) {
+          if (zkCtx._isRead) {
+            zkCtx._monitor.record(path, zkCtx._bytes, zkCtx._startTimeMilliSec,
+                ZkClientMonitor.AccessType.READ);
+          } else {
+            zkCtx._monitor.record(path, zkCtx._bytes, zkCtx._startTimeMilliSec,
+                ZkClientMonitor.AccessType.WRITE);
+          }
+        }
+      }
+
+      _rc = rc;
+      handle();
+
+      synchronized (_lock) {
+        _lock.set(true);
+        _lock.notify();
+      }
+    }
+
+    public boolean waitForSuccess() {
+      try {
+        synchronized (_lock) {
+          while (!_lock.get()) {
+            _lock.wait();
+          }
+        }
+      } catch (InterruptedException e) {
+        LOG.error("Interrupted waiting for success", e);
+      }
+      return true;
+    }
+
+    public int getRc() {
+      return _rc;
+    }
+
+    abstract public void handle();
+  }
+
+  public static class ZkAsyncCallContext {
+    private long _startTimeMilliSec;
+    private int _bytes;
+    private ZkClientMonitor _monitor;
+    private boolean _isRead;
+
+    public ZkAsyncCallContext(final ZkClientMonitor monitor, long startTimeMilliSec, int bytes,
+        boolean isRead) {
+      _monitor = monitor;
+      _startTimeMilliSec = startTimeMilliSec;
+      _bytes = bytes;
+      _isRead = isRead;
+    }
+  }
+
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/deprecated/IZkStateListener.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/deprecated/IZkStateListener.java
new file mode 100644
index 0000000..1955fd3
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/deprecated/IZkStateListener.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.deprecated;
+
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+
+@Deprecated
+public interface IZkStateListener {
+
+    /**
+     * Called when the zookeeper connection state has changed.
+     *
+     * @param state
+     *            The new state.
+     * @throws Exception
+     *             On any error.
+     */
+    public void handleStateChanged(KeeperState state) throws Exception;
+
+    /**
+     * Called after the zookeeper session has expired and a new session has been created. You would have to re-create
+     * any ephemeral nodes here.
+     *
+     * @throws Exception
+     *             On any error.
+     */
+    public void handleNewSession() throws Exception;
+
+    /**
+     * Called when a session cannot be re-established. This should be used to implement connection
+     * failure handling e.g. retry to connect or pass the error up
+     *
+     * @param error
+     *            The error that prevents a session from being established
+     * @throws Exception
+     *             On any error.
+     */
+    public void handleSessionEstablishmentError(final Throwable error) throws Exception;
+
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkBadVersionException.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkBadVersionException.java
new file mode 100644
index 0000000..a85fddd
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkBadVersionException.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.exception;
+
+import org.apache.zookeeper.KeeperException;
+
+public class ZkBadVersionException extends ZkException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ZkBadVersionException() {
+        super();
+    }
+
+    public ZkBadVersionException(KeeperException cause) {
+        super(cause);
+    }
+
+    public ZkBadVersionException(String message, KeeperException cause) {
+        super(message, cause);
+    }
+
+    public ZkBadVersionException(String message) {
+        super(message);
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkException.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkException.java
new file mode 100644
index 0000000..f610449
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkException.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.exception;
+
+import org.apache.zookeeper.KeeperException;
+
+public class ZkException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ZkException() {
+        super();
+    }
+
+    public ZkException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ZkException(String message) {
+        super(message);
+    }
+
+    public ZkException(Throwable cause) {
+        super(cause);
+    }
+
+    public static ZkException create(KeeperException e) {
+        switch (e.code()) {
+        // case DATAINCONSISTENCY:
+        // return new DataInconsistencyException();
+        // case CONNECTIONLOSS:
+        // return new ConnectionLossException();
+        case NONODE:
+            return new ZkNoNodeException(e);
+            // case NOAUTH:
+            // return new ZkNoAuthException();
+        case BADVERSION:
+            return new ZkBadVersionException(e);
+            // case NOCHILDRENFOREPHEMERALS:
+            // return new NoChildrenForEphemeralsException();
+        case NODEEXISTS:
+            return new ZkNodeExistsException(e);
+            // case INVALIDACL:
+            // return new ZkInvalidACLException();
+            // case AUTHFAILED:
+            // return new AuthFailedException();
+            // case NOTEMPTY:
+            // return new NotEmptyException();
+            // case SESSIONEXPIRED:
+            // return new SessionExpiredException();
+            // case INVALIDCALLBACK:
+            // return new InvalidCallbackException();
+
+        default:
+            return new ZkException(e);
+        }
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkInterruptedException.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkInterruptedException.java
new file mode 100644
index 0000000..64a9407
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkInterruptedException.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.exception;
+
+public class ZkInterruptedException extends ZkException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ZkInterruptedException(InterruptedException e) {
+        super(e);
+        Thread.currentThread().interrupt();
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkMarshallingError.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkMarshallingError.java
new file mode 100644
index 0000000..3e1518b
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkMarshallingError.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.exception;
+
+public class ZkMarshallingError extends ZkException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ZkMarshallingError() {
+        super();
+    }
+
+    public ZkMarshallingError(Throwable cause) {
+        super(cause);
+    }
+
+    public ZkMarshallingError(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ZkMarshallingError(String message) {
+        super(message);
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkNoNodeException.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkNoNodeException.java
new file mode 100644
index 0000000..f7b7bae
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkNoNodeException.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.exception;
+
+import org.apache.zookeeper.KeeperException;
+
+public class ZkNoNodeException extends ZkException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ZkNoNodeException() {
+        super();
+    }
+
+    public ZkNoNodeException(KeeperException cause) {
+        super(cause);
+    }
+
+    public ZkNoNodeException(String message, KeeperException cause) {
+        super(message, cause);
+    }
+
+    public ZkNoNodeException(String message) {
+        super(message);
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkNodeExistsException.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkNodeExistsException.java
new file mode 100644
index 0000000..9624a12
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkNodeExistsException.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.exception;
+
+import org.apache.zookeeper.KeeperException;
+
+public class ZkNodeExistsException extends ZkException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ZkNodeExistsException() {
+        super();
+    }
+
+    public ZkNodeExistsException(KeeperException cause) {
+        super(cause);
+    }
+
+    public ZkNodeExistsException(String message, KeeperException cause) {
+        super(message, cause);
+    }
+
+    public ZkNodeExistsException(String message) {
+        super(message);
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkSessionMismatchedException.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkSessionMismatchedException.java
new file mode 100644
index 0000000..88b17c0
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkSessionMismatchedException.java
@@ -0,0 +1,33 @@
+package org.apache.helix.zookeeper.zkclient.exception;
+
+/*
+ * 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.
+ */
+
+/**
+ * Exception thrown when an action is taken by an expected zk session which
+ * does not match the actual zk session.
+ */
+public class ZkSessionMismatchedException extends ZkException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ZkSessionMismatchedException(String message) {
+        super(message);
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkTimeoutException.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkTimeoutException.java
new file mode 100644
index 0000000..738ad08
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/exception/ZkTimeoutException.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.exception;
+
+public class ZkTimeoutException extends ZkException {
+
+    private static final long serialVersionUID = 1L;
+
+    public ZkTimeoutException() {
+        super();
+    }
+
+    public ZkTimeoutException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ZkTimeoutException(String message) {
+        super(message);
+    }
+
+    public ZkTimeoutException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/metric/ZkClientMonitor.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/metric/ZkClientMonitor.java
new file mode 100644
index 0000000..9b0ec34
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/metric/ZkClientMonitor.java
@@ -0,0 +1,252 @@
+package org.apache.helix.zookeeper.zkclient.metric;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.helix.monitoring.mbeans.MBeanRegistrar;
+import org.apache.helix.monitoring.mbeans.MonitorDomainNames;
+import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMBeanProvider;
+import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
+import org.apache.helix.monitoring.mbeans.dynamicMBeans.SimpleDynamicMetric;
+import org.apache.helix.monitoring.mbeans.exception.MetricException;
+import org.apache.helix.zookeeper.zkclient.ZkEventThread;
+
+
+public class ZkClientMonitor extends DynamicMBeanProvider {
+  public static final String MONITOR_TYPE = "Type";
+  public static final String MONITOR_KEY = "Key";
+  protected static final String MBEAN_DESCRIPTION = "Helix Zookeeper Client Monitor";
+
+  public enum AccessType {
+    READ, WRITE
+  }
+
+  private String _sensorName;
+  private String _monitorType;
+  private String _monitorKey;
+  private String _monitorInstanceName;
+  private boolean _monitorRootOnly;
+
+  private SimpleDynamicMetric<Long> _stateChangeEventCounter;
+  private SimpleDynamicMetric<Long> _dataChangeEventCounter;
+  private SimpleDynamicMetric<Long> _outstandingRequestGauge;
+
+  private ZkThreadMetric _zkEventThreadMetric;
+
+  private Map<ZkClientPathMonitor.PredefinedPath, ZkClientPathMonitor> _zkClientPathMonitorMap =
+      new ConcurrentHashMap<>();
+
+  public ZkClientMonitor(String monitorType, String monitorKey, String monitorInstanceName,
+      boolean monitorRootOnly, ZkEventThread zkEventThread) {
+    if (monitorKey == null || monitorKey.isEmpty() || monitorType == null || monitorType
+        .isEmpty()) {
+      throw new MetricException("Cannot create ZkClientMonitor without monitor key and type.");
+    }
+
+    _sensorName =
+        String.format("%s.%s.%s", MonitorDomainNames.HelixZkClient.name(), monitorType, monitorKey);
+    _monitorType = monitorType;
+    _monitorKey = monitorKey;
+    _monitorInstanceName = monitorInstanceName;
+    _monitorRootOnly = monitorRootOnly;
+
+    _stateChangeEventCounter = new SimpleDynamicMetric("StateChangeEventCounter", 0l);
+    _dataChangeEventCounter = new SimpleDynamicMetric("DataChangeEventCounter", 0l);
+    _outstandingRequestGauge = new SimpleDynamicMetric("OutstandingRequestGauge", 0l);
+    if (zkEventThread != null) {
+      _zkEventThreadMetric = new ZkThreadMetric(zkEventThread);
+    }
+  }
+
+  public static ObjectName getObjectName(String monitorType, String monitorKey,
+      String monitorInstanceName) throws MalformedObjectNameException {
+    return MBeanRegistrar
+        .buildObjectName(MonitorDomainNames.HelixZkClient.name(), MONITOR_TYPE, monitorType,
+            MONITOR_KEY,
+            (monitorKey + (monitorInstanceName == null ? "" : "." + monitorInstanceName)));
+  }
+
+  @Override
+  public DynamicMBeanProvider register() throws JMException {
+    List<DynamicMetric<?, ?>> attributeList = new ArrayList<>();
+    attributeList.add(_dataChangeEventCounter);
+    attributeList.add(_outstandingRequestGauge);
+    attributeList.add(_stateChangeEventCounter);
+    if (_zkEventThreadMetric != null) {
+      attributeList.add(_zkEventThreadMetric);
+    }
+    doRegister(attributeList, MBEAN_DESCRIPTION,
+        getObjectName(_monitorType, _monitorKey, _monitorInstanceName));
+    for (ZkClientPathMonitor.PredefinedPath path : ZkClientPathMonitor.PredefinedPath.values()) {
+      // If monitor root path only, check if the current path is Root.
+      // Otherwise, add monitors for every path.
+      if (!_monitorRootOnly || path.equals(ZkClientPathMonitor.PredefinedPath.Root)) {
+        _zkClientPathMonitorMap.put(path,
+            new ZkClientPathMonitor(path, _monitorType, _monitorKey, _monitorInstanceName)
+                .register());
+      }
+    }
+    return this;
+  }
+
+  /**
+   * After unregistered, the MBean can't be registered again, a new monitor has be to created.
+   */
+  public void unregister() {
+    super.unregister();
+    for (ZkClientPathMonitor zkClientPathMonitor : _zkClientPathMonitorMap.values()) {
+      zkClientPathMonitor.unregister();
+    }
+  }
+
+  @Override
+  public String getSensorName() {
+    return _sensorName;
+  }
+
+  public void increaseStateChangeEventCounter() {
+    synchronized (_stateChangeEventCounter) {
+      _stateChangeEventCounter.updateValue(_stateChangeEventCounter.getValue() + 1);
+    }
+  }
+
+  public void increaseDataChangeEventCounter() {
+    synchronized (_dataChangeEventCounter) {
+      _dataChangeEventCounter.updateValue(_dataChangeEventCounter.getValue() + 1);
+    }
+  }
+
+  public void increaseOutstandingRequestGauge() {
+    synchronized (_outstandingRequestGauge) {
+      _outstandingRequestGauge.updateValue(_outstandingRequestGauge.getValue() + 1);
+    }
+  }
+
+  public void decreaseOutstandingRequestGauge() {
+    synchronized (_outstandingRequestGauge) {
+      _outstandingRequestGauge.updateValue(_outstandingRequestGauge.getValue() - 1);
+    }
+  }
+
+  public void recordDataPropagationLatency(String path, long latencyMilliSec) {
+    if (null == path) {
+      return;
+    }
+    Arrays.stream(ZkClientPathMonitor.PredefinedPath.values())
+        .filter(predefinedPath -> predefinedPath.match(path))
+        .forEach(predefinedPath -> {
+      ZkClientPathMonitor zkClientPathMonitor = _zkClientPathMonitorMap.get(predefinedPath);
+      if (zkClientPathMonitor != null) {
+        zkClientPathMonitor.recordDataPropagationLatency(latencyMilliSec);
+      }
+    });
+  }
+
+  private void record(String path, int bytes, long latencyMilliSec, boolean isFailure,
+      boolean isRead) {
+    if (null == path) {
+      return;
+    }
+    Arrays.stream(ZkClientPathMonitor.PredefinedPath.values())
+        .filter(predefinedPath -> predefinedPath.match(path))
+        .forEach(predefinedPath -> {
+      ZkClientPathMonitor zkClientPathMonitor = _zkClientPathMonitorMap.get(predefinedPath);
+      if (zkClientPathMonitor != null) {
+        zkClientPathMonitor.record(bytes, latencyMilliSec, isFailure, isRead);
+      }
+    });
+  }
+
+  public void record(String path, int dataSize, long startTimeMilliSec, AccessType accessType) {
+    switch (accessType) {
+    case READ:
+      record(path, dataSize, System.currentTimeMillis() - startTimeMilliSec, false, true);
+      return;
+    case WRITE:
+      record(path, dataSize, System.currentTimeMillis() - startTimeMilliSec, false, false);
+      return;
+    default:
+      return;
+    }
+  }
+
+  public void recordFailure(String path, AccessType accessType) {
+    switch (accessType) {
+    case READ:
+      record(path, 0, 0, true, true);
+      return;
+    case WRITE:
+      record(path, 0, 0, true, false);
+      return;
+    default:
+      return;
+    }
+  }
+
+  class ZkThreadMetric extends DynamicMetric<ZkEventThread, ZkEventThread> {
+    public ZkThreadMetric(ZkEventThread eventThread) {
+      super("ZkEventThead", eventThread);
+    }
+
+    @Override
+    protected Set<MBeanAttributeInfo> generateAttributeInfos(String metricName,
+        ZkEventThread eventThread) {
+      Set<MBeanAttributeInfo> attributeInfoSet = new HashSet<>();
+      attributeInfoSet.add(new MBeanAttributeInfo("PendingCallbackGauge", Long.TYPE.getName(),
+          DEFAULT_ATTRIBUTE_DESCRIPTION, true, false, false));
+      attributeInfoSet.add(new MBeanAttributeInfo("TotalCallbackCounter", Long.TYPE.getName(),
+          DEFAULT_ATTRIBUTE_DESCRIPTION, true, false, false));
+      attributeInfoSet.add(
+          new MBeanAttributeInfo("TotalCallbackHandledCounter", Long.TYPE.getName(),
+              DEFAULT_ATTRIBUTE_DESCRIPTION, true, false, false));
+      return attributeInfoSet;
+    }
+
+    @Override
+    public Object getAttributeValue(String attributeName) {
+      switch (attributeName) {
+      case "PendingCallbackGauge":
+        return getMetricObject().getPendingEventsCount();
+      case "TotalCallbackCounter":
+        return getMetricObject().getTotalEventCount();
+      case "TotalCallbackHandledCounter":
+        return getMetricObject().getTotalHandledEventCount();
+      default:
+        throw new MetricException("Unknown attribute name: " + attributeName);
+      }
+    }
+
+    @Override
+    public void updateValue(ZkEventThread newEventThread) {
+      setMetricObject(newEventThread);
+    }
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/metric/ZkClientPathMonitor.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/metric/ZkClientPathMonitor.java
new file mode 100644
index 0000000..35f7943
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/metric/ZkClientPathMonitor.java
@@ -0,0 +1,248 @@
+package org.apache.helix.zookeeper.zkclient.metric;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import javax.management.JMException;
+import javax.management.ObjectName;
+
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.SlidingTimeWindowArrayReservoir;
+import org.apache.helix.monitoring.mbeans.MonitorDomainNames;
+import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMBeanProvider;
+import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
+import org.apache.helix.monitoring.mbeans.dynamicMBeans.HistogramDynamicMetric;
+import org.apache.helix.monitoring.mbeans.dynamicMBeans.SimpleDynamicMetric;
+
+
+public class ZkClientPathMonitor extends DynamicMBeanProvider {
+  public static final String MONITOR_PATH = "PATH";
+  private final String _sensorName;
+  private final String _type;
+  private final String _key;
+  private final String _instanceName;
+  private final PredefinedPath _path;
+
+  public enum PredefinedPath {
+    IdealStates(".*/IDEALSTATES/.*"),
+    Instances(".*/INSTANCES/.*"),
+    Configs(".*/CONFIGS/.*"),
+    Controller(".*/CONTROLLER/.*"),
+    ExternalView(".*/EXTERNALVIEW/.*"),
+    LiveInstances(".*/LIVEINSTANCES/.*"),
+    PropertyStore(".*/PROPERTYSTORE/.*"),
+    CurrentStates(".*/CURRENTSTATES/.*"),
+    Messages(".*/MESSAGES/.*"),
+    Root(".*");
+
+    private final String _matchString;
+
+    PredefinedPath(String matchString) {
+      _matchString = matchString;
+    }
+
+    public boolean match(String path) {
+      return path.matches(this._matchString);
+    }
+  }
+
+  public enum PredefinedMetricDomains {
+    WriteTotalLatencyCounter,
+    ReadTotalLatencyCounter,
+    WriteFailureCounter,
+    ReadFailureCounter,
+    WriteBytesCounter,
+    ReadBytesCounter,
+    WriteCounter,
+    ReadCounter,
+    ReadLatencyGauge,
+    WriteLatencyGauge,
+    ReadBytesGauge,
+    WriteBytesGauge,
+    /*
+     * The latency between a ZK data change happening on the server side and the client side.
+     */
+    DataPropagationLatencyGauge,
+    /**
+     * @deprecated
+     * This domain name has a typo. Keep it in case its historical metric data is being used.
+     */
+    @Deprecated
+    DataPropagationLatencyGuage
+  }
+
+  private SimpleDynamicMetric<Long> _readCounter;
+  private SimpleDynamicMetric<Long> _writeCounter;
+  private SimpleDynamicMetric<Long> _readBytesCounter;
+  private SimpleDynamicMetric<Long> _writeBytesCounter;
+  private SimpleDynamicMetric<Long> _readFailureCounter;
+  private SimpleDynamicMetric<Long> _writeFailureCounter;
+  private SimpleDynamicMetric<Long> _readTotalLatencyCounter;
+  private SimpleDynamicMetric<Long> _writeTotalLatencyCounter;
+
+  private HistogramDynamicMetric _readLatencyGauge;
+  private HistogramDynamicMetric _writeLatencyGauge;
+  private HistogramDynamicMetric _readBytesGauge;
+  private HistogramDynamicMetric _writeBytesGauge;
+  private HistogramDynamicMetric _dataPropagationLatencyGauge;
+
+  /**
+   * @deprecated
+   * Keep it for backward-compatibility purpose.
+   */
+  @Deprecated
+  private HistogramDynamicMetric _dataPropagationLatencyGuage;
+
+  @Override
+  public String getSensorName() {
+    return _sensorName;
+  }
+
+  public ZkClientPathMonitor(PredefinedPath path, String monitorType, String monitorKey,
+      String monitorInstanceName) {
+    _type = monitorType;
+    _key = monitorKey;
+    _instanceName = monitorInstanceName;
+    _path = path;
+    _sensorName = String
+        .format("%s.%s.%s.%s", MonitorDomainNames.HelixZkClient.name(), monitorType, monitorKey,
+            path.name());
+
+    _writeTotalLatencyCounter =
+        new SimpleDynamicMetric(PredefinedMetricDomains.WriteTotalLatencyCounter.name(), 0l);
+    _readTotalLatencyCounter =
+        new SimpleDynamicMetric(PredefinedMetricDomains.ReadTotalLatencyCounter.name(), 0l);
+    _writeFailureCounter =
+        new SimpleDynamicMetric(PredefinedMetricDomains.WriteFailureCounter.name(), 0l);
+    _readFailureCounter =
+        new SimpleDynamicMetric(PredefinedMetricDomains.ReadFailureCounter.name(), 0l);
+    _writeBytesCounter =
+        new SimpleDynamicMetric(PredefinedMetricDomains.WriteBytesCounter.name(), 0l);
+    _readBytesCounter =
+        new SimpleDynamicMetric(PredefinedMetricDomains.ReadBytesCounter.name(), 0l);
+    _writeCounter = new SimpleDynamicMetric(PredefinedMetricDomains.WriteCounter.name(), 0l);
+    _readCounter = new SimpleDynamicMetric(PredefinedMetricDomains.ReadCounter.name(), 0l);
+
+    _readLatencyGauge = new HistogramDynamicMetric(PredefinedMetricDomains.ReadLatencyGauge.name(),
+        new Histogram(
+            new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(), TimeUnit.MILLISECONDS)));
+    _writeLatencyGauge =
+        new HistogramDynamicMetric(PredefinedMetricDomains.WriteLatencyGauge.name(), new Histogram(
+            new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(), TimeUnit.MILLISECONDS)));
+    _readBytesGauge = new HistogramDynamicMetric(PredefinedMetricDomains.ReadBytesGauge.name(),
+        new Histogram(
+            new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(), TimeUnit.MILLISECONDS)));
+    _writeBytesGauge = new HistogramDynamicMetric(PredefinedMetricDomains.WriteBytesGauge.name(),
+        new Histogram(
+            new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(), TimeUnit.MILLISECONDS)));
+    _dataPropagationLatencyGauge =
+        new HistogramDynamicMetric(PredefinedMetricDomains.DataPropagationLatencyGauge.name(),
+            new Histogram(new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(),
+                TimeUnit.MILLISECONDS)));
+
+    // This is deprecated and keep it for backward-compatibility purpose.
+    _dataPropagationLatencyGuage =
+        new HistogramDynamicMetric(PredefinedMetricDomains.DataPropagationLatencyGuage.name(),
+            new Histogram(new SlidingTimeWindowArrayReservoir(getResetIntervalInMs(),
+                TimeUnit.MILLISECONDS)));
+  }
+
+  public ZkClientPathMonitor register() throws JMException {
+    List<DynamicMetric<?, ?>> attributeList = new ArrayList<>();
+    attributeList.add(_readCounter);
+    attributeList.add(_writeCounter);
+    attributeList.add(_readBytesCounter);
+    attributeList.add(_writeBytesCounter);
+    attributeList.add(_readFailureCounter);
+    attributeList.add(_writeFailureCounter);
+    attributeList.add(_readTotalLatencyCounter);
+    attributeList.add(_writeTotalLatencyCounter);
+    attributeList.add(_readLatencyGauge);
+    attributeList.add(_writeLatencyGauge);
+    attributeList.add(_readBytesGauge);
+    attributeList.add(_writeBytesGauge);
+    attributeList.add(_dataPropagationLatencyGauge);
+    // This is deprecated and keep it for backward-compatibility purpose.
+    attributeList.add(_dataPropagationLatencyGuage);
+
+    ObjectName objectName = new ObjectName(String
+        .format("%s,%s=%s", ZkClientMonitor.getObjectName(_type, _key, _instanceName).toString(),
+            MONITOR_PATH, _path.name()));
+    doRegister(attributeList, ZkClientMonitor.MBEAN_DESCRIPTION, objectName);
+
+    return this;
+  }
+
+  protected synchronized void record(int bytes, long latencyMilliSec, boolean isFailure,
+      boolean isRead) {
+    if (isFailure) {
+      increaseFailureCounter(isRead);
+    } else {
+      increaseCounter(isRead);
+      increaseTotalLatency(isRead, latencyMilliSec);
+      if (bytes > 0) {
+        increaseBytesCounter(isRead, bytes);
+      }
+    }
+  }
+
+  public void recordDataPropagationLatency(long latency) {
+    _dataPropagationLatencyGauge.updateValue(latency);
+    _dataPropagationLatencyGuage.updateValue(latency);
+  }
+
+  private void increaseFailureCounter(boolean isRead) {
+    if (isRead) {
+      _readFailureCounter.updateValue(_readFailureCounter.getValue() + 1);
+    } else {
+      _writeFailureCounter.updateValue(_writeFailureCounter.getValue() + 1);
+    }
+  }
+
+  private void increaseCounter(boolean isRead) {
+    if (isRead) {
+      _readCounter.updateValue(_readCounter.getValue() + 1);
+    } else {
+      _writeCounter.updateValue(_writeCounter.getValue() + 1);
+    }
+  }
+
+  private void increaseBytesCounter(boolean isRead, int bytes) {
+    if (isRead) {
+      _readBytesCounter.updateValue(_readBytesCounter.getValue() + bytes);
+      _readBytesGauge.updateValue((long) bytes);
+    } else {
+      _writeBytesCounter.updateValue(_writeBytesCounter.getValue() + bytes);
+      _writeBytesGauge.updateValue((long) bytes);
+    }
+  }
+
+  private void increaseTotalLatency(boolean isRead, long latencyDelta) {
+    if (isRead) {
+      _readTotalLatencyCounter.updateValue(_readTotalLatencyCounter.getValue() + latencyDelta);
+      _readLatencyGauge.updateValue(latencyDelta);
+    } else {
+      _writeTotalLatencyCounter.updateValue(_writeTotalLatencyCounter.getValue() + latencyDelta);
+      _writeLatencyGauge.updateValue(latencyDelta);
+    }
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/BasicZkSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/BasicZkSerializer.java
new file mode 100644
index 0000000..dc8d003
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/BasicZkSerializer.java
@@ -0,0 +1,41 @@
+package org.apache.helix.zookeeper.zkclient.serialize;
+
+/*
+ * 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.
+ */
+
+/**
+ * Basic path based serializer which ignores the path and delegates
+ * serialization into a regular {@link ZkSerializer}
+ */
+public class BasicZkSerializer implements PathBasedZkSerializer {
+  private final ZkSerializer _delegate;
+
+  public BasicZkSerializer(ZkSerializer delegate) {
+    _delegate = delegate;
+  }
+
+  public byte[] serialize(Object data, String path) {
+    return _delegate.serialize(data);
+  }
+
+  @Override
+  public Object deserialize(byte[] bytes, String path) {
+    return _delegate.deserialize(bytes);
+  }
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/BytesPushThroughSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/BytesPushThroughSerializer.java
new file mode 100644
index 0000000..f566483
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/BytesPushThroughSerializer.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.serialize;
+
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+
+
+/**
+ * A {@link ZkSerializer} which simply passes byte arrays to zk and back.
+ */
+public class BytesPushThroughSerializer implements ZkSerializer {
+
+    @Override
+    public Object deserialize(byte[] bytes) throws ZkMarshallingError {
+        return bytes;
+    }
+
+    @Override
+    public byte[] serialize(Object bytes) throws ZkMarshallingError {
+        return (byte[]) bytes;
+    }
+
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/PathBasedZkSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/PathBasedZkSerializer.java
new file mode 100644
index 0000000..93d4b61
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/PathBasedZkSerializer.java
@@ -0,0 +1,45 @@
+package org.apache.helix.zookeeper.zkclient.serialize;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+
+
+public interface PathBasedZkSerializer {
+
+  /**
+   * Serialize data differently according to different paths
+   * @param data
+   * @param path
+   * @return
+   * @throws ZkMarshallingError
+   */
+  public byte[] serialize(Object data, String path) throws ZkMarshallingError;
+
+  /**
+   * Deserialize data differently according to different paths
+   * @param bytes
+   * @param path
+   * @return
+   * @throws ZkMarshallingError
+   */
+  public Object deserialize(byte[] bytes, String path) throws ZkMarshallingError;
+
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/SerializableSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/SerializableSerializer.java
new file mode 100644
index 0000000..65a5983
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/SerializableSerializer.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.serialize;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+
+
+public class SerializableSerializer implements ZkSerializer {
+
+    @Override
+    public Object deserialize(byte[] bytes) throws ZkMarshallingError {
+        try {
+            ObjectInputStream inputStream = new TcclAwareObjectIputStream(new ByteArrayInputStream(bytes));
+            Object object = inputStream.readObject();
+            return object;
+        } catch (ClassNotFoundException e) {
+            throw new ZkMarshallingError("Unable to find object class.", e);
+        } catch (IOException e) {
+            throw new ZkMarshallingError(e);
+        }
+    }
+
+    @Override
+    public byte[] serialize(Object serializable) throws ZkMarshallingError {
+        try {
+            ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
+            ObjectOutputStream stream = new ObjectOutputStream(byteArrayOS);
+            stream.writeObject(serializable);
+            stream.close();
+            return byteArrayOS.toByteArray();
+        } catch (IOException e) {
+            throw new ZkMarshallingError(e);
+        }
+    }
+
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/TcclAwareObjectIputStream.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/TcclAwareObjectIputStream.java
new file mode 100644
index 0000000..8fb21e3
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/TcclAwareObjectIputStream.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.serialize;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Proxy;
+
+/**
+ * An ObjectInputStream that is aware of the TCCL.
+ */
+public class TcclAwareObjectIputStream extends ObjectInputStream {
+
+	public TcclAwareObjectIputStream(InputStream in) throws IOException {
+		super(in);
+	}
+
+	/**
+	 * Load the local class equivalent of the specified stream class
+	 * description.
+	 * Uses the current class {@link ClassLoader} and falls back to the {@link Thread} context {@link ClassLoader}.
+	 */
+	protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
+		try {
+			return getClass().getClassLoader().loadClass(classDesc.getName());
+		} catch (ClassNotFoundException ex) {
+			ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+			if (tccl != null) {
+				return tccl.loadClass(classDesc.getName());
+			} else {
+				throw ex;
+			}
+		}
+	}
+
+	/**
+	 * Returns a proxy class that implements the interfaces named in a proxy
+	 * class descriptor; subclasses may implement this method to read custom
+	 * data from the stream along with the descriptors for dynamic proxy
+	 * classes, allowing them to use an alternate loading mechanism for the
+	 * interfaces and the proxy class.
+	 *
+	 * For each interface uses the current class {@link ClassLoader} and falls back to the {@link Thread} context {@link ClassLoader}.
+	 */
+	protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
+		ClassLoader cl = getClass().getClassLoader();
+		Class[] cinterfaces = new Class[interfaces.length];
+
+		for (int i = 0; i < interfaces.length; i++) {
+			try {
+				cinterfaces[i] = cl.loadClass(interfaces[i]);
+			} catch (ClassNotFoundException ex) {
+				ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+				if (tccl != null) {
+					return tccl.loadClass(interfaces[i]);
+				} else {
+					throw ex;
+				}
+			}
+		}
+		try {
+			return Proxy.getProxyClass(cinterfaces[0].getClassLoader(), cinterfaces);
+		} catch (IllegalArgumentException e) {
+			throw new ClassNotFoundException(null, e);
+		}
+	}
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/ZkSerializer.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/ZkSerializer.java
new file mode 100644
index 0000000..e9f065e
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/serialize/ZkSerializer.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed 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.helix.zookeeper.zkclient.serialize;
+
+import org.apache.helix.zookeeper.zkclient.exception.ZkMarshallingError;
+
+
+/**
+ * Zookeeper is able to store data in form of byte arrays. This interface is a bridge between those byte-array format
+ * and higher level objects.
+ *
+ * @see BytesPushThroughSerializer
+ * @see SerializableSerializer
+ */
+public interface ZkSerializer {
+
+    public byte[] serialize(Object data) throws ZkMarshallingError;
+
+    public Object deserialize(byte[] bytes) throws ZkMarshallingError;
+}
diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/util/ExponentialBackoffStrategy.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/util/ExponentialBackoffStrategy.java
new file mode 100644
index 0000000..c6c6cc8
--- /dev/null
+++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/util/ExponentialBackoffStrategy.java
@@ -0,0 +1,53 @@
+package org.apache.helix.zookeeper.zkclient.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Random;
+
+
+public class ExponentialBackoffStrategy {
+  private final long INIT_RETRY_INTERVAL = 500;
+  private final long _maxRetryInterval;
+  private final boolean _addJitter;
+  private final Random _ran;
+
+  public ExponentialBackoffStrategy(long maxRetryInterval, boolean addJitter) {
+    _maxRetryInterval = maxRetryInterval;
+    _addJitter = addJitter;
+    _ran = new Random(System.currentTimeMillis());
+  }
+
+  public long getNextWaitInterval(int numberOfTriesFailed) {
+    double exponentialMultiplier = Math.pow(2.0, numberOfTriesFailed - 1);
+    double result = exponentialMultiplier * INIT_RETRY_INTERVAL;
+
+    if (_maxRetryInterval > 0 && result > _maxRetryInterval) {
+      result = _maxRetryInterval;
+    }
+
+    if (_addJitter) {
+      // Adding jitter so the real result would be 75% to 100% of the original result.
+      // Don't directly add jitter here, since it may exceed the max retry interval setup
+      result = result * (0.75 + _ran.nextDouble() % 0.25);
+    }
+
+    return (long) result;
+  }
+}
diff --git a/zookeeper-api/src/test/conf/testng.xml b/zookeeper-api/src/test/conf/testng.xml
new file mode 100644
index 0000000..6c78c76a4
--- /dev/null
+++ b/zookeeper-api/src/test/conf/testng.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Suite" parallel="false">
+  <test name="Test" preserve-order="true">
+    <packages>
+      <package name="org.apache.helix.zookeeper.api.*"/>
+    </packages>
+  </test>
+</suite>
diff --git a/zookeeper-api/src/test/resources/log4j.properties b/zookeeper-api/src/test/resources/log4j.properties
new file mode 100644
index 0000000..24b6d10
--- /dev/null
+++ b/zookeeper-api/src/test/resources/log4j.properties
@@ -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.
+#
+# Set root logger level to DEBUG and its only appender to R.
+log4j.rootLogger=ERROR, C
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.C=org.apache.log4j.ConsoleAppender
+log4j.appender.C.layout=org.apache.log4j.PatternLayout
+log4j.appender.C.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+
+log4j.appender.R=org.apache.log4j.RollingFileAppender
+log4j.appender.R.layout=org.apache.log4j.PatternLayout
+log4j.appender.R.layout.ConversionPattern=%5p [%C:%M] (%F:%L) - %m%n
+log4j.appender.R.File=target/ClusterManagerLogs/log.txt
+
+log4j.appender.STATUSDUMP=org.apache.log4j.RollingFileAppender
+log4j.appender.STATUSDUMP.layout=org.apache.log4j.SimpleLayout
+log4j.appender.STATUSDUMP.File=target/ClusterManagerLogs/statusUpdates.log
+
+log4j.logger.org.I0Itec=ERROR
+log4j.logger.org.apache=ERROR
+log4j.logger.com.noelios=ERROR
+log4j.logger.org.restlet=ERROR
+
+log4j.logger.org.apache.helix.monitoring.ZKPathDataDumpTask=ERROR,STATUSDUMP
diff --git a/zookeeper-api/zookeeper-api-0.9.2-SNAPSHOT.ivy b/zookeeper-api/zookeeper-api-0.9.2-SNAPSHOT.ivy
new file mode 100644
index 0000000..19a9ed8
--- /dev/null
+++ b/zookeeper-api/zookeeper-api-0.9.2-SNAPSHOT.ivy
@@ -0,0 +1,50 @@
+<?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.
+-->
+<ivy-module version="1.0">
+	<info organisation="org.apache.helix"
+		module="zookeeper-api"
+		revision="0.9.2-SNAPSHOT"
+		status="integration"
+		publication="20170128141623"
+	/>
+	<configurations>
+		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
+		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
+		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
+		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
+		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
+		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases."/>
+		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
+	</configurations>
+	<publications>
+		<artifact name="zookeeper-api" type="jar" ext="jar" conf="master"/>
+	</publications>
+	<dependencies>
+	  <dependency org="org.slf4j" name="slf4j-api" rev="1.7.25" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)">
+        <artifact name="slf4j-api" ext="jar"/>
+    </dependency>
+    <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.14" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)">
+        <artifact name="slf4j-log4j12" ext="jar"/>
+    </dependency>
+		<dependency org="org.codehaus.jackson" name="jackson-core-asl" rev="1.8.5" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>
+		<dependency org="org.codehaus.jackson" name="jackson-mapper-asl" rev="1.8.5" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>
+		<dependency org="commons-cli" name="commons-cli" rev="1.2" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>
+	</dependencies>
+</ivy-module>