Package hudson.ivy

Source Code of hudson.ivy.IvyModule

/*
* The MIT License
*
* Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi, id:cactusman, Timothy Bingaman
*
* 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.ivy;

import hudson.CopyOnWrite;
import hudson.Functions;
import hudson.Util;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.DependencyGraph;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.JDK;
import hudson.model.Job;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.Resource;
import hudson.model.Result;
import hudson.model.Saveable;
import hudson.model.DependencyGraph.Dependency;
import hudson.model.Descriptor.FormException;
import hudson.model.queue.CauseOfBlockage;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildWrapper;
import hudson.tasks.LogRotator;
import hudson.tasks.Publisher;
import hudson.util.DescribableList;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;

import org.apache.commons.lang.StringUtils;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;

/**
* {@link Job} that builds projects based on Ivy.
*
* @author Timothy Bingaman
*/
public final class IvyModule extends AbstractIvyProject<IvyModule, IvyBuild> implements Saveable {
    private static final String IVY_XML_PATH = "ivy.xml";

    private DescribableList<Publisher, Descriptor<Publisher>> publishers = new DescribableList<Publisher, Descriptor<Publisher>>(this);

    /**
     * Name taken from {@link ModuleRevisionId#getName()}.
     */
    private String displayName;

    /**
     * Revision number of this module as of the last build, taken from
     * {@link ModuleRevisionId#getRevision()}.
     *
     * This field can be null if Hudson loaded old data that didn't record this
     * information, so that situation needs to be handled gracefully.
     */
    private String revision;

    /**
     * Ivy branch of this module as of the last build, taken from
     * {@link ModuleRevisionId#getBranch()}.
     *
     * This field can be null if Hudson loaded old data that didn't record this
     * information, so that situation needs to be handled gracefully.
     */
    private String ivyBranch;

    private transient ModuleName moduleName;

    /**
     * Relative path from the workspace to the ivy descriptor file for this
     * module.
     *
     * Strings like "ivy.xml" (if the ivy.xml file is checked out directly in
     * the workspace), "abc/ivy.xml", "foo/bar/zot/ivy.xml".
     */
    private String relativePathToDescriptorFromWorkspace;

    /**
     * If this module has targets specified by itself. Otherwise leave it null
     * to use the default targets specified in the parent.
     */
    private String targets;

    /**
     * Relative path from the workspace to the ivy descriptor file for this
     * module.
     *
     * Strings like "ivy.xml" (if the ivy.xml file is directly in
     * the module root), "ivy/ivy.xml", "build/ivy.xml".
     */
    private String relativePathToDescriptorFromModuleRoot;

    private DescribableList<BuildWrapper,Descriptor<BuildWrapper>> buildWrappers =
        new DescribableList<BuildWrapper, Descriptor<BuildWrapper>>(this);
   
    public DescribableList<BuildWrapper, Descriptor<BuildWrapper>> getBuildWrappersList() {
        if(buildWrappers == null)
        {
            buildWrappers =
                new DescribableList<BuildWrapper, Descriptor<BuildWrapper>>(this);
        }
        return buildWrappers;
    }
   
    /**
     * List of modules that this module declares direct dependencies on.
     */
    @CopyOnWrite
    private volatile Set<ModuleDependency> dependencies;

    /* package */IvyModule(IvyModuleSet parent, IvyModuleInfo moduleInfo, int firstBuildNumber) throws IOException {
        super(parent, moduleInfo.name.toFileSystemName());
        reconfigure(moduleInfo);
        updateNextBuildNumber(firstBuildNumber);
        copyParentBuildWrappers(parent);
    }

    private void copyParentBuildWrappers(IvyModuleSet parent)
    {
        if(!parent.isAggregatorStyleBuild())
        {
            List<BuildWrapper> parentWrappers = parent.getBuildWrappersList().getAll(BuildWrapper.class);
       
           
           
            for (BuildWrapper buildWrapper : parentWrappers) {
                try
                {
                    IvyClonerWrapper cloner = new IvyClonerWrapper();
                    cloner.dontClone(Descriptor.class);
                    getBuildWrappersList().add(cloner.deepClone(buildWrapper));
                }
                catch(IOException e)
                {
                    throw new RuntimeException("Could not copy build wrappers", e);
                }
            }
        }
    }
   
    /**
     * {@link IvyModule} follows the same log rotation schedule as its parent.
     */
    @Override
    public LogRotator getLogRotator() {
        return getParent().getLogRotator();
    }

