All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.util.ArgumentListBuilder Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License
 * 
 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
 * Alan Harder, Yahoo! Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package hudson.util;

import hudson.Util;

import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.Map;
import java.util.BitSet;
import java.util.Properties;
import java.util.Map.Entry;
import java.io.Serializable;
import java.io.File;
import java.io.IOException;
import java.util.Set;

/**
 * Used to build up arguments for a process invocation.
 *
 * @author Kohsuke Kawaguchi
 */
public class ArgumentListBuilder implements Serializable {
    private final List args = new ArrayList();
    /**
     * Bit mask indicating arguments that shouldn't be echoed-back (e.g., password)
     */
    private BitSet mask = new BitSet();

    public ArgumentListBuilder() {
    }

    public ArgumentListBuilder(String... args) {
        add(args);
    }

    public ArgumentListBuilder add(Object a) {
        return add(a.toString(), false);
    }

    /**
     * @since 1.378
     */
    public ArgumentListBuilder add(Object a, boolean mask) {
        return add(a.toString(), mask);
    }

    public ArgumentListBuilder add(File f) {
        return add(f.getAbsolutePath(), false);
    }

    public ArgumentListBuilder add(String a) {
        return add(a,false);
    }

    /**
     * @since 1.378
     */
    public ArgumentListBuilder add(String a, boolean mask) {
        if(a!=null) {
            if(mask) {
                this.mask.set(args.size());
            }
            args.add(a);
        }
        return this;
    }
    
