/** {@inheritDoc} */
  @Override
  protected void submit(StaplerRequest req, StaplerResponse rsp)
      throws IOException, ServletException, Descriptor.FormException {
    super.submit(req, rsp);
    synchronized (this) {
      JSONObject json = req.getSubmittedForm();

      /*
      Set<String> oldSourceIds = new HashSet<String>();
      for (SCMSource source : getSCMSources()) {
          oldSourceIds.add(source.getId());
      }
      */

      sources.replaceBy(req.bindJSONToList(BranchSource.class, json.opt("sources")));
      for (SCMSource scmSource : getSCMSources()) {
        scmSource.setOwner(this);
      }

      setProjectFactory(
          req.bindJSON(BranchProjectFactory.class, json.getJSONObject("projectFactory")));

      save();

      /* TODO currently ComputedFolder.save always reschedules indexing; could define API to be more discerning
      Set<String> newSourceIds = new HashSet<String>();
      for (SCMSource source : getSCMSources()) {
          newSourceIds.add(source.getId());
      }
      reindex = !newSourceIds.equals(oldSourceIds);
      */
    }
  }
 /** {@inheritDoc} */
 @CheckForNull
 public SCMSource getSCMSource(@CheckForNull String sourceId) {
   for (SCMSource source : getSCMSources()) {
     if (source.getId().equals(sourceId)) {
       return source;
     }
   }
   return nullSCMSource;
 }
 /** Consolidated initialization code. */
 private synchronized void init2() {
   if (sources == null) {
     sources = new PersistedList<BranchSource>(this);
   }
   if (nullSCMSource == null) {
     nullSCMSource = new NullSCMSource();
   }
   nullSCMSource.setOwner(this);
   for (SCMSource source : getSCMSources()) {
     source.setOwner(this);
   }
   final BranchProjectFactory<P, R> factory = getProjectFactory();
   factory.setOwner(this);
 }
  /**
   * Creates a {@link Branch} for a specific {@link SCMSource} and {@link SCMHead}.
   *
   * @param source the {@link SCMSource}
   * @param head the {@link SCMHead}.
   * @return the {@link Branch}
   */
  @NonNull
  private Branch newBranch(@NonNull SCMSource source, @NonNull SCMHead head) {
    source.getClass(); // throw NPE if null
    head.getClass(); // throw NPE if null

    String sourceId = source.getId();
    if (NullSCMSource.ID.equals(sourceId)) {
      return new Branch.Dead(head, Collections.<BranchProperty>emptyList());
    } else {
      final BranchPropertyStrategy strategy = getBranchPropertyStrategy(source);
      return new Branch(
          sourceId,
          head,
          source.build(head),
          strategy != null
              ? strategy.getPropertiesFor(head)
              : Collections.<BranchProperty>emptyList());
    }
  }
 /** {@inheritDoc} */
 @Override
 protected void computeChildren(final ChildObserver<P> observer, final TaskListener listener)
     throws IOException, InterruptedException {
   final BranchProjectFactory<P, R> _factory = getProjectFactory();
   for (final SCMSource source : getSCMSources()) {
     source.fetch(
         new SCMHeadObserver() {
           @Override
           public void observe(@NonNull SCMHead head, @NonNull SCMRevision revision) {
             Branch branch = newBranch(source, head);
             String rawName = branch.getName();
             String encodedName = branch.getEncodedName();
             P project = observer.shouldUpdate(encodedName);
             if (project != null) {
               if (!_factory.isProject(project)) {
                 listener
                     .getLogger()
                     .println("Detected unsupported subitem " + project + ", skipping");
                 return;
               }
               boolean needSave = !branch.equals(_factory.getBranch(project));
               _factory.decorate(_factory.setBranch(project, branch));
               if (revision.isDeterministic()) {
                 SCMRevision lastBuild = _factory.getRevision(project);
                 if (!revision.equals(lastBuild)) {
                   listener
                       .getLogger()
                       .println(
                           "Changes detected in "
                               + rawName
                               + " ("
                               + lastBuild
                               + " → "
                               + revision
                               + ")");
                   needSave = true;
                   scheduleBuild(_factory, project, revision, listener, rawName);
                 } else {
                   listener
                       .getLogger()
                       .println(
                           "No changes detected in " + rawName + " (still at " + revision + ")");
                 }
               } else {
                 // fall back to polling when we have a non-deterministic revision/hash.
                 SCMTriggerItem scmProject =
                     SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project);
                 if (scmProject != null) {
                   PollingResult pollingResult = scmProject.poll(listener);
                   if (pollingResult.hasChanges()) {
                     listener.getLogger().println("Changes detected in " + rawName);
                     needSave = true;
                     scheduleBuild(_factory, project, revision, listener, rawName);
                   } else {
                     listener.getLogger().println("No changes detected in " + rawName);
                   }
                 }
               }
               if (needSave) {
                 try {
                   project.save();
                 } catch (IOException e) {
                   e.printStackTrace(listener.error("Could not save changes to " + rawName));
                 }
               }
               return;
             }
             if (!observer.mayCreate(encodedName)) {
               listener.getLogger().println("Ignoring duplicate branch project " + rawName);
               return;
             }
             project = _factory.newInstance(branch);
             if (!project.getName().equals(encodedName)) {
               throw new IllegalStateException(
                   "Name of created project "
                       + project
                       + " did not match expected "
                       + encodedName);
             }
             if (!rawName.equals(encodedName) && project.getDisplayName().equals(encodedName)) {
               try {
                 project.setDisplayName(rawName);
               } catch (IOException e) {
                 e.printStackTrace(listener.error("Could not save changes to " + rawName));
               }
             }
             _factory.decorate(project);
             observer.created(project);
             scheduleBuild(_factory, project, revision, listener, rawName);
           }
         },
         listener);
   }
 }
 /** {@inheritDoc} */
 public void onSCMSourceUpdated(@NonNull SCMSource source) {
   SCMTrigger.SCMTriggerCause cause =
       new SCMTrigger.SCMTriggerCause(source.getDescriptor().getDisplayName());
   scheduleBuild(0, cause);
 }