com.groupon.jenkins.dynamic.build.DynamicProject.java Source code

Java tutorial

Introduction

Here is the source code for com.groupon.jenkins.dynamic.build.DynamicProject.java

Source

/*
The MIT License (MIT)
    
Copyright (c) 2014, Groupon, 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 com.groupon.jenkins.dynamic.build;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.groupon.jenkins.SetupConfig;
import com.groupon.jenkins.branchhistory.BranchHistoryWidget;
import com.groupon.jenkins.dynamic.build.api.DynamicProjectApi;
import com.groupon.jenkins.dynamic.buildtype.BuildType;
import com.groupon.jenkins.dynamic.buildtype.BuildTypeProperty;
import com.groupon.jenkins.dynamic.organizationcontainer.OrganizationContainer;
import com.groupon.jenkins.dynamic.organizationcontainer.OrganizationContainerRepository;
import com.groupon.jenkins.github.GithubRepoProperty;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import hudson.Extension;
import hudson.PermalinkList;
import hudson.matrix.Combination;
import hudson.model.Descriptor;
import hudson.model.DescriptorVisibilityFilter;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Queue.Task;
import hudson.model.Saveable;
import hudson.model.TopLevelItem;
import hudson.util.CaseInsensitiveComparator;
import hudson.util.CopyOnWriteMap;
import hudson.widgets.HistoryWidget;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.mongodb.morphia.annotations.PostLoad;
import org.mongodb.morphia.annotations.PrePersist;

import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;

public class DynamicProject extends DbBackedProject<DynamicProject, DynamicBuild>
        implements TopLevelItem, Saveable, IdentifableItemGroup<DynamicSubProject> {
    @Extension
    public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
    private transient Map<String, DynamicSubProject> items;
    private String containerName;

    protected DynamicProject(final ItemGroup parent, final String name) {
        super(parent, name);
        init();
    }

    @PrePersist
    void saveProjectId() {
        this.containerName = (String) getParent().getId();
    }

    @PostLoad
    void loadParent() {
        try {
            // If it didn't load on main Jenkins start, try loading it again.
            final OrganizationContainer container = new OrganizationContainerRepository()
                    .getOrganizationContainer(this.containerName);
            if (container != null) {
                onLoad(container, getName());
            }
        } catch (final IOException e) {
            throw new IllegalArgumentException("Unable to load container for project: " + this.containerName);
        }

    }

    private void init() {
        final Iterable<DynamicSubProject> projects = SetupConfig.get().getDynamicProjectRepository()
                .getChildren(this);
        this.items = new CopyOnWriteMap.Tree<>(CaseInsensitiveComparator.INSTANCE);
        for (final DynamicSubProject dbBackedProject : projects) {
            this.items.put(dbBackedProject.getName(), dbBackedProject);
        }
    }

    @Override
    public void onLoad(final ItemGroup<? extends Item> parent, final String name) throws IOException {
        super.onLoad(parent, name);
        init();
    }

    @Override
    public DescriptorImpl getDescriptor() {
        return DESCRIPTOR;
    }

    public boolean shouldBuildTags() {
        return getProperty(BuildTagsProperty.class) != null
                && getProperty(BuildTagsProperty.class).isShouldBuildTags();
    }

    @Override
    public PermalinkList getPermalinks() {
        final PermalinkList permalinks = super.getPermalinks();
        permalinks.add(new LastSuccessfulMasterPermalink());
        return permalinks;
    }

    public Iterable<BuildType> getBuildTypes() {
        return SetupConfig.get().getBuildTypes();
    }

    public String getBuildType() {
        return getProperty(BuildTypeProperty.class) == null ? null
                : getProperty(BuildTypeProperty.class).getBuildType();
    }

    public Iterable<String> getBranchTabs() {
        final DynamicProjectBranchTabsProperty branchTabsProperty = getProperty(
                DynamicProjectBranchTabsProperty.class);
        return branchTabsProperty == null ? Arrays.asList("master") : branchTabsProperty.getBranches();
    }

    @Override
    @WithBridgeMethods(value = Jenkins.class, castRequired = true)
    public OrganizationContainer getParent() {
        return (OrganizationContainer) super.getParent();
    }

    @Override
    protected HistoryWidget createHistoryWidget() {
        return new BranchHistoryWidget(this, HISTORY_ADAPTER, SetupConfig.get().getDynamicBuildRepository(),
                getCurrentBranch());
    }

    @Override
    public Object getDynamic(final String token, final StaplerRequest req, final StaplerResponse rsp) {
        try {
            if (useNewUi(token, req)) {
                rsp.forward(this, "newUi", req);
                return null;
            }
        } catch (final ServletException e) {
            throw new RuntimeException(e);
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        if ("sha".equals(token)) {
            final String sha = req.getParameter("value");
            return this.dynamicBuildRepository.getBuildBySha(this, sha);
        }

        final Object permalink = super.getDynamic(token, req, rsp);
        if (permalink == null) {
            final DynamicSubProject item = getItem(token);
            return item;
        }

        return permalink;
    }

    private boolean useNewUi(final String token, final StaplerRequest req) {
        return isNewUi() && (StringUtils.startsWith(token, "dotCI") || //job pages
                (NumberUtils.isNumber(token) && (StringUtils.isEmpty(req.getRestOfPath())
                        || StringUtils.contains(req.getRestOfPath(), "dotCI")))); // buildpages
    }

    public String getJobUrl() {
        return "job/" + getParent().getName() + "/job/" + getName();
    }

    public boolean isNewUi() {
        return getProperty(JobUiProperty.class) != null && getProperty(JobUiProperty.class).isNewUi();
    }

    protected String getCurrentBranch() {
        return (String) Stapler.getCurrentRequest().getSession().getAttribute("branchView" + getName());
    }

    public void doToggleNewUI(final StaplerRequest req, final StaplerResponse rsp)
            throws IOException, ServletException, InterruptedException {
        toggleNewUI();
        rsp.forwardToPreviousPage(req);
    }

    public void doBranchBuilds(final StaplerRequest req, final StaplerResponse rsp)
            throws IOException, ServletException, InterruptedException {
        final String tab = req.getRestOfPath().replace("/", "");
        handleBranchTabs(tab, req);
        rsp.forwardToPreviousPage(req);
    }

    private void handleBranchTabs(final String branch, final StaplerRequest req) {
        if ("all".equals(branch)) {
            req.getSession().removeAttribute("branchView" + this.getName());
        } else {
            req.getSession().setAttribute("branchView" + this.getName(), branch);
        }
    }

    @Override
    protected Class<DynamicBuild> getBuildClass() {
        return DynamicBuild.class;
    }

    @Override
    public String getUrlChildPrefix() {
        return ".";
    }

    private DynamicSubProject createNewSubProject(final Combination requestedCombination) {
        final DynamicSubProject project = new DynamicSubProject(this, requestedCombination);
        try {
            project.save();
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        this.items.put(project.getName(), project);
        return project;
    }

    public Iterable<DynamicSubProject> getSubProjects(final Iterable<Combination> subBuildCombinations) {

        return Iterables.transform(subBuildCombinations, new Function<Combination, DynamicSubProject>() {

            @Override
            public DynamicSubProject apply(final Combination requestedCombination) {
                final DynamicSubProject subProject = Iterables.find(getItems(), new Predicate<DynamicSubProject>() {

                    @Override
                    public boolean apply(final DynamicSubProject subProject) {
                        return requestedCombination.equals(subProject.getCombination());
                    }
                }, null);
                return subProject == null ? DynamicProject.this.createNewSubProject(requestedCombination)
                        : subProject;
            }

        });
    }

    public Task getItem(final Combination combination) {
        return null;
    }

    @Override
    public DynamicSubProject getItem(final String name) {
        return this.dynamicProjectRepository.getChild(this, name);
    }

    private File getConfigurationsDir() {
        return new File(getRootDir(), "configurations");
    }

    @Override
    public File getRootDirFor(final DynamicSubProject child) {
        final File f = new File(getConfigurationsDir(), child.getName());
        f.getParentFile().mkdirs();
        return f;
    }

    @Override
    public Collection<DynamicSubProject> getItems() {
        return this.items == null ? new ArrayList<>() : this.items.values();
    }

    public String getGithubRepoUrl() {
        return getProperty(GithubRepoProperty.class) == null ? null
                : getProperty(GithubRepoProperty.class).getRepoUrl();
    }

    @Override
    public void onRenamed(final DynamicSubProject item, final String oldName, final String newName)
            throws IOException {
        throw new IllegalStateException("Renaming not allowed outside .ci.yml");
    }

    @Override
    public void onDeleted(final DynamicSubProject item) throws IOException {
        throw new IllegalStateException("Cannot delete Sub Project without deleting the parent");
    }

    @Override
    public DynamicBuild getLastBuild() {
        return super.getLastBuild();
    }

    public void doAddBranchTab(final StaplerRequest req, final StaplerResponse rsp)
            throws IOException, ServletException, InterruptedException {
        final String tabRegex = req.getParameter("tabRegex");
        if (StringUtils.isBlank(tabRegex))
            throw new RuntimeException("Branch Regex cannot be exmpty");
        final DynamicProjectBranchTabsProperty branchTabsProperty = getProperty(
                DynamicProjectBranchTabsProperty.class);
        branchTabsProperty.addBranch(tabRegex);
        save();
    }

    public void doRemoveBranchTab(final StaplerRequest req, final StaplerResponse rsp)
            throws IOException, ServletException, InterruptedException {
        final String tabRegex = req.getParameter("tabRegex");
        if (StringUtils.isBlank(tabRegex))
            throw new RuntimeException("Branch Regex cannot be exmpty");
        final DynamicProjectBranchTabsProperty branchTabsProperty = getProperty(
                DynamicProjectBranchTabsProperty.class);
        branchTabsProperty.removeBranch(tabRegex);
        save();
    }

    public void toggleNewUI() {
        final JobUiProperty jobUiProperty = this.getProperty(JobUiProperty.class);
        if (jobUiProperty != null) {
            jobUiProperty.toggle();
            try {
                save();
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Exported
    public DynamicProjectApi getAppData() throws IOException {
        return new DynamicProjectApi(this);
    }

    public String getColor() {
        final int h = Math.abs(getFullName().hashCode());
        final int r = h % 250;
        final int g = h % 251;
        final int b = h % 252;
        return String.format("rgb(%s,%s,%s)", r, g, b);
    }

    @Override
    public int getNextBuildNumber() {
        final int number = this.dynamicProjectRepository.getNextBuildNumber(this);
        return number;
    }

    @Override
    public synchronized int assignBuildNumber() throws IOException {
        return this.dynamicProjectRepository.assignNextBuildNumber(this);
    }

    @Override
    protected synchronized void saveNextBuildNumber() throws IOException {
    }

    public static final class DescriptorImpl extends AbstractProjectDescriptor {
        @Override
        public String getDisplayName() {
            return "DotCi Project";
        }

        @Override
        public TopLevelItem newInstance(final ItemGroup parent, final String name) {
            return new DynamicProject(parent, name);
        }

        /**
         * We are hiding the "DotCI" project from "/newJob" page, because we'll
         * have our own flow for doing this ...
         */
        @Extension
        public static class FilterDotCIProjectTypeFromNewJobPage extends DescriptorVisibilityFilter {
            @Override
            public boolean filter(final Object context, final Descriptor descriptor) {
                return !(descriptor instanceof DynamicProject.DescriptorImpl);
            }
        }

    }
}