/** * 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.I0Itec.zkclient; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.I0Itec.zkclient.exception.ZkBadVersionException; import org.I0Itec.zkclient.exception.ZkTimeoutException; import org.apache.log4j.Logger; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public abstract class AbstractBaseZkClientTest { protected static final Logger LOG = Logger.getLogger(AbstractBaseZkClientTest.class); protected ZkServer _zkServer; protected ZkClient _client; @Before public void setUp() throws Exception { LOG.info("------------ BEFORE -------------"); } @After public void tearDown() throws Exception { LOG.info("------------ AFTER -------------"); } @Test(expected = ZkTimeoutException.class, timeout = 15000) public void testUnableToConnect() throws Exception { LOG.info("--- testUnableToConnect"); // we are using port 4711 to avoid conflicts with the zk server that is // started by the Spring context new ZkClient("localhost:4712", 5000); } @Test public void testWriteAndRead() throws Exception { LOG.info("--- testWriteAndRead"); String data = "something"; String path = "/a"; _client.createPersistent(path, data); String data2 = _client.readData(path); Assert.assertEquals(data, data2); _client.delete(path); } @Test public void testDelete() throws Exception { LOG.info("--- testDelete"); String path = "/a"; assertFalse(_client.delete(path)); _client.createPersistent(path, null); assertTrue(_client.delete(path)); assertFalse(_client.delete(path)); } @Test public void testDeleteWithVersion() throws Exception { LOG.info("--- testDelete"); String path = "/a"; assertFalse(_client.delete(path, 0)); _client.createPersistent(path, null); assertTrue(_client.delete(path, 0)); _client.createPersistent(path, null); _client.writeData(path, new byte[0]); assertTrue(_client.delete(path, 1)); _client.createPersistent(path, null); try { _client.delete(path, 1); fail("Bad version excpetion expected."); } catch (ZkBadVersionException e) { // expected } assertTrue(_client.delete(path, 0)); } @Test public void testDeleteRecursive() throws Exception { LOG.info("--- testDeleteRecursive"); // should be able to call this on a not existing directory _client.deleteRecursive("/doesNotExist"); } @Test public void testWaitUntilExists() { LOG.info("--- testWaitUntilExists"); // create /gaga node asynchronously new Thread() { @Override public void run() { try { Thread.sleep(100); _client.createPersistent("/gaga"); } catch (Exception e) { // ignore } } }.start(); // wait until this was created assertTrue(_client.waitUntilExists("/gaga", TimeUnit.SECONDS, 5)); assertTrue(_client.exists("/gaga")); // waiting for /neverCreated should timeout assertFalse(_client.waitUntilExists("/neverCreated", TimeUnit.MILLISECONDS, 100)); } @Test public void testDataChanges1() throws Exception { LOG.info("--- testDataChanges1"); String path = "/a"; final Holder<String> holder = new Holder<String>(); IZkDataListener listener = new IZkDataListener() { @Override public void handleDataChange(String dataPath, Object data) throws Exception { holder.set((String) data); } @Override public void handleDataDeleted(String dataPath) throws Exception { holder.set(null); } }; _client.subscribeDataChanges(path, listener); _client.createPersistent(path, "aaa"); // wait some time to make sure the event was triggered String contentFromHolder = TestUtil.waitUntil("b", new Callable<String>() { @Override public String call() throws Exception { return holder.get(); } }, TimeUnit.SECONDS, 5); assertEquals("aaa", contentFromHolder); } @Test public void testDataChanges2() throws Exception { LOG.info("--- testDataChanges2"); String path = "/a"; final AtomicInteger countChanged = new AtomicInteger(0); final AtomicInteger countDeleted = new AtomicInteger(0); IZkDataListener listener = new IZkDataListener() { @Override public void handleDataChange(String dataPath, Object data) throws Exception { countChanged.incrementAndGet(); } @Override public void handleDataDeleted(String dataPath) throws Exception { countDeleted.incrementAndGet(); } }; _client.subscribeDataChanges(path, listener); // create node _client.createPersistent(path, "aaa"); // wait some time to make sure the event was triggered TestUtil.waitUntil(1, new Callable<Integer>() { @Override public Integer call() throws Exception { return countChanged.get(); } }, TimeUnit.SECONDS, 5); assertEquals(1, countChanged.get()); assertEquals(0, countDeleted.get()); countChanged.set(0); countDeleted.set(0); // delete node, this should trigger a delete event _client.delete(path); // wait some time to make sure the event was triggered TestUtil.waitUntil(1, new Callable<Integer>() { @Override public Integer call() throws Exception { return countDeleted.get(); } }, TimeUnit.SECONDS, 5); assertEquals(0, countChanged.get()); assertEquals(1, countDeleted.get()); // test if watch was reinstalled after the file got deleted countChanged.set(0); _client.createPersistent(path, "aaa"); // wait some time to make sure the event was triggered TestUtil.waitUntil(1, new Callable<Integer>() { @Override public Integer call() throws Exception { return countChanged.get(); } }, TimeUnit.SECONDS, 5); assertEquals(1, countChanged.get()); // test if changing the contents notifies the listener _client.writeData(path, "bbb"); // wait some time to make sure the event was triggered TestUtil.waitUntil(2, new Callable<Integer>() { @Override public Integer call() throws Exception { return countChanged.get(); } }, TimeUnit.SECONDS, 5); assertEquals(2, countChanged.get()); } @Test(timeout = 15000) public void testHandleChildChanges() throws Exception { LOG.info("--- testHandleChildChanges"); String path = "/a"; final AtomicInteger count = new AtomicInteger(0); final Holder<List<String>> children = new Holder<List<String>>(); IZkChildListener listener = new IZkChildListener() { @Override public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception { count.incrementAndGet(); children.set(currentChilds); } }; _client.subscribeChildChanges(path, listener); // ---- // Create the root node should throw the first child change event // ---- _client.createPersistent(path); // wait some time to make sure the event was triggered TestUtil.waitUntil(1, new Callable<Integer>() { @Override public Integer call() throws Exception { return count.get(); } }, TimeUnit.SECONDS, 5); assertEquals(1, count.get()); assertEquals(0, children.get().size()); // ---- // Creating a child node should throw another event // ---- count.set(0); _client.createPersistent(path + "/child1"); // wait some time to make sure the event was triggered TestUtil.waitUntil(1, new Callable<Integer>() { @Override public Integer call() throws Exception { return count.get(); } }, TimeUnit.SECONDS, 5); assertEquals(1, count.get()); assertEquals(1, children.get().size()); assertEquals("child1", children.get().get(0)); // ---- // Creating another child and deleting the node should also throw an event // ---- count.set(0); _client.createPersistent(path + "/child2"); _client.deleteRecursive(path); // wait some time to make sure the event was triggered Boolean eventReceived = TestUtil.waitUntil(true, new Callable<Boolean>() { @Override public Boolean call() throws Exception { return count.get() > 0 && children.get() == null; } }, TimeUnit.SECONDS, 5); assertTrue(eventReceived); assertNull(children.get()); // ---- // Creating root again should throw an event // ---- count.set(0); _client.createPersistent(path); // wait some time to make sure the event was triggered eventReceived = TestUtil.waitUntil(true, new Callable<Boolean>() { @Override public Boolean call() throws Exception { return count.get() > 0 && children.get() != null; } }, TimeUnit.SECONDS, 5); assertTrue(eventReceived); assertEquals(0, children.get().size()); // ---- // Creating child now should throw an event // ---- count.set(0); _client.createPersistent(path + "/child"); // wait some time to make sure the event was triggered eventReceived = TestUtil.waitUntil(true, new Callable<Boolean>() { @Override public Boolean call() throws Exception { return count.get() > 0; } }, TimeUnit.SECONDS, 5); assertTrue(eventReceived); assertEquals(1, children.get().size()); assertEquals("child", children.get().get(0)); // ---- // Deleting root node should throw an event // ---- count.set(0); _client.deleteRecursive(path); // wait some time to make sure the event was triggered eventReceived = TestUtil.waitUntil(true, new Callable<Boolean>() { @Override public Boolean call() throws Exception { return count.get() > 0 && children.get() == null; } }, TimeUnit.SECONDS, 5); assertTrue(eventReceived); assertNull(children.get()); } @Test public void testGetCreationTime() throws Exception { long start = System.currentTimeMillis(); Thread.sleep(100); String path = "/a"; _client.createPersistent(path); Thread.sleep(100); long end = System.currentTimeMillis(); long creationTime = _client.getCreationTime(path); assertTrue(start < creationTime && end > creationTime); } @Test public void testNumberOfListeners() { IZkChildListener zkChildListener = mock(IZkChildListener.class); _client.subscribeChildChanges("/", zkChildListener); assertEquals(1, _client.numberOfListeners()); IZkDataListener zkDataListener = mock(IZkDataListener.class); _client.subscribeDataChanges("/a", zkDataListener); assertEquals(2, _client.numberOfListeners()); _client.subscribeDataChanges("/b", zkDataListener); assertEquals(3, _client.numberOfListeners()); IZkStateListener zkStateListener = mock(IZkStateListener.class); _client.subscribeStateChanges(zkStateListener); assertEquals(4, _client.numberOfListeners()); _client.unsubscribeChildChanges("/", zkChildListener); assertEquals(3, _client.numberOfListeners()); _client.unsubscribeDataChanges("/b", zkDataListener); assertEquals(2, _client.numberOfListeners()); _client.unsubscribeDataChanges("/a", zkDataListener); assertEquals(1, _client.numberOfListeners()); _client.unsubscribeStateChanges(zkStateListener); assertEquals(0, _client.numberOfListeners()); } }