de.berg.systeme.jenkins.wix.WixCommand.java Source code

Java tutorial

Introduction

Here is the source code for de.berg.systeme.jenkins.wix.WixCommand.java

Source

/*
* This file is part of wix-plugin-jenkins.
* 
* Copyright (C) 2014 Berg Systeme
* 
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package de.berg.systeme.jenkins.wix;

import hudson.EnvVars;
import hudson.FilePath;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang.StringUtils;

/**
 * Abstract class for building commands.
 * @author Bjoern.Berg
 */
public abstract class WixCommand {
    // Logger
    protected ToolsetLogger lg = ToolsetLogger.INSTANCE;
    // Exectuables absolute path
    protected File exec;
    // global settings
    protected ToolsetSettings settings;
    // StringBuffer to build command
    protected StringBuffer cmd = new StringBuffer();
    // List of source files
    protected List<FilePath> sourceFiles = new LinkedList<FilePath>();
    // Map of environment variables
    protected Map<String, String> parameters = new HashMap<String, String>();
    // list of needed extensions
    protected List<String> extensions = new LinkedList<String>();
    // skip printing candle logo information
    protected boolean nologo = true;
    // specify output file (-o)
    protected FilePath outputFile;
    // verbose output (-v)
    protected boolean verbose = false;
    // treat all warnings as errors
    protected boolean wxall = false;
    // not accepted environment variables
    protected List<String> rejectedEnvVars = new LinkedList<String>();

    public WixCommand(ToolsetSettings settings, EnvVars vars) {
        this(null, settings, vars);
    }

    public WixCommand(String ExeName, ToolsetSettings settings, EnvVars vars) {
        // Bugfix:
        // It is stated that candle and light will work, if no installation path
        // is given, so installation path cannot be stated as given. This will
        // also help to configure Wix on a UNIX master and used on a Windows
        // slave machine.
        String path = settings.get(Wix.INST_PATH, "");
        if (StringUtils.isEmpty(path)) {
            // no installation path configured
            this.exec = new File(ExeName);
        } else {
            // installation path is given
            char lastSign = path.charAt(path.length() - 1);
            if (lastSign == '/' || lastSign == '\\') {
                this.exec = new File(path + ExeName);
            } else {
                //String sep = System.getProperty("file.separator");
                String sep = '\\';
                this.exec = new File(path + sep + ExeName);
            }

        }

        this.settings = settings;

        // Environment variables which are not taken into account
        String sysEnvVars = settings.get(Wix.LOV_REJECTED, Wix.DEF_LOV_TO_REJECT);
        for (String s : sysEnvVars.split(",")) {
            addRejectedEnvVar(s);
        }

        parseSettings(this.settings, vars);
    }

    private void addRejectedEnvVar(String envVar) {
        this.rejectedEnvVars.add(envVar.toLowerCase());
    }

    protected boolean isEnvVarRejected(String envVar, String value) {
        boolean reject = false;
        if (envVar.contains("=")) {
            lg.debug(envVar + ": contains illegal character.");
            reject = true;
        } else if (rejectedEnvVars.contains(envVar.toLowerCase())) {
            lg.debug(envVar + ": is marked as ignorable variable.");
            reject = true;
        }
        return reject;
    }

