@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);
 }
    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());
    }
  /** 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;
  }
  /**
   * 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;
  }
  /**
   * 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;
  }
    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 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 [email protected]"));
        logger.println("project=" + project);
        logger.println("project.getModules()=" + project.getModules());
        throw e;
      }
    }