com.anrisoftware.mongoose.buildins.execbuildin.ExecBuildin.java Source code

Java tutorial

Introduction

Here is the source code for com.anrisoftware.mongoose.buildins.execbuildin.ExecBuildin.java

Source

/*
 * Copyright 2012 Erwin Mller <erwin.mueller@deventm.org>
 *
 * This file is part of groovybash-core.
 *
 * groovybash-core is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * groovybash-core 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * groovybash-core. If not, see <http://www.gnu.org/licenses/>.
 */
package com.anrisoftware.mongoose.buildins.execbuildin;

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

import javax.inject.Inject;

import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteResultHandler;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.ProcessDestroyer;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.exec.ShutdownHookProcessDestroyer;
import org.joda.time.Duration;

import com.anrisoftware.mongoose.api.commans.ExecCommand;
import com.anrisoftware.mongoose.api.environment.Environment;
import com.anrisoftware.mongoose.api.exceptions.CommandException;
import com.anrisoftware.mongoose.command.AbstractCommand;

/**
 * Executes the specified command in a separate process with the specified
 * environment and working directory.
 * 
 * @author Erwin Mueller, erwin.mueller@deventm.org
 * @since 1.0
 */
class ExecBuildin extends AbstractCommand implements ExecCommand {

    private static final String SUCCESS_EXIT_VALUES_KEY = "successExitValues";

    private static final String SUCCESS_EXIT_VALUE_KEY = "successExitValue";

    private static final String TIMEOUT_KEY = "timeout";

    private static final String WATCHDOG_KEY = "watchdog";

    private static final String DESTROYER_KEY = "destroyer";

    private static final String HANDLER_KEY = "handler";

    private static final String TERMINAL_KEY = "terminal";

    private static final String TERMINAL_COMMAND_KEY = "terminalCommand";

    private static final String TERMINAL_COMMAND_VARIABLE = "TERMINAL_COMMAND";

    private final ExecBuildinLogger log;

    private final Executor executor;

    private final CommandParser parser;

    private String command;

    private Map<String, String> env;

    private ExecuteResultHandler handler;

    private ProcessDestroyer destroyer;

    private ExecuteWatchdog watchdog;

    @Inject
    ExecBuildin(ExecBuildinLogger logger, Executor executor, CommandParser parser) {
        this.log = logger;
        this.executor = executor;
        this.command = null;
        this.env = null;
        this.handler = new DefaultExecuteResultHandler();
        this.destroyer = new ShutdownHookProcessDestroyer();
        this.watchdog = null;
        this.parser = parser;
    }

    @Override
    public void setEnvironment(Environment environment) throws CommandException {
        super.setEnvironment(environment);
        setDirectory(environment.getWorkingDirectory());
        env = environment.getEnv();
        if (env.containsKey(TERMINAL_COMMAND_VARIABLE)) {
            setTerminalCommand(env.get(TERMINAL_COMMAND_VARIABLE));
        }
    }

    /**
     * Returns the name {@code exec} or the command's executable.
     */
    @Override
    public String getTheName() {
        return command == null ? "exec" : command;
    }

    @Override
    protected void doCall() throws Exception {
        startProcess();
    }

    private void startProcess() throws IOException, InterruptedException {
        executor.setProcessDestroyer(destroyer);
        executor.setWatchdog(watchdog);
        executor.setStreamHandler(new PumpStreamHandler(getOutput(), getError(), getInput()));
        executor.execute(parser.parseCommand(command), env, handler);
        waitForCommand();
        parser.cleanUp();
    }

    private void waitForCommand() throws InterruptedException, CommandException {
        if (handler instanceof DefaultExecuteResultHandler) {
            DefaultExecuteResultHandler h = (DefaultExecuteResultHandler) handler;
            h.waitFor();
            ExecuteException ex = h.getException();
            if (ex != null) {
                throw log.errorCommand(this, ex);
            }
        }
    }

    @Override
    protected void argumentsSet(Map<String, Object> args, List<Object> unnamedArgs) throws Exception {
        super.argumentsSet(args, unnamedArgs);
        if (args.containsKey(HANDLER_KEY)) {
            setHandler(args.get(HANDLER_KEY));
        }
        if (args.containsKey(DESTROYER_KEY)) {
            setDestroyer(args.get(DESTROYER_KEY));
        }
        if (args.containsKey(WATCHDOG_KEY)) {
            setWatchdog(args.get(WATCHDOG_KEY));
        }
        if (args.containsKey(TIMEOUT_KEY)) {
            setTimeout(args.get(TIMEOUT_KEY));
        }
        if (args.containsKey(SUCCESS_EXIT_VALUE_KEY)) {
            setSuccessExitValue(((Number) args.get(SUCCESS_EXIT_VALUE_KEY)).intValue());
        }
        if (args.containsKey(SUCCESS_EXIT_VALUES_KEY)) {
            setSuccessExitValues((List<?>) args.get(SUCCESS_EXIT_VALUES_KEY));
        }
        if (args.containsKey(TERMINAL_KEY)) {
            setTerminal((Boolean) args.get(TERMINAL_KEY));
        }
        if (args.containsKey(TERMINAL_COMMAND_KEY)) {
            setTerminalCommand(args.get(TERMINAL_COMMAND_KEY).toString());
        }
        unnamedArgsSet(unnamedArgs);
    }

