Package org.qzerver.model.agent.action.providers.executor.localcommand

Source Code of org.qzerver.model.agent.action.providers.executor.localcommand.LocalCommandActionExecutor

package org.qzerver.model.agent.action.providers.executor.localcommand;

import com.gainmatrix.lib.spring.validation.BeanValidationUtils;
import com.google.common.base.Preconditions;
import org.apache.commons.io.IOUtils;
import org.qzerver.model.agent.action.providers.ActionDefinition;
import org.qzerver.model.agent.action.providers.ActionExecutor;
import org.qzerver.model.agent.action.providers.ActionPlaceholders;
import org.qzerver.model.agent.action.providers.executor.localcommand.threads.ProcessExecutionThread;
import org.qzerver.model.agent.action.providers.executor.localcommand.threads.ProcessOutputThread;
import org.qzerver.model.agent.action.providers.executor.localcommand.threads.ProcessTimeoutThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.validation.Validator;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class LocalCommandActionExecutor implements ActionExecutor {

    private static final Logger LOGGER = LoggerFactory.getLogger(LocalCommandActionExecutor.class);

    private long maxCaptureSize;

    private Validator beanValidator;

    @Override
    public LocalCommandActionResult execute(ActionDefinition actionDefinition,
        long scheduleExecutionId, String nodeAddress)
    {
        Preconditions.checkNotNull(actionDefinition, "Definition is null");
        Preconditions.checkNotNull(nodeAddress, "Node is not specified");

        BeanValidationUtils.checkValidity(actionDefinition, beanValidator);

        LocalCommandActionDefinition definition = (LocalCommandActionDefinition) actionDefinition;

        LOGGER.debug("Execute local command action on node [{}]", nodeAddress);

        // Process builder
        ProcessBuilder pb = new ProcessBuilder();

        File workingFolder = new File(definition.getDirectory());
        pb.directory(workingFolder);
        pb.redirectErrorStream(definition.isCombineOutput());

        // Environment
        pb.environment().clear();

        if (definition.isEnvironmentInherit()) {
            pb.environment().putAll(System.getenv());
        }

        if (definition.getEnvironmentVariables() != null) {
            for (Map.Entry<String, String> environmentEntry : definition.getEnvironmentVariables().entrySet()) {
                pb.environment().put(environmentEntry.getKey(), environmentEntry.getValue());

            }
        }

        // Parameters list
        List<String> commands = new ArrayList<String>();
        commands.add(definition.getCommand());

        for (String parameter : definition.getParameters()) {
            String effectiveParameter = parameter;
            effectiveParameter = ActionPlaceholders.substituteNode(effectiveParameter, nodeAddress);
            effectiveParameter = ActionPlaceholders.substituteExecution(effectiveParameter, scheduleExecutionId);
            commands.add(effectiveParameter);
        }

        pb.command(commands);

        // Start execution
        Process process;

        try {
            process = pb.start();
        } catch (Exception e) {
            LOGGER.debug("Failed to start a process", e);
            return produceExceptionalResult(e);
        }

        // Wait while process exits and grab all the result
        try {
            return executeProcess(process, definition);
        } catch (Exception e) {
            LOGGER.debug("Failed to execute process", e);
            return produceExceptionalResult(e);
        }
    }

    private LocalCommandActionResult produceExceptionalResult(Exception e) {
        LocalCommandActionOutput standardOutput = new LocalCommandActionOutput();
        standardOutput.setStatus(LocalCommandActionOutputStatus.IDLE);

        LocalCommandActionOutput standardError = new LocalCommandActionOutput();
        standardError.setStatus(LocalCommandActionOutputStatus.IDLE);

        LocalCommandActionResult result = new LocalCommandActionResult();
        result.setStatus(LocalCommandActionResultStatus.EXCEPTION);
        result.setExitCode(-1);
        result.setExceptionClass(e.getClass().getCanonicalName());
        result.setExceptionMessage(e.getLocalizedMessage());
        result.setSucceed(false);
        result.setStdout(standardOutput);
        result.setStderr(standardError);

        return result;
    }

    private LocalCommandActionResult executeProcess(Process process, LocalCommandActionDefinition definition)
        throws Exception
    {
        // Start process thread
        ProcessExecutionThread processExecutionThread = new ProcessExecutionThread(process);
        processExecutionThread.start();

        // Start standard output copier
        ProcessOutputThread processStandardOutputThread = new ProcessOutputThread(
            process.getInputStream(), maxCaptureSize, definition.isSkipStdOutput());
        processStandardOutputThread.start();

        // Start error output copier
        ProcessOutputThread processStandardErrorThread = new ProcessOutputThread(
            process.getErrorStream(), maxCaptureSize, definition.isSkipStdError());
        processStandardErrorThread.start();

        // If timeout is set start watchdog thread. It terminates process thread after timeout exceeds
        if (definition.getTimeoutMs() > 0) {
            ProcessTimeoutThread processTimeoutThread = new ProcessTimeoutThread(
                processExecutionThread, definition.getTimeoutMs());
            processTimeoutThread.start();
        }

        // Wait while process thread exits
        processExecutionThread.join();

        // Release all resources - even the process is not alive (http://kylecartmell.com/?p=9)
        IOUtils.closeQuietly(process.getErrorStream());
        IOUtils.closeQuietly(process.getInputStream());
        IOUtils.closeQuietly(process.getOutputStream());
        process.destroy();

        // Wait while output copiers exit
        processStandardOutputThread.shutdownCapture();
        processStandardErrorThread.shutdownCapture();

        // Compose the result
        LocalCommandActionResult result = new LocalCommandActionResult();
        result.setStatus(processExecutionThread.getStatus());
        result.setExitCode(processExecutionThread.getExitCode());
        result.setStdout(processStandardOutputThread.composeActionOutput());
        result.setStderr(processStandardErrorThread.composeActionOutput());

        // In case of the process termination change "success" status to "terminated"
        if (result.getStatus() == LocalCommandActionResultStatus.TIMEOUT) {
            LocalCommandActionOutput stdout = result.getStdout();
            if (stdout.getStatus() == LocalCommandActionOutputStatus.CAPTURED) {
                stdout.setStatus(LocalCommandActionOutputStatus.TIMEOUT);
            }
            LocalCommandActionOutput stderr = result.getStderr();
            if (stderr.getStatus() == LocalCommandActionOutputStatus.CAPTURED) {
                stderr.setStatus(LocalCommandActionOutputStatus.TIMEOUT);
            }
        }

        // Succeed status
        boolean succeed = (result.getStatus() == LocalCommandActionResultStatus.EXECUTED) &&
            (result.getExitCode() == definition.getExpectedExitCode());
        result.setSucceed(succeed);

        return result;
    }

    @Required
    public void setMaxCaptureSize(long maxCaptureSize) {
        this.maxCaptureSize = maxCaptureSize;
    }

    @Required
    public void setBeanValidator(Validator beanValidator) {
        this.beanValidator = beanValidator;
    }
}
TOP

Related Classes of org.qzerver.model.agent.action.providers.executor.localcommand.LocalCommandActionExecutor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.