    /**
     * @deprecated Not allowed to configure log rotation per module.
     */
    @Deprecated
    @Override
    public void setLogRotator(LogRotator logRotator) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean supportsLogRotator() {
        return false;
    }

    @Override
    public boolean isBuildable() {
        // not buildable if the parent project is disabled
        return super.isBuildable() && getParent().isBuildable();
    }

    /**
     * Called to update the module with the new ivy.xml information.
     * <p>
     * This method is invoked on {@link IvyModule} that has the matching
     * {@link ModuleName}.
     */
    /* package */final void reconfigure(IvyModuleInfo moduleInfo) {
        this.displayName = moduleInfo.displayName;
        this.revision = moduleInfo.revision;
        this.ivyBranch = moduleInfo.branch;
        this.relativePathToDescriptorFromWorkspace = moduleInfo.relativePathToDescriptor;
        this.dependencies = moduleInfo.dependencies;
        disabled = false;
    }

    @Override
    protected void doSetName(String name) {
        moduleName = ModuleName.fromFileSystemName(name);
        super.doSetName(moduleName.toString());
    }

    @Override
    public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOException {
        super.onLoad(parent, name);
        if (publishers == null)
            publishers = new DescribableList<Publisher, Descriptor<Publisher>>(this);
        publishers.setOwner(this);
        if (dependencies == null) {
            dependencies = Collections.emptySet();
        }
    }

    /**
     * Relative path to this module's root directory from the workspace of a
     * {@link IvyModuleSet}.
     *
     * The path separator is normalized to '/'.
     */
    public String getRelativePath() {
        return relativePathToDescriptorFromWorkspace;
    }

    /**
     * Gets the revision number in the ivy.xml file as of the last build.
     *
     * @return This method can return null if Hudson loaded old data that didn't
     *         record this information, so that situation needs to be handled
     *         gracefully.
     */
    public String getRevision() {
        return revision;
    }

    /**
     * Gets the Ivy branch in the ivy.xml file as of the last build.
     *
     * @return This method can return null if Hudson loaded old data that didn't
     *         record this information, so that situation needs to be handled
     *         gracefully.
     */
    public String getIvyBranch() {
        return ivyBranch;
    }

    /**
     * Gets the list of targets to execute for this module.
     */
    public String getTargets() {
        return targets;
    }

    public String getRelativePathToDescriptorFromModuleRoot() {
        if (relativePathToDescriptorFromModuleRoot != null)
            return relativePathToDescriptorFromModuleRoot;
        return getParent().getRelativePathToDescriptorFromModuleRoot();
    }

    public String getUserConfiguredRelativePathToDescriptorFromModuleRoot() {
        return relativePathToDescriptorFromModuleRoot;
    }

    public String getRelativePathToModuleRoot() {
        return StringUtils.removeEnd(relativePathToDescriptorFromWorkspace, StringUtils.defaultString(getRelativePathToDescriptorFromModuleRoot(),
                IVY_XML_PATH));
    }

    @Override
    public DescribableList<Publisher, Descriptor<Publisher>> getPublishersList() {
        if (getParent().isAggregatorStyleBuild()) {
            return publishers;
        }

        DescribableList<Publisher, Descriptor<Publisher>> publishersList = new DescribableList<Publisher, Descriptor<Publisher>>(Saveable.NOOP);
        try {
            publishersList.addAll(createModulePublishers());
        } catch (IOException e) {
            LOGGER.warning("Failed to load module publisher list");
        }
        return publishersList;
    }

    @Override
    public JDK getJDK() {
        // share one setting for the whole module set.
        return getParent().getJDK();
    }

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

    @Override
    protected IvyBuild newBuild() throws IOException {
        return super.newBuild();
    }

    public ModuleName getModuleName() {
        return moduleName;
    }

    /**
     * Gets organisation+name+revision as {@link ModuleDependency}.
     */
    public ModuleDependency asDependency() {
        return new ModuleDependency(moduleName, Functions.defaulted(revision, ModuleDependency.UNKNOWN), Functions.defaulted(ivyBranch,
                ModuleDependency.UNKNOWN));
    }

    @Override
    public String getShortUrl() {
        return moduleName.toFileSystemName() + '/';
    }

    @Exported(visibility = 2)
    @Override
    public String getDisplayName() {
        return displayName;
    }

    @Override
    public String getPronoun() {
        return Messages.IvyModule_Pronoun();
    }

    @Override
    public boolean isNameEditable() {
        return false;
    }

