hudson.plugins.project_inheritance.projects.view.InheritanceViewAction.java Source code

Java tutorial

Introduction

Here is the source code for hudson.plugins.project_inheritance.projects.view.InheritanceViewAction.java

Source

/**
 * Copyright (c) 2011-2013, Intel Mobile Communications GmbH
 * 
 * 
 * This file is part of the Inheritance plug-in for Jenkins.
 * 
 * The Inheritance plug-in is free software: you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation in version 3
 * of the License
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

package hudson.plugins.project_inheritance.projects.view;

import hudson.Extension;
import hudson.Util;
import hudson.model.Action;
import hudson.model.Describable;
import hudson.model.ParameterValue;
import hudson.model.Descriptor;
import hudson.model.ParameterDefinition;
import hudson.model.StringParameterValue;
import hudson.plugins.project_inheritance.projects.InheritanceBuild;
import hudson.plugins.project_inheritance.projects.InheritanceProject;
import hudson.plugins.project_inheritance.projects.InheritanceProject.IMode;
import hudson.plugins.project_inheritance.util.PathMapping;
import hudson.tasks.Builder;
import hudson.tasks.CommandInterpreter;
import hudson.tasks.BatchFile;
import hudson.util.ListBoxModel;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPOutputStream;

import jenkins.model.Jenkins;

import org.apache.commons.io.FileUtils;
import org.apache.tools.tar.TarEntry;
import org.apache.tools.tar.TarOutputStream;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;

import com.google.common.io.Files;

/**
 * This class implements the action that allows you to see and download all the
 * build steps that make up a particular run of a project.
 * 
 */
public class InheritanceViewAction implements Action, Describable<InheritanceViewAction> {
    private transient InheritanceBuild build;

    /**
     * Returns the {@link InheritanceProject} associated with the current
     * {@link StaplerRequest}.
     * 
     * @return null, if no {@link InheritanceProject} is associated with the
     * given request.
     */
    public InheritanceProject getProject() {
        return this.getBuild().getParent();

    }

    /**
     * This method returns the {@link InheritanceProject} associated with the
     * given request; if any are.
     * 
     * Otherwise, returns null.
     * 
     * @param request the request to check for an {@link InheritanceProject}
     * @return null, if no such project is associated with the request
     */
    public InheritanceProject getProject(StaplerRequest request) {
        return InheritanceProject.DESCRIPTOR.getConfiguredProject(request);
    }

    public InheritanceBuild getBuild() {
        if (this.build == null) {
            StaplerRequest req = Stapler.getCurrentRequest();
            return this.getBuild(req);
        }
        return build;
    }

    public InheritanceBuild getBuild(StaplerRequest req) {
        if (build == null && req != null) {
            this.build = req.findAncestorObject(InheritanceBuild.class);
        }
        return build;
    }

    private boolean isApplicableFor(InheritanceProject ip) {
        return (ip != null && ip.isBuildable() && !(ip.isDisabled()));
    }

    public String getIconFileName() {
        //Checking if the project associated with the current request is valid
        InheritanceProject ip = this.getProject();
        if (this.isApplicableFor(ip)) {
            //The rebuild action is allowed to be displayed and gets the
            //default icon for rebuilds
            return "notepad.png";
        } else {
            //Not assigning an icon automagically causes the build action to
            //not be displayed.
            return null;
        }
    }

    public String getDisplayName() {
        InheritanceProject ip = this.getProject();
        if (this.isApplicableFor(ip)) {
            return "View/Download full build flow";
        } else {
            return null;
        }
    }

    public String getUrlName() {
        InheritanceProject ip = this.getProject();
        if (this.isApplicableFor(ip)) {
            return "view";
        } else {
            return null;
        }
    }

    // === REST API CALLS ===

    public ReadOnlyConfigurationArchive doDownload() {
        Map<String, Long> versions = (build != null) ? build.getProjectVersions() : null;
        if (versions != null) {
            InheritanceProject.setVersioningMap(versions);
        }
        List<Builder> builders = this.getProject().getBuildersList(IMode.INHERIT_FORCED).toList();
        try {
            File archive = this.generateExecutableCompoundScript(builders, this.getProject().getName(),
                    this.getBuild().getBuildVariables());
            return new ReadOnlyConfigurationArchive(archive);
        } catch (IOException ex) {
            //TODO: Log this error
            return null;
        }
    }

    // === READ ONLY VIEW HELPER METHODS ===

    public File generateExecutableCompoundScript(List<Builder> builders, String archiveName,
            Map<String, String> params) throws IOException {
        List<File> generatedScripts = new ArrayList<File>();

        File srcDir = Files.createTempDir();

        //Dump the build steps as script files
        boolean isLinux = this.dumpBuildSteps(builders, generatedScripts, srcDir);

        //Dump the main script that calls the steps above
        this.dumpMasterScript(isLinux, generatedScripts, srcDir);

        File dstFile = File.createTempFile(archiveName + "_", ".tgz");
        if (dstFile.getParentFile() != null) {
            dstFile.getParentFile().mkdirs();
        }

        try {
            this.createTgzArchive(dstFile, srcDir.getAbsolutePath(), generatedScripts);
        } catch (IOException e) {
            // The file could not be generated
            return null;
        } finally {
            //Nuke the input directory
            try {
                FileUtils.deleteDirectory(srcDir);
            } catch (IOException e) {
                //The directory probably does not exist anymore
            }
        }
        return dstFile;
    }

