// Copyright 2017 JanusGraph Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.janusgraph.diskstorage.es;
import org.janusgraph.DaemonRunner;
import org.janusgraph.example.GraphOfTheGodsFactory;
import org.janusgraph.util.system.IOUtils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Start and stop a separate Elasticsearch server process.
*/
public class ElasticsearchRunner extends DaemonRunner<ElasticsearchStatus> {
private static final String DEFAULT_HOME_DIR = ".";
private final String homedir;
private static final Logger log =
LoggerFactory.getLogger(ElasticsearchRunner.class);
public static final String ES_PID_FILE = "/tmp/janusgraph-test-es.pid";
public ElasticsearchRunner(String esHome) {
final Pattern VERSION_PATTERN = Pattern.compile("es.dist.version=(.*)");
String version = null;
try (InputStream in = ElasticsearchRunner.class.getClassLoader().getResourceAsStream("janusgraph-es.properties")) {
if (in != null) {
try (Scanner s = new Scanner(in)) {
s.useDelimiter("\\A");
final Matcher m = VERSION_PATTERN.matcher(s.next());
if (m.find()) {
version = m.group(1);
}
}
}
} catch (IOException e) { }
if (version == null) {
throw new RuntimeException("Unable to find Elasticsearch version");
}
this.homedir = esHome + File.separator + "target" + File.separator + "elasticsearch-" + version;
}
public ElasticsearchRunner() {
this(DEFAULT_HOME_DIR);
}
@Override
protected String getDaemonShortName() {
return "Elasticsearch";
}
@Override
protected void killImpl(ElasticsearchStatus stat) throws IOException {
log.info("Killing {} pid {}...", getDaemonShortName(), stat.getPid());
runCommand("/bin/kill", String.valueOf(stat.getPid()));
log.info("Sent SIGTERM to {} pid {}", getDaemonShortName(), stat.getPid());
try {
watchLog(" closed", 60L, TimeUnit.SECONDS);
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
stat.getFile().delete();
log.info("Deleted {}", stat.getFile());
}
@Override
protected ElasticsearchStatus startImpl() throws IOException {
File data = new File(homedir + File.separator + "data");
File logs = new File(homedir + File.separator + "logs");
if (data.exists() && data.isDirectory()) {
log.info("Deleting {}", data);
FileUtils.deleteDirectory(data);
}
if (logs.exists() && logs.isDirectory()) {
log.info("Deleting {}", logs);
FileUtils.deleteDirectory(logs);
}
runCommand(homedir + File.separator + "bin" + File.separator + "elasticsearch", "-d", "-p", ES_PID_FILE);
try {
watchLog(" started", 60L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return readStatusFromDisk();
}
@Override
protected ElasticsearchStatus readStatusFromDisk() {
return ElasticsearchStatus.read(ES_PID_FILE);
}
private void watchLog(String suffix, long duration, TimeUnit unit) throws InterruptedException {
long startMS = System.currentTimeMillis();
long durationMS = TimeUnit.MILLISECONDS.convert(duration, unit);
long elapsedMS;
File logFile = new File(homedir + File.separator + "logs" + File.separator + "elasticsearch.log");
log.info("Watching ES logfile {} for {} token", logFile, suffix);
while ((elapsedMS = System.currentTimeMillis() - startMS) < durationMS) {
// Grep for a logline ending in the suffix and assume that means ES is ready
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(logFile));
String line;
while (null != (line = br.readLine())) {
if (line.endsWith(suffix)) {
log.debug("Read line \"{}\" from ES logfile {}", line, logFile);
return;
}
}
} catch (FileNotFoundException e) {
log.debug("Elasticsearch logfile {} not found", logFile, e);
} catch (IOException e) {
log.debug("Elasticsearch logfile {} could not be read", logFile, e);
} finally {
IOUtils.closeQuietly(br);
}
Thread.sleep(500L);
}
log.info("Elasticsearch logfile timeout ({} {})", elapsedMS, TimeUnit.MILLISECONDS);
}
/**
* Start Elasticsearch process, load GraphOfTheGods, and stop process. Used for integration testing.
* @param args a singleton array containing a path to a JanusGraph config properties file
*/
public static void main(String[] args) {
final ElasticsearchRunner runner = new ElasticsearchRunner();
runner.start();
GraphOfTheGodsFactory.main(args);
runner.stop();
}
}