/**
   * Process the NSI terminate message and dispatch to individual NSA/NRM involved in the path
   * segments.
   *
   * @param terminateMessage Our generic message holding the NSI reservation message.
   */
  private void process(NsaMessage terminateMessage) {

    // We have already verified this field is present and not null.
    NsaSecurityContext nsaSecurityContext = terminateMessage.getNsaSecurityContext();
    GenericRequestType terminate = (GenericRequestType) terminateMessage.getPayload();
    String correlationId = terminateMessage.getCorrelationId();
    String replyTo = terminateMessage.getReplyTo();
    String requesterNSA = terminateMessage.getRequesterNSA();
    String providerNSA = terminateMessage.getProviderNSA();
    String connectionId = terminateMessage.getConnectionId();

    // Log the incoming message information.
    logger.info(
        "TerminateProviderActor.process: Incoming message ["
            + nsaSecurityContext.getUserName()
            + " @ "
            + nsaSecurityContext.getRemoteAddr()
            + ", requesterNSA="
            + requesterNSA
            + ", correlationId="
            + correlationId
            + "]");
    logger.info(MessageDump.dump(GenericRequestType.class, terminate));

    /*
     * TODO: In the next iteration we can apply any needed access control
     * based on the NSA request the resource.  In general, we will only
     * want to give them access to the endpoints within their associated
     * groups.  Whether we trust an NSA 100% and only enforce user level
     * access control will need to be determined.
     */

    // Get a reference to the related data managers.
    DataManager dataManager = DataManager.getInstance();
    StateMachineManager stateMachineManager = dataManager.getStateMachineManager();
    CastingDirector castingDirector = dataManager.getCastingDirector();
    NsaConfigurationManager nsaConfiguration = dataManager.getNsaConfigurationManager();
    PendingOperationManager pendingOperations = dataManager.getPendingOperationManager();
    TopologyFactory topology = dataManager.getTopologyFactory();

    /*
     * If we find any error messages from this point forward we need to
     * send a terminateFailed message back to the requesterNSA.
     */
    StateMachine machine = null;
    try {
      /*
       * Verify we have an existing reservation with the same
       * connectionId.
       */
      machine = stateMachineManager.failOnNoStateMachine(connectionId);

      /*
       * We are always in the correct state to get a terminate request.
       */

      // Parse the session security and assign to state machine.
      SessionSecurity sessionSecurity = new SessionSecurity();
      sessionSecurity.parseSessionSecurityAttr(terminate.getSessionSecurityAttr());

      /*
       * TODO: compare security to determine if sending NSA/User can
       * manipulate this state machine.  We will use the user's NSI role
       * to do this in the future.
       */
    } catch (ServiceException nsi) {
      /*
       * Send this to a web services actor for delivery of a
       * ReservationFailed message.
       */
      logger.info(
          "TerminateProviderActor.process: Sending terminateFailed message " + nsi.toString());

      // Leave the state where it is for now.

      NsaMessage failedMessage = new NsaMessage();
      failedMessage.setCorrelationId(correlationId);
      failedMessage.setReplyTo(replyTo);
      failedMessage.setRequesterNSA(requesterNSA);
      failedMessage.setProviderNSA(providerNSA);
      failedMessage.setConnectionId(connectionId);
      if (machine != null) {
        failedMessage.setConnectionState(machine.getCurrentState());
        failedMessage.setGlobalReservationId(machine.getGlobalReservationId());
      }

      failedMessage.setMessageType(NsaMessage.MessageType.terminateFailedProvider);
      failedMessage.setPayload(nsi.getFaultInfo());

      // Route this message to the appropriate actor for processing.
      castingDirector.send(failedMessage);
      logger.info(
          "TerminateProviderActor.process: terminateFailed message issued to actor so returning");
      return;
    }

    /*
     * If we were already in the TERMINATING or TERMINATED state then we
     * can send back a terminateConfirmed message immediately and leave the
     * state machine in the current state.
     *
     * TODO: should we consider sending this terminate request down the
     * tree if in the TERMINATING state already?  Will this help with error
     * recovery?
     */
    if (machine.getCurrentState() == ConnectionStateType.TERMINATING
        || machine.getCurrentState() == ConnectionStateType.TERMINATED
        || machine.getCurrentState() == ConnectionStateType.CLEANING) {
      NsaMessage childMessage = new NsaMessage();
      childMessage.setCorrelationId(correlationId);
      childMessage.setConnectionId(connectionId);
      childMessage.setConnectionState(machine.getCurrentState());
      childMessage.setGlobalReservationId(machine.getGlobalReservationId());
      childMessage.setMessageType(NsaMessage.MessageType.terminateConfirmedProvider);
      childMessage.setProviderNSA(providerNSA);
      childMessage.setRequesterNSA(requesterNSA);
      childMessage.setReplyTo(replyTo);

      castingDirector.send(childMessage);
      logger.info(
          "TerminateProviderActor.process: terminateConfirmed message issued to actor for processing");
    }

    /*
     * Transition state machine to Terminating state before issuing
     * individual termnate messages to NSA.
     */
    machine.setCurrentState(ConnectionStateType.TERMINATING);

    /*
     * For all path segments in the list that are managed by the local
     * NSA we will send a terminateRequest to local NSA actors, while path
     * segments on remote NSA will be sent to remote NSA actors via the
     * NSI protocol.
     */
    List<PathSegment> pathList = machine.getRoutePathList();

    for (PathSegment segment : pathList) {
      logger.info(
          "TerminateProviderActor.process: Routing terminate requests to NSA = "
              + segment.getManagingNsaURN()
              + ", NsaType = "
              + segment.getNsaType().name()
              + ", childConnectionId = "
              + segment.getChildConnectionId()
              + ", sourceSTP = "
              + segment.getSourceStpURN()
              + ", destSTP = "
              + segment.getDestStpURN());

      TerminateMessage termMessage = new TerminateMessage();

      NsaMessage childMessage =
          termMessage.getRequestMessage(
              correlationId, machine.getGlobalReservationId(), replyTo, providerNSA, segment);

      // Give this to the casting director to find us an actor.
      castingDirector.send(childMessage);
      logger.info(
          "TerminateProviderActor.process: terminateRequest message issued to actor for processing");
    }

    // We are done - all other processing is handled by other actors.
    logger.info(
        "TerminateProviderActor.process: terminateRequests issued to children NSA so returning with state="
            + machine.getCurrentState());
  }