Package hudson.matrix

Source Code of hudson.matrix.MatrixBuild

/*
* The MIT License
*
* Copyright (c) 2004-2011, Oracle Corporation, Kohsuke Kawaguchi, Red Hat, Inc., Tom Huybrechts, Anton Kozak
*
* 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.matrix;

import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Cause.UpstreamCause;
import hudson.model.Executor;
import hudson.model.Fingerprint;
import hudson.model.Hudson;
import hudson.model.JobProperty;
import hudson.model.Node;
import hudson.model.ParametersAction;
import hudson.model.Queue;
import hudson.model.Result;
import hudson.slaves.WorkspaceList;
import hudson.slaves.WorkspaceList.Lease;
import hudson.tasks.Publisher;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

/**
* Build of {@link MatrixProject}.
*
* @author Kohsuke Kawaguchi
*/
public class MatrixBuild extends AbstractBuild<MatrixProject, MatrixBuild> {
    private AxisList axes;

    public MatrixBuild(MatrixProject job) throws IOException {
        super(job);
    }

    public MatrixBuild(MatrixProject job, Calendar timestamp) {
        super(job, timestamp);
    }

    public MatrixBuild(MatrixProject project, File buildDir) throws IOException {
        super(project, buildDir);
    }

    public Object readResolve() {
        // MatrixBuild.axes added in 1.285; default to parent axes for old data
        if (axes == null) {
            axes = getParent().getAxes();
        }
        return this;
    }

    /**
     * Used by view to render a ball for {@link MatrixRun}.
     */
    public final class RunPtr {
        //TODO: review and check whether we can do it private
        public final Combination combination;

        private RunPtr(Combination c) {
            this.combination = c;
        }

        public MatrixRun getRun() {
            return MatrixBuild.this.getRun(combination);
        }

        public String getShortUrl() {
            return Util.rawEncode(combination.toString());
        }

        public String getTooltip() {
            MatrixRun r = getRun();
            if (r != null) {
                return r.getIconColor().getDescription();
            }
            Queue.Item item = Hudson.getInstance().getQueue().getItem(getParent().getItem(combination));
            if (item != null) {
                return item.getWhy();
            }
            return null;    // fall back
        }

        public Combination getCombination() {
            return combination;
        }
    }

    public Layouter<RunPtr> getLayouter() {
        // axes can be null if build page is access right when build starts
        return axes == null ? null : new Layouter<RunPtr>(axes) {
            protected RunPtr getT(Combination c) {
                return new RunPtr(c);
            }
        };
    }

    /**
     * Gets the {@link MatrixRun} in this build that corresponds
     * to the given combination.
     */
    public MatrixRun getRun(Combination c) {
        MatrixConfiguration config = getParent().getItem(c);
        if (config == null) {
            return null;
        }
        return config.getBuildByNumber(getNumber());
    }

    /**
     * Returns all {@link MatrixRun}s for this {@link MatrixBuild}.
     */
    public List<MatrixRun> getRuns() {
        List<MatrixRun> r = new ArrayList<MatrixRun>();
        for (MatrixConfiguration c : getParent().getItems()) {
            MatrixRun b = c.getBuildByNumber(getNumber());
            if (b != null) {
                r.add(b);
            }
        }
        return r;
    }

