/** Register an InvocationHelper in order to monitor its status changes */
  public void registerInvocationHelper(EndpointReferenceType epr, QName name) {

    logger.info("Registering " + name);

    WorkflowInvocationHelperClient invocationHelper;
    try {

      // Request notification from the InvocationHelper
      invocationHelper = new WorkflowInvocationHelperClient(epr);
      invocationHelper.subscribeWithCallback(
          TimestampedStatus.getTypeDesc().getXmlType(), this); // Monitor status changes
      invocationHelper.subscribeWithCallback(
          InstrumentationRecord.getTypeDesc().getXmlType(),
          this); // Monitor instrumentation reports

      // Store reference to the InvocationHelper internally
      String key = invocationHelper.getEPRString();
      this.stageStatus.put(key, new TimestampedStatus(Status.UNCONFIGURED, 0));
      this.EPR2OperationName.put(key, name.toString());
      this.stagesEPRs.add(epr);

    } catch (MalformedURIException e) {
      e.printStackTrace();
    } catch (RemoteException e) {
      e.printStackTrace();
    } catch (ContainerException e) {
      e.printStackTrace();
    }

    logger.info("END");
  }
  /** Process a received notification */
  public void deliver(List arg0, EndpointReferenceType arg1, Object arg2) {

    org.oasis.wsrf.properties.ResourcePropertyValueChangeNotificationType changeMessage =
        ((org.globus.wsrf.core.notification.ResourcePropertyValueChangeNotificationElementType)
                arg2)
            .getResourcePropertyValueChangeNotification();

    MessageElement actual_property = changeMessage.getNewValue().get_any()[0];
    QName message_qname = actual_property.getQName();
    boolean isTimestampedStatusChange =
        message_qname.equals(TimestampedStatus.getTypeDesc().getXmlType());
    boolean isInstrumentationReport =
        message_qname.equals(InstrumentationRecord.getTypeDesc().getXmlType());
    String stageKey = null;
    try {
      stageKey = new WorkflowInvocationHelperClient(arg1).getEPRString();
    } catch (RemoteException e1) {
      e1.printStackTrace();
    } catch (MalformedURIException e1) {
      e1.printStackTrace();
    }

    logger.debug(
        "[CreateTestWorkflowsStep] Received message of type "
            + message_qname.getLocalPart()
            + " from "
            + stageKey);

    /// Handle status change notifications
    if (isTimestampedStatusChange) {
      TimestampedStatus status = null;
      ;
      try {
        status =
            (TimestampedStatus)
                actual_property.getValueAsType(message_qname, TimestampedStatus.class);
      } catch (Exception e) {
        e.printStackTrace();
      }

      logger.info(
          "Received new status value: "
              + status.getStatus().toString()
              + ':'
              + status.getTimestamp()
              + " from "
              + this.EPR2OperationName.get(stageKey));

      this.isFinishedKey.lock();
      try {

        boolean timestampChanged = false;
        if (this.stageStatus.containsKey(stageKey)) {

          TimestampedStatus curr_status = this.stageStatus.get(stageKey);
          timestampChanged =
              (!curr_status.getStatus().equals(status.getStatus()))
                  && (curr_status.getTimestamp() < status.getTimestamp());

          if (timestampChanged) {

            this.stageStatus.remove(stageKey);
            this.stageStatus.put(stageKey, status);
          }

        } else
          logger.error(
              "[WorkflowInstanceHelperResource] Unrecognized stage notified status change: "
                  + stageKey);

        // Calculate new status value
        if (timestampChanged) {

          // Set new status
          Status new_status = this.calculateStatus();
          TimestampedStatus currTimestampedStatus = this.getTimestampedStatus();
          if (currTimestampedStatus == null) {

            String errMsg =
                "Unable to retrieve current timestamped status from " + this.getEPRString();
            logger.error(errMsg);
            throw new RemoteException(errMsg);
          }

          boolean statusesDiffer = (!new_status.equals(currTimestampedStatus.getStatus()));
          if (statusesDiffer)
            this.localWorkflowInstrumentation.eventEnd(
                currTimestampedStatus.getStatus().toString());

          TimestampedStatus nextStatus = new TimestampedStatus(new_status, ++this.timestamp);

          // Get and store time for the start of the new state
          if (statusesDiffer) {

            this.localWorkflowInstrumentation.eventStart(
                currTimestampedStatus.getStatus().toString());

            // If the local workflow is finished, copy all the collected instrumentation data to a
            // resource property so it is exposed by this resource
            if (nextStatus.getStatus().equals(Status.FINISHED)
                || nextStatus.getStatus().equals(Status.ERROR)) {

              LocalWorkflowInstrumentationRecord localWorkflowInstrumentationRecord =
                  new LocalWorkflowInstrumentationRecord();
              localWorkflowInstrumentationRecord.setIdentifier(this.getEPRString());
              InstrumentationRecord localWorkflowRecord =
                  new InstrumentationRecord(
                      this.localWorkflowInstrumentation.getIdentifier(),
                      this.localWorkflowInstrumentation.retrieveRecordAsArray());
              localWorkflowInstrumentationRecord.setLocalWorkflowRecord(
                  localWorkflowRecord); // Info about local workflow as a whole

              InstrumentationRecord[] stagesRecords =
                  this.stagesInstrumentation.toArray(new InstrumentationRecord[0]);
              localWorkflowInstrumentationRecord.setStagesRecords(
                  stagesRecords); // Info about the InvocationHelpers under this resource
              this.setLocalWorkflowInstrumentationRecord(localWorkflowInstrumentationRecord);
              //							System.out.println("[InstanceHelperResource::deliver] Sending "+
              // stagesRecords.length +" instrumentation reports to Manager"); //DEBUG
            }
          }

          this.setTimestampedStatus(nextStatus);
        }

      } catch (ResourceException e) {
        logger.error(e.getMessage(), e);
        e.printStackTrace();
      } catch (Exception e) {
        logger.error(e.getMessage(), e);
        e.printStackTrace();
      } finally {
        this.isFinishedKey.unlock();
      }
    }

    /// Handle instrumentation reports
    else if (isInstrumentationReport) {

      InstrumentationRecord instrumentation_data = null;
      try {
        instrumentation_data =
            (InstrumentationRecord)
                actual_property.getValueAsType(message_qname, InstrumentationRecord.class);
      } catch (Exception e) {
        e.printStackTrace();
      }

      // Store the received measurements
      this.stagesInstrumentation.add(instrumentation_data);

      // Update the instrumentation data exposed by this resource
      LocalWorkflowInstrumentationRecord localWorkflowInstrumentationRecord =
          new LocalWorkflowInstrumentationRecord();
      localWorkflowInstrumentationRecord.setIdentifier(this.getEPRString());
      InstrumentationRecord localWorkflowRecord =
          new InstrumentationRecord(
              this.localWorkflowInstrumentation.getIdentifier(),
              this.localWorkflowInstrumentation.retrieveRecordAsArray());
      localWorkflowInstrumentationRecord.setLocalWorkflowRecord(
          localWorkflowRecord); // Info about local workflow as a whole

      InstrumentationRecord[] stagesRecords =
          this.stagesInstrumentation.toArray(new InstrumentationRecord[0]);
      localWorkflowInstrumentationRecord.setStagesRecords(
          stagesRecords); // Info about the InvocationHelpers under this resource
      //			try {
      //				this.setLocalWorkflowInstrumentationRecord(localWorkflowInstrumentationRecord);
      //			} catch (ResourceException e) {
      //				logger.error(e.getMessage(), e);
      //				e.printStackTrace();
      //			}

      // Log the instrumentation data
      logger.info(
          "Instrumentation data for InvocationHelper identified by "
              + instrumentation_data.getIdentifier());
      //			System.out.println("[InstanceHelperResource::deliver] "+ stagesRecords.length +" reports
      // stored so far"); //DEBUG
      //			System.out.println("[InstanceHelperResource::deliver] Instrumentation data for
      // InvocationHelper identified by "+ instrumentation_data.getIdentifier()); //DEBUG
      final int numMeasurements = instrumentation_data.getRecords().length;
      for (int i = 0; i < numMeasurements; i++) {

        EventTimePeriod curr_measurement = instrumentation_data.getRecords(i);
        long elapsedTimeInNanos = curr_measurement.getEndTime() - curr_measurement.getStartTime();
        logger.info(
            curr_measurement.getEvent() + ": " + (elapsedTimeInNanos / 1000000) + " miliseconds");
        //				System.out.println(curr_measurement.getEvent()+ ": "+ (elapsedTimeInNanos/1000000) + "
        // miliseconds"); //DEBUG
      }
      logger.info("-------------------------------------------------------");
      //			System.out.println("-------------------------------------------------------"); //DEBUG

    }
  }