package mil.nga.giat.geowave.test;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.gc.SimpleGarbageCollector;
import org.apache.accumulo.master.Master;
import org.apache.accumulo.minicluster.impl.MiniAccumuloClusterImpl;
import org.apache.accumulo.minicluster.impl.MiniAccumuloConfigImpl;
import org.apache.accumulo.server.init.Initialize;
import org.apache.accumulo.tserver.TabletServer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.Assert;
import mil.nga.giat.geowave.core.store.DataStore;
import mil.nga.giat.geowave.core.store.GenericStoreFactory;
import mil.nga.giat.geowave.core.store.StoreFactoryOptions;
import mil.nga.giat.geowave.datastore.accumulo.AccumuloDataStoreFactory;
import mil.nga.giat.geowave.datastore.accumulo.minicluster.MiniAccumuloClusterFactory;
import mil.nga.giat.geowave.datastore.accumulo.operations.config.AccumuloRequiredOptions;
import mil.nga.giat.geowave.test.annotation.GeoWaveTestStore.GeoWaveStoreType;
public class AccumuloStoreTestEnvironment extends
StoreTestEnvironment
{
private static final GenericStoreFactory<DataStore> STORE_FACTORY = new AccumuloDataStoreFactory();
private static AccumuloStoreTestEnvironment singletonInstance = null;
public static synchronized AccumuloStoreTestEnvironment getInstance() {
if (singletonInstance == null) {
singletonInstance = new AccumuloStoreTestEnvironment();
}
return singletonInstance;
}
private final static Logger LOGGER = LoggerFactory.getLogger(AccumuloStoreTestEnvironment.class);
protected static final String DEFAULT_MINI_ACCUMULO_PASSWORD = "Ge0wave";
protected static final String HADOOP_WINDOWS_UTIL = "winutils.exe";
protected static final String HADOOP_DLL = "hadoop.dll";
// breaks on windows if temp directory isn't on same drive as project
protected static final File TEMP_DIR = new File(
"./target/accumulo_temp");
protected String zookeeper;
protected String accumuloInstance;
protected String accumuloUser;
protected String accumuloPassword;
protected MiniAccumuloClusterImpl miniAccumulo;
private final List<Process> cleanup = new ArrayList<Process>();
private AccumuloStoreTestEnvironment() {}
@Override
public void setup() {
if (!TestUtils.isSet(zookeeper)) {
zookeeper = System.getProperty(ZookeeperTestEnvironment.ZK_PROPERTY_NAME);
if (!TestUtils.isSet(zookeeper)) {
zookeeper = ZookeeperTestEnvironment.getInstance().getZookeeper();
LOGGER.debug("Using local zookeeper URL: " + zookeeper);
}
}
if (!TestUtils.isSet(accumuloInstance) || !TestUtils.isSet(accumuloUser) || !TestUtils.isSet(accumuloPassword)) {
accumuloInstance = System.getProperty("instance");
accumuloUser = System.getProperty("username");
accumuloPassword = System.getProperty("password");
if (!TestUtils.isSet(accumuloInstance) || !TestUtils.isSet(accumuloUser)
|| !TestUtils.isSet(accumuloPassword)) {
try {
if (!TEMP_DIR.exists()) {
if (!TEMP_DIR.mkdirs()) {
throw new IOException(
"Could not create temporary directory");
}
}
TEMP_DIR.deleteOnExit();
final MiniAccumuloConfigImpl config = new MiniAccumuloConfigImpl(
TEMP_DIR,
DEFAULT_MINI_ACCUMULO_PASSWORD);
config.setZooKeeperPort(Integer.parseInt(zookeeper.split(":")[1]));
config.setNumTservers(2);
miniAccumulo = MiniAccumuloClusterFactory.newAccumuloCluster(
config,
AccumuloStoreTestEnvironment.class);
startMiniAccumulo(config);
accumuloUser = "root";
accumuloInstance = miniAccumulo.getInstanceName();
accumuloPassword = DEFAULT_MINI_ACCUMULO_PASSWORD;
}
catch (IOException | InterruptedException e) {
LOGGER.warn(
"Unable to start mini accumulo instance",
e);
LOGGER.info("Check '" + TEMP_DIR.getAbsolutePath() + File.separator + "logs' for more info");
if (SystemUtils.IS_OS_WINDOWS) {
LOGGER
.warn("For windows, make sure that Cygwin is installed and set a CYGPATH environment variable to %CYGWIN_HOME%/bin/cygpath to successfully run a mini accumulo cluster");
}
Assert.fail("Unable to start mini accumulo instance: '" + e.getLocalizedMessage() + "'");
}
}
}
}
private void startMiniAccumulo(
final MiniAccumuloConfigImpl config )
throws IOException,
InterruptedException {
final LinkedList<String> jvmArgs = new LinkedList<>();
jvmArgs.add("-XX:CompressedClassSpaceSize=512m");
jvmArgs.add("-XX:MaxMetaspaceSize=512m");
jvmArgs.add("-Xmx512m");
Runtime.getRuntime().addShutdownHook(
new Thread() {
@Override
public void run() {
tearDown();
}
});
final Map<String, String> siteConfig = config.getSiteConfig();
siteConfig.put(
Property.INSTANCE_ZK_HOST.getKey(),
zookeeper);
config.setSiteConfig(siteConfig);
final LinkedList<String> args = new LinkedList<>();
args.add("--instance-name");
args.add(config.getInstanceName());
args.add("--password");
args.add(config.getRootPassword());
final Process initProcess = miniAccumulo.exec(
Initialize.class,
jvmArgs,
args.toArray(new String[0]));
cleanup.add(initProcess);
final int ret = initProcess.waitFor();
if (ret != 0) {
throw new RuntimeException(
"Initialize process returned " + ret + ". Check the logs in " + config.getLogDir() + " for errors.");
}
LOGGER.info("Starting MAC against instance " + config.getInstanceName() + " and zookeeper(s) "
+ config.getZooKeepers());
for (int i = 0; i < config.getNumTservers(); i++) {
cleanup.add(miniAccumulo.exec(
TabletServer.class,
jvmArgs));
}
cleanup.add(miniAccumulo.exec(
Master.class,
jvmArgs));
cleanup.add(miniAccumulo.exec(
SimpleGarbageCollector.class,
jvmArgs));
}
@Override
public void tearDown() {
zookeeper = null;
accumuloInstance = null;
accumuloUser = null;
accumuloPassword = null;
if (miniAccumulo != null) {
try {
for (final Process p : cleanup) {
p.destroy();
p.waitFor();
}
for (final Process p : cleanup) {
p.destroy();
p.waitFor();
}
miniAccumulo = null;
}
catch (final InterruptedException e) {
LOGGER.warn(
"Unable to stop mini accumulo instance",
e);
}
}
if (TEMP_DIR != null) {
try {
// sleep because mini accumulo processes still have a
// hold on the log files and there is no hook to get
// notified when it is completely stopped
Thread.sleep(2000);
FileUtils.deleteDirectory(TEMP_DIR);
}
catch (final IOException | InterruptedException e) {
LOGGER.warn(
"Unable to delete mini Accumulo temporary directory",
e);
}
}
}
@Override
protected void initOptions(
final StoreFactoryOptions options ) {
final AccumuloRequiredOptions accumuloOpts = (AccumuloRequiredOptions) options;
accumuloOpts.setUser(accumuloUser);
accumuloOpts.setPassword(accumuloPassword);
accumuloOpts.setInstance(accumuloInstance);
accumuloOpts.setZookeeper(zookeeper);
}
@Override
protected GenericStoreFactory<DataStore> getDataStoreFactory() {
return STORE_FACTORY;
}
@Override
protected GeoWaveStoreType getStoreType() {
return GeoWaveStoreType.ACCUMULO;
}
public String getZookeeper() {
return zookeeper;
}
public String getAccumuloInstance() {
return accumuloInstance;
}
public String getAccumuloUser() {
return accumuloUser;
}
public String getAccumuloPassword() {
return accumuloPassword;
}
@Override
public TestEnvironment[] getDependentEnvironments() {
return new TestEnvironment[] {
ZookeeperTestEnvironment.getInstance()
};
}
}