    @Override
    public IvyModuleSet getParent() {
        return (IvyModuleSet) super.getParent();
    }

    /**
     * {@link IvyModule} uses the workspace of the {@link IvyModuleSet}, so it
     * always needs to be built on the same slave as the parent.
     */
    @Override
    public Label getAssignedLabel() {
        Node n = getParent().getLastBuiltOn();
        if (n == null)
            return null;
        return n.getSelfLabel();
    }

    /**
     * Workspace of a {@link IvyModule} is a part of the parent's workspace.
     * <p>
     * That is, {@Link IvyModuleSet} builds are incompatible with any
     * {@link IvyModule} builds, whereas {@link IvyModule} builds are compatible
     * with each other.
     *
     * @deprecated as of 1.319 in {@link AbstractProject}.
     */
    @Deprecated
    @Override
    public Resource getWorkspaceResource() {
        return new Resource(getParent().getWorkspaceResource(), getDisplayName() + " workspace");
    }

    @Override
    public boolean isFingerprintConfigured() {
        return true;
    }

    @Override // to make this accessible to IvyModuleSet
    protected void updateTransientActions() {
        super.updateTransientActions();
    }

    @Override
    protected void buildDependencyGraph(DependencyGraph graph) {
        // Allow a module's publishers to add to the dependency graph.
        // This permits the standard build trigger to still work in
        // addition to the triggers generated by ivy dependencies.
        publishers.buildDependencyGraph(this,graph);
       
        if (!isBuildable() || (getParent().ignoreUpstreamChanges() && getParent().isAggregatorStyleBuild()))
            return;
       
        IvyDependencyComputationData data = graph.getComputationalData(IvyDependencyComputationData.class);

        // Build a map of all Ivy modules in this Jenkins instance as dependencies.
        if (!getParent().ignoreUpstreamChanges() && data == null) {
            Map<ModuleDependency, IvyModule> modules = new HashMap<ModuleDependency, IvyModule>();
            for (IvyModule m : getAllIvyModules()) {
                if(!m.isBuildable() || !m.getParent().isAllowedToTriggerDownstream())  continue;
                ModuleDependency moduleDependency = m.asDependency();
                modules.put(moduleDependency, m);
                modules.put(moduleDependency.withUnknownRevision(), m);
            }
            data = new IvyDependencyComputationData(modules);
            graph.putComputationalData(IvyDependencyComputationData.class, data);
        }

        // In case two modules with the same name are defined, modules in the same IvyModuleSet
        // take precedence.
        Map<ModuleDependency, IvyModule> myParentsModules = new HashMap<ModuleDependency, IvyModule>();

        for (IvyModule m : getParent().getModules()) {
            if(m.isDisabled())  continue;
            ModuleDependency moduleDependency = m.asDependency();
            myParentsModules.put(moduleDependency,m);
            myParentsModules.put(moduleDependency.withUnknownRevision(),m);
        }

        // if the build style is the aggregator build, define dependencies against project,
        // not module.
        AbstractProject<?, ?> downstream = getParent().isAggregatorStyleBuild() ? getParent() : this;

        for (ModuleDependency d : dependencies) {
            IvyModule src = myParentsModules.get(d);
            if (src == null) {
                src = myParentsModules.get(d.withUnknownRevision());
            }
            if(src == null && !getParent().ignoreUpstreamChanges()) {
                src = data.allModules.get(d);
                if (src == null) {
                    src = data.allModules.get(d.withUnknownRevision());
                }
            }

            if (src == null)
                continue;

            AbstractProject upstream;
            if (src.getParent().isAggregatorStyleBuild()) {
                upstream = src.getParent();
            } else {
                // Add a virtual dependency from the parent project to the
                // downstream one to make the
                // "Block build when upstream project is building" option behave
                // properly
                if (!this.getParent().equals(src.getParent()) && !hasDependency(graph, src.getParent(), downstream))
                    graph.addDependency(new IvyVirtualDependency(src.getParent(), downstream));
                upstream = src;
            }
           
            AbstractProject revisedDownstream = downstream;                                                              
            if(!getParent().equals(src.getParent()) && !getParent().isAggregatorStyleBuild())                            
            {                                                                                                            
                revisedDownstream = getParent();                                                                         
                if (!src.getParent().isAggregatorStyleBuild() && !hasDependency(graph, src.getParent(),revisedDownstream))
                {                                                                                                        
                    graph.addDependency(new IvyVirtualDependency(src.getParent(), revisedDownstream));                   
                }                                                                                                        
            }                                                                                                            

            // Create the build dependency, ignoring self-referencing or already existing deps
            if (upstream != revisedDownstream && !hasDependency(graph, upstream, revisedDownstream))
                graph.addDependency(new IvyThresholdDependency(upstream, revisedDownstream, Result.SUCCESS, isUseUpstreamParameters()));
        }
    }

