org.jenkins.plugins.leroy.NewProject.java Source code

Java tutorial

Introduction

Here is the source code for org.jenkins.plugins.leroy.NewProject.java

Source

/*
 * The MIT License
 * 
 * Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
 * Jorg Heymans, Stephen Connolly, Tom Huybrechts
 * 
 * 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 org.jenkins.plugins.leroy;

import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.*;
import hudson.model.Descriptor.FormException;
import hudson.plugins.copyartifact.CopyArtifact;
import hudson.plugins.copyartifact.StatusBuildSelector;
import hudson.scm.NullSCM;
import hudson.scm.SCM;
import hudson.tasks.*;
import hudson.tasks.Maven.MavenInstallation;
import hudson.tasks.Maven.ProjectWithMaven;
import hudson.triggers.Trigger;
import hudson.util.DescribableList;
import hudson.util.RunList;
import net.sf.json.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.AbstractFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang.StringUtils;
import org.jenkins.plugins.leroy.util.LeroyUtils;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.bind.JavaScriptMethod;

import javax.servlet.ServletException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Leroy Deployment Project. Has such a non-descriptive name because of historical reasons.
 * @param <P>
 * @param <B>
 */
public abstract class NewProject<P extends NewProject<P, B>, B extends NewBuild<P, B>> extends AbstractProject<P, B>
        implements SCMedItem, Saveable, ProjectWithMaven, BuildableItemWithBuildWrappers {

    /**
     * List of active {@link Builder}s configured for this project.
     */
    private volatile DescribableList<Builder, Descriptor<Builder>> builders;
    private static final AtomicReferenceFieldUpdater<NewProject, DescribableList> buildersSetter = AtomicReferenceFieldUpdater
            .newUpdater(NewProject.class, DescribableList.class, "builders");

    /**
     * List of active {@link Publisher}s configured for this project.
     */
    private volatile DescribableList<Publisher, Descriptor<Publisher>> publishers;
    private static final AtomicReferenceFieldUpdater<NewProject, DescribableList> publishersSetter = AtomicReferenceFieldUpdater
            .newUpdater(NewProject.class, DescribableList.class, "publishers");

    /**
     * List of active {@link BuildWrapper}s configured for this project.
     */
    private volatile DescribableList<BuildWrapper, Descriptor<BuildWrapper>> buildWrappers;
    private static final AtomicReferenceFieldUpdater<NewProject, DescribableList> buildWrappersSetter = AtomicReferenceFieldUpdater
            .newUpdater(NewProject.class, DescribableList.class, "buildWrappers");

    private static Logger LOGGER = Logger.getLogger(NewProject.class.getName());

    private List<String> workflow;

    private List<String> environment;

    /**
     * Creates a new deployment project. Has such no descriptive name because of historical reasons
     */
    public NewProject(ItemGroup parent, String name) throws IOException {
        super(parent, name);
    }

    @Override
    public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOException {
        super.onLoad((ItemGroup) parent, name);
        getBuildersList().setOwner(this);
        getPublishersList().setOwner(this);
        getBuildWrappersList().setOwner(this);
    }

    public AbstractProject<?, ?> asProject() {
        return this;
    }

    private void readWorkflowsFromDisk(String workflowsDir) {
        File wfDir = new File(workflowsDir);
        workflow = new ArrayList<String>();
        if (wfDir.exists() && wfDir.canRead()) {
            //get file names
            IOFileFilter workflowFileFilter = new AbstractFileFilter() {
                @Override
                public boolean accept(File file) {
                    try {
                        if (LeroyUtils.isWorkflow(file)) {
                            return true;
                        }
                    } catch (Throwable t) {
                        t.printStackTrace();
                    }
                    return false;
                }
            };
            Iterator<File> fileIterator = FileUtils.iterateFiles(new File(workflowsDir), workflowFileFilter,
                    TrueFileFilter.INSTANCE);
            if (fileIterator != null) {
                URI workFlowsBase = new File(workflowsDir).toURI();
                while (fileIterator.hasNext()) {
                    // get relative path using workflow folder as a base and remove extension
                    File wf = fileIterator.next();
                    String relative = workFlowsBase.relativize(wf.toURI()).getPath();
                    String wfName = relative.substring(0, relative.lastIndexOf('.'));
                    workflow.add(wfName);
                }
            }
        }
    }

    /**
     * This method loads files from SCM
     */
    public void loadConfigFromSCM() {
        try {
            SCM scm = this.getScm();
            if (scm == null || scm instanceof NullSCM) {
                return;
            }
            FilePath checkoutDir = new FilePath(Files.createTempDirectory("leroyTempDir").toFile());
            AbstractBuild dumbBuild = new NewBuild(this);
            Launcher dumbLauncher = Hudson.getInstance().createLauncher(TaskListener.NULL);
            BuildListener dumbListener = new StreamBuildListener(
                    new FileOutputStream(File.createTempFile("dumbListener", "tmp")));
            boolean check = scm.checkout(dumbBuild, dumbLauncher, checkoutDir, dumbListener,
                    File.createTempFile("changelog", "tmp"));
            if (!check) {
                LOGGER.log(Level.SEVERE, "Error trying to load environments and workflows from SCM");
                return;
            }

            readWorkflowsFromDisk(checkoutDir + "/workflows/");

        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Error trying to load environments and workflows from SCM", e);
            return;
        }
    }

    public List<Builder> getBuilders() {
        // first load config from SCM
        loadConfigFromSCM();
        // if Leroy Builder and Copy Artifact already present return builders
        boolean containsLeroy = false;
        boolean containsCopyartifact = false;
        List<Builder> builders = getBuildersList().toList();
        for (Builder builder : builders) {
            if (builder instanceof LeroyBuilder) {
                containsLeroy = true;
                // pass workflows to builder
                ((LeroyBuilder) builder).setWorkflows(workflow);
            } else if (builder instanceof CopyArtifact) {
                containsCopyartifact = true;
            }
        }

        builders = new ArrayList<Builder>(builders);
        if (!containsCopyartifact) {
            builders.add(new CopyArtifact("", "", new StatusBuildSelector(true), "", "${LEROY_HOME}/artifacts/",
                    false, false, true));
        }
        if (!containsLeroy) {
            LeroyBuilder builder = new LeroyBuilder(this.getName(), new ArrayList<LeroyBuilder.Target>(), "",
                    false);
            builder.setWorkflows(workflow);
            builders.add(builder);
        }
        return builders;
    }

    /**
     * @deprecated as of 1.463
     * We will be soon removing the restriction that only one instance of publisher is allowed per type.
     * Use {@link #getPublishersList()} instead.
     */
    public Map<Descriptor<Publisher>, Publisher> getPublishers() {
        return getPublishersList().toMap();
    }

    public DescribableList<Builder, Descriptor<Builder>> getBuildersList() {
        if (builders == null) {
            buildersSetter.compareAndSet(this, null, new DescribableList<Builder, Descriptor<Builder>>(this));
        }
        return builders;
    }

    public DescribableList<Publisher, Descriptor<Publisher>> getPublishersList() {
        if (publishers == null) {
            publishersSetter.compareAndSet(this, null, new DescribableList<Publisher, Descriptor<Publisher>>(this));
        }
        return publishers;
    }

    public Map<Descriptor<BuildWrapper>, BuildWrapper> getBuildWrappers() {
        return getBuildWrappersList().toMap();
    }

    public DescribableList<BuildWrapper, Descriptor<BuildWrapper>> getBuildWrappersList() {
        if (buildWrappers == null) {
            buildWrappersSetter.compareAndSet(this, null,
                    new DescribableList<BuildWrapper, Descriptor<BuildWrapper>>(this));
        }
        return buildWrappers;
    }

    @Override
    protected Set<ResourceActivity> getResourceActivities() {
        final Set<ResourceActivity> activities = new HashSet<ResourceActivity>();

        activities.addAll(super.getResourceActivities());
        activities.addAll(Util.filter(getBuildersList(), ResourceActivity.class));
        activities.addAll(Util.filter(getPublishersList(), ResourceActivity.class));
        activities.addAll(Util.filter(getBuildWrappersList(), ResourceActivity.class));

        return activities;
    }

    /**
     * Adds a new {@link BuildStep} to this {@link Project} and saves the configuration.
     *
     * @deprecated as of 1.290
     * Use {@code getPublishersList().add(x)}
     */
    public void addPublisher(Publisher buildStep) throws IOException {
        getPublishersList().add(buildStep);
    }

    /**
     * Removes a publisher from this project, if it's active.
     *
     * @deprecated as of 1.290
     * Use {@code getPublishersList().remove(x)}
     */
    public void removePublisher(Descriptor<Publisher> descriptor) throws IOException {
        getPublishersList().remove(descriptor);
    }

    public Publisher getPublisher(Descriptor<Publisher> descriptor) {
        for (Publisher p : getPublishersList()) {
            if (p.getDescriptor() == descriptor)
                return p;
        }
        return null;
    }

    protected void buildDependencyGraph(DependencyGraph graph) {
        getPublishersList().buildDependencyGraph(this, graph);
        getBuildersList().buildDependencyGraph(this, graph);
        getBuildWrappersList().buildDependencyGraph(this, graph);
    }

    @Override
    public boolean isFingerprintConfigured() {
        return getPublishersList().get(Fingerprinter.class) != null;
    }

    public MavenInstallation inferMavenInstallation() {
        Maven m = getBuildersList().get(Maven.class);
        if (m != null)
            return m.getMaven();
        return null;
    }

    @Override
    protected void submit(StaplerRequest req, StaplerResponse rsp)
            throws IOException, ServletException, FormException {
        JSONObject json = req.getSubmittedForm();
        super.submit(req, rsp);
        getBuildWrappersList().rebuild(req, json, BuildWrappers.getFor(this));
        getBuildersList().rebuildHetero(req, json, Builder.all(), "builder");
        getPublishersList().rebuildHetero(req, json, Publisher.all(), "publisher");
    }

    @Override
    protected List<Action> createTransientActions() {
        List<Action> r = super.createTransientActions();

        for (BuildStep step : getBuildersList())
            r.addAll(step.getProjectActions(this));
        for (BuildStep step : getPublishersList())
            r.addAll(step.getProjectActions(this));
        for (BuildWrapper step : getBuildWrappers().values())
            r.addAll(step.getProjectActions(this));
        for (Trigger trigger : triggers())
            r.addAll(trigger.getProjectActions());

        return r;
    }

    /**
     * Return information about the latest builds run on each environment/workflow combination
     * Called from jelly view
     */
    public List<Stat> getJobStats() {
        RunList<B> builds = getBuilds();
        List<Stat> stats = StatsHelper.getLastStats(builds);
        Collections.sort(stats, new Comparator<Stat>() {
            @Override
            public int compare(Stat o1, Stat o2) {
                int result = o1.env.compareTo(o2.env);
                if (result == 0) {
                    result = o2.timestamp.compareTo(o1.timestamp);
                }
                return result;
            }
        });
        return stats;
    }

    @JavaScriptMethod
    public List<Stat> getBuildsByEnvironment(String env) {
        RunList<B> builds = getBuilds();
        if (env == null) {
            env = "null";
        }
        List<Stat> stats = StatsHelper.getBuildsByEnvironment(builds, env);
        Collections.sort(stats, new Comparator<Stat>() {
            @Override
            public int compare(Stat o1, Stat o2) {
                return o2.timestamp.compareTo(o1.timestamp);
            }
        });
        return stats;
    }

    @JavaScriptMethod
    public List<Stat> getBuildsByWorkflow(String workflow) {
        RunList<B> builds = getBuilds();
        if (workflow == null) {
            workflow = "null";
        }
        List<Stat> stats = StatsHelper.getBuildByWorkflow(builds, workflow);
        Collections.sort(stats, new Comparator<Stat>() {
            @Override
            public int compare(Stat o1, Stat o2) {
                return o2.timestamp.compareTo(o1.timestamp);
            }
        });
        return stats;
    }

    /**
     * This class is used to handle statistics of a build for LEroy Deployment Job
     * (e.g. select all altest builds for wf/env, get all builds which were run at a specific workflow/env, etc.)
     */
    public static class StatsHelper {

        public static List<Stat> getAllStats(RunList builds) {
            Iterator it = builds.iterator();
            List<Stat> stats = new ArrayList<Stat>();
            while (it.hasNext()) {
                AbstractBuild b = (AbstractBuild) it.next();
                if (!b.isBuilding() && !b.hasntStartedYet() && b.getResult().isCompleteBuild()) {
                    Stat stat = new Stat();
                    stat.buildNumber = String.valueOf(b.getNumber());
                    stat.artifactsLink = stat.buildNumber + "/artifact/";
                    stat.deployer = getDeployer(b);
                    stat.env = getEnv(b);
                    stat.workflow = getWorkflow(b);
                    stat.timestampString = b.getTimestampString2();
                    stat.timestamp = b.getTimestamp();
                    stat.resultIcon = getStatusImage(b);
                    stat.console = stat.buildNumber + "/console";
                    if (!StringUtils.isEmpty(stat.env) && !StringUtils.isEmpty(stat.workflow)) {
                        stats.add(stat);
                    }
                }
            }
            return stats;
        }

        /**
         * This metod is used to get all leroy builds which were run on specified <code>environment</code>
         * @param builds
         * @param env
         * @return
         */
        public static List<Stat> getBuildsByEnvironment(RunList builds, final String env) {
            if (CollectionUtils.isEmpty(builds)) {
                return new ArrayList<Stat>();
            }
            List<Stat> stats = StatsHelper.getAllStats(builds);
            CollectionUtils.filter(stats, new Predicate() {
                @Override
                public boolean evaluate(Object o) {
                    Stat stat = (Stat) o;
                    return (env.equalsIgnoreCase(stat.env));
                }
            });
            return stats;
        }

        /**
         * This metod is used to get all leroy builds which were run on specified <code>workflow</code>
         * @param builds
         * @param workflow
         * @return
         */
        public static List<Stat> getBuildByWorkflow(RunList builds, final String workflow) {
            if (CollectionUtils.isEmpty(builds)) {
                return new ArrayList<Stat>();
            }
            List<Stat> stats = StatsHelper.getAllStats(builds);
            CollectionUtils.filter(stats, new Predicate() {
                @Override
                public boolean evaluate(Object o) {
                    Stat stat = (Stat) o;
                    return (workflow.equalsIgnoreCase(stat.workflow));
                }
            });
            return stats;
        }

        /**
         * This method selects the latest build for each env/workflow combination,
         * and return the list of statistics for these builds.
         * @param builds
         * @return
         */
        public static List<Stat> getLastStats(RunList builds) {
            List<Stat> allStats = getAllStats(builds);
            Map<String, Stat> latestStatMap = new HashMap<String, Stat>();
            for (Stat s : allStats) {
                String key = s.env + "%delim%" + s.workflow;
                Stat latest = latestStatMap.get(key);
                if (latest == null) {
                    latestStatMap.put(key, s);
                } else {
                    if (latest.timestamp.before(s.timestamp)) {
                        latestStatMap.put(key, s);
                    }
                }
            }
            return new ArrayList<Stat>(latestStatMap.values());
        }

        /**
         * This method returns the deployer of the build
         * @param build
         * @return
         */
        public static String getDeployer(AbstractBuild build) {
            String depl = build.getDescription();
            if (!StringUtils.isEmpty(depl)) {
                // remove "By: "
                return depl.substring(4);
            }
            return "";
        }

        /**
         * This method return the environment this build was run at
         * @param build
         * @return
         */
        public static String getEnv(AbstractBuild build) {
            String displayName = build.getDisplayName();
            // <env>_<workflow>
            if (!StringUtils.isEmpty(displayName) && displayName.contains("_")) {
                return displayName.substring(0, displayName.indexOf("_"));
            }
            return "";
        }

        /**
         * This method return the workflow this build was run at
         * @param build
         * @return
         */
        public static String getWorkflow(AbstractBuild build) {
            String displayName = build.getDisplayName();
            // <env>_<workflow>
            if (!StringUtils.isEmpty(displayName) && displayName.contains("_")) {
                return displayName.substring(displayName.indexOf("_") + 1);
            }
            return "";
        }

        /**
         * Returns build icon. Use the icon for Jenkins icons set
         * @param build
         * @return
         */
        public static String getStatusImage(AbstractBuild build) {
            return build.getIconColor().getImageOf("16x16");
        }
    }

    /**
     * Represents a build statistics we display on UI for Leroy Deployment Job
     */
    public static class Stat {
        public String resultIcon;
        public String env;
        public String workflow;
        public String deployer;
        public String timestampString;
        public Calendar timestamp;
        public String artifactsLink; // relative link to artifacts
        public String buildNumber;
        public String console; // relative link to console "/<build #>/console"
    }

}