/** 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);
    }
  }