    private void dumpMasterScript(boolean isLinux, List<File> generatedScripts, File srcDir) throws IOException {
        Map<String, String> parameters = getResolvedBuildParameters(this.getBuild());

        String scrFile = (isLinux) ? "build.sh" : "build.bat";
        String scrPreamble = (isLinux) ? "#!/bin/bash" : "@echo off";
        String scrSetCmd = (isLinux) ? "export" : "set";
        String scrInvoke = (isLinux) ? "./" : "CALL ";
        String lineEnd = (isLinux) ? "\n" : "\r\n";

        File mainScript = new File(srcDir, scrFile);
        BufferedWriter out = new BufferedWriter(new FileWriter(mainScript, true));
        out.write(scrPreamble);
        out.write(lineEnd);

        if (parameters.size() > 0) {
            for (String parameter : parameters.keySet()) {
                out.write(
                        String.format("%s %s=\"%s\"%s", scrSetCmd, parameter, parameters.get(parameter), lineEnd));
            }
        }

        for (File script : generatedScripts) {
            if (isLinux) {
                out.write(String.format("echo 'Will run %s now ...'%s", script.getName(), lineEnd));
            } else {
                out.write(String.format("echo Will run %s now ...%s", script.getName(), lineEnd));
            }
            out.write(String.format("%s%s%s", scrInvoke, script.getName(), lineEnd));
        }
        if (isLinux) {
            mainScript.setExecutable(true, false);
            mainScript.setWritable(true, false);
            mainScript.setReadable(true, false);
        }
        out.close();
        generatedScripts.add(mainScript);
    }

    private boolean dumpBuildSteps(List<Builder> builders, List<File> generatedScripts, File srcDir)
            throws IOException {
        boolean isLinux = true;
        int i = 0;
        for (Builder builder : builders) {
            if (!(builder instanceof CommandInterpreter)) {
                continue;
            }
            CommandInterpreter ci = (CommandInterpreter) builder;
            File builderScript;
            if (builder instanceof BatchFile) {
                isLinux = false;
                builderScript = new File(srcDir, String.format("step_%d.bat", i++));
            } else {
                builderScript = new File(srcDir, String.format("step_%d.sh", i++));
            }

            BufferedWriter out = new BufferedWriter(new FileWriter(builderScript, true));
            String cmd = ci.getCommand();
            if (!isLinux) {
                //Ensure windows line-endings
                cmd = cmd.replaceAll("\r\n", "\n").replaceAll("\n", "\r\n");
            }
            out.write(cmd);
            builderScript.setExecutable(true, false);
            builderScript.setReadable(true, false);
            builderScript.setWritable(true, false);
            out.close();
            generatedScripts.add(builderScript);
        }
        return isLinux;
    }

    public void createTgzArchive(File dstFile, String srcDir, List<File> srcFiles) throws IOException {
        //Creating the output ZipFile
        FileOutputStream fos = new FileOutputStream(dstFile);
        GZIPOutputStream gzos = new GZIPOutputStream(fos);
        TarOutputStream tos = new TarOutputStream(gzos);

        try {
            byte[] buffer = new byte[8192];
            for (File srcFile : srcFiles) {
                if (!srcFile.exists()) {
                    throw new IOException("No such file " + srcFile.getPath());
                }
                //Strip the prefix off the srcFile
                String relPath = PathMapping.getRelativePath(srcFile.getPath(), srcDir, File.separator);

                TarEntry entry = new TarEntry(relPath);
                entry.setMode(0777);
                entry.setSize(srcFile.length());
                //Put the entry into the ZIP file
                tos.putNextEntry(entry);
                //Dump the data
                FileInputStream fis = new FileInputStream(srcFile);
                try {
                    while (true) {
                        int read = fis.read(buffer);
                        if (read <= 0) {
                            break;
                        }
                        tos.write(buffer, 0, read);
                    }
                } finally {
                    fis.close();
                }
                //Close the entry
                tos.closeEntry();
            }
        } finally {
            if (tos != null) {
                tos.close();
            }
        }
    }

    public static Map<String, String> getResolvedBuildParameters(InheritanceBuild build) {
        //FIXME: Should we really resolve parameters here? After all, Jenkins
        //does not do that either!
        Map<String, String> result = new HashMap<String, String>();

        Map<String, String> buildVariables = build.getBuildVariables();
        for (String pName : buildVariables.keySet()) {
            String resolved = Util.replaceMacro(buildVariables.get(pName), buildVariables);
            result.put(pName, resolved);
        }
        return result;
    }

    public static Map<String, String> getResolvedBuildParameters(InheritanceProject project) {
        Map<String, String> result = new HashMap<String, String>();

        List<ParameterDefinition> parameters = project.getParameters(IMode.INHERIT_FORCED);
        for (ParameterDefinition pDef : parameters) {
            ParameterValue pVal = pDef.getDefaultParameterValue();
            if (pVal instanceof StringParameterValue) {
                result.put(pDef.getName(), ((StringParameterValue) pVal).value);
            }
        }
        //We do not resolve parameters; as they are most likely not complete anyway
        return result;
    }

    public InheritanceViewActionDescriptor getDescriptor() {
        return (InheritanceViewActionDescriptor) Jenkins.getInstance().getDescriptorOrDie(this.getClass());
    }

    public static InheritanceViewActionDescriptor getDescriptorStatic() {
        return (InheritanceViewActionDescriptor) Jenkins.getInstance()
                .getDescriptorOrDie(InheritanceViewAction.class);
    }

    @Extension(ordinal = 1000)
    public static final class InheritanceViewActionDescriptor extends Descriptor<InheritanceViewAction> {

        public InheritanceViewActionDescriptor() {
            //Does nothing yet
        }

        @Override
        public String getDisplayName() {
            return "Inheritance View";
        }

        public ListBoxModel doFillProjectClassItems() {
            return InheritanceProject.DESCRIPTOR.doFillCreationClassItems();
        }
    }
}