cz.cas.lib.proarc.common.process.ExternalProcess.java Source code

Java tutorial

Introduction

Here is the source code for cz.cas.lib.proarc.common.process.ExternalProcess.java

Source

/*
 * Copyright (C) 2014 Jan Pokorsky
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */
package cz.cas.lib.proarc.common.process;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConversionException;

/**
 * The helper to run external processes. It builds command line and environment
 * from {@link Configuration} properties.
 *
 * @author Jan Pokorsky
 */
public class ExternalProcess implements Runnable {

    private static final Logger LOG = Logger.getLogger(ExternalProcess.class.getName());
    /**
     * The process environment.
     */
    public static final String PROP_ENVIRONMENT = "environment";
    /**
     * The full path to executable file.
     */
    public static final String PROP_EXEC = "exec";
    /**
     * The command line argument.
     */
    public static final String PROP_ARG = "arg";
    /**
     * The number of attempts to tun a process again in case of failure.
     */
    public static final String PROP_RETRY = "retry";
    /**
     * The time to wait for a running process.
     */
    public static final String PROP_TIMEOUT = "timeout";
    /**
     * The processor type.
     */
    public static final String PROP_TYPE = "type";

    public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000;
    public static final int DEFAULT_RETRY_ATTEMPTS = 0;

    private final Configuration conf;
    private AsyncProcess asyncProcess;
    public String style;

    protected ExternalProcess(Configuration conf) {
        this.conf = conf;
    }

    @Override
    public void run() {
        Map<String, String> env = buildEnv(conf);
        List<String> cmdLine = buildCmdLine(conf);
        try {
            int retry = getRetryCount() + 1;
            for (int i = 0; i < retry; i++) {
                runCmdLine(cmdLine, env);
                if (isOk()) {
                    return;
                }
                LOG.log(Level.WARNING, "{0}. failure, \n{1}, \nCmd: {2}",
                        new Object[] { i + 1, getFullOutput(), cmdLine });
            }
        } catch (IOException ex) {
            throw new IllegalStateException(ex);
        } catch (InterruptedException ex) {
            throw new IllegalStateException(ex);
        }
    }

    protected Map<String, String> buildEnv(Configuration conf) {
        Configuration envConfig = conf.subset(PROP_ENVIRONMENT);
        Map<String, String> env = new HashMap<String, String>();
        for (Iterator<String> it = envConfig.getKeys(); it.hasNext();) {
            String envKey = it.next();
            env.put(envKey, envConfig.getString(envKey));
        }
        return env;
    }

    protected List<String> buildCmdLine(Configuration conf) {
        String exec = conf.getString(PROP_EXEC);
        if (exec == null) {
            throw new IllegalStateException("Missing 'exec'!");
        }
        String[] args = conf.getStringArray(PROP_ARG);
        List<String> cmdLine = new ArrayList<String>();
        cmdLine.add(exec);
        cmdLine.addAll(Arrays.asList(args));
        return cmdLine;
    }

    private int runCmdLine(List<String> cmdLine, Map<String, String> env) throws IOException, InterruptedException {
        StringBuilder debug = new StringBuilder();
        for (String arg : cmdLine) {
            debug.append(arg).append(" ");
        }
        LOG.fine("run: " + debug);
        asyncProcess = new AsyncProcess(cmdLine, env);
        asyncProcess.start();
        long timeout = getTimeout();
        asyncProcess.join(timeout);
        asyncProcess.kill();
        LOG.fine(getFullOutput());
        return asyncProcess.getExitCode();
    }

    public String getOut() {
        return asyncProcess == null ? null : asyncProcess.getOut();
    }

    public String getErr() {
        return null;
    }

    public int getExitCode() {
        return asyncProcess == null ? -1 : asyncProcess.getExitCode();
    }

    public boolean isOk() {
        return getExitCode() == 0;
    }

    public String getFullOutput() {
        return String.format("exit: %s,\nout: %s", getExitCode(), getOut());
    }

    int getRetryCount() {
        try {
            int retry = conf.getInt("retry", DEFAULT_RETRY_ATTEMPTS);
            retry = Math.max(0, retry);
            retry = Math.min(100, retry);
            return retry;
        } catch (ConversionException ex) {
            LOG.log(Level.WARNING, null, ex);
            return DEFAULT_RETRY_ATTEMPTS;
        }
    }

    long getTimeout() {
        try {
            long timeout = conf.getLong(PROP_TIMEOUT, DEFAULT_TIMEOUT);
            timeout = Math.max(0, timeout);
            return timeout;
        } catch (ConversionException ex) {
            LOG.log(Level.WARNING, null, ex);
            return DEFAULT_TIMEOUT;
        }
    }

}