private boolean updateExistingJob(AbstractProject<?, ?> project, String config) {
    boolean created;

    // Leverage XMLUnit to perform diffs
    Diff diff;
    try {
      String oldJob = project.getConfigFile().asString();
      diff = XMLUnit.compareXML(oldJob, config);
      if (diff.similar()) {
        LOGGER.log(Level.FINE, String.format("Project %s is identical", project.getName()));
        return false;
      }
    } catch (Exception e) {
      // It's not a big deal if we can't diff, we'll just move on
      LOGGER.warning(e.getMessage());
    }

    // TODO Perform comparison between old and new, and print to console
    // TODO Print out, for posterity, what the user might have changed, in the format of the DSL

    LOGGER.log(Level.FINE, String.format("Updating project %s as %s", project.getName(), config));
    StreamSource streamSource =
        new StreamSource(new StringReader(config)); // TODO use real xmlReader
    try {
      project.updateByXml(streamSource);
      created = true;
    } catch (IOException ioex) {
      LOGGER.log(Level.WARNING, String.format("Error writing updated project to file."), ioex);
      created = false;
    }
    return created;
  }
 @DataBoundConstructor
 public JobIngredient(String name, String description) {
   this.name = name;
   this.description = description;
   AbstractProject i = Jenkins.getInstance().getItemByFullName(name, AbstractProject.class);
   if (i == null) throw new IllegalArgumentException("No such job: " + name);
   this.definition = XStreamDOM.from(i.getConfigFile().getXStream(), i);
 }
  private String lookupJob(String jobName) throws IOException {
    LOGGER.log(Level.FINE, String.format("Looking up Job %s", jobName));
    String jobXml = "";

    AbstractProject<?, ?> project =
        (AbstractProject<?, ?>) Jenkins.getInstance().getItemByFullName(jobName);
    if (project != null) {
      XmlFile xmlFile = project.getConfigFile();
      jobXml = xmlFile.asString();
    } else {
      LOGGER.log(Level.WARNING, String.format("No Job called %s could be found.", jobName));
      throw new IOException(String.format("No Job called %s could be found.", jobName));
    }

    LOGGER.log(Level.FINE, String.format("Looked up Job with config %s", jobXml));
    return jobXml;
  }
  /** Uses generatedJobs as existing data, so call before updating generatedJobs. */
  private Set<String> updateTemplates(
      AbstractBuild<?, ?> build, BuildListener listener, Set<GeneratedJob> freshJobs)
      throws IOException {
    AbstractProject<?, ?> seedJob = build.getProject();

    Set<String> freshTemplates = getTemplates(freshJobs);
    Set<String> existingTemplates =
        getTemplates(extractGeneratedObjects(seedJob, GeneratedJobsAction.class));
    Set<String> newTemplates = Sets.difference(freshTemplates, existingTemplates);
    Set<String> removedTemplates = Sets.difference(existingTemplates, freshTemplates);

    logItems(listener, "Existing templates", existingTemplates);
    logItems(listener, "New templates", newTemplates);
    logItems(listener, "Unreferenced templates", removedTemplates);

    // Collect information about the templates we loaded
    final String seedJobName = seedJob.getName();
    DescriptorImpl descriptor = Jenkins.getInstance().getDescriptorByType(DescriptorImpl.class);
    boolean descriptorMutated = false;

    // Clean up
    for (String templateName : removedTemplates) {
      Collection<SeedReference> seedJobReferences =
          descriptor.getTemplateJobMap().get(templateName);
      Collection<SeedReference> matching =
          Collections2.filter(seedJobReferences, new SeedNamePredicate(seedJobName));
      if (!matching.isEmpty()) {
        seedJobReferences.removeAll(matching);
        descriptorMutated = true;
      }
    }

    // Ensure we have a reference
    for (String templateName : freshTemplates) {
      Collection<SeedReference> seedJobReferences =
          descriptor.getTemplateJobMap().get(templateName);
      Collection<SeedReference> matching =
          Collections2.filter(seedJobReferences, new SeedNamePredicate(seedJobName));

      AbstractProject templateProject =
          getLookupStrategy().getItem(seedJob, templateName, AbstractProject.class);
      final String digest =
          Util.getDigestOf(new FileInputStream(templateProject.getConfigFile().getFile()));

      if (matching.size() == 1) {
        // Just update digest
        SeedReference ref = Iterables.get(matching, 0);
        if (digest.equals(ref.getDigest())) {
          ref.setDigest(digest);
          descriptorMutated = true;
        }
      } else {
        if (matching.size() > 1) {
          // Not sure how there could be more one, throw it all away and start over
          seedJobReferences.removeAll(matching);
        }
        seedJobReferences.add(new SeedReference(templateName, seedJobName, digest));
        descriptorMutated = true;
      }
    }

    if (descriptorMutated) {
      descriptor.save();
    }
    return freshTemplates;
  }