Package hudson.ivy

Source Code of hudson.ivy.IvyModuleSetBuild$RunnerImpl

/*
* The MIT License
*
* Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi, Red Hat, Inc., Victor Glushenkov, 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.AbortException;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.ivy.IvyBuild.ProxyImpl2;
import hudson.ivy.builder.IvyBuilderType;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Build;
import hudson.model.BuildListener;
import hudson.model.DependencyGraph;
import hudson.model.Environment;
import hudson.model.Fingerprint;
import hudson.model.Hudson;
import hudson.model.ParametersAction;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.scm.ChangeLogSet;
import hudson.tasks.BuildWrapper;
import hudson.tasks.Publisher;
import hudson.util.StreamTaskListener;

import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.PrintStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.text.ParseException;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang.StringUtils;
import org.apache.ivy.Ivy;
import org.apache.ivy.Ivy.IvyCallback;
import org.apache.ivy.core.IvyContext;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.settings.IvySettings;
import org.apache.ivy.core.sort.SortOptions;
import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
import org.apache.ivy.util.Message;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.types.FileSet;
import org.jenkinsci.lib.configprovider.model.Config;
import org.jenkinsci.plugins.configfiles.common.CleanTempFilesAction;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

/**
* {@link Build} for {@link IvyModuleSet}.
*
* <p>
* A "build" of {@link IvyModuleSet} consists of:
*
* <ol>
* <li>Update the workspace.
* <li>Parse ivy.xml files
* <li>Trigger module builds.
* </ol>
*
* This object remembers the changelog and what {@link IvyBuild}s are done on
* this.
*
* @author Timothy Bingaman
*/
public class IvyModuleSetBuild extends AbstractIvyBuild<IvyModuleSet, IvyModuleSetBuild> {
    /**
     * {@link IvyReporter}s that will contribute project actions. Can be null if
     * there's none.
     */
    /* package */List<IvyReporter> projectActionReporters;

    /**
     * Absolute path to ivy settings file on slave
     */
    private transient String settings;

    public IvyModuleSetBuild(IvyModuleSet job) throws IOException {
        super(job);
    }

    public IvyModuleSetBuild(IvyModuleSet project, File buildDir) throws IOException {
        super(project, buildDir);
    }

    /**
     * Exposes {@code ANT_OPTS} to forked processes.
     *
     * When we fork Ant, we do so directly by executing Java, thus this
     * environment variable is pointless (we have to tweak JVM launch option
     * correctly instead, which can be seen in {@link IvyProcessFactory}), but
     * setting the environment variable explicitly is still useful in case this
     * Ant forks other Ant processes via normal way. See HUDSON-3644.
     */
    @Override
    public EnvVars getEnvironment(TaskListener log) throws IOException, InterruptedException {
        EnvVars envs = super.getEnvironment(log);
        envs.putAll(project.getIvyBuilderType().getEnvironment());
        return envs;
    }

    /**
     * Displays the combined status of all modules.
     * <p>
     * More precisely, this picks up the status of this build itself, plus all
     * the latest builds of the modules that belongs to this build.
     */
    @Override
    public Result getResult() {
        Result r = super.getResult();

        for (IvyBuild b : getModuleLastBuilds().values()) {
            Result br = b.getResult();
            if (r == null)
                r = br;
            else if (br == Result.NOT_BUILT)
                continue; // UGLY: when computing combined status, ignore the
            // modules that were not built
            else if (br != null)
                r = r.combine(br);
        }

        return r;
    }

    /**
     * Returns the filtered changeset entries that match the given module.
     */
    /* package */List<ChangeLogSet.Entry> getChangeSetFor(final IvyModule mod) {
        return new ArrayList<ChangeLogSet.Entry>() {
            {
                for (ChangeLogSet.Entry e : getChangeSet()) {
                    if (isDescendantOf(e, mod)) {
                        add(e);
                    }
                }
            }

            /**
             * Does this change happen somewhere in the given module or its
             * descendants?
             */
            private boolean isDescendantOf(ChangeLogSet.Entry e, IvyModule mod) {
                for (String path : e.getAffectedPaths())
                    if (path != null && path.startsWith(mod.getRelativePathToModuleRoot()))
                        return true;
                return false;
            }
        };
    }