    /**
     * Sets the timeout for the process.
     * 
     * @param object
     *            the {@link Duration} of the timeout; or the {@link Number}
     *            timeout in milliseconds; or an {@link Object} that is parsed
     *            as the duration.
     * 
     * @throws CommandException
     *             if any errors set the timeout watchdog.
     * 
     * @throws IllegalArgumentException
     *             if the parsed duration is not valid.
     */
    public void setTimeout(Object object) throws CommandException {
        if (object instanceof Duration) {
            Duration timeout = (Duration) object;
            setWatchdog(new ExecuteWatchdog(timeout.getMillis()));
        } else if (object instanceof Number) {
            Number timeout = (Number) object;
            setWatchdog(new ExecuteWatchdog(timeout.longValue()));
        } else {
            Duration timeout = Duration.parse(object.toString());
            setWatchdog(new ExecuteWatchdog(timeout.getMillis()));
        }
    }

    /**
     * Sets the callback that is called when the process completes.
     * 
     * @param object
     *            either the {@link ExecuteResultHandler} or a {@link Class};
     *            the class type is instantiated with the default constructor
     *            and set as the handler.
     * 
     * @throws CommandException
     *             if the specified object is not an
     *             {@link ExecuteResultHandler}; if there were errors
     *             instantiate the class type.
     */
    public void setHandler(Object object) throws CommandException {
        if (object instanceof Class) {
            @SuppressWarnings("unchecked")
            Class<ExecuteResultHandler> type = (Class<ExecuteResultHandler>) object;
            this.handler = createType(type);
        } else if (object instanceof ExecuteResultHandler) {
            this.handler = (ExecuteResultHandler) object;
        } else {
            throw log.errorHandlerType(this, object);
        }
        log.handlerSet(this, object);
    }

    /**
     * Destroys the process under certain conditions.
     * 
     * @param object
     *            either the {@link ProcessDestroyer} or a {@link Class}; the
     *            class type is instantiated with the default constructor and
     *            set as the handler.
     * 
     * @throws CommandException
     *             if the specified object is not an {@link ProcessDestroyer};
     *             if there were errors instantiate the class type.
     */
    public void setDestroyer(Object object) throws CommandException {
        if (object instanceof Class) {
            @SuppressWarnings("unchecked")
            Class<ProcessDestroyer> type = (Class<ProcessDestroyer>) object;
            this.destroyer = createType(type);
        } else if (object instanceof ExecuteResultHandler) {
            this.destroyer = (ProcessDestroyer) object;
        } else {
            throw log.errorDestroyerType(this, object);
        }
        log.destroyerSet(this, object);
    }

    /**
     * Destroys the process after a specified timeout.
     * 
     * @param object
     *            either the {@link ExecuteWatchdog} or a {@link Class}; the
     *            class type is instantiated with the default constructor and
     *            set as the handler.
     * 
     * @throws CommandException
     *             if the specified object is not an {@link ExecuteWatchdog}; if
     *             there were errors instantiate the class type.
     */
    public void setWatchdog(Object object) throws CommandException {
        if (object instanceof Class) {
            @SuppressWarnings("unchecked")
            Class<ExecuteWatchdog> type = (Class<ExecuteWatchdog>) object;
            this.watchdog = createType(type);
        } else if (object instanceof ExecuteWatchdog) {
            this.watchdog = (ExecuteWatchdog) object;
        } else {
            throw log.errorWatchdogType(this, object);
        }
        log.watchdogSet(this, object);
    }

    private void unnamedArgsSet(List<Object> args) {
        setCommand(args.get(0).toString());
        if (args.size() > 1) {
            setEnv(asMap(args.get(1)));
        }
        if (args.size() > 2) {
            setDirectory(args.get(2));
        }
    }

    @SuppressWarnings("unchecked")
    private Map<String, String> asMap(Object obj) {
        return (Map<String, String>) obj;
    }

    /**
     * Sets the command string.
     * 
     * @param object
     *            the command string.
     * 
     * @throws NullPointerException
     *             if the command string is {@code null}.
     * 
     * @throws IllegalArgumentException
     *             if the command string is empty.
     */
    public void setCommand(String command) {
        this.command = command;
        log.commandSet(this, this.command);
    }

    public void setEnv(Map<String, String> env) {
        this.env = env;
        log.envSet(this, this.env);
    }

    public void setDirectory(Object object) {
        executor.setWorkingDirectory(asFile(object));
        log.directorySet(this, object);
    }

    private File asFile(Object object) {
        if (object instanceof File) {
            return (File) object;
        } else {
            return new File(object.toString());
        }
    }

    /**
     * @see Executor#setExitValue(int)
     */
    public void setSuccessExitValue(int value) {
        executor.setExitValue(value);
        log.exitValueSet(this, value);
    }

    /**
     * @see Executor#setExitValues(int[])
     */
    public void setSuccessExitValues(List<?> list) {
        int[] values;
        if (list == null) {
            values = null;
        } else {
            values = new int[list.size()];
            for (int i = 0; i < list.size(); i++) {
                Object item = list.get(i);
                values[i] = ((Number) item).intValue();
            }
        }
        executor.setExitValues(values);
        log.exitValuesSet(this, values);
    }

    /**
     * @throws IllegalStateException
     *             if the handler is not of type
     *             {@link DefaultExecuteResultHandler}.
     */
    @Override
    public int getTheExitValue() {
        if (handler instanceof DefaultExecuteResultHandler) {
            DefaultExecuteResultHandler h = (DefaultExecuteResultHandler) handler;
            return h.getExitValue();
        } else {
            throw log.notExitValueAvailable(this);
        }
    }

    public void setTerminalCommand(String command) {
        parser.setTerminalCommand(command);
    }

    public void setTerminal(boolean terminal) {
        parser.setUseTerminal(terminal);
    }

}