/* (non-Javadoc)
   * @see org.cagrid.workflow.helper.instance.service.globus.resource.WorkflowInstanceHelperResourceBase#remove()
   */
  public void remove() throws ResourceException {

    logger.info("Destroying InstanceHelper resource");

    super.remove();

    // Destroy each InvocationHelper associated with this resource
    Iterator<EndpointReferenceType> stages_iter = this.stagesEPRs.iterator();
    while (stages_iter.hasNext()) {

      EndpointReferenceType curr_epr = stages_iter.next();
      WorkflowInvocationHelperClient curr_client;
      try {
        curr_client = new WorkflowInvocationHelperClient(curr_epr);

        String stageID = curr_client.getEPRString();
        this.stageStatus.remove(stageID); // Stop monitoring current stage's status changes

        curr_client.destroy(); // Destroy the resource associated with current stage
      } catch (MalformedURIException e) {
        e.printStackTrace();
      } catch (RemoteException e) {
        e.printStackTrace();
      }

      logger.info("Done");
      return;
    }
  }
  /** 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");
  }
  /**
   * Register a service-operation either as secure or unsecure
   *
   * @param serviceOperationEPR EndpointReference for the target service-operation
   * @param isSecure Must be true if the service-operation is secure and false otherwise
   */
  public void setIsInvocationHelperSecure(EndpointReference serviceOperationEPR, boolean isSecure) {

    WorkflowInvocationHelperClient client = null;
    String EPRStr = null;
    try {
      client = new WorkflowInvocationHelperClient(serviceOperationEPR);
      EPRStr = client.getEPRString();

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

    if (isSecure) {

      logger.info("Creating locks");

      // Initializes mutual exclusion key that denotes the availability of the delegated credential
      Lock key = new ReentrantLock();
      Condition credentialAvailability = key.newCondition();
      this.serviceConditionVariable.put(EPRStr, credentialAvailability);
      this.servicelLock.put(EPRStr, key);

      this.printCredentials();

    } else {

      logger.info("Adding service to unsecure list");

      this.unsecureInvocations.add(EPRStr);
    }
    logger.info("Done");
  }
  /**
   * Associate an InvocationHelper with a GlobusCredential, removing previous associations if any
   *
   * @param serviceOperationEPR EPR of the InvocationHelper
   * @param proxyEPR the credential to be used by the InvocationHelper
   */
  public void replaceCredential(EndpointReference serviceOperationEPR, EndpointReference proxyEPR) {

    logger.info("BEGIN");

    String serviceOperationEPR_str = null;
    try {
      WorkflowInvocationHelperClient client =
          new WorkflowInvocationHelperClient(serviceOperationEPR);
      serviceOperationEPR_str = client.getEPRString();

    } catch (MalformedURIException e1) {
      e1.printStackTrace();
    } catch (RemoteException e1) {
      e1.printStackTrace();
    }

    // Initializes mutual exclusion key that denotes the availability of the delegated credential
    Lock key;
    Condition credentialAvailability;
    if (this.serviceConditionVariable.containsKey(serviceOperationEPR_str)) {

      key = this.servicelLock.get(serviceOperationEPR_str);
      credentialAvailability = this.serviceConditionVariable.get(serviceOperationEPR_str);
    } else {

      key = new ReentrantLock();
      credentialAvailability = key.newCondition();
      this.servicelLock.put(serviceOperationEPR_str, key);
      this.serviceConditionVariable.put(serviceOperationEPR_str, credentialAvailability);
    }

    key.lock();
    try {

      // Delete old credential (if any) from the associations
      boolean serviceExists = this.servicesCredentials.containsKey(serviceOperationEPR_str);
      if (serviceExists) {

        this.servicesCredentials.remove(serviceOperationEPR_str);
        this.eprCredential.remove(proxyEPR);
      }

      // Retrieve the delegated credential
      GlobusCredential credential = null;

      if (proxyEPR != null) {
        try {

          credential = CredentialHandlingUtil.getDelegatedCredential(proxyEPR);

        } catch (Exception e) {
          System.err.println("Error retrieving delegated credential");
          e.printStackTrace();
        }
      }

      // Add the new credential
      this.servicesCredentials.put(serviceOperationEPR_str, credential);
      this.eprCredential.put(proxyEPR, credential);

      // Signal any waiting threads that the credential is available
      credentialAvailability.signalAll();

    } finally {
      key.unlock();
    }

    logger.info("END");

    return;
  }
  /**
   * Retrieve the GlobusCredential associated with a service-operation. If credential retrieval is
   * pending, caller is blocked until the credential becomes available.
   *
   * @param serviceOperationEPR EPR of the InvocationHelper
   * @return The associated GlobusCredential, or null if no such association does exist
   * @throws RemoteException
   */
  public GlobusCredential getCredential(EndpointReference serviceOperationEPR)
      throws RemoteException {

    this.printCredentials();

    String eprStr = null;
    try {
      WorkflowInvocationHelperClient client =
          new WorkflowInvocationHelperClient(serviceOperationEPR);
      eprStr = client.getEPRString();

    } catch (MalformedURIException e1) {
      e1.printStackTrace();
    } catch (RemoteException e1) {
      e1.printStackTrace();
    }

    GlobusCredential credential = null;
    boolean serviceIsUnsecure = this.unsecureInvocations.contains(eprStr);

    if (serviceIsUnsecure) {
      logger.info(
          "[WorkflowInstanceHelperResource.getCredential] Service is unsecure, returning null credential");
      return null;
    } else {

      logger.info("[WorkflowInstanceHelperResource.getCredential] Service is secure");

      Lock key = this.servicelLock.get(eprStr);
      Condition credentialAvailability = this.serviceConditionVariable.get(eprStr);

      // Mutual exclusive access session: we can only return the credential if it was already
      // retrieved from the
      // Credential Delegation Service
      key.lock();
      try {

        printCredentials();

        // If credential is unavailable, block until it is available
        boolean credentialIsNotSet = (!this.servicesCredentials.containsKey(eprStr));

        if (credentialIsNotSet) {

          boolean explicitlyAwaken = credentialAvailability.await(60, TimeUnit.SECONDS);
          if (!explicitlyAwaken)
            throw new RemoteException(
                "[WorkflowInstanceHelperResource.getCredential] Couldn't retrieve credential. "
                    + "Was it registered into the InstanceHelper? Is the EndpointReference for the credential proxy valid?");
        }
        credential = this.servicesCredentials.get(eprStr);

        logger.info("[getCredential] Retrieved credential: " + credential.getIdentity());

      } catch (InterruptedException e) {
        logger.error("[getCredential] Error retrieving credential");
        e.printStackTrace();
      } finally {
        key.unlock();
      }

      logger.info(
          "Returning credential: " + ((credential != null) ? credential.getIdentity() : null));
      return credential;
    }
  }