/* * * This is a simple Content Management System (CMS) * Copyright (C) 2011 Imran M Yousuf (imyousuf@smartitengineering.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.smartitengineering.cms.spi.lock.impl.distributed; import com.smartitengineering.cms.api.factory.write.Lock; import com.smartitengineering.cms.spi.lock.Key; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.net.InetSocketAddress; import java.net.Socket; import java.util.concurrent.TimeUnit; import junit.framework.Assert; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.NIOServerCnxn; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class AppTest { public static final String ROOT_NODE = "/smart-cms"; private static NIOServerCnxn.Factory standaloneServerFactory; private static final int CLIENT_PORT = 3882; private static final int CONNECTION_TIMEOUT = 30000; private final String connectString = "localhost:" + CLIENT_PORT; private final Key key = new Key() { public String getKeyStringRep() { return "random-key-1"; } }; @BeforeClass public static void startZooKeeperServer() { try { File snapDir = new File("./target/zk/"); snapDir.mkdirs(); ZooKeeperServer server = new ZooKeeperServer(snapDir, snapDir, 2000); standaloneServerFactory = new NIOServerCnxn.Factory(new InetSocketAddress(CLIENT_PORT)); standaloneServerFactory.startup(server); if (!waitForServerUp(CLIENT_PORT, CONNECTION_TIMEOUT)) { throw new IOException("Waiting for startup of standalone server"); } } catch (Exception ex) { throw new IllegalStateException(ex); } } @AfterClass public static void shutdownZooKeeperServer() { standaloneServerFactory.shutdown(); if (!waitForServerDown(CLIENT_PORT, CONNECTION_TIMEOUT)) { throw new IllegalStateException("Waiting for shutdown of standalone server"); } } // XXX: From o.a.zk.t.ClientBase public static boolean waitForServerUp(int port, long timeout) { long start = System.currentTimeMillis(); while (true) { try { Socket sock = new Socket("localhost", port); BufferedReader reader = null; try { OutputStream outstream = sock.getOutputStream(); outstream.write("stat".getBytes()); outstream.flush(); Reader isr = new InputStreamReader(sock.getInputStream()); reader = new BufferedReader(isr); String line = reader.readLine(); if (line != null && line.startsWith("Zookeeper version:")) { return true; } } finally { sock.close(); if (reader != null) { reader.close(); } } } catch (IOException e) { // ignore as this is expected } if (System.currentTimeMillis() > start + timeout) { break; } try { Thread.sleep(250); } catch (InterruptedException e) { // ignore } } return false; } // XXX: From o.a.zk.t.ClientBase public static boolean waitForServerDown(int port, long timeout) { long start = System.currentTimeMillis(); while (true) { try { Socket sock = new Socket("localhost", CLIENT_PORT); try { OutputStream outstream = sock.getOutputStream(); outstream.write("stat".getBytes()); outstream.flush(); } finally { sock.close(); } } catch (IOException e) { return true; } if (System.currentTimeMillis() > start + timeout) { break; } try { Thread.sleep(250); } catch (InterruptedException e) { // ignore } } return false; } @Test public void testInitialization() throws Exception { ZooKeeper zooKeeper = new ZooKeeper(connectString, CONNECTION_TIMEOUT, new Watcher() { public void process(WatchedEvent event) { } }); Stat stat = zooKeeper.exists(ROOT_NODE, false); Assert.assertNull(stat); final LocalLockRegistrarImpl localLockRegistrarImpl = new LocalLockRegistrarImpl(); localLockRegistrarImpl.initTimeoutChecking(); ZooKeeperLockHandler handler = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node1", CONNECTION_TIMEOUT, localLockRegistrarImpl); stat = zooKeeper.exists(ROOT_NODE, false); Assert.assertNotNull(stat); } @Test public void testSimpleLocking() throws Exception { final LocalLockRegistrarImpl localLockRegistrarImpl1 = new LocalLockRegistrarImpl(); localLockRegistrarImpl1.initTimeoutChecking(); ZooKeeperLockHandler handler1 = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node1", CONNECTION_TIMEOUT, localLockRegistrarImpl1); Lock lock1 = handler1.getLock(key); Assert.assertNotNull(lock1); Assert.assertTrue(lock1.tryLock()); Assert.assertTrue(lock1.isLockOwned()); lock1.unlock(); Assert.assertFalse(lock1.isLockOwned()); } @Test public void testRepeatativeLocking() throws Exception { final LocalLockRegistrarImpl localLockRegistrarImpl1 = new LocalLockRegistrarImpl(); localLockRegistrarImpl1.initTimeoutChecking(); ZooKeeperLockHandler handler1 = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node1", CONNECTION_TIMEOUT, localLockRegistrarImpl1); Lock lock2 = handler1.getLock(key); Assert.assertNotNull(lock2); Assert.assertTrue(lock2.tryLock()); Assert.assertTrue(lock2.tryLock()); lock2.unlock(); } @Test public void testSignleJVMLocking() throws Exception { final LocalLockRegistrarImpl localLockRegistrarImpl1 = new LocalLockRegistrarImpl(); localLockRegistrarImpl1.initTimeoutChecking(); ZooKeeperLockHandler handler1 = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node1", CONNECTION_TIMEOUT, localLockRegistrarImpl1); Lock lock1 = handler1.getLock(key); Lock lock2 = handler1.getLock(key); Assert.assertNotNull(lock1); Assert.assertNotNull(lock2); Assert.assertTrue(lock1.tryLock()); Assert.assertTrue(lock1.isLockOwned()); Assert.assertFalse(lock2.tryLock()); lock1.unlock(); Assert.assertFalse(lock1.isLockOwned()); Assert.assertTrue(lock2.tryLock()); Assert.assertTrue(lock2.tryLock()); lock2.unlock(); Assert.assertFalse(lock2.isLockOwned()); } @Test public void testMultiJVMLocking() throws Exception { final LocalLockRegistrarImpl localLockRegistrarImpl1 = new LocalLockRegistrarImpl(); localLockRegistrarImpl1.initTimeoutChecking(); ZooKeeperLockHandler handler1 = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node1", CONNECTION_TIMEOUT, localLockRegistrarImpl1); Lock lock1 = handler1.getLock(key); //Simulate multi-jvm final LocalLockRegistrarImpl localLockRegistrarImpl2 = new LocalLockRegistrarImpl(); localLockRegistrarImpl2.initTimeoutChecking(); ZooKeeperLockHandler handler2 = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node2", CONNECTION_TIMEOUT, localLockRegistrarImpl2); Lock lock3 = handler2.getLock(key); Assert.assertTrue(lock1.tryLock()); Assert.assertFalse(lock3.tryLock()); Assert.assertFalse(lock3.isLockOwned()); lock1.unlock(); Assert.assertFalse(lock1.isLockOwned()); Assert.assertTrue(lock3.tryLock()); lock3.unlock(); Assert.assertFalse(lock3.isLockOwned()); } @Test public void testSignleJVMWaitLocking() throws Exception { final LocalLockRegistrarImpl localLockRegistrarImpl1 = new LocalLockRegistrarImpl(); localLockRegistrarImpl1.initTimeoutChecking(); ZooKeeperLockHandler handler1 = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node1", CONNECTION_TIMEOUT, localLockRegistrarImpl1); Lock lock1 = handler1.getLock(key); final Lock lock2 = handler1.getLock(key); Assert.assertNotNull(lock1); Assert.assertNotNull(lock2); Assert.assertTrue(lock1.tryLock()); Assert.assertTrue(lock1.isLockOwned()); new Thread(new Runnable() { public void run() { lock2.lock(); } }).start(); Assert.assertFalse(lock2.isLockOwned()); Thread.sleep(1000); lock1.unlock(); Assert.assertFalse(lock1.isLockOwned()); Thread.sleep(100); Assert.assertTrue(lock2.isLockOwned()); lock2.unlock(); Assert.assertTrue(lock1.tryLock()); new Thread(new Runnable() { public void run() { try { Assert.assertFalse(lock2.tryLock(300, TimeUnit.MILLISECONDS)); } catch (Exception ex) { Assert.fail("Should not arise!"); } } }).start(); Assert.assertFalse(lock2.isLockOwned()); Thread.sleep(350); lock1.unlock(); Assert.assertTrue(lock1.tryLock()); new Thread(new Runnable() { public void run() { try { Assert.assertTrue(lock2.tryLock(300, TimeUnit.MILLISECONDS)); lock2.unlock(); } catch (Exception ex) { Assert.fail("Should not arise!"); } } }).start(); Assert.assertFalse(lock2.isLockOwned()); Thread.sleep(150); lock1.unlock(); } @Test public void testMultiJVMWaitLocking() throws Exception { final LocalLockRegistrarImpl localLockRegistrarImpl1 = new LocalLockRegistrarImpl(); localLockRegistrarImpl1.initTimeoutChecking(); ZooKeeperLockHandler handler1 = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node1", CONNECTION_TIMEOUT, localLockRegistrarImpl1); Lock lock1 = handler1.getLock(key); //Simulate multi-jvm final LocalLockRegistrarImpl localLockRegistrarImpl2 = new LocalLockRegistrarImpl(); localLockRegistrarImpl2.initTimeoutChecking(); ZooKeeperLockHandler handler2 = new ZooKeeperLockHandler(connectString, ROOT_NODE, "node2", CONNECTION_TIMEOUT, localLockRegistrarImpl2); final Lock lock3 = handler2.getLock(key); Assert.assertTrue(lock1.tryLock()); new Thread(new Runnable() { public void run() { lock3.lock(); } }).start(); Assert.assertFalse(lock3.isLockOwned()); Thread.sleep(1000); lock1.unlock(); Assert.assertFalse(lock1.isLockOwned()); Thread.sleep(100); Assert.assertTrue(lock3.isLockOwned()); lock3.unlock(); Assert.assertTrue(lock1.tryLock()); new Thread(new Runnable() { public void run() { try { Assert.assertFalse(lock3.tryLock(300, TimeUnit.MILLISECONDS)); } catch (Exception ex) { Assert.fail("Should not arise!"); } } }).start(); Assert.assertFalse(lock3.isLockOwned()); Thread.sleep(350); lock1.unlock(); Assert.assertTrue(lock1.tryLock()); new Thread(new Runnable() { public void run() { try { Assert.assertTrue(lock3.tryLock(300, TimeUnit.MILLISECONDS)); lock3.unlock(); } catch (Exception ex) { Assert.fail("Should not arise!"); } } }).start(); Assert.assertFalse(lock3.isLockOwned()); Thread.sleep(150); lock1.unlock(); } }