ServiceCommand.java

package emissary.command;

import emissary.client.EmissaryResponse;
import emissary.core.EmissaryRuntimeException;
import emissary.directory.EmissaryNode;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
import picocli.CommandLine.Option;

import static emissary.server.api.HealthCheckAction.HEALTH;
import static emissary.server.api.Shutdown.SHUTDOWN;

/**
 * Abstract command to control service components (stop/pause/unpause)
 */
public abstract class ServiceCommand extends HttpCommand {

    static final Logger LOG = LoggerFactory.getLogger(ServiceCommand.class);

    public static final String COMMAND_NAME = "ServiceCommand";
    public static final String SERVICE_HEALTH_ENDPOINT = "/api/" + HEALTH;
    public static final String SERVICE_SHUTDOWN_ENDPOINT = "/api/" + SHUTDOWN;
    public static final String SERVICE_KILL_ENDPOINT = SERVICE_SHUTDOWN_ENDPOINT + "/force";

    @Option(names = {"--csrf"}, description = "disable csrf protection\nDefault: ${DEFAULT-VALUE}", arity = "1")
    private boolean csrf = true;

    @Option(names = {"--stop"}, description = "Shutdown the service\nDefault: ${DEFAULT-VALUE}")
    private boolean stop = false;

    @Option(names = {"--kill"}, description = "Force the shutdown of the service\nDefault: ${DEFAULT-VALUE}")
    private boolean kill = false;

    @Option(names = {"--pause"}, description = "Stop the service from taking work\nDefault: ${DEFAULT-VALUE}")
    private boolean pause = false;

    @Option(names = {"--unpause"}, description = "Allow a paused service to take work\nDefault: ${DEFAULT-VALUE}")
    private boolean unpause = false;

    public boolean isCsrf() {
        return csrf;
    }

    public boolean isStop() {
        return stop;
    }

    public boolean isKill() {
        return kill;
    }

    public boolean isPause() {
        return pause;
    }

    public boolean isUnpause() {
        return unpause;
    }

    public String getServiceHealthEndpoint() {
        return SERVICE_HEALTH_ENDPOINT;
    }

    public String getServiceShutdownEndpoint() {
        return isKill() ? SERVICE_KILL_ENDPOINT : SERVICE_SHUTDOWN_ENDPOINT;
    }

    public String getServiceName() {
        return getCommandName();
    }

    /**
     * Startup the service
     */
    protected abstract void startService();

    @Override
    public void setupHttp() {
        super.setupHttp();

        // check to see if this is set -- feed will call server command and reset this value
        if (StringUtils.isBlank(System.getProperty(EmissaryNode.NODE_SERVICE_TYPE_PROPERTY))) {
            logInfo("Setting {} to {} ", EmissaryNode.NODE_SERVICE_TYPE_PROPERTY, getCommandName());
            System.setProperty(EmissaryNode.NODE_SERVICE_TYPE_PROPERTY, getCommandName());
        }
    }

    @Override
    public void run(CommandLine c) {
        setup();

        // let's check to see if the server is already running
        LOG.debug("Checking to see if Emissary {} is running at {}", getServiceName(), getServiceHealthEndpoint());
        EmissaryResponse response = performGet(getServiceHealthEndpoint());
        boolean isRunning = response.getStatus() == 200;
        if (isStop() || isKill()) {
            if (isRunning) {
                stopService();
            } else {
                LOG.warn("Error stopping service: no service {} running", getServiceName());
            }
        } else {
            if (isRunning) {
                // the server is already running so pause/unpause or fail
                if (isPause()) {
                    pauseService();
                } else if (isUnpause()) {
                    unpauseService();
                } else {
                    throw new EmissaryRuntimeException("Emissary " + getServiceName() + " is already running");
                }
            } else {
                if (isPause()) {
                    // we hadn't intended to start the service, so don't try to do so now
                    throw new EmissaryRuntimeException("Error pausing service: request returned status " + response.getStatus());
                } else {
                    // no running server so fire it up
                    startService();
                }
            }
        }
    }

    /**
     * Shutdown method that uses an endpoint to stop a running service
     */
    protected void stopService() {
        LOG.info("Stopping Emissary {} at {}", getServiceName(), getServiceShutdownEndpoint());
        EmissaryResponse response = performPost(getServiceShutdownEndpoint());
        if (response.getStatus() != 200) {
            LOG.error("Problem shutting down {} -- {}", getServiceName(), response.getContentString());
        } else {
            LOG.info("Emissary {} stopped", getServiceName());
        }
    }

    /**
     * A method that stops a running service from taking work
     */
    protected void pauseService() {
        throw new UnsupportedOperationException("Pause not implemented for " + getServiceName());
    }

    /**
     * A method that allows a paused service to take work
     */
    protected void unpauseService() {
        throw new UnsupportedOperationException("Unpause not implemented for " + getServiceName());
    }

}