You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by an...@apache.org on 2018/10/24 09:32:26 UTC
[10/36] zookeeper git commit: ZOOKEEPER-3032: MAVEN MIGRATION -
branch-3.4 - zookeeper-server
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/ACLTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/ACLTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/ACLTest.java
new file mode 100644
index 0000000..793265c
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/ACLTest.java
@@ -0,0 +1,186 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.PortAssignment;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZKTestCase;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.server.ServerCnxnFactory;
+import org.apache.zookeeper.server.SyncRequestProcessor;
+import org.apache.zookeeper.server.ZooKeeperServer;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ACLTest extends ZKTestCase implements Watcher {
+ private static final Logger LOG = LoggerFactory.getLogger(ACLTest.class);
+ private static final String HOSTPORT =
+ "127.0.0.1:" + PortAssignment.unique();
+ private volatile CountDownLatch startSignal;
+
+ @Test
+ public void testDisconnectedAddAuth() throws Exception {
+ File tmpDir = ClientBase.createTmpDir();
+ ClientBase.setupTestEnv();
+ ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000);
+ SyncRequestProcessor.setSnapCount(1000);
+ final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]);
+ ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1);
+ f.startup(zks);
+ try {
+ LOG.info("starting up the zookeeper server .. waiting");
+ Assert.assertTrue("waiting for server being up",
+ ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT));
+ ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this);
+ try {
+ zk.addAuthInfo("digest", "pat:test".getBytes());
+ zk.setACL("/", Ids.CREATOR_ALL_ACL, -1);
+ } finally {
+ zk.close();
+ }
+ } finally {
+ f.shutdown();
+ zks.shutdown();
+ Assert.assertTrue("waiting for server down",
+ ClientBase.waitForServerDown(HOSTPORT,
+ ClientBase.CONNECTION_TIMEOUT));
+ }
+ }
+
+ /**
+ * Verify that acl optimization of storing just
+ * a few acls and there references in the data
+ * node is actually working.
+ */
+ @Test
+ public void testAcls() throws Exception {
+ File tmpDir = ClientBase.createTmpDir();
+ ClientBase.setupTestEnv();
+ ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000);
+ SyncRequestProcessor.setSnapCount(1000);
+ final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]);
+ ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1);
+ f.startup(zks);
+ ZooKeeper zk;
+ String path;
+ try {
+ LOG.info("starting up the zookeeper server .. waiting");
+ Assert.assertTrue("waiting for server being up",
+ ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT));
+ zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this);
+ LOG.info("starting creating acls");
+ for (int i = 0; i < 100; i++) {
+ path = "/" + i;
+ zk.create(path, path.getBytes(), Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ }
+ Assert.assertTrue("size of the acl map ", (1 == zks.getZKDatabase().getAclSize()));
+ for (int j = 100; j < 200; j++) {
+ path = "/" + j;
+ ACL acl = new ACL();
+ acl.setPerms(0);
+ Id id = new Id();
+ id.setId("1.1.1."+j);
+ id.setScheme("ip");
+ acl.setId(id);
+ ArrayList<ACL> list = new ArrayList<ACL>();
+ list.add(acl);
+ zk.create(path, path.getBytes(), list, CreateMode.PERSISTENT);
+ }
+ Assert.assertTrue("size of the acl map ", (101 == zks.getZKDatabase().getAclSize()));
+ } finally {
+ // now shutdown the server and restart it
+ f.shutdown();
+ zks.shutdown();
+ Assert.assertTrue("waiting for server down",
+ ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT));
+ }
+ startSignal = new CountDownLatch(1);
+
+ zks = new ZooKeeperServer(tmpDir, tmpDir, 3000);
+ f = ServerCnxnFactory.createFactory(PORT, -1);
+
+ f.startup(zks);
+ try {
+ Assert.assertTrue("waiting for server up",
+ ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT));
+
+ startSignal.await(CONNECTION_TIMEOUT,
+ TimeUnit.MILLISECONDS);
+ Assert.assertTrue("count == 0", startSignal.getCount() == 0);
+
+ Assert.assertTrue("acl map ", (101 == zks.getZKDatabase().getAclSize()));
+ for (int j = 200; j < 205; j++) {
+ path = "/" + j;
+ ACL acl = new ACL();
+ acl.setPerms(0);
+ Id id = new Id();
+ id.setId("1.1.1."+j);
+ id.setScheme("ip");
+ acl.setId(id);
+ ArrayList<ACL> list = new ArrayList<ACL>();
+ list.add(acl);
+ zk.create(path, path.getBytes(), list, CreateMode.PERSISTENT);
+ }
+ Assert.assertTrue("acl map ", (106 == zks.getZKDatabase().getAclSize()));
+
+ zk.close();
+ } finally {
+ f.shutdown();
+ zks.shutdown();
+ Assert.assertTrue("waiting for server down",
+ ClientBase.waitForServerDown(HOSTPORT,
+ ClientBase.CONNECTION_TIMEOUT));
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.zookeeper.Watcher#process(org.apache.zookeeper.WatcherEvent)
+ */
+ public void process(WatchedEvent event) {
+ LOG.info("Event:" + event.getState() + " " + event.getType() + " "
+ + event.getPath());
+ if (event.getState() == KeeperState.SyncConnected) {
+ if (startSignal != null && startSignal.getCount() > 0) {
+ LOG.info("startsignal.countDown()");
+ startSignal.countDown();
+ } else {
+ LOG.warn("startsignal " + startSignal);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncHammerTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncHammerTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncHammerTest.java
new file mode 100644
index 0000000..ebfc963
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncHammerTest.java
@@ -0,0 +1,245 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
+import static org.apache.zookeeper.test.ClientBase.verifyThreadTerminated;
+
+import java.util.LinkedList;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.TestableZooKeeper;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.ZKTestCase;
+import org.apache.zookeeper.AsyncCallback.DataCallback;
+import org.apache.zookeeper.AsyncCallback.StringCallback;
+import org.apache.zookeeper.AsyncCallback.VoidCallback;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.test.ClientBase.CountdownWatcher;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AsyncHammerTest extends ZKTestCase
+ implements StringCallback, VoidCallback, DataCallback
+{
+ private static final Logger LOG = LoggerFactory.getLogger(AsyncHammerTest.class);
+
+ private QuorumBase qb = new QuorumBase();
+
+ private volatile boolean bang;
+
+ public void setUp(boolean withObservers) throws Exception {
+ qb.setUp(withObservers);
+ }
+
+ protected void restart() throws Exception {
+ LOG.info("RESTARTING " + getTestName());
+ qb.tearDown();
+
+ // don't call setup - we don't want to reassign ports/dirs, etc...
+ JMXEnv.setUp();
+ qb.startServers();
+ }
+
+ public void tearDown() throws Exception {
+ LOG.info("Test clients shutting down");
+ qb.tearDown();
+ }
+
+ /**
+ * Create /test- sequence nodes asynchronously, max 30 outstanding
+ */
+ class HammerThread extends Thread implements StringCallback, VoidCallback {
+ private static final int MAX_OUTSTANDING = 30;
+
+ private TestableZooKeeper zk;
+ private int outstanding;
+
+ private volatile boolean failed = false;
+
+ public HammerThread(String name) {
+ super(name);
+ }
+
+ public void run() {
+ try {
+ CountdownWatcher watcher = new CountdownWatcher();
+ zk = new TestableZooKeeper(qb.hostPort, CONNECTION_TIMEOUT,
+ watcher);
+ watcher.waitForConnected(CONNECTION_TIMEOUT);
+ while(bang) {
+ incOutstanding(); // before create otw race
+ zk.create("/test-", new byte[0], Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT_SEQUENTIAL, this, null);
+ }
+ } catch (InterruptedException e) {
+ if (bang) {
+ LOG.error("sanity check Assert.failed!!!"); // sanity check
+ return;
+ }
+ } catch (Exception e) {
+ LOG.error("Client create operation Assert.failed", e);
+ return;
+ } finally {
+ if (zk != null) {
+ try {
+ zk.close();
+ if (!zk.testableWaitForShutdown(CONNECTION_TIMEOUT)) {
+ failed = true;
+ LOG.error("Client did not shutdown");
+ }
+ } catch (InterruptedException e) {
+ LOG.info("Interrupted", e);
+ }
+ }
+ }
+ }
+
+ private synchronized void incOutstanding() throws InterruptedException {
+ outstanding++;
+ while(outstanding > MAX_OUTSTANDING) {
+ wait();
+ }
+ }
+
+ private synchronized void decOutstanding() {
+ outstanding--;
+ Assert.assertTrue("outstanding >= 0", outstanding >= 0);
+ notifyAll();
+ }
+
+ public void process(WatchedEvent event) {
+ // ignore for purposes of this test
+ }
+
+ public void processResult(int rc, String path, Object ctx, String name) {
+ if (rc != KeeperException.Code.OK.intValue()) {
+ if (bang) {
+ failed = true;
+ LOG.error("Create Assert.failed for 0x"
+ + Long.toHexString(zk.getSessionId())
+ + "with rc:" + rc + " path:" + path);
+ }
+ decOutstanding();
+ return;
+ }
+ try {
+ decOutstanding();
+ zk.delete(name, -1, this, null);
+ } catch (Exception e) {
+ if (bang) {
+ failed = true;
+ LOG.error("Client delete Assert.failed", e);
+ }
+ }
+ }
+
+ public void processResult(int rc, String path, Object ctx) {
+ if (rc != KeeperException.Code.OK.intValue()) {
+ if (bang) {
+ failed = true;
+ LOG.error("Delete Assert.failed for 0x"
+ + Long.toHexString(zk.getSessionId())
+ + "with rc:" + rc + " path:" + path);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testHammer() throws Exception {
+ setUp(false);
+ bang = true;
+ LOG.info("Starting hammers");
+ HammerThread[] hammers = new HammerThread[100];
+ for (int i = 0; i < hammers.length; i++) {
+ hammers[i] = new HammerThread("HammerThread-" + i);
+ hammers[i].start();
+ }
+ LOG.info("Started hammers");
+ Thread.sleep(5000); // allow the clients to run for max 5sec
+ bang = false;
+ LOG.info("Stopping hammers");
+ for (int i = 0; i < hammers.length; i++) {
+ hammers[i].interrupt();
+ verifyThreadTerminated(hammers[i], 60000);
+ Assert.assertFalse(hammers[i].failed);
+ }
+
+ // before restart
+ LOG.info("Hammers stopped, verifying consistency");
+ qb.verifyRootOfAllServersMatch(qb.hostPort);
+
+ restart();
+
+ // after restart
+ LOG.info("Verifying hammers 2");
+ qb.verifyRootOfAllServersMatch(qb.hostPort);
+ tearDown();
+ }
+
+ @Test
+ public void testObserversHammer() throws Exception {
+ setUp(true);
+ bang = true;
+ Thread[] hammers = new Thread[100];
+ for (int i = 0; i < hammers.length; i++) {
+ hammers[i] = new HammerThread("HammerThread-" + i);
+ hammers[i].start();
+ }
+ Thread.sleep(5000); // allow the clients to run for max 5sec
+ bang = false;
+ for (int i = 0; i < hammers.length; i++) {
+ hammers[i].interrupt();
+ verifyThreadTerminated(hammers[i], 60000);
+ }
+ // before restart
+ qb.verifyRootOfAllServersMatch(qb.hostPort);
+ tearDown();
+ }
+
+ @SuppressWarnings("unchecked")
+ public void processResult(int rc, String path, Object ctx, String name) {
+ synchronized(ctx) {
+ ((LinkedList<Integer>)ctx).add(rc);
+ ctx.notifyAll();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void processResult(int rc, String path, Object ctx) {
+ synchronized(ctx) {
+ ((LinkedList<Integer>)ctx).add(rc);
+ ctx.notifyAll();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void processResult(int rc, String path, Object ctx, byte[] data,
+ Stat stat) {
+ synchronized(ctx) {
+ ((LinkedList<Integer>)ctx).add(rc);
+ ctx.notifyAll();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOps.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOps.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOps.java
new file mode 100644
index 0000000..2dbe644
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOps.java
@@ -0,0 +1,655 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.AsyncCallback.ACLCallback;
+import org.apache.zookeeper.AsyncCallback.Children2Callback;
+import org.apache.zookeeper.AsyncCallback.ChildrenCallback;
+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.ZooDefs.Ids;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Assert;
+
+public class AsyncOps {
+ /**
+ * This is the base class for all of the async callback classes. It will
+ * verify the expected value against the actual value.
+ *
+ * Basic operation is that the subclasses will generate an "expected" value
+ * which is defined by the "toString" method of the subclass. This is
+ * passed through to the verify clause by specifying it as the ctx object
+ * of each async call (processResult methods get the ctx as part of
+ * the callback). Additionally the callback will also overwrite any
+ * instance fields with matching parameter arguments to the processResult
+ * method. The cb instance can then compare the expected to the
+ * actual value by again calling toString and comparing the two.
+ *
+ * The format of each expected value differs (is defined) by subclass.
+ * Generally the expected value starts with the result code (rc) and path
+ * of the node being operated on, followed by the fields specific to
+ * each operation type (cb subclass). For example ChildrenCB specifies
+ * a list of the expected children suffixed onto the rc and path. See
+ * the toString() method of each subclass for details of it's format.
+ */
+ public static abstract class AsyncCB {
+ protected final ZooKeeper zk;
+ protected long defaultTimeoutMillis = 30000;
+
+ /** the latch is used to await the results from the server */
+ CountDownLatch latch;
+
+ Code rc = Code.OK;
+ String path = "/foo";
+ String expected;
+
+ public AsyncCB(ZooKeeper zk, CountDownLatch latch) {
+ this.zk = zk;
+ this.latch = latch;
+ }
+
+ public void setRC(Code rc) {
+ this.rc = rc;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public void processResult(Code rc, String path, Object ctx)
+ {
+ this.rc = rc;
+ this.path = path;
+ this.expected = (String)ctx;
+ latch.countDown();
+ }
+
+ /** String format is rc:path:<suffix> where <suffix> is defined by each
+ * subclass individually. */
+ @Override
+ public String toString() {
+ return rc + ":" + path + ":";
+ }
+
+ protected void verify() {
+ try {
+ latch.await(defaultTimeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Assert.fail("unexpected interrupt");
+ }
+ // on the lookout for timeout
+ Assert.assertSame(0L, latch.getCount());
+
+ String actual = toString();
+
+ Assert.assertEquals(expected, actual);
+ }
+ }
+
+ public static class StringCB extends AsyncCB implements StringCallback {
+ byte[] data = new byte[10];
+ List<ACL> acl = Ids.CREATOR_ALL_ACL;
+ CreateMode flags = CreateMode.PERSISTENT;
+ String name = path;
+
+ StringCB(ZooKeeper zk) {
+ this(zk, new CountDownLatch(1));
+ }
+
+ StringCB(ZooKeeper zk, CountDownLatch latch) {
+ super(zk, latch);
+ }
+
+ public void setPath(String path) {
+ super.setPath(path);
+ this.name = path;
+ }
+
+ public String nodeName() {
+ return path.substring(path.lastIndexOf('/') + 1);
+ }
+
+ public void processResult(int rc, String path, Object ctx, String name)
+ {
+ this.name = name;
+ super.processResult(Code.get(rc), path, ctx);
+ }
+
+ public AsyncCB create() {
+ zk.create(path, data, acl, flags, this, toString());
+ return this;
+ }
+
+ public AsyncCB createEphemeral() {
+ zk.create(path, data, acl, CreateMode.EPHEMERAL, this, toString());
+ return this;
+ }
+
+ public void verifyCreate() {
+ create();
+ verify();
+ }
+
+ public void verifyCreateEphemeral() {
+ createEphemeral();
+ verify();
+ }
+
+ public void verifyCreateFailure_NodeExists() {
+ new StringCB(zk).verifyCreate();
+
+ rc = Code.NODEEXISTS;
+ name = null;
+ zk.create(path, data, acl, flags, this, toString());
+ verify();
+ }
+
+ public void verifyCreateFailure_NoNode() {
+
+ rc = Code.NONODE;
+ name = null;
+ path = path + "/bar";
+ zk.create(path, data, acl, flags, this, toString());
+
+ verify();
+ }
+
+ public void verifyCreateFailure_NoChildForEphemeral() {
+ new StringCB(zk).verifyCreateEphemeral();
+
+ rc = Code.NOCHILDRENFOREPHEMERALS;
+ name = null;
+ path = path + "/bar";
+ zk.create(path, data, acl, flags, this, toString());
+
+ verify();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + name;
+ }
+ }
+
+ public static class ACLCB extends AsyncCB implements ACLCallback {
+ List<ACL> acl = Ids.CREATOR_ALL_ACL;
+ int version = 0;
+ Stat stat = new Stat();
+ byte[] data = "testing".getBytes();
+
+ ACLCB(ZooKeeper zk) {
+ this(zk, new CountDownLatch(1));
+ }
+
+ ACLCB(ZooKeeper zk, CountDownLatch latch) {
+ super(zk, latch);
+ stat.setAversion(0);
+ stat.setCversion(0);
+ stat.setEphemeralOwner(0);
+ stat.setVersion(0);
+ }
+
+ public void processResult(int rc, String path, Object ctx,
+ List<ACL> acl, Stat stat)
+ {
+ this.acl = acl;
+ this.stat = stat;
+ super.processResult(Code.get(rc), path, ctx);
+ }
+
+ public void verifyGetACL() {
+ new StringCB(zk).verifyCreate();
+
+ zk.getACL(path, stat, this, toString());
+ verify();
+ }
+
+ public void verifyGetACLFailure_NoNode(){
+ rc = Code.NONODE;
+ stat = null;
+ acl = null;
+ zk.getACL(path, stat, this, toString());
+
+ verify();
+ }
+
+ public String toString(List<ACL> acls) {
+ if (acls == null) {
+ return "";
+ }
+
+ StringBuilder result = new StringBuilder();
+ for(ACL acl : acls) {
+ result.append(acl.getPerms() + "::");
+ }
+ return result.toString();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + toString(acl) + ":"
+ + ":" + version + ":" + new String(data)
+ + ":" + (stat == null ? "null" : stat.getAversion() + ":"
+ + stat.getCversion() + ":" + stat.getEphemeralOwner()
+ + ":" + stat.getVersion());
+ }
+ }
+
+ public static class ChildrenCB extends AsyncCB implements ChildrenCallback {
+ List<String> children = new ArrayList<String>();
+
+ ChildrenCB(ZooKeeper zk) {
+ this(zk, new CountDownLatch(1));
+ }
+
+ ChildrenCB(ZooKeeper zk, CountDownLatch latch) {
+ super(zk, latch);
+ }
+
+ public void processResult(int rc, String path, Object ctx,
+ List<String> children)
+ {
+ this.children =
+ (children == null ? new ArrayList<String>() : children);
+ Collections.sort(this.children);
+ super.processResult(Code.get(rc), path, ctx);
+ }
+
+ public StringCB createNode() {
+ StringCB parent = new StringCB(zk);
+ parent.verifyCreate();
+
+ return parent;
+ }
+
+ public StringCB createNode(StringCB parent) {
+ String childName = "bar";
+
+ return createNode(parent, childName);
+ }
+
+ public StringCB createNode(StringCB parent, String childName) {
+ StringCB child = new StringCB(zk);
+ child.setPath(parent.path + "/" + childName);
+ child.verifyCreate();
+
+ return child;
+ }
+
+ public void verifyGetChildrenEmpty() {
+ StringCB parent = createNode();
+ path = parent.path;
+ verify();
+ }
+
+ public void verifyGetChildrenSingle() {
+ StringCB parent = createNode();
+ StringCB child = createNode(parent);
+
+ path = parent.path;
+ children.add(child.nodeName());
+
+ verify();
+ }
+
+ public void verifyGetChildrenTwo() {
+ StringCB parent = createNode();
+ StringCB child1 = createNode(parent, "child1");
+ StringCB child2 = createNode(parent, "child2");
+
+ path = parent.path;
+ children.add(child1.nodeName());
+ children.add(child2.nodeName());
+
+ verify();
+ }
+
+ public void verifyGetChildrenFailure_NoNode() {
+ rc = KeeperException.Code.NONODE;
+ verify();
+ }
+
+ @Override
+ public void verify() {
+ zk.getChildren(path, false, this, toString());
+ super.verify();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + children.toString();
+ }
+ }
+
+ public static class Children2CB extends AsyncCB implements Children2Callback {
+ List<String> children = new ArrayList<String>();
+
+ Children2CB(ZooKeeper zk) {
+ this(zk, new CountDownLatch(1));
+ }
+
+ Children2CB(ZooKeeper zk, CountDownLatch latch) {
+ super(zk, latch);
+ }
+
+ public void processResult(int rc, String path, Object ctx,
+ List<String> children, Stat stat)
+ {
+ this.children =
+ (children == null ? new ArrayList<String>() : children);
+ Collections.sort(this.children);
+ super.processResult(Code.get(rc), path, ctx);
+ }
+
+ public StringCB createNode() {
+ StringCB parent = new StringCB(zk);
+ parent.verifyCreate();
+
+ return parent;
+ }
+
+ public StringCB createNode(StringCB parent) {
+ String childName = "bar";
+
+ return createNode(parent, childName);
+ }
+
+ public StringCB createNode(StringCB parent, String childName) {
+ StringCB child = new StringCB(zk);
+ child.setPath(parent.path + "/" + childName);
+ child.verifyCreate();
+
+ return child;
+ }
+
+ public void verifyGetChildrenEmpty() {
+ StringCB parent = createNode();
+ path = parent.path;
+ verify();
+ }
+
+ public void verifyGetChildrenSingle() {
+ StringCB parent = createNode();
+ StringCB child = createNode(parent);
+
+ path = parent.path;
+ children.add(child.nodeName());
+
+ verify();
+ }
+
+ public void verifyGetChildrenTwo() {
+ StringCB parent = createNode();
+ StringCB child1 = createNode(parent, "child1");
+ StringCB child2 = createNode(parent, "child2");
+
+ path = parent.path;
+ children.add(child1.nodeName());
+ children.add(child2.nodeName());
+
+ verify();
+ }
+
+ public void verifyGetChildrenFailure_NoNode() {
+ rc = KeeperException.Code.NONODE;
+ verify();
+ }
+
+ @Override
+ public void verify() {
+ zk.getChildren(path, false, this, toString());
+ super.verify();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + children.toString();
+ }
+ }
+
+ public static class DataCB extends AsyncCB implements DataCallback {
+ byte[] data = new byte[10];
+ Stat stat = new Stat();
+
+ DataCB(ZooKeeper zk) {
+ this(zk, new CountDownLatch(1));
+ }
+
+ DataCB(ZooKeeper zk, CountDownLatch latch) {
+ super(zk, latch);
+ stat.setAversion(0);
+ stat.setCversion(0);
+ stat.setEphemeralOwner(0);
+ stat.setVersion(0);
+ }
+
+ public void processResult(int rc, String path, Object ctx, byte[] data,
+ Stat stat)
+ {
+ this.data = data;
+ this.stat = stat;
+ super.processResult(Code.get(rc), path, ctx);
+ }
+
+ public void verifyGetData() {
+ new StringCB(zk).verifyCreate();
+
+ zk.getData(path, false, this, toString());
+ verify();
+ }
+
+ public void verifyGetDataFailure_NoNode() {
+ rc = KeeperException.Code.NONODE;
+ data = null;
+ stat = null;
+ zk.getData(path, false, this, toString());
+ verify();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString()
+ + ":" + (data == null ? "null" : new String(data))
+ + ":" + (stat == null ? "null" : stat.getAversion() + ":"
+ + stat.getCversion() + ":" + stat.getEphemeralOwner()
+ + ":" + stat.getVersion());
+ }
+ }
+
+ public static class StatCB extends AsyncCB implements StatCallback {
+ List<ACL> acl = Ids.CREATOR_ALL_ACL;
+ int version = 0;
+ Stat stat = new Stat();
+ byte[] data = "testing".getBytes();
+
+ StatCB(ZooKeeper zk) {
+ this(zk, new CountDownLatch(1));
+ }
+
+ StatCB(ZooKeeper zk, CountDownLatch latch) {
+ super(zk, latch);
+ stat.setAversion(0);
+ stat.setCversion(0);
+ stat.setEphemeralOwner(0);
+ stat.setVersion(0);
+ }
+
+ public void processResult(int rc, String path, Object ctx, Stat stat) {
+ this.stat = stat;
+ super.processResult(Code.get(rc), path, ctx);
+ }
+
+ public void verifySetACL() {
+ stat.setAversion(1);
+ new StringCB(zk).verifyCreate();
+
+ zk.setACL(path, acl, version, this, toString());
+ verify();
+ }
+
+ public void verifySetACLFailure_NoNode() {
+ rc = KeeperException.Code.NONODE;
+ stat = null;
+ zk.setACL(path, acl, version, this, toString());
+ verify();
+ }
+
+ public void verifySetACLFailure_BadVersion() {
+ new StringCB(zk).verifyCreate();
+
+ rc = Code.BADVERSION;
+ stat = null;
+ zk.setACL(path, acl, version + 1, this, toString());
+
+ verify();
+ }
+
+ public void setData() {
+ zk.setData(path, data, version, this, toString());
+ }
+
+ public void verifySetData() {
+ stat.setVersion(1);
+ new StringCB(zk).verifyCreate();
+
+ setData();
+ verify();
+ }
+
+ public void verifySetDataFailure_NoNode() {
+ rc = KeeperException.Code.NONODE;
+ stat = null;
+ zk.setData(path, data, version, this, toString());
+ verify();
+ }
+
+ public void verifySetDataFailure_BadVersion() {
+ new StringCB(zk).verifyCreate();
+
+ rc = Code.BADVERSION;
+ stat = null;
+ zk.setData(path, data, version + 1, this, toString());
+
+ verify();
+ }
+
+ public void verifyExists() {
+ new StringCB(zk).verifyCreate();
+
+ zk.exists(path, false, this, toString());
+ verify();
+ }
+
+ public void verifyExistsFailure_NoNode() {
+ rc = KeeperException.Code.NONODE;
+ stat = null;
+ zk.exists(path, false, this, toString());
+ verify();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + version
+ + ":" + new String(data)
+ + ":" + (stat == null ? "null" : stat.getAversion() + ":"
+ + stat.getCversion() + ":" + stat.getEphemeralOwner()
+ + ":" + stat.getVersion());
+ }
+ }
+
+ public static class VoidCB extends AsyncCB implements VoidCallback {
+ int version = 0;
+
+ VoidCB(ZooKeeper zk) {
+ this(zk, new CountDownLatch(1));
+ }
+
+ VoidCB(ZooKeeper zk, CountDownLatch latch) {
+ super(zk, latch);
+ }
+
+ public void processResult(int rc, String path, Object ctx) {
+ super.processResult(Code.get(rc), path, ctx);
+ }
+
+ public void delete() {
+ zk.delete(path, version, this, toString());
+ }
+
+ public void verifyDelete() {
+ new StringCB(zk).verifyCreate();
+
+ delete();
+ verify();
+ }
+
+ public void verifyDeleteFailure_NoNode() {
+ rc = Code.NONODE;
+ zk.delete(path, version, this, toString());
+ verify();
+ }
+
+ public void verifyDeleteFailure_BadVersion() {
+ new StringCB(zk).verifyCreate();
+ rc = Code.BADVERSION;
+ zk.delete(path, version + 1, this, toString());
+ verify();
+ }
+
+ public void verifyDeleteFailure_NotEmpty() {
+ StringCB scb = new StringCB(zk);
+ scb.create();
+ scb.setPath(path + "/bar");
+ scb.create();
+
+ rc = Code.NOTEMPTY;
+ zk.delete(path, version, this, toString());
+ verify();
+ }
+
+ public void sync() {
+ zk.sync(path, this, toString());
+ }
+
+ public void verifySync() {
+ sync();
+ verify();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + version;
+ }
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOpsTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOpsTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOpsTest.java
new file mode 100644
index 0000000..c807d72
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOpsTest.java
@@ -0,0 +1,227 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.test.AsyncOps.ACLCB;
+import org.apache.zookeeper.test.AsyncOps.Children2CB;
+import org.apache.zookeeper.test.AsyncOps.ChildrenCB;
+import org.apache.zookeeper.test.AsyncOps.DataCB;
+import org.apache.zookeeper.test.AsyncOps.StatCB;
+import org.apache.zookeeper.test.AsyncOps.StringCB;
+import org.apache.zookeeper.test.AsyncOps.VoidCB;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AsyncOpsTest extends ClientBase {
+ private static final Logger LOG = LoggerFactory.getLogger(AsyncOpsTest.class);
+
+ private ZooKeeper zk;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ LOG.info("Creating client " + getTestName());
+
+ zk = createClient();
+ zk.addAuthInfo("digest", "ben:passwd".getBytes());
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ zk.close();
+
+ super.tearDown();
+
+ LOG.info("Test clients shutting down");
+ }
+
+ @Test
+ public void testAsyncCreate() {
+ new StringCB(zk).verifyCreate();
+ }
+
+ @Test
+ public void testAsyncCreateThree() {
+ CountDownLatch latch = new CountDownLatch(3);
+
+ StringCB op1 = new StringCB(zk, latch);
+ op1.setPath("/op1");
+ StringCB op2 = new StringCB(zk, latch);
+ op2.setPath("/op2");
+ StringCB op3 = new StringCB(zk, latch);
+ op3.setPath("/op3");
+
+ op1.create();
+ op2.create();
+ op3.create();
+
+ op1.verify();
+ op2.verify();
+ op3.verify();
+ }
+
+ @Test
+ public void testAsyncCreateFailure_NodeExists() {
+ new StringCB(zk).verifyCreateFailure_NodeExists();
+ }
+
+ @Test
+ public void testAsyncCreateFailure_NoNode() {
+ new StringCB(zk).verifyCreateFailure_NoNode();
+ }
+
+ @Test
+ public void testAsyncCreateFailure_NoChildForEphemeral() {
+ new StringCB(zk).verifyCreateFailure_NoChildForEphemeral();
+ }
+
+ @Test
+ public void testAsyncDelete() {
+ new VoidCB(zk).verifyDelete();
+ }
+
+ @Test
+ public void testAsyncDeleteFailure_NoNode() {
+ new VoidCB(zk).verifyDeleteFailure_NoNode();
+ }
+
+ @Test
+ public void testAsyncDeleteFailure_BadVersion() {
+ new VoidCB(zk).verifyDeleteFailure_BadVersion();
+ }
+
+ @Test
+ public void testAsyncDeleteFailure_NotEmpty() {
+ new VoidCB(zk).verifyDeleteFailure_NotEmpty();
+ }
+
+ @Test
+ public void testAsyncSync() {
+ new VoidCB(zk).verifySync();
+ }
+
+ @Test
+ public void testAsyncSetACL() {
+ new StatCB(zk).verifySetACL();
+ }
+
+ @Test
+ public void testAsyncSetACLFailure_NoNode() {
+ new StatCB(zk).verifySetACLFailure_NoNode();
+ }
+
+ @Test
+ public void testAsyncSetACLFailure_BadVersion() {
+ new StatCB(zk).verifySetACLFailure_BadVersion();
+ }
+
+ @Test
+ public void testAsyncSetData() {
+ new StatCB(zk).verifySetData();
+ }
+
+ @Test
+ public void testAsyncSetDataFailure_NoNode() {
+ new StatCB(zk).verifySetDataFailure_NoNode();
+ }
+
+ @Test
+ public void testAsyncSetDataFailure_BadVersion() {
+ new StatCB(zk).verifySetDataFailure_BadVersion();
+ }
+
+ @Test
+ public void testAsyncExists() {
+ new StatCB(zk).verifyExists();
+ }
+
+ @Test
+ public void testAsyncExistsFailure_NoNode() {
+ new StatCB(zk).verifyExistsFailure_NoNode();
+ }
+
+ @Test
+ public void testAsyncGetACL() {
+ new ACLCB(zk).verifyGetACL();
+ }
+
+ @Test
+ public void testAsyncGetACLFailure_NoNode() {
+ new ACLCB(zk).verifyGetACLFailure_NoNode();
+ }
+
+ @Test
+ public void testAsyncGetChildrenEmpty() {
+ new ChildrenCB(zk).verifyGetChildrenEmpty();
+ }
+
+ @Test
+ public void testAsyncGetChildrenSingle() {
+ new ChildrenCB(zk).verifyGetChildrenSingle();
+ }
+
+ @Test
+ public void testAsyncGetChildrenTwo() {
+ new ChildrenCB(zk).verifyGetChildrenTwo();
+ }
+
+ @Test
+ public void testAsyncGetChildrenFailure_NoNode() {
+ new ChildrenCB(zk).verifyGetChildrenFailure_NoNode();
+ }
+
+ @Test
+ public void testAsyncGetChildren2Empty() {
+ new Children2CB(zk).verifyGetChildrenEmpty();
+ }
+
+ @Test
+ public void testAsyncGetChildren2Single() {
+ new Children2CB(zk).verifyGetChildrenSingle();
+ }
+
+ @Test
+ public void testAsyncGetChildren2Two() {
+ new Children2CB(zk).verifyGetChildrenTwo();
+ }
+
+ @Test
+ public void testAsyncGetChildren2Failure_NoNode() {
+ new Children2CB(zk).verifyGetChildrenFailure_NoNode();
+ }
+
+ @Test
+ public void testAsyncGetData() {
+ new DataCB(zk).verifyGetData();
+ }
+
+ @Test
+ public void testAsyncGetDataFailure_NoNode() {
+ new DataCB(zk).verifyGetDataFailure_NoNode();
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncTest.java
new file mode 100644
index 0000000..2182032
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncTest.java
@@ -0,0 +1,172 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZKTestCase;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.AsyncCallback.DataCallback;
+import org.apache.zookeeper.AsyncCallback.StringCallback;
+import org.apache.zookeeper.AsyncCallback.VoidCallback;
+import org.apache.zookeeper.KeeperException.Code;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.Stat;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AsyncTest extends ZKTestCase
+ implements StringCallback, VoidCallback, DataCallback
+{
+ private static final Logger LOG = LoggerFactory.getLogger(AsyncTest.class);
+
+ private QuorumBase qb = new QuorumBase();
+
+ @Before
+ public void setUp() throws Exception {
+ qb.setUp();
+ }
+
+ public void restart() throws Exception {
+ JMXEnv.setUp();
+ qb.startServers();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ LOG.info("Test clients shutting down");
+ qb.tearDown();
+ }
+
+ private static class CountdownWatcher implements Watcher {
+ volatile CountDownLatch clientConnected = new CountDownLatch(1);
+
+ public void process(WatchedEvent event) {
+ if (event.getState() == KeeperState.SyncConnected) {
+ clientConnected.countDown();
+ }
+ }
+ }
+
+ private ZooKeeper createClient() throws IOException,InterruptedException {
+ return createClient(qb.hostPort);
+ }
+
+ private ZooKeeper createClient(String hp)
+ throws IOException, InterruptedException
+ {
+ CountdownWatcher watcher = new CountdownWatcher();
+ ZooKeeper zk = new ZooKeeper(hp, CONNECTION_TIMEOUT, watcher);
+ if(!watcher.clientConnected.await(CONNECTION_TIMEOUT,
+ TimeUnit.MILLISECONDS))
+ {
+ Assert.fail("Unable to connect to server");
+ }
+ return zk;
+ }
+
+ LinkedList<Integer> results = new LinkedList<Integer>();
+ @Test
+ public void testAsync()
+ throws IOException, InterruptedException, KeeperException
+ {
+ ZooKeeper zk = null;
+ zk = createClient();
+ try {
+ zk.addAuthInfo("digest", "ben:passwd".getBytes());
+ zk.create("/ben", new byte[0], Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT, this, results);
+ zk.create("/ben/2", new byte[0], Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, this, results);
+ zk.delete("/ben", -1, this, results);
+ zk.create("/ben2", new byte[0], Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, this, results);
+ zk.getData("/ben2", false, this, results);
+ synchronized (results) {
+ while (results.size() < 5) {
+ results.wait();
+ }
+ }
+ Assert.assertEquals(0, (int) results.get(0));
+ Assert.assertEquals(Code.NOAUTH, Code.get(results.get(1)));
+ Assert.assertEquals(0, (int) results.get(2));
+ Assert.assertEquals(0, (int) results.get(3));
+ Assert.assertEquals(0, (int) results.get(4));
+ } finally {
+ zk.close();
+ }
+
+ zk = createClient();
+ try {
+ zk.addAuthInfo("digest", "ben:passwd2".getBytes());
+ try {
+ zk.getData("/ben2", false, new Stat());
+ Assert.fail("Should have received a permission error");
+ } catch (KeeperException e) {
+ Assert.assertEquals(Code.NOAUTH, e.code());
+ }
+ } finally {
+ zk.close();
+ }
+
+ zk = createClient();
+ try {
+ zk.addAuthInfo("digest", "ben:passwd".getBytes());
+ zk.getData("/ben2", false, new Stat());
+ } finally {
+ zk.close();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void processResult(int rc, String path, Object ctx, String name) {
+ synchronized(ctx) {
+ ((LinkedList<Integer>)ctx).add(rc);
+ ctx.notifyAll();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void processResult(int rc, String path, Object ctx) {
+ synchronized(ctx) {
+ ((LinkedList<Integer>)ctx).add(rc);
+ ctx.notifyAll();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void processResult(int rc, String path, Object ctx, byte[] data,
+ Stat stat) {
+ synchronized(ctx) {
+ ((LinkedList<Integer>)ctx).add(rc);
+ ctx.notifyAll();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/AtomicFileOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/AtomicFileOutputStreamTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AtomicFileOutputStreamTest.java
new file mode 100644
index 0000000..cbd2b77
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AtomicFileOutputStreamTest.java
@@ -0,0 +1,200 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.zookeeper.ZKTestCase;
+import org.apache.zookeeper.common.AtomicFileOutputStream;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AtomicFileOutputStreamTest extends ZKTestCase {
+ private static final String TEST_STRING = "hello world";
+ private static final String TEST_STRING_2 = "goodbye world";
+
+ private File testDir;
+ private File dstFile;
+
+ @Before
+ public void setupTestDir() throws IOException {
+ testDir = ClientBase.createEmptyTestDir();
+ dstFile = new File(testDir, "test.txt");
+ }
+ @After
+ public void cleanupTestDir() throws IOException {
+ ClientBase.recursiveDelete(testDir);
+ }
+
+ /**
+ * Test case where there is no existing file
+ */
+ @Test
+ public void testWriteNewFile() throws IOException {
+ OutputStream fos = new AtomicFileOutputStream(dstFile);
+ assertFalse(dstFile.exists());
+ fos.write(TEST_STRING.getBytes());
+ fos.flush();
+ assertFalse(dstFile.exists());
+ fos.close();
+ assertTrue(dstFile.exists());
+
+ String readBackData = ClientBase.readFile(dstFile);
+ assertEquals(TEST_STRING, readBackData);
+ }
+
+ /**
+ * Test case where there is no existing file
+ */
+ @Test
+ public void testOverwriteFile() throws IOException {
+ assertTrue("Creating empty dst file", dstFile.createNewFile());
+
+ OutputStream fos = new AtomicFileOutputStream(dstFile);
+
+ assertTrue("Empty file still exists", dstFile.exists());
+ fos.write(TEST_STRING.getBytes());
+ fos.flush();
+
+ // Original contents still in place
+ assertEquals("", ClientBase.readFile(dstFile));
+
+ fos.close();
+
+ // New contents replace original file
+ String readBackData = ClientBase.readFile(dstFile);
+ assertEquals(TEST_STRING, readBackData);
+ }
+
+ /**
+ * Test case where the flush() fails at close time - make sure that we clean
+ * up after ourselves and don't touch any existing file at the destination
+ */
+ @Test
+ public void testFailToFlush() throws IOException {
+ // Create a file at destination
+ FileOutputStream fos = new FileOutputStream(dstFile);
+ fos.write(TEST_STRING_2.getBytes());
+ fos.close();
+
+ OutputStream failingStream = createFailingStream();
+ failingStream.write(TEST_STRING.getBytes());
+ try {
+ failingStream.close();
+ fail("Close didn't throw exception");
+ } catch (IOException ioe) {
+ // expected
+ }
+
+ // Should not have touched original file
+ assertEquals(TEST_STRING_2, ClientBase.readFile(dstFile));
+
+ assertEquals("Temporary file should have been cleaned up",
+ dstFile.getName(), ClientBase.join(",", testDir.list()));
+ }
+
+ /**
+ * Create a stream that fails to flush at close time
+ */
+ private OutputStream createFailingStream() throws FileNotFoundException {
+ return new AtomicFileOutputStream(dstFile) {
+ @Override
+ public void flush() throws IOException {
+ throw new IOException("injected failure");
+ }
+ };
+ }
+
+ /**
+ * Ensure the tmp file is cleaned up and dstFile is not created when
+ * aborting a new file.
+ */
+ @Test
+ public void testAbortNewFile() throws IOException {
+ AtomicFileOutputStream fos = new AtomicFileOutputStream(dstFile);
+
+ fos.abort();
+
+ assertEquals(0, testDir.list().length);
+ }
+
+ /**
+ * Ensure the tmp file is cleaned up and dstFile is not created when
+ * aborting a new file.
+ */
+ @Test
+ public void testAbortNewFileAfterFlush() throws IOException {
+ AtomicFileOutputStream fos = new AtomicFileOutputStream(dstFile);
+ fos.write(TEST_STRING.getBytes());
+ fos.flush();
+
+ fos.abort();
+
+ assertEquals(0, testDir.list().length);
+ }
+
+ /**
+ * Ensure the tmp file is cleaned up and dstFile is untouched when
+ * aborting an existing file overwrite.
+ */
+ @Test
+ public void testAbortExistingFile() throws IOException {
+ FileOutputStream fos1 = new FileOutputStream(dstFile);
+ fos1.write(TEST_STRING.getBytes());
+ fos1.close();
+
+ AtomicFileOutputStream fos2 = new AtomicFileOutputStream(dstFile);
+
+ fos2.abort();
+
+ // Should not have touched original file
+ assertEquals(TEST_STRING, ClientBase.readFile(dstFile));
+ assertEquals(1, testDir.list().length);
+ }
+
+ /**
+ * Ensure the tmp file is cleaned up and dstFile is untouched when
+ * aborting an existing file overwrite.
+ */
+ @Test
+ public void testAbortExistingFileAfterFlush() throws IOException {
+ FileOutputStream fos1 = new FileOutputStream(dstFile);
+ fos1.write(TEST_STRING.getBytes());
+ fos1.close();
+
+ AtomicFileOutputStream fos2 = new AtomicFileOutputStream(dstFile);
+ fos2.write(TEST_STRING_2.getBytes());
+ fos2.flush();
+
+ fos2.abort();
+
+ // Should not have touched original file
+ assertEquals(TEST_STRING, ClientBase.readFile(dstFile));
+ assertEquals(1, testDir.list().length);
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthTest.java
new file mode 100644
index 0000000..60e3453
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthTest.java
@@ -0,0 +1,174 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.TestableZooKeeper;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AuthTest extends ClientBase {
+ static {
+ // password is test
+ System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest",
+ "super:D/InIHSb7yEEbrWz8b9l71RjZJU=");
+ System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.test.InvalidAuthProvider");
+ }
+
+ private final CountDownLatch authFailed = new CountDownLatch(1);
+
+ @Override
+ protected TestableZooKeeper createClient(String hp)
+ throws IOException, InterruptedException
+ {
+ MyWatcher watcher = new MyWatcher();
+ return createClient(watcher, hp);
+ }
+
+ private class MyWatcher extends CountdownWatcher {
+ @Override
+ public synchronized void process(WatchedEvent event) {
+ if (event.getState() == KeeperState.AuthFailed) {
+ authFailed.countDown();
+ }
+ else {
+ super.process(event);
+ }
+ }
+ }
+
+ @Test
+ public void testBadAuthNotifiesWatch() throws Exception {
+ ZooKeeper zk = createClient();
+ try {
+ zk.addAuthInfo("FOO", "BAR".getBytes());
+ zk.getData("/path1", false, null);
+ Assert.fail("Should get auth state error");
+ } catch(KeeperException.AuthFailedException e) {
+ if(!authFailed.await(CONNECTION_TIMEOUT,
+ TimeUnit.MILLISECONDS))
+ {
+ Assert.fail("Should have called my watcher");
+ }
+ }
+ finally {
+ zk.close();
+ }
+ }
+
+ @Test
+ public void testBadAuthThenSendOtherCommands() throws Exception {
+ ZooKeeper zk = createClient();
+ try {
+ zk.addAuthInfo("INVALID", "BAR".getBytes());
+ zk.exists("/foobar", false);
+ zk.getData("/path1", false, null);
+ Assert.fail("Should get auth state error");
+ } catch(KeeperException.AuthFailedException e) {
+ if(!authFailed.await(CONNECTION_TIMEOUT,
+ TimeUnit.MILLISECONDS))
+ {
+ Assert.fail("Should have called my watcher");
+ }
+ }
+ finally {
+ zk.close();
+ }
+ }
+
+
+ @Test
+ public void testSuper() throws Exception {
+ ZooKeeper zk = createClient();
+ try {
+ zk.addAuthInfo("digest", "pat:pass".getBytes());
+ zk.create("/path1", null, Ids.CREATOR_ALL_ACL,
+ CreateMode.PERSISTENT);
+ zk.close();
+ // verify no auth
+ zk = createClient();
+ try {
+ zk.getData("/path1", false, null);
+ Assert.fail("auth verification");
+ } catch (KeeperException.NoAuthException e) {
+ // expected
+ }
+ zk.close();
+ // verify bad pass Assert.fails
+ zk = createClient();
+ zk.addAuthInfo("digest", "pat:pass2".getBytes());
+ try {
+ zk.getData("/path1", false, null);
+ Assert.fail("auth verification");
+ } catch (KeeperException.NoAuthException e) {
+ // expected
+ }
+ zk.close();
+ // verify super with bad pass Assert.fails
+ zk = createClient();
+ zk.addAuthInfo("digest", "super:test2".getBytes());
+ try {
+ zk.getData("/path1", false, null);
+ Assert.fail("auth verification");
+ } catch (KeeperException.NoAuthException e) {
+ // expected
+ }
+ zk.close();
+ // verify super with correct pass success
+ zk = createClient();
+ zk.addAuthInfo("digest", "super:test".getBytes());
+ zk.getData("/path1", false, null);
+ } finally {
+ zk.close();
+ }
+ }
+
+ @Test
+ public void testSuperACL() throws Exception {
+ ZooKeeper zk = createClient();
+ try {
+ zk.addAuthInfo("digest", "pat:pass".getBytes());
+ zk.create("/path1", null, Ids.CREATOR_ALL_ACL,
+ CreateMode.PERSISTENT);
+ zk.close();
+ // verify super can do anything and ignores ACLs
+ zk = createClient();
+ zk.addAuthInfo("digest", "super:test".getBytes());
+ zk.getData("/path1", false, null);
+
+ zk.setACL("/path1", Ids.READ_ACL_UNSAFE, -1);
+ zk.create("/path1/foo", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
+
+
+ zk.setACL("/path1", Ids.OPEN_ACL_UNSAFE, -1);
+
+ } finally {
+ zk.close();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/BufferSizeTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/BufferSizeTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/BufferSizeTest.java
new file mode 100644
index 0000000..6d74e54
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/BufferSizeTest.java
@@ -0,0 +1,132 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.jute.BinaryInputArchive;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.junit.Before;
+import org.junit.Test;
+
+public class BufferSizeTest extends ClientBase {
+ public static final int TEST_MAXBUFFER = 100;
+ private static final File TEST_DATA = new File(
+ System.getProperty("test.data.dir", "build/test/data"),
+ "buffersize");
+
+ private ZooKeeper zk;
+
+ @Before
+ public void setMaxBuffer() throws IOException, InterruptedException {
+ System.setProperty("jute.maxbuffer", "" + TEST_MAXBUFFER);
+ assertEquals("Can't set jute.maxbuffer!", TEST_MAXBUFFER, BinaryInputArchive.maxBuffer);
+ zk = createClient();
+ }
+
+ @Test
+ public void testCreatesReqs() throws Exception {
+ testRequests(new ClientOp() {
+ @Override
+ public void execute(byte[] data) throws Exception {
+ zk.create("/create_test", data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
+ }
+ });
+ }
+
+ @Test
+ public void testSetReqs() throws Exception {
+ final String path = "/set_test";
+ zk.create(path, new byte[1], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ testRequests(new ClientOp() {
+ @Override
+ public void execute(byte[] data) throws Exception {
+ zk.setData(path, data, -1);
+ }
+ });
+ }
+
+ /** Issues requests containing data smaller, equal, and greater than TEST_MAXBUFFER. */
+ private void testRequests(ClientOp clientOp) throws Exception {
+ clientOp.execute(new byte[TEST_MAXBUFFER - 60]);
+ try {
+ // This should fail since the buffer size > the data size due to extra fields
+ clientOp.execute(new byte[TEST_MAXBUFFER]);
+ fail("Request exceeding jute.maxbuffer succeeded!");
+ } catch (KeeperException.ConnectionLossException e) {}
+ try {
+ clientOp.execute(new byte[TEST_MAXBUFFER + 10]);
+ fail("Request exceeding jute.maxbuffer succeeded!");
+ } catch (KeeperException.ConnectionLossException e) {}
+ }
+
+ private interface ClientOp {
+ void execute(byte[] data) throws Exception;
+ }
+
+ @Test
+ public void testStartup() throws Exception {
+ final String path = "/test_node";
+ zk.create(path, new byte[TEST_MAXBUFFER - 60], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ zk.setData(path, new byte[TEST_MAXBUFFER - 50], -1);
+
+ stopServer();
+ startServer();
+ }
+
+ @Test
+ public void testStartupFailureCreate() throws Exception {
+ // Empty snapshot and logfile containing a 5000-byte create
+ testStartupFailure(new File(TEST_DATA, "create"),
+ "Server started despite create exceeding jute.maxbuffer!");
+ }
+
+ @Test
+ public void testStartupFailureSet() throws Exception {
+ // Empty snapshot and logfile containing a 1-byte create and 5000-byte set
+ testStartupFailure(new File(TEST_DATA, "set"),
+ "Server started despite set exceeding jute.maxbuffer!");
+ }
+
+ @Test
+ public void testStartupFailureSnapshot() throws Exception {
+ // Snapshot containing 5000-byte znode and logfile containing create txn
+ testStartupFailure(new File(TEST_DATA, "snapshot"),
+ "Server started despite znode exceeding jute.maxbuffer!");
+ }
+
+ private void testStartupFailure(File testDir, String failureMsg) throws Exception {
+ stopServer();
+ // Point server at testDir
+ tmpDir = testDir;
+ try {
+ startServer();
+ fail(failureMsg);
+ } catch (IOException e) {
+ LOG.info("Successfully caught IOException: " + e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootAsyncTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootAsyncTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootAsyncTest.java
new file mode 100644
index 0000000..e60ea04
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootAsyncTest.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.ZooDefs.Ids;
+
+public class ChrootAsyncTest extends AsyncOpsTest {
+ private static final Logger LOG = LoggerFactory.getLogger(ChrootAsyncTest.class);
+
+ @Override
+ public void setUp() throws Exception {
+ String hp = hostPort;
+ hostPort = hostPort + "/chrootasynctest";
+
+ super.setUp();
+
+ LOG.info("Creating client " + getTestName());
+
+ ZooKeeper zk = createClient(hp);
+ try {
+ zk.create("/chrootasynctest", null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ } finally {
+ zk.close();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootClientTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootClientTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootClientTest.java
new file mode 100644
index 0000000..e0286e1
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootClientTest.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.junit.Test;
+
+public class ChrootClientTest extends ClientTest {
+ private static final Logger LOG = LoggerFactory.getLogger(ChrootClientTest.class);
+
+ @Override
+ public void setUp() throws Exception {
+ String hp = hostPort;
+ hostPort = hostPort + "/chrootclienttest";
+
+ System.out.println(hostPort);
+ super.setUp();
+
+ LOG.info("STARTING " + getTestName());
+
+ ZooKeeper zk = createClient(hp);
+ try {
+ zk.create("/chrootclienttest", null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ } finally {
+ zk.close();
+ }
+ }
+
+ @Test
+ public void testPing() throws Exception {
+ // not necessary to repeat this, expensive and not chroot related
+ }
+}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootTest.java
----------------------------------------------------------------------
diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootTest.java
new file mode 100644
index 0000000..3df068b
--- /dev/null
+++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootTest.java
@@ -0,0 +1,136 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zookeeper.test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ChrootTest extends ClientBase {
+ private class MyWatcher implements Watcher {
+ private final String path;
+ private String eventPath;
+ private CountDownLatch latch = new CountDownLatch(1);
+
+ public MyWatcher(String path) {
+ this.path = path;
+ }
+ public void process(WatchedEvent event) {
+ System.out.println("latch:" + path + " " + event.getPath());
+ this.eventPath = event.getPath();
+ latch.countDown();
+ }
+ public boolean matches() throws InterruptedException {
+ if (!latch.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ Assert.fail("No watch received within timeout period " + path);
+ }
+ return path.equals(eventPath);
+ }
+ }
+
+ @Test
+ public void testChrootSynchronous()
+ throws IOException, InterruptedException, KeeperException
+ {
+ ZooKeeper zk1 = createClient();
+ try {
+ zk1.create("/ch1", null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT);
+ } finally {
+ if(zk1 != null)
+ zk1.close();
+ }
+ ZooKeeper zk2 = createClient(hostPort + "/ch1");
+ try {
+ Assert.assertEquals("/ch2",
+ zk2.create("/ch2", null, Ids.OPEN_ACL_UNSAFE,
+ CreateMode.PERSISTENT));
+ } finally {
+ if(zk2 != null)
+ zk2.close();
+ }
+
+ zk1 = createClient();
+ zk2 = createClient(hostPort + "/ch1");
+ try {
+ // check get
+ MyWatcher w1 = new MyWatcher("/ch1");
+ Assert.assertNotNull(zk1.exists("/ch1", w1));
+ MyWatcher w2 = new MyWatcher("/ch1/ch2");
+ Assert.assertNotNull(zk1.exists("/ch1/ch2", w2));
+
+ MyWatcher w3 = new MyWatcher("/ch2");
+ Assert.assertNotNull(zk2.exists("/ch2", w3));
+
+ // set watches on child
+ MyWatcher w4 = new MyWatcher("/ch1");
+ zk1.getChildren("/ch1",w4);
+ MyWatcher w5 = new MyWatcher("/");
+ zk2.getChildren("/",w5);
+
+ // check set
+ zk1.setData("/ch1", "1".getBytes(), -1);
+ zk2.setData("/ch2", "2".getBytes(), -1);
+
+ // check watches
+ Assert.assertTrue(w1.matches());
+ Assert.assertTrue(w2.matches());
+ Assert.assertTrue(w3.matches());
+
+ // check exceptions
+ try {
+ zk2.setData("/ch3", "3".getBytes(), -1);
+ } catch (KeeperException.NoNodeException e) {
+ Assert.assertEquals("/ch3", e.getPath());
+ }
+
+ Assert.assertTrue(Arrays.equals("1".getBytes(),
+ zk1.getData("/ch1", false, null)));
+ Assert.assertTrue(Arrays.equals("2".getBytes(),
+ zk1.getData("/ch1/ch2", false, null)));
+ Assert.assertTrue(Arrays.equals("2".getBytes(),
+ zk2.getData("/ch2", false, null)));
+
+ // check delete
+ zk2.delete("/ch2", -1);
+ Assert.assertTrue(w4.matches());
+ Assert.assertTrue(w5.matches());
+
+ zk1.delete("/ch1", -1);
+ Assert.assertNull(zk1.exists("/ch1", false));
+ Assert.assertNull(zk1.exists("/ch1/ch2", false));
+ Assert.assertNull(zk2.exists("/ch2", false));
+ } finally {
+ if(zk1 != null)
+ zk1.close();
+ if(zk2 != null)
+ zk2.close();
+ }
+ }
+}