sorcer.launcher.JavaProcessBuilder.java Source code

Java tutorial

Introduction

Here is the source code for sorcer.launcher.JavaProcessBuilder.java

Source

/*
 * Copyright 2013 Rafa Krupiski.
 * Copyright 2013 Sorcersoft.com S.A.
 *
 * 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 sorcer.launcher;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sorcer.util.Process2;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static java.util.Arrays.asList;

/**
 * @author Rafa Krupiski
 */
public class JavaProcessBuilder {
    protected Logger log = LoggerFactory.getLogger(getClass());
    protected Map<String, String> properties;
    protected Collection<String> classPathList;
    protected String mainClass;
    protected List<String> parameters;
    protected File workingDir;
    protected boolean debugger;
    protected File output;
    protected String command = "java";
    protected int debugPort = 8000;

    public void setProperties(Map<String, String> environment) {
        this.properties = environment;
    }

    public void setClassPath(Collection<String> classPath) {
        this.classPathList = classPath;
    }

    public void setMainClass(String mainClass) {
        this.mainClass = mainClass;
    }

    public void setParameters(List<String> parameters) {
        this.parameters = parameters;
    }

    public void setWorkingDir(File workingDir) {
        this.workingDir = workingDir;
    }

    public void setDebugger(boolean debugger) {
        this.debugger = debugger;
    }

    public int getDebugPort() {
        return debugPort;
    }

    public void setDebugPort(int debugPort) {
        this.debugPort = debugPort;
    }

    /**
     * Set standard and error output
    *
    * @param output output file
    */
    public void setOutput(File output) {
        this.output = output;
    }

    public Process2 startProcess() throws IOException {
        ProcessBuilder procBld = new ProcessBuilder().command(command);

        if (debugger) {
            procBld.command().addAll(
                    Arrays.asList("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=" + debugPort));
        }

        procBld.command().addAll(_D(properties));
        String classPath = StringUtils.join(classPathList, File.pathSeparator);
        procBld.command().addAll(asList("-classpath", classPath, mainClass));
        if (parameters != null) {
            procBld.command().addAll(parameters);
        }

        if (workingDir == null) {
            // the default
            // make explicit for logging purpose
            workingDir = new File(System.getProperty("user.dir"));
        }
        procBld.directory(workingDir);

        Map<String, String> env = procBld.environment();
        updateEnvironment(env);

        StringBuilder cmdStr = new StringBuilder("[").append(workingDir.getPath()).append("] ")
                .append(StringUtils.join(procBld.command(), " "));
        if (output != null) {
            cmdStr.append(" > ").append(output.getPath());
        }

        log.info(cmdStr.toString());

        redirectIO(procBld);

        Process proc = null;
        try {
            proc = procBld.start();

            try {
                // give it a moment to exit on error
                Thread.sleep(100);
            } catch (InterruptedException ignored) {
                //ignore
            }

            // if the next call throws exception, then we're probably good -
            // process hasn't finished yet.
            int x = proc.exitValue();
            throw new IllegalStateException("Process exited with value " + x);
        } catch (IllegalThreadStateException x) {
            return new Process2(proc);
        }
    }

    protected void updateEnvironment(Map<String, String> env) {
        //do nothing
    }

    /**
    * Redirect output and error to ours IF the method
    * {@link ProcessBuilder#inheritIO()} is available (since jdk 1.7)
    */
    private void redirectIO(ProcessBuilder processBuilder) {
        if (output != null) {
            invokeIgnoreErrors(processBuilder, "redirectErrorStream", new Class[] { Boolean.TYPE }, true);
            // processBuilder.redirectErrorStream(true);
            invokeIgnoreErrors(processBuilder, "redirectOutput", new Class[] { File.class }, output);
        } else {
            invokeIgnoreErrors(processBuilder, "inheritIO", new Class[0]);
        }
    }

    protected Object invokeIgnoreErrors(Object target, String methodName, Class[] argTypes, Object... args) {
        try {
            Method method = target.getClass().getDeclaredMethod(methodName, argTypes);
            return method.invoke(target, args);
        } catch (NoSuchMethodException e) {
            // looks like we're not in jdk1.7
            log.warn(e.getMessage(), e);
            return null;
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e.getMessage(), e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    private List<String> _D(Map<String, String> d) {
        List<String> result = new ArrayList<String>(d.size());
        for (Map.Entry<String, String> e : d.entrySet()) {
            result.add(_D(e.getKey(), e.getValue()));
        }
        return result;
    }

    private String _D(String key, String value) {
        return "-D" + key + '=' + value;
    }
}