package net.sf.katta.integrationTest.support;
import java.io.File;
import java.util.List;
import net.sf.katta.AbstractTest;
import net.sf.katta.client.DeployClient;
import net.sf.katta.lib.lucene.LuceneServer;
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.protocol.metadata.IndexMetaData;
import net.sf.katta.protocol.metadata.IndexMetaData.Shard;
import net.sf.katta.testutil.TestResources;
import net.sf.katta.testutil.TestUtil;
import net.sf.katta.util.FileUtil;
import net.sf.katta.util.NodeConfiguration;
import net.sf.katta.util.ZkConfiguration;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
/**
* Test which starts a katta mini cluster for the test class. A test class can
* decide how many nodes should run and if the cluster is restarted between each
* test method. If not restarted, the deployed indices are removed between test
* methods. Also nodes are restarted if shut down during a test.
*/
public abstract class AbstractIntegrationTest extends AbstractTest {
final static Logger LOG = Logger.getLogger(AbstractIntegrationTest.class);
protected final static File INDEX_FILE = TestResources.INDEX1;
protected final static String INDEX_NAME = TestResources.INDEX1.getName() + 0;
protected final static int SHARD_COUNT = INDEX_FILE.list(FileUtil.VISIBLE_FILES_FILTER).length;
protected static KattaMiniCluster _miniCluster;
protected static InteractionProtocol _protocol;
private static int _lastNodeStartPort = 20000;
private final int _nodeCount;
private final boolean _shutdownAfterEachTest;
private final boolean _undeployIndicesAfterEachTest;
private final Class<? extends IContentServer> _contentServerClass;
public AbstractIntegrationTest(int nodeCount) {
this(LuceneServer.class, nodeCount);
}
public AbstractIntegrationTest(Class<? extends IContentServer> nodeServerClass, int nodeCount) {
this(nodeServerClass, nodeCount, false, true);
}
public AbstractIntegrationTest(int nodeCount, boolean shutdownAfterEachTest, boolean undeployIndicesAfterEachTest) {
this(LuceneServer.class, nodeCount, shutdownAfterEachTest, undeployIndicesAfterEachTest);
}
public AbstractIntegrationTest(Class<? extends IContentServer> nodeServerClass, int nodeCount,
boolean shutdownAfterEachTest, boolean undeployIndicesAfterEachTest) {
_contentServerClass = nodeServerClass;
_nodeCount = nodeCount;
_shutdownAfterEachTest = shutdownAfterEachTest;
_undeployIndicesAfterEachTest = undeployIndicesAfterEachTest;
}
@AfterClass
public final static void tearDownClass() throws Exception {
if (_miniCluster != null) {
_miniCluster.stop();
_miniCluster = null;
}
}
@After
public final void tearDown() throws Exception {
if (_miniCluster != null) {
_lastNodeStartPort += _miniCluster.getStartedNodeCount();
} else {
_lastNodeStartPort += _nodeCount;
}
if (_shutdownAfterEachTest && _miniCluster != null) {
_miniCluster.stop();
_miniCluster = null;
}
}
@Before
public final void setUp() throws Exception {
LOG.info("~~~~~~~~~~~~~~~~~~" + "SETUP CLUSTER" + "~~~~~~~~~~~~~~~~~~");
if (_miniCluster == null) {
startMiniCluster(_nodeCount, 0, 0);
afterClusterStart();
} else if (!_shutdownAfterEachTest) {
// restart nodes
LOG.info("nodes " + _miniCluster.getNodes() + " running.");
int numberOfRunningNode = _miniCluster.getRunningNodeCount();
int nodesToStop = numberOfRunningNode - _nodeCount;
int nodesToStart = _nodeCount - numberOfRunningNode;
if (nodesToStop > 0) {
for (int i = 0; i < nodesToStop; i++) {
Node node = _miniCluster.shutdownNode(i);
LOG.info("stopped " + node.getName());
}
} else if (nodesToStart > 0) {
for (int i = 0; i < nodesToStart; i++) {
Node node = _miniCluster.startAdditionalNode();
LOG.info("started " + node.getName());
}
}
TestUtil.waitUntilNumberOfLiveNode(_protocol, _nodeCount);
// remove all indices
if (_undeployIndicesAfterEachTest) {
List<String> indices = _protocol.getIndices();
for (String index : indices) {
IndexMetaData indexMD = _protocol.getIndexMD(index);
DeployClient deployClient = new DeployClient(_protocol);
deployClient.removeIndex(index);
TestUtil.waitUntilShardsUndeployed(_protocol, indexMD);
}
} else {
List<String> indices = _protocol.getIndices();
for (String index : indices) {
TestUtil.waitUntilIndexBalanced(_protocol, index);
}
}
LOG.info("~~~~~~~~~~~~~~~~~~" + "FIN SETUP CLUSTER" + "~~~~~~~~~~~~~~~~~~");
}
}
protected void afterClusterStart() throws Exception {
// subclasses may override
}
protected ZkConfiguration getZkConfiguration() {
return _miniCluster.getZkConfiguration();
}
public int getNodeCount() {
return _nodeCount;
}
public Class<? extends IContentServer> getContentServerClass() {
return _contentServerClass;
}
private final KattaMiniCluster startMiniCluster(int nodeCount, int indexCount, int replicationCount) throws Exception {
ZkConfiguration conf = new ZkConfiguration();
FileUtil.deleteFolder(new File(conf.getZKDataDir()));
FileUtil.deleteFolder(new File(conf.getZKDataLogDir()));
FileUtil.deleteFolder(new NodeConfiguration().getShardFolder());
// start katta cluster
_miniCluster = new KattaMiniCluster(_contentServerClass, conf, nodeCount, _lastNodeStartPort);
// we permanently start the node on other
// ports because hadoop rpc seems to make
// some trouble if a rpc server is restarted on the same port immediately
_miniCluster.start();
deployTestIndices(indexCount, replicationCount);
_protocol = _miniCluster.getProtocol();
return _miniCluster;
}
protected void shutdownCluster() {
_miniCluster.stop();
_miniCluster = null;
}
protected void shutdownNodes() {
for (int i = 0; i < _miniCluster.getRunningNodeCount(); i++) {
_miniCluster.shutdownNode(i);
}
}
protected List<String> deployTestIndices(int indexCount, int replicationCount) throws InterruptedException {
return _miniCluster.deployTestIndexes(INDEX_FILE, indexCount, replicationCount);
}
protected final int countShardDeployments(InteractionProtocol protocol, String indexName) {
IndexMetaData indexMD = protocol.getIndexMD(indexName);
int shardDeployCount = 0;
for (Shard shard : indexMD.getShards()) {
shardDeployCount += protocol.getShardNodes(shard.getName()).size();
}
return shardDeployCount;
}
protected IndexMetaData deployIndex(String indexName, File indexFile, int replication) throws Exception {
IndexDeployOperation deployOperation = new IndexDeployOperation(indexName, "file://" + indexFile.getAbsolutePath(),
replication);
_protocol.addMasterOperation(deployOperation);
TestUtil.waitUntilIndexDeployed(_protocol, INDEX_NAME);
return _protocol.getIndexMD(indexName);
}
}