Java tutorial
/* * GRAKN.AI - THE KNOWLEDGE GRAPH * Copyright (C) 2018 Grakn Labs Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package grakn.core.daemon.executor; import com.google.auto.value.AutoValue; import org.apache.commons.io.FileUtils; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.ProcessResult; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeoutException; /** * This class is responsible for spawning process. */ public class Executor { @AutoValue public abstract static class Result { public static Result create(String stdout, String stderr, int exitCode) { return new AutoValue_Executor_Result(stdout, stderr, exitCode); } public boolean success() { return exitCode() == 0; } public abstract String stdout(); public abstract String stderr(); public abstract int exitCode(); } static final long WAIT_INTERVAL_SECOND = 2; private static final String SH = "/bin/sh"; public CompletableFuture<Result> executeAsync(List<String> command, File workingDirectory) { return CompletableFuture.supplyAsync(() -> executeAndWait(command, workingDirectory)); } public Result executeAndWait(List<String> command, File workingDirectory) { try { ByteArrayOutputStream stderr = new ByteArrayOutputStream(); ProcessResult result = new ProcessExecutor().readOutput(true).redirectError(stderr) .directory(workingDirectory).command(command).execute(); return Result.create(result.outputUTF8(), stderr.toString(StandardCharsets.UTF_8.name()), result.getExitValue()); } catch (IOException | InterruptedException | TimeoutException e) { throw new RuntimeException(e); } } public String retrievePid(Path pidFile) { if (!pidFile.toFile().exists()) { return null; } try { String pid = new String(Files.readAllBytes(pidFile), StandardCharsets.UTF_8); return pid.trim(); } catch (NumberFormatException | IOException e) { return null; } } public void waitUntilStopped(Path pidFile) { while (isProcessRunning(pidFile)) { System.out.print("."); System.out.flush(); try { Thread.sleep(WAIT_INTERVAL_SECOND * 1000); } catch (InterruptedException e) { // DO NOTHING } } System.out.println("SUCCESS"); FileUtils.deleteQuietly(pidFile.toFile()); } public boolean isProcessRunning(Path pidFile) { String processPid; if (pidFile.toFile().exists()) { try { processPid = new String(Files.readAllBytes(pidFile), StandardCharsets.UTF_8); if (processPid.trim().isEmpty()) { return false; } Result command = executeAndWait(checkPIDRunningCommand(processPid), null); if (command.exitCode() != 0) { System.out.println(command.stderr()); } return command.exitCode() == 0; } catch (NumberFormatException | IOException e) { return false; } } return false; } /** * Method used to check whether the pid contained in the pid file actually corresponds * to a Grakn(Storage) process. * * @param pidFile path to pid file * @param className name of Class associated to the given pid (e.g. "grakn.core.server.Grakn") * @return true if PID is associated to the a Grakn process, false otherwise. */ public boolean isAGraknProcess(Path pidFile, String className) { String processPid; if (pidFile.toFile().exists()) { try { processPid = new String(Files.readAllBytes(pidFile), StandardCharsets.UTF_8); if (processPid.trim().isEmpty()) { return false; } Result command = executeAndWait(getGraknPIDArgsCommand(processPid), null); if (command.exitCode() != 0) { return false; } return command.stdout().contains(className); } catch (NumberFormatException | IOException e) { return false; } } return false; } private List<String> checkPIDRunningCommand(String pid) { if (isWindows()) { return Arrays.asList("cmd", "/c", "tasklist /fi \"PID eq " + pid.trim() + "\" | findstr \"" + pid.trim() + "\""); } else { return Arrays.asList(SH, "-c", "ps -p " + pid.trim()); } } private List<String> getGraknPIDArgsCommand(String pid) { if (isWindows()) { return Arrays.asList("cmd", "/c", "wmic process where processId='" + pid.trim() + "' get Commandline | findstr Grakn"); } else { return Arrays.asList(SH, "-c", "ps -p " + pid.trim() + " -o command | awk '{print $NF}' | grep Grakn"); } } public void stopProcessIfRunning(Path pidFile, String programName) { System.out.print("Stopping " + programName + "..."); System.out.flush(); boolean programIsRunning = isProcessRunning(pidFile); if (!programIsRunning) { System.out.println("NOT RUNNING"); } else { stopProcess(pidFile); } } public void processStatus(Path pidFile, String name, String className) { if (isProcessRunning(pidFile) && isAGraknProcess(pidFile, className)) { System.out.println(name + ": RUNNING"); } else { System.out.println(name + ": NOT RUNNING"); } } private void stopProcess(Path pidFile) { String pid = retrievePid(pidFile); if (pid == null) return; kill(pid); waitUntilStopped(pidFile); } private List<String> killProcessCommand(String pid) { if (isWindows()) { return Arrays.asList("cmd", "/c", "taskkill /F /PID " + pid.trim()); } else { return Arrays.asList(SH, "-c", "kill " + pid.trim()); } } private void kill(String pid) { executeAndWait(killProcessCommand(pid), null); } private boolean isWindows() { return System.getProperty("os.name").toLowerCase().contains("win"); } }