    @Override
    public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) {
        try {
            MatrixRun item = getRun(Combination.fromString(token));
            if (item != null) {
                return item;
            }
        } catch (IllegalArgumentException _) {
            // failed to parse the token as Combination. Must be something else
        }
        return super.getDynamic(token, req, rsp);
    }

    @Override
    public void run() {
        run(new RunnerImpl());
    }

    @Override
    public Fingerprint.RangeSet getDownstreamRelationship(AbstractProject that) {
        Fingerprint.RangeSet rs = super.getDownstreamRelationship(that);
        for (MatrixRun run : getRuns()) {
            rs.add(run.getDownstreamRelationship(that));
        }
        return rs;
    }

    private class RunnerImpl extends AbstractRunner {
        private final List<MatrixAggregator> aggregators = new ArrayList<MatrixAggregator>();

        protected Result doRun(BuildListener listener) throws Exception {
            MatrixProject p = getProject();
            PrintStream logger = listener.getLogger();

            // list up aggregators
            for (Publisher pub : p.getPublishers().values()) {
                if (pub instanceof MatrixAggregatable) {
                    MatrixAggregatable ma = (MatrixAggregatable) pub;
                    MatrixAggregator a = ma.createAggregator(MatrixBuild.this, launcher, listener);
                    if (a != null) {
                        aggregators.add(a);
                    }
                }
            }

            //let properties do their job
            for (JobProperty prop : p.getProperties().values()) {
                if (prop instanceof MatrixAggregatable) {
                    MatrixAggregatable ma = (MatrixAggregatable) prop;
                    MatrixAggregator a = ma.createAggregator(MatrixBuild.this, launcher, listener);
                    if (a != null) {
                        aggregators.add(a);
                    }
                }
            }

            axes = p.getAxes();
            Collection<MatrixConfiguration> activeConfigurations = p.getActiveConfigurations();
            final int n = getNumber();

            String touchStoneFilter = p.getTouchStoneCombinationFilter();
            Collection<MatrixConfiguration> touchStoneConfigurations = new HashSet<MatrixConfiguration>();
            Collection<MatrixConfiguration> delayedConfigurations = new HashSet<MatrixConfiguration>();
            for (MatrixConfiguration c : activeConfigurations) {
                AxisList axes = p.getAxes();
                String touchStoneCombinationFilter = p.getTouchStoneCombinationFilter();
                if (touchStoneFilter != null && c.getCombination()
                    .evalGroovyExpression(axes, touchStoneCombinationFilter)) {
                    touchStoneConfigurations.add(c);
                } else {
                    delayedConfigurations.add(c);
                }
            }

            for (MatrixAggregator a : aggregators) {
                if (!a.startBuild()) {
                    return Result.FAILURE;
                }
            }

            try {
                if (!p.isRunSequentially()) {
                    for (MatrixConfiguration c : touchStoneConfigurations) {
                        scheduleConfigurationBuild(logger, c);
                    }
                }

                Result r = Result.SUCCESS;
                for (MatrixConfiguration c : touchStoneConfigurations) {
                    if (p.isRunSequentially()) {
                        scheduleConfigurationBuild(logger, c);
                    }
                    Result buildResult = waitForCompletion(listener, c);
                    r = r.combine(buildResult);
                }

                if (p.getTouchStoneResultCondition() != null && r.isWorseThan(p.getTouchStoneResultCondition())) {
                    logger.printf("Touchstone configurations resulted in %s, so aborting...\n", r);
                    return r;
                }

                if (!p.isRunSequentially()) {
                    for (MatrixConfiguration c : delayedConfigurations) {
                        scheduleConfigurationBuild(logger, c);
                    }
                }

                for (MatrixConfiguration c : delayedConfigurations) {
                    if (p.isRunSequentially()) {
                        scheduleConfigurationBuild(logger, c);
                    }
                    Result buildResult = waitForCompletion(listener, c);
                    r = r.combine(buildResult);
                }

                return r;
            } catch (InterruptedException e) {
                logger.println("Aborted");
                return Result.ABORTED;
            } catch (AggregatorFailureException e) {
                return Result.FAILURE;
            } finally {
                // if the build was aborted in the middle. Cancel all the configuration builds.
                Queue q = Hudson.getInstance().getQueue();
                synchronized (q) {// avoid micro-locking in q.cancel.
                    for (MatrixConfiguration c : activeConfigurations) {
                        if (q.cancel(c)) {
                            logger.println(Messages.MatrixBuild_Cancelled(c.getDisplayName()));
                        }
                        MatrixRun b = c.getBuildByNumber(n);
                        if (b != null) {
                            Executor exe = b.getExecutor();
                            if (exe != null) {
                                logger.println(Messages.MatrixBuild_Interrupting(b.getDisplayName()));
                                exe.interrupt();
                            }
                        }
                    }
                }
            }
        }

        private Result waitForCompletion(BuildListener listener, MatrixConfiguration c)
            throws InterruptedException, IOException, AggregatorFailureException {
            String whyInQueue = "";
            long startTime = System.currentTimeMillis();

            // wait for the completion
            int appearsCancelledCount = 0;
            while (true) {
                MatrixRun b = c.getBuildByNumber(getNumber());

                // two ways to get beyond this. one is that the build starts and gets done,
                // or the build gets cancelled before it even started.
                Result buildResult = null;
                if (b != null && !b.isBuilding()) {
                    buildResult = b.getResult();
                }
                Queue.Item qi = c.getQueueItem();
                if (b == null && qi == null) {
                    appearsCancelledCount++;
                } else {
                    appearsCancelledCount = 0;
                }

                if (appearsCancelledCount >= 5) {
                    // there's conceivably a race condition in computating b and qi, as their computation
                    // are not synchronized. There are indeed several reports of Hudson incorrectly assuming
                    // builds being cancelled. See
                    // http://www.nabble.com/Master-slave-problem-tt14710987.html and also
                    // http://www.nabble.com/Anyone-using-AccuRev-plugin--tt21634577.html#a21671389
                    // because of this, we really make sure that the build is cancelled by doing this 5
                    // times over 5 seconds
                    listener.getLogger().println(Messages.MatrixBuild_AppearsCancelled(c.getDisplayName()));
                    buildResult = Result.ABORTED;
                }

                if (buildResult != null) {
                    for (MatrixAggregator a : aggregators) {
                        if (!a.endRun(b)) {
                            throw new AggregatorFailureException();
                        }
                    }
                    return buildResult;
                }

                if (qi != null) {
                    // if the build seems to be stuck in the queue, display why
                    String why = qi.getWhy();
                    if (!why.equals(whyInQueue) && System.currentTimeMillis() - startTime > 5000) {
                        listener.getLogger().println(c.getDisplayName() + " is still in the queue: " + why);
                        whyInQueue = why;
                    }
                }

                Thread.sleep(1000);
            }
        }

        private void scheduleConfigurationBuild(PrintStream logger, MatrixConfiguration c) {
            logger.println(Messages.MatrixBuild_Triggering(c.getDisplayName()));
            c.scheduleBuild(getAction(ParametersAction.class), new UpstreamCause(MatrixBuild.this));
        }

        public void post2(BuildListener listener) throws Exception {
            for (MatrixAggregator a : aggregators) {
                a.endBuild();
            }
        }

        @Override
        protected Lease decideWorkspace(Node n, WorkspaceList wsl) throws IOException, InterruptedException {
            String customWorkspace = getProject().getCustomWorkspace();
            if (customWorkspace != null) {
                // we allow custom workspaces to be concurrently used between jobs.
                return Lease.createDummyLease(n.getRootPath().child(getEnvironment(listener).expand(customWorkspace)));
            }
            return super.decideWorkspace(n, wsl);
        }

    }

    /**
     * A private exception to help maintain the correct control flow after extracting the 'waitForCompletion' method
     */
    private static class AggregatorFailureException extends Exception {
    }

}
TOP

Related Classes of hudson.matrix.MatrixBuild

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.