Package org.arquillian.spacelift.process.impl

Source Code of org.arquillian.spacelift.process.impl.CommandTool

/*
* JBoss, Home of Professional Open Source
* Copyright 2014, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.arquillian.spacelift.process.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.arquillian.spacelift.execution.Execution;
import org.arquillian.spacelift.execution.ExecutionException;
import org.arquillian.spacelift.execution.Tasks;
import org.arquillian.spacelift.process.Command;
import org.arquillian.spacelift.process.CommandBuilder;
import org.arquillian.spacelift.process.ProcessInteraction;
import org.arquillian.spacelift.process.ProcessInteractionBuilder;
import org.arquillian.spacelift.process.ProcessResult;
import org.arquillian.spacelift.tool.Tool;

/**
* Tool that is able to execute an external, operating system dependent command.
*
*
* @author <a href="kpiwko@redhat.com">Karel Piwko</a>
*
*/
public class CommandTool extends Tool<Object, ProcessResult> {

    protected CommandBuilder commandBuilder;
    protected ProcessInteraction interaction;
    protected List<Integer> allowedExitCodes;
    protected File workingDirectory;
    protected Map<String, String> environment;
    protected boolean isDaemon;

    protected ProcessReference processRef;

    public CommandTool() {
        this.interaction = ProcessInteractionBuilder.NO_INTERACTION;
        this.allowedExitCodes = new ArrayList<Integer>();
        this.workingDirectory = null;
        this.environment = new HashMap<String, String>();
        this.isDaemon = false;
    }

    @Override
    protected Collection<String> aliases() {
        return Arrays.asList("run");
    }

    /**
     * Sets executable to be executed. This can either a an absolute path or executed on path of underlying file system
     *
     * @param programName program name
     * @return
     * @throws IllegalArgumentException If program name is null or empty
     */
    public CommandTool programName(CharSequence programName) throws IllegalArgumentException {
        Validate.notNullOrEmpty(programName, "Program name must not be empty nor null");
        this.commandBuilder = new CommandBuilder(programName);
        return this;
    }

    /**
     * Adds a list of parameters to the command to be executed
     *
     * @param parameters parameters
     * @return
     */
    public CommandTool parameters(List<? extends CharSequence> parameters) {
        commandBuilder.parameters(parameters);
        return this;
    }

    /**
     * Adds a list of parameters to the command to be executed
     *
     * @param parameters parameters
     * @return
     */
    public CommandTool parameters(CharSequence... parameters) {
        commandBuilder.parameters(parameters);
        return this;
    }

    /**
     * Adds a parameter to the command to be executed
     *
     * @param parameter parameter
     * @return
     */
    public CommandTool parameter(CharSequence parameter) {
        commandBuilder.parameter(parameter);
        return this;
    }

    /**
     * Splits {@code sequenceToBeParsed} into list of parameters, using unescaped spaces as delimiters
     *
     * @param sequenceToBeParsed string to be parsed
     * @return
     */
    public CommandTool splitToParameters(CharSequence sequenceToBeParsed) {
        commandBuilder.parameters(sequenceToBeParsed);
        return this;
    }

    /**
     * Sets interaction for the command
     *
     * @param interaction the interaction
     * @return
     */
    public CommandTool interaction(ProcessInteraction interaction) {
        this.interaction = interaction;
        return this;
    }

    /**
     * Sets interaction for the command
     *
     * @param interactionBuilder the interaction
     * @return
     */
    public CommandTool interaction(ProcessInteractionBuilder interactionBuilder) {
        this.interaction = interactionBuilder.build();
        return this;
    }

    /**
     * Sets the command. Overrides all previous parameters and program name
     *
     * @param command the command
     * @return
     */
    public CommandTool command(Command command) {
        this.commandBuilder = new CommandBuilder(command.getFullCommand().toArray(new String[0]));
        return this;
    }

    /**
     * Sets the command. Overrides all previous parameters and program name
     *
     * @param command the command
     * @return
     */
    public CommandTool command(CommandBuilder commandBuilder) {
        this.commandBuilder = commandBuilder;
        return this;
    }

