/* * Copyright 2010 Outerthought bvba * * 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.lilyproject.util.zookeeper.test; import java.io.File; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.lilyproject.hadooptestfw.TestHelper; import org.lilyproject.util.io.Closer; import org.lilyproject.util.net.NetUtils; import org.lilyproject.util.zookeeper.ZkLock; import org.lilyproject.util.zookeeper.ZkUtil; import org.lilyproject.util.zookeeper.ZooKeeperItf; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class ZkLockTest { private static MiniZooKeeperCluster ZK_CLUSTER; private static File ZK_DIR; private static int ZK_CLIENT_PORT; private static ZooKeeperItf ZK; private static Log log = LogFactory.getLog(ZkLockTest.class); @BeforeClass public static void setUpBeforeClass() throws Exception { TestHelper.setupLogging("org.lilyproject.util.zookeeper"); ZK_DIR = new File(System.getProperty("java.io.tmpdir") + File.separator + "lily.zklocktest"); ZK_CLIENT_PORT = NetUtils.getFreePort(); ZK_CLUSTER = new MiniZooKeeperCluster(); ZK_CLUSTER.setDefaultClientPort(ZK_CLIENT_PORT); ZK_CLUSTER.startup(ZK_DIR); ZK = ZkUtil.connect("localhost:" + ZK_CLIENT_PORT, 30000); } @AfterClass public static void tearDownAfterClass() throws Exception { Closer.close(ZK); if (ZK_CLUSTER != null) { ZK_CLUSTER.shutdown(); } } @Test public void testObtainReleaseLock() throws Exception { ZkUtil.createPath(ZK, "/lily/test/zklockA"); String obtainedLock = ZkLock.lock(ZK, "/lily/test/zklockA"); ZkLock.unlock(ZK, obtainedLock); } /** * Tests the following: first user takes lock, then second user tries to take lock before first has released * it. Then first releases it, after this second user gets the lock. * @throws Exception */ @Test public void testTwoUsersForSameLock() throws Exception { final String lockPath = "/lily/test/zklockB"; ZkUtil.createPath(ZK, lockPath); log.debug("Request first lock."); String obtainedLock1 = ZkLock.lock(ZK, lockPath); log.debug("First lock obtained."); final Variable<String> obtainedLock2 = new Variable<String>(); final Variable<Long> obtainTime = new Variable<Long>(); final Variable<Throwable> throwable = new Variable<Throwable>(); // The following will block Thread t = new Thread(new Runnable() { @Override public void run() { try { log.debug("Request second lock."); obtainedLock2.value = ZkLock.lock(ZK, lockPath); obtainTime.value = System.currentTimeMillis(); log.debug("Second lock obtained."); } catch (Throwable t) { throwable.value = t; } } }); t.start(); // Wait more then enough time for the second lock to be obtained (thread needs to start) Thread.sleep(2000); long releaseTime = System.currentTimeMillis(); log.debug("Will now release first lock."); ZkLock.unlock(ZK, obtainedLock1); log.debug("First lock released."); // wait for thread to end t.join(); if (throwable.value != null) { throwable.value.printStackTrace(); fail("Failure in second lock thread."); } // It should only be after release of the first lock that the second lock can be obtained. log.debug("First lock released at " + releaseTime + ", second lock obtained at " + obtainTime.value); assertTrue(releaseTime <= obtainTime.value); // remove second lock log.debug("Will now release second lock."); ZkLock.unlock(ZK, obtainedLock2.value); log.debug("Second lock released."); } public static class Variable<T> { public T value; } }