All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.janusgraph.DaemonRunner Maven / Gradle / Ivy

There is a newer version: 100.3.2.1
Show newest version
// 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;

import com.google.common.base.Joiner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public abstract class DaemonRunner {

    private static final Logger log =
            LoggerFactory.getLogger(DaemonRunner.class);

    private Thread killerHook;

    protected abstract String getDaemonShortName();

    protected abstract void killImpl(final S stat) throws IOException;

    protected abstract S startImpl() throws IOException;

    protected abstract S readStatusFromDisk();

    /**
     * Read daemon status from disk, then try to start the daemon
     * if the status file says it is stopped.  Do nothing if the
     * status read from disk says the daemon is running.
     *
     * After successfully starting the daemon (no exceptions from
     * {@link #startImpl()}, register a shutdown hook with the VM
     * that will call {@link org.janusgraph.DaemonRunner#killImpl} on shutdown.
     *
     * @return status representing the daemon, either just-started
     *         or already running
     */
    public synchronized S start() {
        S stat = readStatusFromDisk();

        if (stat != null) {
            log.info("{} already started", getDaemonShortName());
            return stat;
        }

        try {
            stat = startImpl();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        registerKillerHook(stat);

        return stat;
    }

    /**
     * Read daemon status from disk, then try to kill the daemon
     * if the status file says it is running.  Do nothing if the
     * status read from disk says the daemon is stopped.
     */
    public synchronized void stop() {
        S stat = readStatusFromDisk();

        if (null == stat) {
            log.info("{} is not running", getDaemonShortName());
            return;
        }

        killAndUnregisterHook(stat);
    }

    private synchronized void registerKillerHook(final S stat) {
        if (null != killerHook) {
            log.debug("Daemon killer hook already registered: {}", killerHook);
            return;
        }
        killerHook = new Thread(() -> killAndUnregisterHook(stat));
        Runtime.getRuntime().addShutdownHook(killerHook);
        log.debug("Registered daemon killer hook: {}", killerHook);
    }

    private synchronized void killAndUnregisterHook(final S stat) {

        try {
            killImpl(stat);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if (null != killerHook) {
            try {
                Runtime.getRuntime().removeShutdownHook(killerHook);
                log.debug("Unregistered killer hook: {}", killerHook);
            } catch (IllegalStateException e) {
                /* Can receive "java.lang.IllegalStateException: Shutdown in progress"
                 * when called from JVM shutdown (as opposed to called from the stop method).
                 */
                log.debug("Could not unregister killer hook: {}", e);
            }
            killerHook = null;
        }
    }

    /**
     * Run the parameter as an external process. Returns if the command starts
     * without throwing an exception and returns exit status 0. Throws an
     * exception if there's any problem invoking the command or if it does not
     * return zero exit status.
     *
     * Blocks indefinitely while waiting for the command to complete.
     *
     * @param argv
     *            passed directly to {@link ProcessBuilder}'s constructor
     */
    public static void runCommand(String... argv) {

        final String cmd = Joiner.on(" ").join(argv);
        log.info("Executing {}", cmd);

        ProcessBuilder pb = new ProcessBuilder(argv);
        pb.redirectErrorStream(true);
        Process startup;
        try {
            startup = pb.start();
        } catch (IOException e) {
            throw new RuntimeException("Unable to start process in " + System.getProperty("user.dir") + ": " + e.getMessage(), e);
        }
        StreamLogger sl = new StreamLogger(startup.getInputStream());
        sl.setDaemon(true);
        sl.start();

        waitForProcessAndCheckStatus(cmd, startup);

        try {
            sl.join(1000L);
        } catch (InterruptedException e) {
            log.warn("Failed to cleanup stdin handler thread after running command \"{}\"", cmd, e);
        }
    }

    private static void waitForProcessAndCheckStatus(String cmd, Process startup) {
        try {
            int exitCode = startup.waitFor(); // wait for script to return
            if (0 == exitCode) {
                log.info("Command \"{}\" exited with status 0", cmd);
            } else {
                throw new RuntimeException("Command \"" + cmd + "\" exited with status " + exitCode);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /*
     * This could be retired in favor of ProcessBuilder.Redirect when we move to
     * source level 1.7.
     */
    private static class StreamLogger extends Thread {

        private final BufferedReader reader;
        private static final Logger log =
                LoggerFactory.getLogger(StreamLogger.class);

        private StreamLogger(InputStream is) {
            this.reader = new BufferedReader(new InputStreamReader(is));
        }

        @Override
        public void run() {
            String line;
            try {
                while (null != (line = reader.readLine())) {
                    log.info("> {}", line);
                    if (Thread.currentThread().isInterrupted()) {
                        break;
                    }
                }

                log.info("End of stream.");
            } catch (IOException e) {
                log.error("Unexpected IOException while reading stream {}", reader, e);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy