ExtensionMigratorBase is an abstract class to facilitate migration of extensions from one version of the extension to the next. Extension developers create subclasses of this class and include the class in their extension jar. The class name and package are set in the 'migratorClass' attribute of the ExtensionPackage XML in their extension metadata.

 

Below is an example of Extension Migrator which performs two migrations:

  1. 1. If upgrading from 1.0, exclude a particular entity.
  2. 2. If upgrading from 1.1 or 1.0, change a configuration setting name.

 

package com.thingworx.testextension;

import ch.qos.logback.classic.Logger;
import org.apache.commons.lang.StringUtils;

import com.thingworx.contentmanagement.ImportedEntityCollection;
import com.thingworx.migration.ExtensionMigratorBase;
import com.thingworx.logging.LogUtilities;
import com.thingworx.entities.RootEntity;
import com.thingworx.entities.collections.RootEntityCollection;
import com.thingworx.relationships.RelationshipTypes.ThingworxRelationshipTypes;
import com.thingworx.types.ConfigurationTable;
import com.thingworx.types.collections.ValueCollection;

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Arrays;
import java.util.HashSet;

public class TestExtensionMigrator extends ExtensionMigratorBase {

private static final Logger LOGGER = LogUtilities.getInstance().getApplicationLogger(TestExtensionMigrator.class);

  public static final String[] EXCLUDED_ENTITIES_V1_1_VALUES = new String[] { "TW.TEST.Thing1", "TW.TEST.Mashup1" };
  public static final Set<String> EXCLUDED_ENTITIES_V1_1_SET = new HashSet<String>(Arrays.asList(EXCLUDED_ENTITIES_V1_1_VALUES));
  public static final String OLD_CONFIG_SETTING_NAME = "TestMashupOriginal";

  @Override
public void migrate(ImportedEntityCollection importedEntityCollection) throws Exception {
  // Ignore patch version numbers
  String fromVersion = getFromVersion().getEntityMajor() + "." + getFromVersion().getEntityMinor();
  switch (fromVersion) {
  case "1.0":
  LOGGER.info("TestExtension v1.0 -> v1.1 migration is being performed");
  migrateFromV1_0ToV1_1(importedEntityCollection);
  // purposefully fall through

  case "1.1":
  LOGGER.info("TestExtension v1.1 -> v2.0 migration is being performed");
  migrateFromV1_1ToV2_0(importedEntityCollection);
  break;

  default:
  LOGGER.warn("Unknown source version of MyExtension '{}'; no migration will be performed",
  fromVersion);
  break;
  }
}

  /**
  * Do migration from v1.0 to v1.1 - exclude entities that have been removed in version 1.1
  *
  * @param importedEntityCollection Entity collection
  * @throws Exception exception
  */
  public static void migrateFromV1_0ToV1_1(ImportedEntityCollection importedEntityCollection) throws Exception {
  removeExcludedEntities(importedEntityCollection, EXCLUDED_ENTITIES_V1_1_SET);
  }

  /**
  * Do migration from v1.1 to v2.0
  *
  * @param importedEntityCollection Entity collection
  * @throws Exception exception
  */
  public static void migrateFromV1_1ToV2_0(ImportedEntityCollection importedEntityCollection) throws Exception {
  // Stub
  }

  /**
  * Remove excluded entities from the collection of imported entities.
  * 
  * @param importedEntityCollection collection of entities being imported
  * @param excludedEntities set of entities to exclude
  */
  private static void removeExcludedEntities(ImportedEntityCollection importedEntityCollection, Set<String> excludedEntities) {
  Iterator<Entry<ThingworxRelationshipTypes, RootEntityCollection>> entityCollectionIterator =
  importedEntityCollection.entrySet().iterator();
  while (entityCollectionIterator.hasNext()) {
  Entry<ThingworxRelationshipTypes, RootEntityCollection> entityCollection = entityCollectionIterator.next();

  Iterator<RootEntity> entityIterator = entityCollection.getValue().values().iterator();
  while (entityIterator.hasNext()) {
  RootEntity entity = entityIterator.next();

  if (excludedEntities.contains(entity.getName())) {
  LOGGER.warn("Excluding entity \"" + entity.getName() + "\" from import.");
  entityIterator.remove();
  continue;
  }
  }
  }
  }

  /**
  * Update a configuration table setting name
  * 
  * @param things - a collection of things
  * @param configTableName - the configuration table name 
  * @param oldSettingName - the old configuration setting name
  * @param newSettingName - the new configuration setting name
  * @throws Exception
  */
  private static void updateConfigurationSettingName(RootEntityCollection things, String configTableName,
  String oldSettingName, String newSettingName) throws Exception {
  if (things != null) {
  for (RootEntity entity : things.values()) {
  try {
  ConfigurationTable configTable = entity.getConfigurationTable(configTableName);
  if (configTable != null) {
  ValueCollection row = configTable.getFirstRow();
  if (StringUtils.isNotBlank(row.getStringValue(oldSettingName))) {
  String oldSettingValue = row.getStringValue(oldSettingName);
  row.remove(oldSettingName);
  row.SetStringValue(newSettingName, oldSettingValue);
  }
  }
  } catch (Exception e) {
  LOGGER.warn("Error trying to update thing configuration. " + e.getMessage());
  }
  }
  }
  }

}