com.anrisoftware.globalpom.exec.core.DefaultProcessTask.java Source code

Java tutorial

Introduction

Here is the source code for com.anrisoftware.globalpom.exec.core.DefaultProcessTask.java

Source

/*
 * Copyright 2014 Erwin Mller <erwin.mueller@deventm.org>
 *
 * This file is part of globalpomutils-exec.
 *
 * globalpomutils-exec is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * globalpomutils-exec 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 Lesser General Public License
 * along with globalpomutils-exec. If not, see <http://www.gnu.org/licenses/>.
 */
package com.anrisoftware.globalpom.exec.core;

import static java.util.Arrays.asList;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ProcessBuilder.Redirect;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.inject.Inject;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;

import com.anrisoftware.globalpom.exec.api.CommandExecException;
import com.anrisoftware.globalpom.exec.api.CommandInput;
import com.anrisoftware.globalpom.exec.api.CommandLine;
import com.anrisoftware.globalpom.exec.api.CommandOutput;
import com.anrisoftware.globalpom.exec.api.ProcessTask;
import com.anrisoftware.globalpom.exec.pipeoutputs.PipeCommandInputFactory;
import com.anrisoftware.globalpom.exec.pipeoutputs.PipeCommandOutputFactory;
import com.anrisoftware.globalpom.threads.api.Threads;
import com.google.inject.assistedinject.Assisted;

/**
 * Executes command task.
 *
 * @author Erwin Mueller, erwin.mueller@deventm.org
 * @since 1.11
 */
class DefaultProcessTask extends Observable implements ProcessTask {

    private static final String EXECUTABLE = "executable";

    private final CommandLine commandLine;

    @Inject
    private DefaultProcessTaskLogger log;

    @Inject
    private PipeCommandOutputFactory commandOutputFactory;

    @Inject
    private PipeCommandOutputFactory commandErrorFactory;

    @Inject
    private PipeCommandInputFactory commandInputFactory;

    private Process process;

    private CommandOutput output;

    private CommandOutput error;

    private CommandInput input;

    private int[] exitCodes;

    private Threads threads;

    private int ret;

    private ByteArrayOutputStream outputStream;

    private ByteArrayOutputStream errorStream;

    @Inject
    DefaultProcessTask(@Assisted CommandLine commandLine) {
        this.commandLine = commandLine;
    }

    @Override
    public CommandLine getCommandLine() {
        return commandLine;
    }

    public void setThreads(Threads threads) {
        this.threads = threads;
    }

    public void setCommandOutput(CommandOutput output) {
        this.output = output;
    }

    public void setCommandError(CommandOutput error) {
        this.error = error;
    }

    public void setCommandInput(CommandInput input) {
        this.input = input;
    }

    public void setExitCodes(int[] exitCodes) {
        if (exitCodes != null) {
            this.exitCodes = exitCodes.clone();
        }
    }

    @Override
    public ProcessTask call() throws CommandExecException {
        List<String> command = commandLine.getCommand();
        ProcessBuilder builder = new ProcessBuilder(command);
        builder.directory(commandLine.getWorkingDir());
        builder.redirectOutput(Redirect.PIPE);
        builder.redirectError(Redirect.PIPE);
        builder.redirectInput(Redirect.PIPE);
        try {
            startProcess(builder);
        } catch (IOException e) {
            throw log.errorStartCommand(this, e, commandLine);
        } catch (InterruptedException e) {
            throw log.commandInterrupted(this, e, commandLine);
        } catch (ExecutionException e) {
            throw log.errorStartCommand(this, e.getCause(), commandLine);
        }
        return this;
    }

    private void startProcess(ProcessBuilder builder)
            throws IOException, InterruptedException, CommandExecException, ExecutionException {
        Process process = builder.start();
        List<Future<?>> streamsTasks = createProcessStreams(process);
        this.process = process;
        this.ret = process.waitFor();
        waitForStreams(streamsTasks);
        setChanged();
        notifyObservers();
        if (exitCodes != null && !checkExitCodes(ret, exitCodes)) {
            throw log.invalidExitCode(this, ret, exitCodes, commandLine);
        }
    }

    private void waitForStreams(List<Future<?>> streamsTasks) throws InterruptedException, ExecutionException {
        for (Future<?> future : streamsTasks) {
            future.get();
        }
    }

    @SuppressWarnings("unchecked")
    private List<Future<?>> createProcessStreams(Process process) {
        if (output == null) {
            this.outputStream = new ByteArrayOutputStream();
            this.output = commandOutputFactory.create(outputStream);
        }
        if (error == null) {
            this.errorStream = new ByteArrayOutputStream();
            this.error = commandErrorFactory.create(errorStream);
        }
        if (input == null) {
            byte[] bytes = "".getBytes();
            InputStream stream = new ByteArrayInputStream(bytes);
            this.input = commandInputFactory.create(stream);
        }
        input.setOutput(process.getOutputStream());
        output.setInput(process.getInputStream());
        error.setInput(process.getErrorStream());
        return new ArrayList<Future<?>>(
                asList(threads.submit(input), threads.submit(output), threads.submit(error)));
    }

    private boolean checkExitCodes(int ret, int[] exitCodes) {
        return ArrayUtils.contains(exitCodes, ret);
    }

    @Override
    public Process getProcess() {
        return process;
    }

    @Override
    public void destroy() {
        process.destroy();
    }

    @Override
    public String getOut() {
        if (outputStream != null) {
            return outputStream.toString();
        } else {
            return null;
        }
    }

    @Override
    public String getErr() {
        if (errorStream != null) {
            return errorStream.toString();
        } else {
            return null;
        }
    }

    @Override
    public int getExitValue() {
        return ret;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append(EXECUTABLE, commandLine.getExecutable()).toString();
    }

    @Override
    public void addObserver(Observer... observer) {
        for (Observer o : observer) {
            addObserver(o);
        }
    }

    @Override
    public void removeObserver(Observer... observer) {
        for (Observer o : observer) {
            deleteObserver(o);
        }
    }
}