private void assertVersion2(ResourceType resourceType) {
    PropertyDefinition prop;
    Set<String> seen =
        new HashSet<String>(2); // we use to this remember names of the things that we've seen

    assert resourceType.getChildSubCategories().size() == 1;
    assert resourceType.getChildSubCategories().get(0).getName().equals(SUBCAT);
    assert resourceType.getChildSubCategories().get(0).getDisplayName().equals(SUBCAT_DISPLAYNAME);

    seen.clear();
    ConfigurationDefinition pcDef = resourceType.getPluginConfigurationDefinition();
    assert pcDef.getGroupDefinitions().size() == 2;
    for (PropertyGroupDefinition group : pcDef.getGroupDefinitions()) {
      seen.add(group.getName());
      if (group.getName().equals(CHANGED_PC_GROUP)) {
        assert group.isDefaultHidden() == CHANGED_PC_GROUP_HIDDEN;
      } else if (group.getName().equals(NEW_PC_GROUP)) {
        assert group.isDefaultHidden() == NEW_PC_GROUP_HIDDEN;
      } else {
        assert false : "Unexpected group [" + group.getName() + "]:" + group;
      }
    }
    if (seen.size() != 2) {
      assert false : "did not see what we expected to see: " + seen;
    }

    prop = pcDef.get(CHANGED_PC_PROP);
    assert prop != null;
    assert prop.getName().equals(CHANGED_PC_PROP);
    assert prop.isRequired() == CHANGED_PC_PROP_REQUIRED;
    assert prop.getPropertyGroupDefinition().getName().equals(CHANGED_PC_GROUP);
    prop = pcDef.get(NEW_PC_PROP);
    assert prop != null;
    assert prop.getName().equals(NEW_PC_PROP);
    assert prop.isRequired() == NEW_PC_PROP_REQUIRED;
    assert prop.getPropertyGroupDefinition().getName().equals(NEW_PC_GROUP);

    seen.clear();
    assert resourceType.getProcessScans().size() == 2;
    for (ProcessScan processScan : resourceType.getProcessScans()) {
      seen.add(processScan.getName());
      if (processScan.getName().equals(CHANGED_PROCESS_SCAN_NAME)) {
        assert processScan.getQuery().equals(CHANGED_PROCESS_SCAN_QUERY);
      } else if (processScan.getName().equals(NEW_PROCESS_SCAN_NAME)) {
        assert processScan.getQuery().equals(NEW_PROCESS_SCAN_QUERY);
      } else {
        assert false : "Unexpected process scan[" + processScan.getName() + "]:" + processScan;
      }
    }
    if (seen.size() != 2) {
      assert false : "did not see what we expected to see: " + seen;
    }

    seen.clear();
    assert resourceType.getOperationDefinitions().size() == 2;
    for (OperationDefinition op : resourceType.getOperationDefinitions()) {
      seen.add(op.getName());
      if (op.getName().equals(CHANGED_OP_NAME)) {
        assert op.getTimeout().intValue() == CHANGED_OP_TIMEOUT;
        assert op.getDescription().equals(CHANGED_OP_DESC);
      } else if (op.getName().equals(NEW_OP_NAME)) {
        assert op.getTimeout().intValue() == NEW_OP_TIMEOUT;
        assert op.getDescription().equals(NEW_OP_DESC);
      } else {
        assert false : "Unexpected operation [" + op.getName() + "]:" + op;
      }
    }
    if (seen.size() != 2) {
      assert false : "did not see what we expected to see: " + seen;
    }

    seen.clear();
    assert resourceType.getMetricDefinitions().size() == 3; // include built-in Availability metric
    for (MeasurementDefinition metric : resourceType.getMetricDefinitions()) {
      if (metric.getName().equals(MeasurementDefinition.AVAILABILITY_NAME)) {
        // expected, ignore
        continue;
      }

      seen.add(metric.getName());
      if (metric.getName().equals(CHANGED_METRIC_PROP)) {
        // even though our _v2 plugin set this to something different, our upgrade doesn't change it
        // because
        // we don't want to overwrite changes a user possibly made to the defaut interval (aka
        // metric template)
        assert metric.getDefaultInterval() == METRIC_DEFAULT_INTERVAL;
      } else if (metric.getName().equals(NEW_METRIC_PROP)) {
        assert metric.getDefaultInterval() == NEW_METRIC_DEFAULT_INTERVAL;
      } else {
        assert false : "Unexpected metric [" + metric.getName() + "]:" + metric;
      }
    }
    if (seen.size() != 2) {
      assert false : "did not see what we expected to see: " + seen;
    }

    seen.clear();
    assert resourceType.getEventDefinitions().size() == 2;
    for (EventDefinition event : resourceType.getEventDefinitions()) {
      seen.add(event.getName());
      if (event.getName().equals(CHANGED_EVENT_NAME)) {
        assert event.getDescription().equals(CHANGED_EVENT_DESC);
      } else if (event.getName().equals(NEW_EVENT_NAME)) {
        assert event.getDescription().equals(NEW_EVENT_DESC);
      } else {
        assert false : "Unexpected event [" + event.getName() + "]:" + event;
      }
    }
    if (seen.size() != 2) {
      assert false : "did not see what we expected to see: " + seen;
    }

    assert resourceType.getResourceConfigurationDefinition().getGroupDefinitions().size() == 0;
    prop = resourceType.getResourceConfigurationDefinition().get(CHANGED_RC_PROP);
    assert prop != null;
    assert prop.getName().equals(CHANGED_RC_PROP);
    assert prop.isRequired() == CHANGED_RC_PROP_REQUIRED;
    prop = resourceType.getResourceConfigurationDefinition().get(NEW_RC_PROP);
    assert prop != null;
    assert prop.getName().equals(NEW_RC_PROP);
    assert prop.isRequired() == NEW_RC_PROP_REQUIRED;

    seen.clear();
    assert resourceType.getDriftDefinitionTemplates().size() == 2;
    for (DriftDefinitionTemplate drift : resourceType.getDriftDefinitionTemplates()) {
      DriftDefinition def = drift.getTemplateDefinition();
      seen.add(def.getName());
      if (def.getName().equals(CHANGED_DRIFT_DEF_NAME)) {
        BaseDirectory driftBasedir = def.getBasedir();
        assert driftBasedir.getValueContext().equals(CHANGED_DRIFT_DEF_BASEDIR_CONTEXT);
        assert driftBasedir.getValueName().equals(CHANGED_DRIFT_DEF_BASEDIR_VALUE);
      } else if (def.getName().equals(NEW_DRIFT_DEF_NAME)) {
        BaseDirectory driftBasedir = def.getBasedir();
        assert driftBasedir.getValueContext().equals(NEW_DRIFT_DEF_BASEDIR_CONTEXT);
        assert driftBasedir.getValueName().equals(NEW_DRIFT_DEF_BASEDIR_VALUE);
      } else {
        assert false : "Unexpected drift def [" + def.getName() + "]:" + def;
      }
    }
    if (seen.size() != 2) {
      assert false : "did not see what we expected to see: " + seen;
    }

    seen.clear();
    ResourceTypeBundleConfiguration bundle = resourceType.getResourceTypeBundleConfiguration();
    assert bundle.getBundleDestinationBaseDirectories().size() == 2;
    for (BundleDestinationBaseDirectory bundleBasedir :
        bundle.getBundleDestinationBaseDirectories()) {
      seen.add(bundleBasedir.getName());
      if (bundleBasedir.getName().equals(CHANGED_BUNDLE_TARGET_NAME)) {
        assert bundleBasedir.getValueContext().equals(CHANGED_BUNDLE_BASEDIR_CONTEXT);
        assert bundleBasedir.getValueName().equals(CHANGED_BUNDLE_BASEDIR_VALUE);
      } else if (bundleBasedir.getName().equals(NEW_BUNDLE_TARGET_NAME)) {
        assert bundleBasedir.getValueContext().equals(NEW_BUNDLE_BASEDIR_CONTEXT);
        assert bundleBasedir.getValueName().equals(NEW_BUNDLE_BASEDIR_VALUE);
      } else {
        assert false
            : "Unexpected bundle basedir [" + bundleBasedir.getName() + "]:" + bundleBasedir;
      }
    }
    if (seen.size() != 2) {
      assert false : "did not see what we expected to see: " + seen;
    }
  }
  public ConfigurationDefinitionUpdateReport updateConfigurationDefinition(
      ConfigurationDefinition newDefinition, ConfigurationDefinition existingDefinition) {

    ConfigurationDefinitionUpdateReport updateReport =
        new ConfigurationDefinitionUpdateReport(existingDefinition);

    /*
     * handle grouped and ungrouped properties separately. for ungrouped, we don't need to care about the group, but
     * for the grouped ones we need to start at group level and then look at the properties. This is done below.
     *
     * First look at the ungrouped ones.
     */

    List<PropertyDefinition> existingPropertyDefinitions =
        existingDefinition.getNonGroupedProperties();
    List<PropertyDefinition> newPropertyDefinitions = newDefinition.getNonGroupedProperties();
    if (existingPropertyDefinitions != null) {
      for (PropertyDefinition newProperty : newPropertyDefinitions) {
        PropertyDefinition existingProp = existingDefinition.get(newProperty.getName());
        if (existingProp != null) {
          log.debug("Updating nonGrouped property [" + existingProp + "]");

          updatePropertyDefinition(existingProp, newProperty);
          updateReport.addUpdatedPropertyDefinition(newProperty);
        } else {
          log.debug("Adding nonGrouped property [" + newProperty + "]");

          existingDefinition.put(newProperty);
          updateReport.addNewPropertyDefinition(newProperty);
        }
      }

      existingDefinition =
          removeNoLongerUsedProperties(
              newDefinition, existingDefinition, existingPropertyDefinitions);

    } else {
      // TODO what if existingDefinitions is null?
      // we probably don't run in here, as the initial persisting is done
      // somewhere else.
    }

    /*
     * Now update / delete contained groups We need to be careful here, as groups are present in PropertyDefinition
     * as "backlink" from PropertyDefinition to group
     */
    List<PropertyGroupDefinition> existingGroups = existingDefinition.getGroupDefinitions();
    List<PropertyGroupDefinition> newGroups = newDefinition.getGroupDefinitions();

    List<PropertyGroupDefinition> toPersist = missingInFirstList(existingGroups, newGroups);
    List<PropertyGroupDefinition> toDelete = missingInFirstList(newGroups, existingGroups);
    List<PropertyGroupDefinition> toUpdate = intersection(existingGroups, newGroups);

    // delete groups no longer present
    for (PropertyGroupDefinition group : toDelete) {
      List<PropertyDefinition> groupedDefinitions =
          existingDefinition.getPropertiesInGroup(group.getName());

      // first look for contained stuff
      for (PropertyDefinition def : groupedDefinitions) {
        log.debug("Removing property [" + def + "] from group [" + group + "]");

        existingPropertyDefinitions.remove(def);
        existingDefinition.getPropertyDefinitions().remove(def.getName());
        def.setPropertyGroupDefinition(null);
        entityManager.remove(def);
      }

      // then remove the definition itself
      log.debug("Removing group [" + group + "]");

      existingGroups.remove(group);
      entityManager.remove(group);
    }

    // update existing groups that stay
    for (PropertyGroupDefinition group : toUpdate) {
      String groupName = group.getName();

      List<PropertyDefinition> newGroupedDefinitions =
          newDefinition.getPropertiesInGroup(groupName);
      for (PropertyDefinition nDef : newGroupedDefinitions) {
        PropertyDefinition existingProperty =
            existingDefinition.getPropertyDefinitions().get(nDef.getName());
        if (existingProperty != null) {
          log.debug("Updating property [" + nDef + "] in group [" + group + "]");

          updatePropertyDefinition(existingProperty, nDef);
          updateReport.addUpdatedPropertyDefinition(nDef);

        } else {
          log.debug("Adding property [" + nDef + "] to group [" + group + "]");

          existingDefinition.put(nDef);
          updateReport.addNewPropertyDefinition(nDef);
        }
      }

      // delete outdated properties of this group
      existingDefinition =
          removeNoLongerUsedProperties(
              newDefinition,
              existingDefinition,
              existingDefinition.getPropertiesInGroup(groupName));
    }

    // persist new groups
    for (PropertyGroupDefinition group : toPersist) {

      // First persist a new group definition and then link the properties to it
      log.debug("Persisting new group [" + group + "]");

      entityManager.persist(group);
      existingGroups.add(group); // iterating over this does not update the underlying crap

      List<PropertyDefinition> defs = newDefinition.getPropertiesInGroup(group.getName());
      Map<String, PropertyDefinition> exPDefs = existingDefinition.getPropertyDefinitions();
      for (PropertyDefinition def : defs) {
        entityManager.persist(def);
        def.setPropertyGroupDefinition(group);
        def.setConfigurationDefinition(existingDefinition);

        if (!exPDefs.containsKey(def.getName())) {
          updateReport.addNewPropertyDefinition(def);
        }

        exPDefs.put(def.getName(), def);
      }
    }

    /*
     * Now work on the templates.
     */
    Map<String, ConfigurationTemplate> existingTemplates = existingDefinition.getTemplates();
    Map<String, ConfigurationTemplate> newTemplates = newDefinition.getTemplates();
    List<String> toRemove = new ArrayList<String>();
    List<String> templatesToUpdate = new ArrayList<String>();

    for (String name : existingTemplates.keySet()) {
      if (newTemplates.containsKey(name)) {
        templatesToUpdate.add(name);
      } else {
        toRemove.add(name);
      }
    }

    for (String name : toRemove) {
      log.debug("Removing template [" + name + "]");

      ConfigurationTemplate template = existingTemplates.remove(name);
      entityManager.remove(template);
    }

    for (String name : templatesToUpdate) {
      log.debug("Updating template [" + name + "]");

      updateTemplate(existingDefinition.getTemplate(name), newTemplates.get(name));
    }

    for (String name : newTemplates.keySet()) {
      // add completely new templates
      if (!existingTemplates.containsKey(name)) {
        log.debug("Adding template [" + name + "]");

        ConfigurationTemplate newTemplate = newTemplates.get(name);

        // we need to set a valid configurationDefinition, where we will live on.
        newTemplate.setConfigurationDefinition(existingDefinition);

        entityManager.persist(newTemplate);
        existingTemplates.put(name, newTemplate);
      }
    }

    return updateReport;
  }