    /**
     * Computes the module builds that correspond to this build.
     * <p>
     * A module may be built multiple times (by the user action), so the value
     * is a list.
     */
    public Map<IvyModule, List<IvyBuild>> getModuleBuilds() {
        Collection<IvyModule> mods = getParent().getModules();

        // identify the build number range. [start,end)
        IvyModuleSetBuild nb = getNextBuild();
        int end = nb != null ? nb.getNumber() : Integer.MAX_VALUE;

        // preserve the order by using LinkedHashMap
        Map<IvyModule, List<IvyBuild>> r = new LinkedHashMap<IvyModule, List<IvyBuild>>(mods.size());

        for (IvyModule m : mods) {
            List<IvyBuild> builds = new ArrayList<IvyBuild>();
            IvyBuild b = m.getNearestBuild(number);
            while (b != null && b.getNumber() < end) {
                builds.add(b);
                b = b.getNextBuild();
            }
            r.put(m, builds);
        }

        return r;
    }

    @Override
    public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) {
        // map corresponding module build under this object
        if (token.indexOf('$') > 0) {
            IvyModule m = getProject().getModule(token);
            if (m != null)
                return m.getBuildByNumber(getNumber());
        }
        return super.getDynamic(token, req, rsp);
    }

    /**
     * Computes the latest module builds that correspond to this build.
     */
    public Map<IvyModule, IvyBuild> getModuleLastBuilds() {
        Collection<IvyModule> mods = getParent().getModules();

        // identify the build number range. [start,end)
        IvyModuleSetBuild nb = getNextBuild();
        int end = nb != null ? nb.getNumber() : Integer.MAX_VALUE;

        // preserve the order by using LinkedHashMap
        Map<IvyModule, IvyBuild> r = new LinkedHashMap<IvyModule, IvyBuild>(mods.size());

        for (IvyModule m : mods) {
            IvyBuild b = m.getNearestOldBuild(end - 1);
            if (b != null && b.getNumber() >= getNumber())
                r.put(m, b);
        }

        return r;
    }

    public void registerAsProjectAction(IvyReporter reporter) {
        if (projectActionReporters == null)
            projectActionReporters = new ArrayList<IvyReporter>();
        projectActionReporters.add(reporter);
    }

    /**
     * Finds {@link Action}s from all the module builds that belong to this
     * {@link IvyModuleSetBuild}. One action per one {@link IvyModule}, and
     * newer ones take precedence over older ones.
     */
    public <T extends Action> List<T> findModuleBuildActions(Class<T> action) {
        Collection<IvyModule> mods = getParent().getModules();
        List<T> r = new ArrayList<T>(mods.size());

        // identify the build number range. [start,end)
        IvyModuleSetBuild nb = getNextBuild();
        int end = nb != null ? nb.getNumber() - 1 : Integer.MAX_VALUE;

        for (IvyModule m : mods) {
            IvyBuild b = m.getNearestOldBuild(end);
            while (b != null && b.getNumber() >= number) {
                T a = b.getAction(action);
                if (a != null) {
                    r.add(a);
                    break;
                }
                b = b.getPreviousBuild();
            }
        }

        return r;
    }

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

    @Override
    public Fingerprint.RangeSet getDownstreamRelationship(AbstractProject that) {
        Fingerprint.RangeSet rs = super.getDownstreamRelationship(that);
        for (List<IvyBuild> builds : getModuleBuilds().values())
            for (IvyBuild b : builds)
                rs.add(b.getDownstreamRelationship(that));
        return rs;
    }

    /**
     * Called when a module build that corresponds to this module set build has
     * completed.
     */
    /* package */void notifyModuleBuild(IvyBuild newBuild) {
        try {
            // update module set build number
            getParent().updateNextBuildNumber();

            // update actions
            Map<IvyModule, List<IvyBuild>> moduleBuilds = getModuleBuilds();

            // actions need to be replaced atomically especially
            // given that two builds might complete simultaneously.
            synchronized (this) {
                boolean modified = false;

                List<Action> actions = getActions();
                Set<Class<? extends AggregatableAction>> individuals = new HashSet<Class<? extends AggregatableAction>>();
                for (Action a : actions) {
                    if (a instanceof IvyAggregatedReport) {
                        IvyAggregatedReport mar = (IvyAggregatedReport) a;
                        mar.update(moduleBuilds, newBuild);
                        individuals.add(mar.getIndividualActionType());
                        modified = true;
                    }
                }

                // see if the new build has any new aggregatable action that we
                // haven't seen.
                for (AggregatableAction aa : newBuild.getActions(AggregatableAction.class)) {
                    if (individuals.add(aa.getClass())) {
                        // new AggregatableAction
                        IvyAggregatedReport mar = aa.createAggregatedAction(this, moduleBuilds);
                        mar.update(moduleBuilds, newBuild);
                        actions.add(mar);
                        modified = true;
                    }
                }

                if (modified) {
                    save();
                    getProject().updateTransientActions();
                }
            }

            // symlink to this module build
            String moduleFsName = newBuild.getProject().getModuleName().toFileSystemName();
            Util.createSymlink(getRootDir(),
                    "../../modules/" + moduleFsName + "/builds/" + newBuild.getId() /*ugly!*/,
                    moduleFsName, StreamTaskListener.NULL);
        } catch (IOException e) {
            LOGGER.log(Level.WARNING, "Failed to update " + this, e);
        } catch (InterruptedException e) {
            LOGGER.log(Level.WARNING, "Failed to update " + this, e);
        }
    }

    /**
     * The sole job of the {@link IvyModuleSet} build is to update SCM and
     * triggers module builds.
     */
    private class RunnerImpl extends AbstractRunner {
        private Map<ModuleName,IvyBuild.ProxyImpl2> proxies;

        @Override
        protected Result doRun(final BuildListener listener) throws Exception {
            PrintStream logger = listener.getLogger();
            try {
                EnvVars envVars = getEnvironment(listener);

                Config config = IvyConfig.provider.getConfigById(project.getSettings());
                if (config != null) {
                    FilePath tmp = getWorkspace().createTextTempFile("ivy", "xml", config.content);
                    settings = tmp.getRemote();
                    addAction(new CleanTempFilesAction(settings));

                } else {
                    String settingsFile = project.getIvySettingsFile();
                    if (settingsFile != null) {
                        settings = getWorkspace().child(settingsFile).getRemote();
                    }
                }

                if (!project.isAggregatorStyleBuild()) {
                    // start module builds
                    parseIvyDescriptorFiles(listener, logger, envVars);
                    Set<IvyModule> triggeredModules = new HashSet<IvyModule>();
                    if (!project.isIncrementalBuild() || IvyModuleSetBuild.this.getChangeSet().isEmptySet()) {
                        for (IvyModule module : project.sortedActiveModules) {
                            // Don't trigger builds if we've already triggered
                            // one
                            // of their dependencies.
                            // It's safe to just get the direct dependencies
                            // since
                            // the modules are sorted in dependency order.
                            List<AbstractProject> ups = module.getUpstreamProjects();
                            boolean triggerBuild = true;
                            for (AbstractProject upstreamDep : ups) {
                                if (triggeredModules.contains(upstreamDep)) {
                                    triggerBuild = false;
                                    break;
                                }
                            }

                            if (triggerBuild) {
                                logger.println("Triggering " + module.getModuleName());
                                module.scheduleBuild(new ParameterizedUpstreamCause(((Run<?, ?>) IvyModuleSetBuild.this), IvyModuleSetBuild.this.getActions(ParametersAction.class)));
                            }
                            triggeredModules.add(module);
                        }
                    } else {
                        for (IvyModule module : project.sortedActiveModules) {
                            // If there are changes for this module, add it.
                            // Also add it if we've never seen this module
                            // before,
                            // or if the previous build of this module
                            // failed or was unstable.
                            boolean triggerBuild = false;
                            if ((module.getLastBuild() == null) || (!getChangeSetFor(module).isEmpty())
                                    || (module.getLastBuild().getResult().isWorseThan(Result.SUCCESS))) {
                                triggerBuild = true;
                                List<AbstractProject> ups = module.getUpstreamProjects();
                                for (AbstractProject upstreamDep : ups) {
                                    if (triggeredModules.contains(upstreamDep)) {
                                        triggerBuild = false;
                                        triggeredModules.add(module);
                                        break;
                                    }
                                }
                            }

                            if (triggerBuild) {
                                logger.println("Triggering " + module.getModuleName());
                                module.scheduleBuild(new ParameterizedUpstreamCause(((Run<?, ?>) IvyModuleSetBuild.this), IvyModuleSetBuild.this.getActions(ParametersAction.class)));
                                triggeredModules.add(module);
                            }
                        }
                    }
                } else {
                    // do builds here
                    try {
                        List<BuildWrapper> wrappers = new ArrayList<BuildWrapper>();
                        for (BuildWrapper w : project.getBuildWrappersList())
                            wrappers.add(w);
                        ParametersAction parameters = getAction(ParametersAction.class);
                        if (parameters != null)
                            parameters.createBuildWrappers(IvyModuleSetBuild.this, wrappers);

                        for (BuildWrapper w : wrappers) {
                            Environment e = w.setUp(IvyModuleSetBuild.this, launcher, listener);
                            if (e == null)
                                return Result.FAILURE;
                            buildEnvironments.add(e);
                            e.buildEnvVars(envVars); // #3502: too late for
                            // getEnvironment to do
                            // this
                        }

                        if (!preBuild(listener, project.getPublishers()))
                            return Result.FAILURE;

                        Properties additionalProperties = null;
                        if (project.isIncrementalBuild()) {
                            parseIvyDescriptorFiles(listener, logger, envVars);
                            List<String> changedModules = new ArrayList<String>();
                            for (IvyModule m : project.sortedActiveModules) {
                                // Check if incrementalBuild is selected and that
                                // there are changes -
                                // we act as if incrementalBuild is not set if there
                                // are no changes.
                                if (!IvyModuleSetBuild.this.getChangeSet().isEmptySet()) {
                                    // If there are changes for this module, add it.
                                    if (!getChangeSetFor(m).isEmpty()) {
                                        changedModules.add(m.getModuleName().name);
                                    }
                                }
                            }

                            if (project.isAggregatorStyleBuild()) {
                                additionalProperties = new Properties();
                                additionalProperties.put(project.getChangedModulesProperty() == null ? "hudson.ivy.changedModules" : project
                                        .getChangedModulesProperty(), StringUtils.join(changedModules, ','));
                            }
                        }
                       
                        IvyBuilderType ivyBuilderType = project.getIvyBuilderType();
                        hudson.tasks.Builder builder = ivyBuilderType.getBuilder(additionalProperties, null, buildEnvironments);
                        logger.println("Building project with " + ivyBuilderType.getDescriptor().getDisplayName());
                       
                        if (builder.perform(IvyModuleSetBuild.this, launcher, listener))
                            return Result.SUCCESS;

                        return Result.FAILURE;
                    } finally {
                        // tear down in reverse order
                        boolean failed = false;
                        for (int i = buildEnvironments.size() - 1; i >= 0; i--) {
                            if (!buildEnvironments.get(i).tearDown(IvyModuleSetBuild.this, listener)) {
                                failed = true;
                            }
                        }
                        buildEnvironments = null;
                        // WARNING The return in the finally clause will trump
                        // any return before
                        if (failed)
                            return Result.FAILURE;
                    }
                }

                return null;
            } catch (AbortException e) {
                if (e.getMessage() != null)
                    listener.error(e.getMessage());
                return Result.FAILURE;
            } catch (InterruptedIOException e) {
                e.printStackTrace(listener.error("Aborted Ivy execution for InterruptedIOException"));
                return Result.ABORTED;
            } catch (InterruptedException e) {
                e.printStackTrace(listener.error("Aborted Ivy execution for InterruptedException"));
                return Result.ABORTED;
            } catch (IOException e) {
                e.printStackTrace(listener.error(Messages.IvyModuleSetBuild_FailedToParseIvyXml()));
                return Result.FAILURE;
            } catch (RunnerAbortedException e) {
                return Result.FAILURE;
            } catch (RuntimeException e) {
                // bug in the code.
                e.printStackTrace(listener.error("Processing failed due to a bug in the code. Please report this to users@hudson.dev.java.net"));
                logger.println("project=" + project);
                logger.println("project.getModules()=" + project.getModules());
                throw e;
            }
        }

        private void parseIvyDescriptorFiles(BuildListener listener, PrintStream logger, EnvVars envVars) throws IOException, InterruptedException {
            logger.println("Parsing Ivy Descriptor Files");

            List<IvyModuleInfo> ivyDescriptors;
            try {
              IvyXmlParser parser = new IvyXmlParser(listener, project, settings, getModuleRoot().getRemote());
              if (getModuleRoot().getChannel() instanceof Channel)
                ((Channel) getModuleRoot().getChannel()).preloadJar(parser, Ivy.class);
                ivyDescriptors = getModuleRoot().act(parser);
            } catch (IOException e) {
                if (e.getCause() instanceof AbortException)
                    throw (AbortException) e.getCause();
                throw e;
            } catch (Throwable e) {
        throw new IOException("Unable to parse ivy descriptors", e);
      }

            // update the module list
            Map<ModuleName, IvyModule> modules = project.modules;
            synchronized (modules) {
                Map<ModuleName, IvyModule> old = new HashMap<ModuleName, IvyModule>(modules);
                List<IvyModule> sortedModules = new ArrayList<IvyModule>();

                modules.clear();
                for (IvyModuleInfo ivyDescriptor : ivyDescriptors) {
                    IvyModule mm = old.get(ivyDescriptor.name);
                    if (mm != null) {// found an existing matching module
                        if (debug)
                            logger.println("Reconfiguring " + mm);
                        mm.reconfigure(ivyDescriptor);
                        modules.put(ivyDescriptor.name, mm);
                    } else {// this looks like a new module
                        logger.println(Messages.IvyModuleSetBuild_DiscoveredModule(ivyDescriptor.name, ivyDescriptor.displayName));
                        mm = new IvyModule(project, ivyDescriptor, getNumber());
                        modules.put(mm.getModuleName(), mm);
                    }
                    sortedModules.add(mm);
                    mm.save();
                }

                // at this point the list contains all the live modules
                project.sortedActiveModules = sortedModules;

                // remaining modules are no longer active.
                old.keySet().removeAll(modules.keySet());
                for (IvyModule om : old.values()) {
                    if (debug)
                        logger.println("Disabling " + om);
                    om.makeDisabled(true);
                }
                modules.putAll(old);
            }

            // we might have added new modules
            Hudson.getInstance().rebuildDependencyGraph();

            // module builds must start with this build's number
            for (IvyModule m : modules.values())
                m.updateNextBuildNumber(getNumber());
        }

        @Override
        protected void post2(BuildListener listener) throws Exception {
            // asynchronous executions from the build might have left some
            // unsaved state,
            // so just to be safe, save them all.
            // TODO: uncomment when proxying stuff is done
//            for (IvyBuild b : getModuleLastBuilds().values())
//                b.save();

            if (project.isAggregatorStyleBuild()) {
                performAllBuildSteps(listener, project.getPublishers(), true);
            }
            performAllBuildSteps(listener, project.getProperties(), true);

            // aggregate all module fingerprints to us,
            // so that dependencies between module builds can be understood as
            // dependencies between module set builds.
            // TODO: we really want to implement this as a publisher,
            // but we don't want to ask for a user configuration, nor should it
            // show up in the persisted record.
            // IvyFingerprinter.aggregate(IvyModuleSetBuild.this);
        }

        @Override
        public void cleanUp(BuildListener listener) throws Exception {
            super.cleanUp(listener);
       
            if (project.isAggregatorStyleBuild()) {
                // schedule downstream builds. for non aggregator style builds,
                // this is done by each module
                scheduleDownstreamBuilds(listener);
                performAllBuildSteps(listener, project.getPublishers(), false);
            }

            performAllBuildSteps(listener, project.getProperties(), false);
        }
    }

    /**
     * Runs Ant and builds the project.
     *
     * This is only used for {@link IvyModuleSet#isAggregatorStyleBuild() the
     * aggregator style build}.
     */
    private static final class Builder extends IvyBuilder {
        private final Map<ModuleName,IvyBuildProxy2> proxies;
        private final Map<ModuleName,List<Publisher>> modulePublishers = new HashMap<ModuleName,List<Publisher>>();

        private IvyBuildProxy2 lastProxy;

        /**
         * Kept so that we can finalize them in the end method.
         */
        private final transient Map<ModuleName,ProxyImpl2> sourceProxies;

        public Builder(BuildListener listener,Map<ModuleName,ProxyImpl2> proxies, Collection<IvyModule> modules, List<String> goals, Map<String,String> systemProps) {
            super(listener,goals,systemProps);
            this.sourceProxies = proxies;
            this.proxies = new HashMap<ModuleName, IvyBuildProxy2>(proxies);
            for (Entry<ModuleName,IvyBuildProxy2> e : this.proxies.entrySet())
                e.setValue(new FilterImpl(e.getValue()));

            for (IvyModule m : modules)
                modulePublishers.put(m.getModuleName(),m.createModulePublishers());
        }

        private class FilterImpl extends IvyBuildProxy2.Filter<IvyBuildProxy2> implements Serializable {
            public FilterImpl(IvyBuildProxy2 core) {
                super(core);
            }

            @Override
            public void executeAsync(final BuildCallable<?,?> program) throws IOException {
                futures.add(Channel.current().callAsync(new AsyncInvoker(core,program)));
            }

            private static final long serialVersionUID = 1L;
        }

        /**
         * Invoked after the Ant has finished running, and in the master, not in the Ant process.
         */
        void end(Launcher launcher) throws IOException, InterruptedException {
            for (Map.Entry<ModuleName,ProxyImpl2> e : sourceProxies.entrySet()) {
                ProxyImpl2 p = e.getValue();
                for (Publisher publisher : modulePublishers.get(e.getKey())) {
                    // we'd love to do this when the module build ends, but doing so requires
                    // we know how many task segments are in the current build.
                    publisher.perform(p.owner(),launcher,listener);
                    p.appendLastLog();
                }
                p.close();
            }
        }

        @Override
        public Result call() throws IOException {
            try {
                return super.call();
            } finally {
                if(lastProxy!=null)
                    lastProxy.appendLastLog();
            }
        }

        @Override
        void preBuild(BuildEvent event) throws IOException, InterruptedException {
            // TODO
        }

        @Override
        void postBuild(BuildEvent event) throws IOException, InterruptedException {
            // TODO
        }

        @Override
        void preModule(BuildEvent event) throws InterruptedException, IOException, AbortException {
            File baseDir = event.getProject().getBaseDir();
            // TODO: find the module that contains this path?
//            ModuleName name = new ModuleName(event.getProject().getBaseDir());
//            IvyBuildProxy2 proxy = proxies.get(name);
//            listener.getLogger().flush();   // make sure the data until here are all written
//            proxy.start();
//            for (IvyReporter r : reporters.get(name))
//                if(!r.preBuild(proxy,event,listener))
//                    throw new AbortException(r+" failed");
        }

        @Override
        void postModule(BuildEvent event) throws InterruptedException, IOException, AbortException {
//            ModuleName name = new ModuleName(project);
//            IvyBuildProxy2 proxy = proxies.get(name);
//            List<IvyReporter> rs = reporters.get(name);
//            if(rs==null) { // probe for issue #906
//                throw new AssertionError("reporters.get("+name+")==null. reporters="+reporters+" proxies="+proxies);
//            }
//            for (IvyReporter r : rs)
//                if(!r.postBuild(proxy,event,listener))
//                    throw new hudson.maven.agent.AbortException(r+" failed");
//            listener.getLogger().flush();   // make sure the data until here are all written
//            proxy.end();
//            lastProxy = proxy;
        }

        private static final long serialVersionUID = 1L;
    }

    /**
     * Used to tunnel exception from Ant through remoting.
     */
    private static final class AntExecutionException extends RuntimeException {
        private AntExecutionException(Exception cause) {
            super(cause);
        }

        @Override
        public Exception getCause() {
            return (Exception) super.getCause();
        }

        private static final long serialVersionUID = 1L;
    }

    /**
     * Executed on the slave to parse ivy.xml files and extract information into
     * {@link IvyModuleInfo}, which will be then brought back to the master.
     */
    private static final class IvyXmlParser implements Callable<List<IvyModuleInfo>, Throwable> {
        private static final String IVY_XML_PATTERN = "**/ivy.xml";
        private final BuildListener listener;
        /**
         * Capture the value of the static field so that the debug flag takes an
         * effect even when {@link IvyXmlParser} runs in a slave.
         */
        private final boolean verbose = debug;
        private final String ivyFilePattern;
        private final String ivyFileExcludePattern;

        /** Absolute path to ivy settings file */
        private final String ivySettingsFile;

        private final String ivySettingsPropertyFiles;
        private final String ivyBranch;
        private final String workspace;
        private final String workspaceProper;

        public IvyXmlParser(BuildListener listener, IvyModuleSet project, String ivySettingsFile, String workspace) {
            // project cannot be shipped to the remote JVM, so all the relevant
            // properties need to be captured now.
            this.listener = listener;
            this.ivyFilePattern = project.getIvyFilePattern() == null ? IVY_XML_PATTERN : project.getIvyFilePattern();
            this.ivyFileExcludePattern = project.getIvyFileExcludesPattern();
            this.ivyBranch = project.getIvyBranch();
            this.workspace = workspace;
            this.ivySettingsFile = ivySettingsFile;
            this.ivySettingsPropertyFiles = project.getIvySettingsPropertyFiles();
            this.workspaceProper = project.getLastBuild().getWorkspace().getRemote();
        }

    @SuppressWarnings("unchecked")
        public List<IvyModuleInfo> call() throws Throwable {
      File ws = new File(workspace);
            FileSet ivyFiles = Util.createFileSet(ws, ivyFilePattern, ivyFileExcludePattern);
            final PrintStream logger = listener.getLogger();

            Ivy ivy = getIvy(logger);
            HashMap<ModuleDescriptor, String> moduleDescriptors = new HashMap<ModuleDescriptor, String>();
            for (String ivyFilePath : ivyFiles.getDirectoryScanner().getIncludedFiles()) {
                final File ivyFile = new File(ws, ivyFilePath);

                ModuleDescriptor module = (ModuleDescriptor) ivy.execute(new IvyCallback() {
                    public Object doInIvyContext(Ivy ivy, IvyContext context) {
                        try {
                            return ModuleDescriptorParserRegistry.getInstance().parseDescriptor(ivy.getSettings(), ivyFile.toURI().toURL(),
                                    ivy.getSettings().doValidate());
                        } catch (MalformedURLException e) {
                            logger.println("The URL is malformed : " + ivyFile);
                            return null;
                        } catch (ParseException e) {
                            logger.println("Parsing error while reading the ivy file " + ivyFile);
                            return null;
                        } catch (IOException e) {
                            logger.println("I/O error while reading the ivy file " + ivyFile);
                            return null;
                        }
                    }
                });
                moduleDescriptors.put(module, ivyFilePath.replace('\\', '/'));
            }

            List<IvyModuleInfo> infos = new ArrayList<IvyModuleInfo>();
            List<ModuleDescriptor> sortedModuleDescriptors = ivy.sortModuleDescriptors(moduleDescriptors.keySet(), SortOptions.DEFAULT);
            for (ModuleDescriptor moduleDescriptor : sortedModuleDescriptors) {
                infos.add(new IvyModuleInfo(moduleDescriptor, moduleDescriptors.get(moduleDescriptor)));
            }

            if (verbose) {
                for (IvyModuleInfo moduleInfo : infos) {
                    logger.printf("Discovered module %s at %s.\n", moduleInfo.displayName, moduleInfo.relativePathToDescriptor);
                }
            }

            return infos;
        }

        /**
         *
         * @return the Ivy instance based on the {@link #ivyConfName}
         * @throws AbortException
         *
         * @throws ParseException
         * @throws IOException
         */
        public Ivy getIvy(PrintStream logger) throws AbortException {
            Message.setDefaultLogger(new IvyMessageImpl());
           
            File settingsLoc = (ivySettingsFile == null) ? null : new File(ivySettingsFile);

            if ((settingsLoc != null) && (!settingsLoc.exists())) {
                throw new AbortException(Messages.IvyModuleSetBuild_NoSuchIvySettingsFile(settingsLoc.getAbsolutePath()));
            }
           
            ArrayList<File> propertyFiles = new ArrayList<File>();
            if (StringUtils.isNotBlank(ivySettingsPropertyFiles)) {
                for (String file : StringUtils.split(ivySettingsPropertyFiles, ',')) {
                    File propertyFile = new File(workspaceProper, file.trim());
                    if (!propertyFile.exists()) {
                        propertyFile = new File(file.trim());
                        if (!propertyFile.isAbsolute() || !propertyFile.exists()) {
                            throw new AbortException(Messages.IvyModuleSetBuild_NoSuchPropertyFile(file));
                        }
                    }
                    propertyFiles.add(propertyFile);
                }
            }
           
            try {
                IvySettings ivySettings = new IvySettings();
                for (File file : propertyFiles) {
                    ivySettings.loadProperties(file);
                }
                if (settingsLoc != null) {
                    ivySettings.load(settingsLoc);
                    if (verbose)
                        logger.println("Configured Ivy using custom settings " + settingsLoc.getAbsolutePath());
                } else {
                    ivySettings.loadDefault();
                    if (verbose)
                        logger.println("Configured Ivy using default 2.1 settings");
                }
                if (ivyBranch != null) {
                    ivySettings.setDefaultBranch(ivyBranch);
                }
                return Ivy.newInstance(ivySettings);
            } catch (Exception e) {
                logger.println("Error while reading the default Ivy 2.1 settings: " + e.getMessage());
                logger.println(e.getStackTrace());
            }
            return null;
        }

        private static final long serialVersionUID = 1L;
    }

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

    /**
     * Extra verbose debug switch.
     */
    public static boolean debug = false;

    @Override
    public IvyModuleSet getParent() {// don't know why, but javac wants this
        return super.getParent();
    }

    private static final class IvyPreloadTask implements Callable<Boolean, IOException> {
    private static final long serialVersionUID = 1L;

    public Boolean call() throws IOException {
      try {
        return Channel.current().preloadJar(this, Ivy.class);
      } catch (InterruptedException e) {
      }
      return false;
    }
  }
}
TOP

Related Classes of hudson.ivy.IvyModuleSetBuild$RunnerImpl

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.