    /**
     * parse settings and assign variables to command.
     * @param settings global settings.
     * @param vars environment variables.
     */
    private void parseSettings(ToolsetSettings settings, EnvVars vars) {
        // Add extensions, we use reflection to avoid a lot of code
        Field[] fields = Wix.class.getFields();
        for (Field field : fields) {
            try {
                String fieldValue = (String) field.get(String.class.newInstance());
                if (fieldValue.endsWith("Extension") && settings.get(fieldValue, false)) {
                    addExtension(fieldValue);
                }
            } catch (InstantiationException ex) {
                Logger.getLogger(WixCommand.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                Logger.getLogger(WixCommand.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        // add all environment variables as parameter
        // environment variables are only added, if option is set
        if (settings.get(Wix.ENBL_ENV_AS_PARAM, false)) {
            for (Map.Entry<String, String> entry : vars.entrySet()) {
                String varName = entry.getKey();
                String value = entry.getValue();
                // contains value a directory it is better to escape everything
                if (isEnvVarRejected(varName, value)) {
                    lg.debug("Rejected Environment variable: " + varName);
                } else {
                    value = value.replace("\"", "\\\"");
                    lg.debug("VarName: " + varName + "; Value: " + value);
                    addParameter(varName, value);
                }
            }
        } else {
            lg.log("Environment variables are not automatically added as parameters.");
        }
    }

    /**
     * input file.
     * @param filepath 
     */
    void addSourceFile(FilePath filepath) {
        sourceFiles.add(filepath);
    }

    /**
     * define a parameter for the preprocessor.
     * @param name name of parameter.
     * @param value value of parameter.
     */
    public void addParameter(String name, String value) {
        if (!this.parameters.containsKey(name)) {
            this.parameters.put(name, value);
        }
    }

    /**
     * extension assembly or "class, assembly".
     * @param extensionName 
     */
    public void addExtension(String extensionName) {
        this.extensions.add(extensionName);
    }

    /**
     * specify output file (default: write to current directory).
     * @param outputFile 
     */
    public void setOutputFile(FilePath outputFile) {
        this.outputFile = outputFile;
    }

    /**
     * verbose output.
     * @param verbose true enables verbose output (default: false)
     */
    public void setVerbose(boolean verbose) {
        this.verbose = verbose;
    }

    /**
     * treat all warnings as errors.
     * @param wxall true enables. (default: false).
     */
    public void setWxall(boolean wxall) {
        this.wxall = wxall;
    }

    /**
     * skip printing logo information.
     * @param nologo set to false shows logo (default: true).
     */
    public void setNologo(boolean nologo) {
        this.nologo = nologo;
    }

    /**
     * create command before execution. createCommand is used by execute().
     * @throws ToolsetException 
     */
    protected abstract void createCommand() throws ToolsetException;

    /**
     * Checks if binary exists and toolset is properly installed.
     * @return true if binary exists.
     * @throws ToolsetException if toolset is not configured.
     */
    public boolean exists() throws ToolsetException {
        boolean testResult = true;
        int len = exec.getPath().length();
        if (len <= 10) {
            // candle.exe has 10 signs (light.exe less), if path contains not more than
            // 10 signs we expect that we are configured running on a Windows slave
            lg.log("Expecting executable in path.");
            testResult = false;
        } else {
            // The following can only work if full path is given
            if (!exec.exists()) {
                throw new ToolsetException("No binary found: " + exec.getPath());
            }
            if (!exec.canExecute()) {
                throw new ToolsetException("No execution rights on " + exec.getName());
            }
        }
        return testResult;
    }

    protected void appendExtensions() {
        for (String extension : extensions) {
            cmd.append("-ext ");
            cmd.append(extension);
            cmd.append(" ");
        }
    }

    protected void appendParameters() {
        for (String key : parameters.keySet()) {
            String value = parameters.get(key);
            cmd.append("-d");
            cmd.append(key);
            cmd.append("=\"");
            cmd.append(value);
            cmd.append("\" ");
        }
    }

    protected void appendSources() {
        for (FilePath source : sourceFiles) {
            cmd.append("\"");
            cmd.append(source.getRemote());
            cmd.append("\" ");
        }
    }

    /**
     * creates new StringBuffer.
     */
    protected void clean() {
        cmd = null;
        cmd = new StringBuffer();
    }

    /**
     * checks if command is properly configured.
     * @throws ToolsetException 
     */
    protected void check() throws ToolsetException {
        if (sourceFiles.isEmpty()) {
            throw new ToolsetException("missing source files");
        } else if (outputFile == null /*|| !outputFile.exists()*/) {
            throw new ToolsetException("missing output filename");
        }
    }

    /**
     * checks if output contains information about errors.
     * @param line output line from process.
     * @return true if errors are found.
     */
    private boolean checkForErrors(String line) {
        return line.toLowerCase().matches(".*error.*");
    }

    /**
     * executes command and parses the output checking for errors.
     * @return true if execution was successful otherwise false.
     * @throws Exception
     * @throws ToolsetException 
     */
    public boolean execute() throws Exception, ToolsetException {
        // false berschreibt immer true
        boolean success = true;
        String line = null;

        this.createCommand();

        Process p = Runtime.getRuntime().exec(cmd.toString());
        BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream()));
        BufferedReader stderr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        // Log stdout of executable
        while ((line = stdout.readLine()) != null) {
            lg.log(line);
            success &= checkForErrors(line);
        }
        stdout.close();
        // Log stderr of executable
        while ((line = stderr.readLine()) != null) {
            lg.log(line);
            success &= checkForErrors(line);
        }
        stderr.close();
        // Wait for the process to end
        p.waitFor();
        success &= (p.exitValue() > 0);

        return !success; // inverted logic at this point
    }
}