    /**
     * Adds list of valid exit codes for the command. If command finishes execution and exit code does
     * not match the one set, {@see ExecutionException} is thrown
     *
     * By default, allowed exit code is set to {@code 0}
     *
     * @param exitCodes the exit code
     * @param others possible other codes
     * @return
     * @see ExecutionException
     */
    public CommandTool shouldExitWith(Integer... exitCodes) {
        if (exitCodes.length > 0) {
            allowedExitCodes.addAll(Arrays.asList(exitCodes));
        }
        return this;
    }

    /**
     * Sets working directory for the command
     *
     * @param workingDirectory working directory, can be {@code null} to use current directory of running Java process
     * @return
     * @throws IllegalArgumentException if working directory does not exist
     */
    public CommandTool workingDir(String workingDirectory) throws IllegalArgumentException {

        if (workingDirectory == null) {
            this.workingDirectory = null;
            return this;
        }

        File workingDirectoryFile = new File(workingDirectory);

        if (!workingDirectoryFile.exists()) {
            throw new IllegalArgumentException("Specified path " + workingDirectoryFile.getAbsolutePath() + " does not exist!");
        }
        if (!workingDirectoryFile.isDirectory()) {
            throw new IllegalArgumentException("Specified path " + workingDirectoryFile.getAbsolutePath()
                + " is not a directory!");
        }

        this.workingDirectory = workingDirectoryFile;
        return this;
    }

    /**
     * Adds a map of key, value environment variables to the default process environment
     *
     * @param envVariables environment variables. Value might be null.
     * @return
     * @throws IllegalArgumentException
     */
    public CommandTool addEnvironment(Map<String, String> envVariables) throws IllegalArgumentException {
        Validate.notNull(envVariables, "Environment variables must not be null");
        this.environment.putAll(envVariables);
        return this;
    }

    /**
     * Adds a sequence of key, value environment variables to the default process environment
     *
     * @param envVariables environment variables. Value might be null.
     * @return
     * @throws IllegalArgumentException If values do not form complete pairs or if key is null
     */
    public CommandTool addEnvironment(CharSequence... envVariables) throws IllegalArgumentException {

        if (envVariables.length % 2 == 1) {
            throw new IllegalArgumentException("Environment variables must be a sequence of key, value pairs.");
        }

        for (int i = 0; i < (envVariables.length / 2); i += 2) {
            CharSequence key = envVariables[i];
            CharSequence value = envVariables[i + 1];

            Validate.notNull(key, "Environment variable name must not be null nor empty");
            environment.put(key.toString(), value != null ? value.toString() : null);
        }

        return this;
    }

    /**
     * Indicates that command should be executed as daemon and survive JVM process.
     *
     * @return
     */
    public CommandTool runAsDaemon() {
        this.isDaemon = true;
        return this;
    }

    @Override
    public Execution<ProcessResult> execute() throws ExecutionException {
        // here we rewrap future based execution into process based execution to get better details about execution
        // and ability to terminate the process
        this.processRef = new ProcessReference(commandBuilder.build().getProgramName());
        Execution<ProcessResult> processFutureExecution = super.execute();

        ProcessBasedExecution<ProcessResult> execution = new ProcessBasedExecution<ProcessResult>(processFutureExecution,
            processRef,
            commandBuilder.build().getProgramName(),
            allowedExitCodes);

        return execution;
    }

    @Override
    protected ProcessResult process(Object input) throws Exception {

        Validate.executionNotNull(commandBuilder, "Command must not be null");

        Command command = commandBuilder.build();
        Process process = null;

        Execution<Process> spawnedProcess = Tasks.prepare(SpawnProcessTask.class)
            .redirectErrorStream(true)
            .shouldExitWith(allowedExitCodes)
            .command(command)
            .workingDir(workingDirectory)
            .addEnvironment(environment)
            .runAsDaemon(isDaemon)
            .execute();

        // wait for process to finish
        process = spawnedProcess.await();

        // set processReference
        processRef.setProcess(process);

        // handle IO of spawned process
        Execution<ProcessResult> processConsumer = Tasks.chain(spawnedProcess, ConsumeProcessOutputTask.class)
            .programName(command.getProgramName()).interaction(interaction).execute();

        // wait for process to finish IO
        ProcessResult result = processConsumer.await();

        if (spawnedProcess.hasFailed()) {
            throw new ExecutionException("Invocation of \"{0}\" failed with {1}", new Object[] {
                command,
                result.exitValue() });
        }

        return result;
    }
}
TOP

Related Classes of org.arquillian.spacelift.process.impl.CommandTool

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.