    /**
     * Returns all Ivy modules in this Jenkins instance.
     */
    protected Collection<IvyModule> getAllIvyModules() {
        return Hudson.getInstance().getAllItems(IvyModule.class);
    }
   
    private boolean hasDependency(DependencyGraph graph, AbstractProject upstream, AbstractProject downstream) {
        for (Dependency dep : graph.getDownstreamDependencies(upstream)) {
            if (dep instanceof IvyDependency && dep.getDownstreamProject().equals(downstream))
                return true;
        }
        return false;
    }

    private static class IvyDependencyComputationData {
        Map<ModuleDependency, IvyModule> allModules;

        public IvyDependencyComputationData(Map<ModuleDependency, IvyModule> modules) {
            this.allModules = modules;
        }
    }

    @Override
    public CauseOfBlockage getCauseOfBlockage() {
        CauseOfBlockage cob = super.getCauseOfBlockage();
        if (cob != null)
            return cob;

        if (!getParent().isAggregatorStyleBuild()) {
            DependencyGraph graph = Hudson.getInstance().getDependencyGraph();
            for (AbstractProject tup : graph.getTransitiveUpstream(this)) {
                if(getParent() == tup.getParent() && (tup.isBuilding() || tup.isInQueue()))
                        return new BecauseOfUpstreamModuleBuildInProgress(tup);
            }
        }

        return null;
    }

    /**
     * Because the upstream module build is in progress, and we are configured to wait for that.
     */
    public static class BecauseOfUpstreamModuleBuildInProgress extends CauseOfBlockage {
        public final AbstractProject<?,?> up;

        public BecauseOfUpstreamModuleBuildInProgress(AbstractProject<?,?> up) {
            this.up = up;
        }

        @Override
        public String getShortDescription() {
            return Messages.IvyModule_UpstreamModuleBuildInProgress(up.getName());
        }
    }

    @Override
    protected void addTransientActionsFromBuild(IvyBuild build, List<Action> collection, Set<Class> added) {
        if (build == null)
            return;
        List<IvyReporter> list = build.projectActionReporters;
        if (list == null)
            return;

        for (IvyReporter step : list) {
            if (!added.add(step.getClass()))
                continue; // already added
            try {
                collection.addAll(step.getProjectActions(this));
            } catch (Exception e) {
                LOGGER.log(Level.WARNING, "Failed to getProjectAction from " + step + ". Report issue to plugin developers.", e);
            }
        }
    }

    /**
     * List of active {@link Publisher}s configured for this module.
     */
    public DescribableList<Publisher, Descriptor<Publisher>> getPublishers() {
        return publishers;
    }

    @Override
    protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
        super.submit(req, rsp);

        targets = Util.fixEmptyAndTrim(req.getParameter("targets"));
        relativePathToDescriptorFromModuleRoot = Util.fixEmptyAndTrim(req.getParameter("relativePathToDescriptorFromModuleRoot"));

        publishers.rebuild(req,req.getSubmittedForm(),BuildStepDescriptor.filter(Publisher.all(),this.getClass()));

        // dependency setting might have been changed by the user, so rebuild.
        Hudson.getInstance().rebuildDependencyGraph();
    }

    @Override
    protected void performDelete() throws IOException, InterruptedException {
        super.performDelete();
         getParent().onModuleDeleted(this);
    }

    /**
     * Creates a list of {@link Publisher}s to be used for a build of this project.
     */
    protected final List<Publisher> createModulePublishers() {
        List<Publisher> modulePublisherList = new ArrayList<Publisher>();

        getPublishers().addAllTo(modulePublisherList);
        if (!getParent().isAggregatorStyleBuild()) {
            getParent().getPublishers().addAllTo(modulePublisherList);
        }

        return modulePublisherList;
    }

   
   
    @Override
    public boolean isUseUpstreamParameters() {
        return getParent().isUseUpstreamParameters();
    }
   
    private static final Logger LOGGER = Logger.getLogger(IvyModule.class.getName());
}
TOP

Related Classes of hudson.ivy.IvyModule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.