package net.sf.katta.integrationTest; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.junit.Test; import net.sf.katta.integrationTest.support.AbstractIntegrationTest; import net.sf.katta.node.IContentServer; import net.sf.katta.node.Node; import net.sf.katta.operation.master.IndexDeployOperation; import net.sf.katta.protocol.InteractionProtocol; import net.sf.katta.testutil.TestUtil; import net.sf.katta.util.NodeConfiguration; public class NodeReconnectWhileStartingTest extends AbstractIntegrationTest { public static class TestContentServer implements IContentServer { Set<String> shards = new HashSet<String>(); @Override public long getProtocolVersion(String arg0, long arg1) throws IOException { return 0; } @Override public void init(String nodeName, NodeConfiguration nodeConfiguration) { } @Override public void addShard(String shardName, File shardDir) throws Exception { addCallBlock(); shards.add(shardName); } @Override public void removeShard(String shardName) throws Exception { shards.remove(shardName); } @Override public Collection<String> getShards() { return Collections.unmodifiableSet(shards); } @Override public Map<String, String> getShardMetaData(String shardName) throws Exception { return Collections.emptyMap(); } @Override public void shutdown() throws Exception { shards.clear(); } } public NodeReconnectWhileStartingTest() { super(TestContentServer.class, 1); } public static void addCallBlock() throws InterruptedException { if (currentlyRunningAddCalls == 1) { throw new RuntimeException("Simultaneous addShard calls detected"); } currentlyRunningAddCalls++; addCalls++; while (addCalls == 5) Thread.sleep(50); --currentlyRunningAddCalls; } private static volatile int addCalls = 0; private static volatile int currentlyRunningAddCalls = 0; @Test public void testReconnectNodeDuringInit() throws Exception { final InteractionProtocol protocol = _miniCluster.getProtocol(); IndexDeployOperation deployOperation = new IndexDeployOperation(INDEX_NAME, "file://" + INDEX_FILE.getAbsolutePath(), getNodeCount()); protocol.addMasterOperation(deployOperation); TestUtil.waitUntilIndexDeployed(protocol, INDEX_NAME); final Node[] newNode = new Node[1]; final Exception[] childException = new Exception[1]; Thread t = new Thread() { @Override public void run() { Node shutdownNode = _miniCluster.getNode(0); NodeConfiguration nodeConfiguration = new NodeConfiguration(_miniCluster.getDefaultNodeConfiguration().getPropertiesCopy()); nodeConfiguration.setStartPort(shutdownNode.getRPCServerPort()); _miniCluster.shutdownNode(0); newNode[0] = new Node(_protocol, nodeConfiguration, shutdownNode.getContext().getContentServer()); try { newNode[0].start(); } catch (Exception e) { childException[0] = e; } } }; t.start(); /* The 5th call to waitOnAddIfDesired() is the first addShard call from the * second thread. * Don't continue until that call occurs. */ while (addCalls < 5) Thread.sleep(50); Thread t2 = new Thread() { @Override public void run() { newNode[0].disconnect(); newNode[0].reconnect(); } }; t2.start(); /* Before KATTA-216 fix in Node.disconnect(): NPE thrown on * _nodeOperatorThread.interrupt() * After KATTA-216 fix: Child thread restarts successfully, although logging * exceptions for already existing ZK nodes for the redeployed shards. */ addCalls++; t2.join(); t.join(); if (childException[0] != null) { throw childException[0]; } } }