/**
   * Creates an Event for the outputDigitalObject that records properties on its predecessor and on
   * the duration the migration lasted.
   *
   * @param migrInput
   * @param migrOutput
   * @param migrationDuration
   * @return
   */
  private Event buildMigrationOutputEvent(
      DigitalObject migrInput,
      List<Parameter> migrParameters,
      long startTime,
      long duration,
      ServiceDescription serDescr,
      String serEndpoint) {

    List<Property> propList = new ArrayList<Property>();
    Property pMigrDuration =
        new Property.Builder(URI.create("planets://service/migration/performance/duration"))
            .name("duration of migration")
            .value(duration + "")
            .description("Measurement of the Migration.Action taken on the batch processor's end")
            .unit("time in millis")
            .type("service characteristic")
            .build();
    Property pSerParams =
        new Property.Builder(URI.create("planets://service/migration/configuration"))
            .name("service configuration")
            .value(migrParameters.toString())
            .description("Record of the specific parameter configuration that were applied")
            .type("service characteristic")
            .build();
    Property pOldContentLink =
        new Property.Builder(URI.create("planets://data/predecessor"))
            .name("prececessor reference")
            .value(migrInput.getPermanentUri() + "")
            .description(
                "A reference to the predecessor object the migration result was derived from.")
            .type("directional pointer")
            .build();

    if (serDescr != null && serEndpoint != null) {
      Property serDesc =
          new Property.Builder(URI.create("planets://service/migration/description"))
              .name("service description")
              .value("endpoint: " + serEndpoint + " service description: " + serDescr.toString())
              .description("Information about the tool and the service that has been called")
              .type("service characteristic")
              .build();
      propList.add(serDesc);
    }

    propList.add(pMigrDuration);
    propList.add(pSerParams);
    propList.add(pOldContentLink);

    Event migrateEvent =
        new Event(
            "planets://action/service/migrate",
            startTime + "",
            new Long(duration).doubleValue(),
            wfi.getWEEAgent(),
            propList);
    return migrateEvent;
  }
  /**
   * @return an URI to the resulting digital object's data registry reference
   * @throws Exception
   */
  public URI runMigration() throws Exception {

    // an object used for documenting the results of a service action
    // document the service type and start-time
    WorkflowResultItem wfResultItem;
    if (endOfRoundtripp) {
      wfResultItem =
          new WorkflowResultItem(
              pedigreeDigoRef,
              WorkflowResultItem.SERVICE_ACTION_FINAL_MIGRATION,
              System.currentTimeMillis(),
              wfi.getWorkflowReportingLogger());
    } else {
      wfResultItem =
          new WorkflowResultItem(
              pedigreeDigoRef,
              WorkflowResultItem.SERVICE_ACTION_MIGRATION,
              System.currentTimeMillis(),
              wfi.getWorkflowReportingLogger());
    }
    wfi.addWFResultItem(wfResultItem);

    try {
      // get all parameters that were added in the configuration file
      List<Parameter> parameterList;
      if (wfi.getServiceCallConfigs(migrationService) != null) {
        parameterList = wfi.getServiceCallConfigs(migrationService).getAllPropertiesAsParameters();
      } else {
        parameterList = new ArrayList<Parameter>();
      }
      wfResultItem.setServiceParameters(parameterList);

      // get from config: migrate_to_fmt for this service
      URI migrateToURI, migrateFromURI;
      if (this.outputFormat != null) {
        // e.g. when using a identification prior to chose the output format
        migrateToURI = this.outputFormat;
      } else {
        // get the ones from the ServiceCallConfigs
        migrateToURI =
            wfi.getServiceCallConfigs(migrationService)
                .getPropertyAsURI(WorkflowTemplate.SER_PARAM_MIGRATE_TO);
      }
      wfResultItem.addLogInfo("set migrate to: " + migrateToURI);

      if (this.inputFormat != null) {
        // e.g. when using a identification prior to chose the input format
        migrateFromURI = this.inputFormat;
      } else {
        // get the ones from the ServiceCallConfigs
        migrateFromURI =
            wfi.getServiceCallConfigs(migrationService)
                .getPropertyAsURI(WorkflowTemplate.SER_PARAM_MIGRATE_FROM);
      }
      wfResultItem.addLogInfo("set migrate from: " + migrateFromURI);

      if ((migrateToURI == null) && (migrateFromURI == null)) {
        String err = "No parameter for: 'migrate_to/from_filetype' specified";
        wfResultItem.addLogInfo(err);
        throw new Exception(err);
      }

      // document
      wfResultItem.setInputDigitalObjectRef(digOToMigrateRef);
      wfResultItem.setServiceParameters(parameterList);
      wfResultItem.setStartTime(System.currentTimeMillis());
      // document the endpoint if available - retrieve from
      // WorkflowContext
      String endpoint =
          wfi.getWorkflowContext()
              .getContextObject(
                  migrationService,
                  WorkflowContext.Property_ServiceEndpoint,
                  java.lang.String.class);
      if (endpoint != null) {
        wfResultItem.setServiceEndpoint(new URL(endpoint));
      }
      ServiceDescription serDescr = migrationService.describe();
      wfResultItem.setServiceDescription(serDescr);

      // retrieve the actual digital object
      DigitalObject digoToMigrate = dataRegistry.retrieve(digOToMigrateRef);

      // now call the migration
      MigrateResult migrateResult =
          migrationService.migrate(digoToMigrate, migrateFromURI, migrateToURI, parameterList);

      // document
      wfResultItem.setEndTime(System.currentTimeMillis());
      ServiceReport report = migrateResult.getReport();
      // report service status and type
      wfResultItem.setServiceReport(report);
      if (report.getType() == Type.ERROR) {
        String s = "Service execution failed: " + report.getMessage();
        wfResultItem.addLogInfo(s);
        throw new Exception(s);
      }

      DigitalObject migOutput = migrateResult.getDigitalObject();

      // add Migration Event to DigitalObject
      Event migrEvent =
          buildMigrationOutputEvent(
              digoToMigrate,
              parameterList,
              wfResultItem.getStartTime(),
              wfResultItem.getDuration(),
              serDescr,
              endpoint);

      List<Event> lEvents = migOutput.getEvents();
      lEvents.add(migrEvent);

      // create an updated DigitalObject containing the Migration-Event
      // note, as the FileSystemDigoManager requires a title != null, we'll use a random one here
      String title = (migOutput.getTitle() == null) ? UUID.randomUUID() + "" : migOutput.getTitle();
      URI suggStorageURI =
          helperCreateDOMURIWithFileExtension(
              wfi.getWorklowInstanceID(), digOToMigrateRef, dataRepositoryID, migrateToURI);
      DigitalObject digoUpdated =
          new DigitalObject.Builder(migOutput.getContent())
              .title(title)
              .permanentUri(migOutput.getPermanentUri())
              .manifestationOf(migOutput.getManifestationOf())
              .format(migOutput.getFormat())
              .metadata((Metadata[]) migOutput.getMetadata().toArray(new Metadata[0]))
              .events((Event[]) lEvents.toArray(new Event[0]))
              .build();

      // decide in which repository to store the received DigitalObject
      URI digoRef;
      if (this.dataRepositoryID != null) {
        digoRef = wfi.storeDigitalObjectInRepository(suggStorageURI, digoUpdated, dataRepositoryID);
      } else {
        // in this case use the default data registry manager location for persisting
        digoRef = wfi.storeDigitalObject(digoUpdated);
      }

      wfResultItem.addLogInfo("storing digital object with permanent URI: " + digoRef);
      wfResultItem.setOutputDigitalObjectRef(digoRef);
      wfResultItem.addLogInfo("migration completed");

      return digoRef;

    } catch (Exception e) {
      wfResultItem.addLogInfo("migration failed " + e);
      throw e;
    }
  }