Java tutorial
/* * Copyright 2012 DTO Labs, Inc. (http://dtolabs.com) * * 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. * */ /* * ScriptExecUtil.java * * User: Greg Schueler <a href="mailto:greg@dtosolutions.com">greg@dtosolutions.com</a> * Created: 12/13/12 5:05 PM * */ package com.dtolabs.rundeck.core.utils; import com.dtolabs.rundeck.core.cli.CLIUtils; import com.dtolabs.rundeck.core.common.INodeEntry; import com.dtolabs.rundeck.core.dispatcher.DataContextUtils; import com.dtolabs.rundeck.core.execution.ExecArgList; import com.dtolabs.utils.Streams; import org.apache.commons.collections.Predicate; import org.apache.commons.collections.PredicateUtils; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; /** * Provides methods for running scripts/commands. * * @author Greg Schueler <a href="mailto:greg@dtosolutions.com">greg@dtosolutions.com</a> */ public class ScriptExecUtil { /** * Run a command with environment variables in a working dir, and copy the streams * * @param command the command array to run * @param envMap the environment variables to pass in * @param workingdir optional working dir location (or null) * @param outputStream stream for stdout * @param errorStream stream for stderr * * @return the exit code of the command * * @throws IOException if any IO exception occurs * @throws InterruptedException if interrupted while waiting for the command to finish */ public static int runLocalCommand(final String[] command, final Map<String, String> envMap, final File workingdir, final OutputStream outputStream, final OutputStream errorStream) throws IOException, InterruptedException { final String[] envarr = createEnvironmentArray(envMap); final Runtime runtime = Runtime.getRuntime(); final Process exec = runtime.exec(command, envarr, workingdir); final Streams.StreamCopyThread errthread = Streams.copyStreamThread(exec.getErrorStream(), errorStream); final Streams.StreamCopyThread outthread = Streams.copyStreamThread(exec.getInputStream(), outputStream); errthread.start(); outthread.start(); exec.getOutputStream().close(); final int result = exec.waitFor(); errthread.join(); outthread.join(); if (null != outthread.getException()) { throw outthread.getException(); } if (null != errthread.getException()) { throw errthread.getException(); } return result; } /** * Create the environment array for executing via {@link Runtime}. */ private static String[] createEnvironmentArray(final Map<String, String> envMap) { final ArrayList<String> envlist = new ArrayList<String>(); for (final Map.Entry<String, String> entry : envMap.entrySet()) { envlist.add(entry.getKey() + "=" + entry.getValue()); } return envlist.toArray(new String[envlist.size()]); } /** * Generate argument array for a script file invocation * * @param localDataContext data context properties to expand among the args * @param scriptargs arguments to the script file * @param scriptargsarr arguments to the script file as an array * @param scriptinterpreter interpreter invocation for the file, or null to invoke it directly * @param interpreterargsquoted if true, pass the script file and args as a single argument to the interpreter * @param filepath remote filepath for the script */ public static String[] createScriptArgs(final Map<String, Map<String, String>> localDataContext, final String scriptargs, final String[] scriptargsarr, final String scriptinterpreter, final boolean interpreterargsquoted, final String filepath) { return createScriptArgs(localDataContext, null, scriptargs, scriptargsarr, scriptinterpreter, interpreterargsquoted, filepath); } /** * Generate argument array for a script file invocation * * @param filepath remote filepath for the script * @param scriptargs arguments to the script file * @param scriptargsarr arguments to the script file as an array * @param scriptinterpreter interpreter invocation for the file, or null to invoke it directly * @param interpreterargsquoted if true, pass the script file and args as a single argument to the interpreter */ public static ExecArgList createScriptArgList(final String filepath, final String scriptargs, final String[] scriptargsarr, final String scriptinterpreter, final boolean interpreterargsquoted) { ExecArgList.Builder builder = ExecArgList.builder(); if (null != scriptinterpreter) { builder.args(Arrays.asList(OptsUtil.burst(scriptinterpreter)), false); } if (null != scriptinterpreter && interpreterargsquoted) { ExecArgList.Builder sub = builder.subList(true); addScriptFileArgList(filepath, scriptargs, scriptargsarr, sub, needsQuoting); sub.parent(); } else { addScriptFileArgList(filepath, scriptargs, scriptargsarr, builder, needsQuoting); } return builder.build(); } static Predicate any(Predicate... preds) { return PredicateUtils.anyPredicate(preds); } static final Predicate needsQuoting = any(DataContextUtils.stringContainsPropertyReferencePredicate, CLIUtils.stringContainsWhitespacePredicate, CLIUtils.stringContainsQuotePredicate); private static void addScriptFileArgList(String filepath, String scriptargs, String[] scriptargsarr, ExecArgList.Builder builder, Predicate quoted) { builder.arg(filepath, false); if (null != scriptargs) { builder.args(OptsUtil.burst(scriptargs), quoted); } else if (null != scriptargsarr) { builder.args(scriptargsarr, quoted); } } /** * Generate argument array for a script file invocation * * @param localDataContext data context properties to expand among the args * @param node node to use for os-type argument quoting. * @param scriptargs arguments to the script file * @param scriptargsarr arguments to the script file as an array * @param scriptinterpreter interpreter invocation for the file, or null to invoke it directly * @param interpreterargsquoted if true, pass the script file and args as a single argument to the interpreter * @param filepath remote filepath for the script */ public static String[] createScriptArgs(final Map<String, Map<String, String>> localDataContext, final INodeEntry node, final String scriptargs, final String[] scriptargsarr, final String scriptinterpreter, final boolean interpreterargsquoted, final String filepath) { final ArrayList<String> arglist = new ArrayList<String>(); if (null != scriptinterpreter) { arglist.addAll(Arrays.asList(OptsUtil.burst(scriptinterpreter))); } if (null != scriptinterpreter && interpreterargsquoted) { final ArrayList<String> sublist = new ArrayList<String>(); sublist.add(filepath); addQuotedArgs(localDataContext, node, scriptargs, scriptargsarr, sublist, true); arglist.add(DataContextUtils.join(sublist, " ")); } else { arglist.add(filepath); addQuotedArgs(localDataContext, node, scriptargs, scriptargsarr, arglist, false); } return arglist.toArray(new String[arglist.size()]); } private static void addQuotedArgs(Map<String, Map<String, String>> localDataContext, INodeEntry node, String scriptargs, String[] scriptargsarr, ArrayList<String> arglist, boolean quoted) { if (null != scriptargs) { arglist.addAll( Arrays.asList(DataContextUtils.replaceDataReferences(scriptargs.split(" "), localDataContext))); } else if (null != scriptargsarr) { if (!quoted) { arglist.addAll(Arrays.asList(scriptargsarr)); } else { final String[] newargs = DataContextUtils.replaceDataReferences(scriptargsarr, localDataContext); Converter<String, String> quote = getQuoteConverterForNode(node); //quote args that have substituted context input, or have whitespace //allow other args to be used literally for (int i = 0; i < newargs.length; i++) { String replaced = newargs[i]; if (null != quote && (!replaced.equals(scriptargsarr[i]) || CLIUtils.containsSpace(replaced))) { arglist.add(quote.convert(replaced)); } else { arglist.add(replaced); } } } } } private static Converter<String, String> getQuoteConverterForNode(INodeEntry node) { Converter<String, String> quote; if (null != node) { quote = CLIUtils.argumentQuoteForOperatingSystem(node.getOsFamily()); } else { quote = CLIUtils.argumentQuoteForOperatingSystem(null); } return quote; } }