    public ArgumentListBuilder prepend(String... args) {
        // left-shift the mask
        BitSet nm = new BitSet(this.args.size()+args.length);
        for(int i=0; i-D portion is configurable as the 'prefix' parameter.
     * @since 1.114
     */
    public ArgumentListBuilder addKeyValuePairs(String prefix, Map props) {
        for (Entry e : props.entrySet())
            addKeyValuePair(prefix, e.getKey(), e.getValue(), false);
        return this;
    }

    /**
     * Adds key value pairs as "-Dkey=value -Dkey=value ..." with masking.
     *
     * @param prefix
     *      Configures the -D portion of the example. Defaults to -D if null.
     * @param props
     *      The map of key/value pairs to add
     * @param propsToMask
     *      Set containing key names to mark as masked in the argument list. Key
     *      names that do not exist in the set will be added unmasked.
     * @since 1.378
     */
    public ArgumentListBuilder addKeyValuePairs(String prefix, Map props, Set propsToMask) {
        for (Entry e : props.entrySet()) {
            addKeyValuePair(prefix, e.getKey(), e.getValue(), (propsToMask == null) ? false : propsToMask.contains(e.getKey()));
        }
        return this;
    }

    /**
     * Adds key value pairs as "-Dkey=value -Dkey=value ..." by parsing a given string using {@link Properties}.
     *
     * @param prefix
     *      The '-D' portion of the example. Defaults to -D if null.
     * @param properties
     *      The persisted form of {@link Properties}. For example, "abc=def\nghi=jkl". Can be null, in which
     *      case this method becomes no-op.
     * @param vr
     *      {@link VariableResolver} to be performed on the values.
     * @since 1.262
     */
    public ArgumentListBuilder addKeyValuePairsFromPropertyString(String prefix, String properties, VariableResolver vr) throws IOException {
        if(properties==null)    return this;

        for (Entry entry : Util.loadProperties(properties).entrySet()) {
            addKeyValuePair(prefix, (String)entry.getKey(), Util.replaceMacro(entry.getValue().toString(),vr), false);
        }
        return this;
    }

    /**
     * Adds key value pairs as "-Dkey=value -Dkey=value ..." by parsing a given string using {@link Properties} with masking.
     *
     * @param prefix
     *      The '-D' portion of the example. Defaults to -D if null.
     * @param properties
     *      The persisted form of {@link Properties}. For example, "abc=def\nghi=jkl". Can be null, in which
     *      case this method becomes no-op.
     * @param vr
     *      {@link VariableResolver} to be performed on the values.
     * @param propsToMask
     *      Set containing key names to mark as masked in the argument list. Key
     *      names that do not exist in the set will be added unmasked.
     * @since 1.378
     */
    public ArgumentListBuilder addKeyValuePairsFromPropertyString(String prefix, String properties, VariableResolver vr, Set propsToMask) throws IOException {
        if(properties==null)    return this;

        for (Entry entry : Util.loadProperties(properties).entrySet()) {
            addKeyValuePair(prefix, (String)entry.getKey(), Util.replaceMacro(entry.getValue().toString(),vr), (propsToMask == null) ? false : propsToMask.contains((String)entry.getKey()));
        }
        return this;
    }

    @Override
    public String toString() {
        return args.toString();
    }

    public String[] toCommandArray() {
        return args.toArray(new String[args.size()]);
    }
    
    @Override
    public ArgumentListBuilder clone() {
        ArgumentListBuilder r = new ArgumentListBuilder();
        r.args.addAll(this.args);
        r.mask = (BitSet) this.mask.clone();
        return r;
    }

    /**
     * Re-initializes the arguments list.
     */
    public void clear() {
        args.clear();
        mask.clear();
    }

    public List toList() {
        return args;
    }

    /**
     * Just adds quotes around args containing spaces, but no other special characters,
     * so this method should generally be used only for informational/logging purposes.
     */
    public String toStringWithQuote() {
        StringBuilder buf = new StringBuilder();
        for (String arg : args) {
            if(buf.length()>0)  buf.append(' ');

            if(arg.indexOf(' ')>=0 || arg.length()==0)
                buf.append('"').append(arg).append('"');
            else
                buf.append(arg);
        }
        return buf.toString();
    }

    /**
     * Wrap command in a CMD.EXE call so we can return the exit code (ERRORLEVEL).
     * This method takes care of escaping special characters in the command, which
     * is needed since the command is now passed as a string to the CMD.EXE shell.
     * This is done as follows:
     * Wrap arguments in double quotes if they contain any of:
     *   space *?,;^&<>|" or % followed by a letter.
     * 
When testing from command prompt, these characters also need to be * prepended with a ^ character: ^&<>| -- however, invoking cmd.exe from * Hudson does not seem to require this extra escaping so it is not added by * this method. *
A " is prepended with another " character. Note: Windows has issues * escaping some combinations of quotes and spaces. Quotes should be avoided. *
A % followed by a letter has that letter wrapped in double quotes, * to avoid possible variable expansion. ie, %foo% becomes "%"f"oo%". * The second % does not need special handling because it is not followed * by a letter.
* Example: "-Dfoo=*abc?def;ghi^jkl&mnostu|vwx""yz%"e"nd" * @return new ArgumentListBuilder that runs given command through cmd.exe /C * @since 1.386 */ public ArgumentListBuilder toWindowsCommand() { StringBuilder quotedArgs = new StringBuilder(); boolean quoted, percent; for (String arg : args) { quoted = percent = false; for (int i = 0; i < arg.length(); i++) { char c = arg.charAt(i); if (!quoted && (c == ' ' || c == '*' || c == '?' || c == ',' || c == ';')) { quoted = startQuoting(quotedArgs, arg, i); } else if (c == '^' || c == '&' || c == '<' || c == '>' || c == '|') { if (!quoted) quoted = startQuoting(quotedArgs, arg, i); // quotedArgs.append('^'); See note in javadoc above } else if (c == '"') { if (!quoted) quoted = startQuoting(quotedArgs, arg, i); quotedArgs.append('"'); } else if (percent && ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { if (!quoted) quoted = startQuoting(quotedArgs, arg, i); quotedArgs.append('"').append(c); c = '"'; } percent = (c == '%'); if (quoted) quotedArgs.append(c); } if (quoted) quotedArgs.append('"'); else quotedArgs.append(arg); quotedArgs.append(' '); } // (comment copied from old code in hudson.tasks.Ant) // on Windows, executing batch file can't return the correct error code, // so we need to wrap it into cmd.exe. // double %% is needed because we want ERRORLEVEL to be expanded after // batch file executed, not before. This alone shows how broken Windows is... quotedArgs.append("&& exit %%ERRORLEVEL%%"); return new ArgumentListBuilder().add("cmd.exe", "/C").addQuoted(quotedArgs.toString()); } private static boolean startQuoting(StringBuilder buf, String arg, int atIndex) { buf.append('"').append(arg.substring(0, atIndex)); return true; } /** * Returns true if there are any masked arguments. * @return true if there are any masked arguments; false otherwise */ public boolean hasMaskedArguments() { return mask.length()>0; } /** * Returns an array of booleans where the masked arguments are marked as true * @return an array of booleans. */ public boolean[] toMaskArray() { boolean[] mask = new boolean[args.size()]; for( int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy