package eu.dnetlib.iis.common.lock;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.curator.test.TestingServer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ha.ZKFailoverController;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.Timeout;
import com.google.common.base.Stopwatch;
import eu.dnetlib.iis.common.java.PortBindings;
import eu.dnetlib.iis.common.lock.LockManagingProcess.LockMode;
/**
* {@link LockManagingProcess} test class.
* @author mhorst
*
*/
public class LockManagingProcessTest {
private final LockManagingProcess lockManager = new LockManagingProcess();
private final PortBindings portBindings = null;
private final Configuration conf = new Configuration();
private TestingServer zookeeperServer;
@Rule
public final ExpectedException exception = ExpectedException.none();
@Rule
public final Timeout globalTimeout = Timeout.seconds(5);
@Before
public void initialize() throws Exception {
zookeeperServer = new TestingServer(true);
conf.clear();
conf.set(ZKFailoverController.ZK_QUORUM_KEY, "localhost:" + zookeeperServer.getPort());
}
@After
public void shutdown() throws IOException {
zookeeperServer.stop();
}
@Test
public void testLockingWithoutNodeId() throws Exception {
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.obtain.name());
exception.expect(IllegalArgumentException.class);
lockManager.run(portBindings, conf, parameters);
}
@Test
public void testLockingWithoutMode() throws Exception {
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_NODE_ID, "nodeid");
exception.expect(IllegalArgumentException.class);
lockManager.run(portBindings, conf, parameters);
}
@Test
public void testLockingWithoutQuorumKey() throws Exception {
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_NODE_ID, "nodeid");
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.obtain.name());
conf.clear();
exception.expect(IllegalArgumentException.class);
lockManager.run(portBindings, conf, parameters);
}
@Test
public void testLockingWithInvalidQuorumKey() throws Exception {
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_NODE_ID, "nodeid");
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.obtain.name());
conf.set(ZKFailoverController.ZK_QUORUM_KEY, "invalid");
exception.expect(IllegalArgumentException.class);
lockManager.run(portBindings, conf, parameters);
}
@Test
public void testLockingWithUnsupportedMode() throws Exception {
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_NODE_ID, "nodeid");
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, "unsupported");
exception.expect(IllegalArgumentException.class);
lockManager.run(portBindings, conf, parameters);
}
@Test
public void testLockingAndUnlocking() throws Exception {
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_NODE_ID, "nodeid");
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.obtain.name());
lockManager.run(portBindings, conf, parameters);
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.release.name());
lockManager.run(portBindings, conf, parameters);
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.obtain.name());
lockManager.run(portBindings, conf, parameters);
}
@Test
public void testLockingOnDifferentNodes() throws Exception {
for (int i=0; i < 10; i++) {
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.obtain.name());
parameters.put(LockManagingProcess.PARAM_NODE_ID, "nodeid_" + i);
lockManager.run(portBindings, conf, parameters);
}
}
@Test
public void testUnlockingOnNotExistingNode() throws Exception {
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.release.name());
parameters.put(LockManagingProcess.PARAM_NODE_ID, "not_existing_nodeid");
exception.expect(NoNodeException.class);
lockManager.run(portBindings, conf, parameters);
}
@Test
public void testAsyncLocking() throws Exception {
final String nodeId = "nodeid";
final long timeout = 100;
Map<String, String> parameters = new HashMap<>();
parameters.put(LockManagingProcess.PARAM_NODE_ID, nodeId);
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.obtain.name());
lockManager.run(portBindings, conf, parameters);
final CompletableFuture<Long> threadWaitTimeFuture = new CompletableFuture<>();
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
try {
LockManagingProcess lockManager = new LockManagingProcess();
Map<String, String> params = new HashMap<>();
params.put(LockManagingProcess.PARAM_NODE_ID, nodeId);
params.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.obtain.name());
Stopwatch timer = new Stopwatch().start();
// obtaining lock
lockManager.run(portBindings, conf, params);
// returning processing time
threadWaitTimeFuture.complete(timer.elapsedMillis());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
Thread.sleep(timeout);
assertFalse(threadWaitTimeFuture.isDone());
parameters.put(LockManagingProcess.PARAM_LOCK_MODE, LockMode.release.name());
lockManager.run(portBindings, conf, parameters);
assertTrue(threadWaitTimeFuture.get() >= timeout);
}
}