/**
   * Get all configuration for a specified server type placed in repository. This would search the
   * servicemgr category inside repository and apply XPath to figure out configuration files
   * belonging to a specific server type.
   *
   * @param type
   * @return List containing file names.
   * @throws ProcessingException
   */
  public static List<String> getConfigCategory(String type) throws ProcessingException {

    NVPair[] pairs = null;
    try {
      pairs =
          RepositoryManager.getInstance()
              .listMetaData(ServerManagerFactory.MGR_CONFIG_CATEGORY, false, XML_FILE_SUFFIX);

      if (pairs == null || pairs.length == 0) {
        Debug.log(
            Debug.NORMAL_STATUS,
            "No XML file found in repository folder ["
                + ServerManagerFactory.MGR_CONFIG_CATEGORY
                + "]");

        return null;
      }
    } catch (Exception e) {
      Debug.error(
          "An exception occured while listing xml files from repository folder ["
              + ServerManagerFactory.MGR_CONFIG_CATEGORY
              + "]");

      Debug.logStackTrace(e);

      throw new ProcessingException(
          "An exception occured while listing xml files from repository folder  ["
              + ServerManagerFactory.MGR_CONFIG_CATEGORY
              + "]");
    }

    List<String> cfgFileLst = new ArrayList<String>();
    String fileNm = null;
    try {
      for (int j = 0; j < pairs.length; j++) {
        fileNm = pairs[j].name;
        Document document = getDocument(fileNm);
        ParsedXPath parseXpath = new ParsedXPath(serverType2Xpath.get(type));

        if (Debug.isLevelEnabled(Debug.NORMAL_STATUS))
          Debug.log(Debug.NORMAL_STATUS, " --> Evaluating file :" + fileNm + " for type :" + type);

        if (parseXpath.getBooleanValue(document.getDocumentElement())) cfgFileLst.add(fileNm);
      }

      if (Debug.isLevelEnabled(Debug.NORMAL_STATUS))
        Debug.log(
            Debug.NORMAL_STATUS,
            " --> returning list of files for type[" + type + "] : " + cfgFileLst);

      return cfgFileLst;
    } catch (Exception e) {
      Debug.log(
          Debug.NORMAL_STATUS,
          "Failed to parse and evaluate XPath on file :" + fileNm + "\n" + Debug.getStackTrace(e));

      throw new ProcessingException(
          "Failed to parse and evaluate XPath on file :" + fileNm + "\n" + Debug.getStackTrace(e));
    }
  }
  /**
   * Starts all the configured Poll Comm Servers
   *
   * @throws ProcessingException
   */
  public void startAll() throws ProcessingException {
    if (Debug.isLevelEnabled(Debug.NORMAL_STATUS))
      Debug.log(Debug.NORMAL_STATUS, "Starting All Comm Server of type = " + getType());

    Iterator iter = commServerConfMap.keySet().iterator();
    while (iter.hasNext()) {
      String id = (String) iter.next();
      CommServerConf conf = commServerConfMap.get(id);
      if (conf.isStarted()) {
        Notification notification = null;
        Map params = Collections.singletonMap(ATTR_COMM_SERVER_ID, id);
        try {
          start(params);
          notification =
              new Notification("Started Comm Server of type = " + getType() + " id : " + id);
        } catch (Exception e) {
          Debug.log(
              Debug.MSG_ERROR,
              "Could not start Comm Server of type = " + getType() + " id : " + id);
          Debug.error(Debug.getStackTrace(e));
          notification =
              new Notification(
                  "Error starting Comm Server of type = " + getType() + " id : " + id, e);
        }
        notificationHandler.handle(notification);
      }
    }
    if (Debug.isLevelEnabled(Debug.NORMAL_STATUS))
      Debug.log(Debug.NORMAL_STATUS, "done starting Comm Servers of type = " + getType());
  }
  public java.lang.String processSync(java.lang.String header, java.lang.String request)
      throws java.rmi.RemoteException {
    try {
      WSRequestHandler rh = new WSRequestHandler();
      rh.setInstanceId((String) (ServiceInitializer.getInitParameters().get(GWS_SERVER_NAME)));

      return (rh.processSync(header, request));
    } catch (Throwable t) {
      Debug.error(t.toString());

      throw new RemoteException("WSRequest-handler processSync() call failed", t);
    }
  }
  /**
   * Add the event to the end of queue.
   *
   * @param event The event to add to the queue.
   * @exception FrameworkException Thrown on errors.
   */
  public void add(Event event) throws FrameworkException {
    Debug.log(Debug.MSG_STATUS, "QUEUE OPERATION: Adding event to database queue ...");

    Connection dbConn = null;

    PreparedStatement ps = null;

    long startTime = -1;

    if (Debug.isLevelEnabled(Debug.BENCHMARK)) startTime = System.currentTimeMillis();

    try {
      event.id = PersistentSequence.getNextSequenceValue(SEQUENCE_NAME);

      dbConn = DBConnectionPool.getInstance().acquireConnection();

      event.arrivalTime = new java.sql.Timestamp(System.currentTimeMillis());

      if (Debug.isLevelEnabled(Debug.DB_DATA))
        Debug.log(Debug.DB_DATA, "\n" + LINE + "\nExecuting SQL:\n" + INSERT_EVENT_SQL);

      if (Debug.isLevelEnabled(Debug.DB_DATA))
        Debug.log(Debug.DB_DATA, "Event being inserted into database:\n" + event.describe());

      if (Debug.isLevelEnabled(Debug.MSG_DATA))
        Debug.log(Debug.MSG_DATA, "Event contents:\n" + event.message);

      ps = dbConn.prepareStatement(INSERT_EVENT_SQL);

      ps.setString(1, event.channelName);
      ps.setInt(2, event.id);

      DBLOBUtils.setCLOB(ps, 3, event.message);

      ps.setTimestamp(4, event.arrivalTime);

      int numRows = ps.executeUpdate();

      if (numRows != 1) {
        String errMsg =
            "Execution of SQL statement ["
                + INSERT_EVENT_SQL
                + "] affected ["
                + numRows
                + "] rows.";

        Debug.error(errMsg);

        throw new FrameworkException(errMsg);
      }

      DBConnectionPool.getInstance().commit(dbConn);

      if (Debug.isLevelEnabled(Debug.DB_DATA))
        Debug.log(Debug.DB_DATA, "Successfully committed SQL operation.\n" + LINE);

      // NOTE: We don't add the item just inserted into the database into the in-memory
      // queue, as we want it loaded by the separate dequeueing thread.
    } catch (SQLException sqle) {
      throw new DatabaseException(
          "ERROR: Could not execute SQL statement:\n" + DBInterface.getSQLErrorMessage(sqle));
    } catch (Exception e) {
      throw new DatabaseException("ERROR: Could not execute SQL statement:\n" + e.toString());
    } finally {
      releaseDatabaseResources(dbConn, ps);

      if (Debug.isLevelEnabled(Debug.BENCHMARK) && (startTime > 0)) {
        long stopTime = System.currentTimeMillis();

        Debug.log(
            Debug.BENCHMARK,
            "ELAPSED TIME ["
                + (stopTime - startTime)
                + "] msec:  "
                + "SQL: Time to insert event into PersistentEvent database table.");
      }
    }
  }
  /**
   * Update the given event as indicated.
   *
   * @param event The event to update.
   * @param eventStatus The event delivery status.
   * @exception FrameworkException Thrown on errors.
   */
  public void update(Event event, EventStatus eventStatus) throws FrameworkException {
    if (Debug.isLevelEnabled(Debug.MSG_STATUS))
      Debug.log(
          Debug.MSG_STATUS,
          "QUEUE OPERATION: Updating database queue using event status ["
              + eventStatus.name
              + "] ...");

    // If no consumers were available, the event delivery wasn't attempted so leave
    // the queue in its current state.
    if (eventStatus == EventStatus.NO_CONSUMERS_AVAILABLE) {
      Debug.log(
          Debug.MSG_STATUS, "Skipping queue update, as no consumers were available to process it.");

      return;
    }

    Connection dbConn = null;

    PreparedStatement ps = null;

    long startTime = -1;

    if (Debug.isLevelEnabled(Debug.BENCHMARK)) startTime = System.currentTimeMillis();

    try {
      dbConn = DBConnectionPool.getInstance().acquireConnection();

      if (eventStatus == EventStatus.DELIVERY_SUCCESSFUL) {
        // If the event was successfully delivered, update status in database to delivered.
        if (Debug.isLevelEnabled(Debug.DB_DATA))
          Debug.log(Debug.DB_DATA, "\n" + LINE + "\nExecuting SQL:\n" + UPDATE_EVENT_SUCCESS_SQL);

        ps = dbConn.prepareStatement(UPDATE_EVENT_SUCCESS_SQL);

        java.sql.Timestamp ts = new java.sql.Timestamp(System.currentTimeMillis());

        ps.setTimestamp(1, ts);
        ps.setString(2, event.channelName);
        ps.setInt(3, event.id);
      } else if (eventStatus == EventStatus.DELIVERY_FAILED) {
        // If the event delivery failed, we mark it as failed in the database.
        if (Debug.isLevelEnabled(Debug.DB_DATA))
          Debug.log(Debug.DB_DATA, "\n" + LINE + "\nExecuting SQL:\n" + UPDATE_EVENT_ERROR_SQL);

        // Truncate error message if it's larger than database column.
        if ((event.lastErrorMessage != null)
            && (event.lastErrorMessage.length() > MAX_ERROR_MESSAGE_LENGTH))
          event.lastErrorMessage = event.lastErrorMessage.substring(0, MAX_ERROR_MESSAGE_LENGTH);

        event.lastErrorTime = new java.sql.Timestamp(System.currentTimeMillis());

        ps = dbConn.prepareStatement(UPDATE_EVENT_ERROR_SQL);

        ps.setTimestamp(1, event.lastErrorTime);
        ps.setString(2, event.lastErrorMessage);
        ps.setString(3, event.channelName);
        ps.setInt(4, event.id);
      } else {
        throw new FrameworkException(
            "ERROR: Invalid event update type [" + eventStatus.name + "].");
      }

      if (Debug.isLevelEnabled(Debug.DB_DATA))
        Debug.log(Debug.DB_DATA, "Event being operated on in database:\n" + event.describe());

      int numRows = ps.executeUpdate();

      if (numRows > 1) {
        String errMsg = "Execution of update SQL statement affected [" + numRows + "] rows.";

        Debug.error(errMsg);

        throw new FrameworkException(errMsg);
      }

      DBConnectionPool.getInstance().commit(dbConn);

      if (Debug.isLevelEnabled(Debug.DB_DATA))
        Debug.log(Debug.DB_DATA, "Successfully committed SQL operation.\n" + LINE);

      // At this point, the event should be removed from the in-memory buffer of events as well,
      // irrespective of processing outcome.
      if (Debug.isLevelEnabled(Debug.MSG_STATUS))
        Debug.log(
            Debug.MSG_STATUS,
            "Removing event [" + event.describe() + "] from in-memory queue buffer.");

      boolean removed = queue.remove(event);

      if (Debug.isLevelEnabled(Debug.MSG_STATUS))
        Debug.log(
            Debug.MSG_STATUS,
            "Event removed? ["
                + removed
                + "].  In-memory queue buffer size ["
                + queue.size()
                + "].");
    } catch (SQLException sqle) {
      throw new DatabaseException(
          "ERROR: Could not execute SQL statement:\n" + DBInterface.getSQLErrorMessage(sqle));
    } catch (Exception e) {
      throw new DatabaseException("ERROR: Could not execute SQL statement:\n" + e.toString());
    } finally {
      releaseDatabaseResources(dbConn, ps);

      if (Debug.isLevelEnabled(Debug.BENCHMARK) && (startTime > 0)) {
        long stopTime = System.currentTimeMillis();

        Debug.log(
            Debug.BENCHMARK,
            "ELAPSED TIME ["
                + (stopTime - startTime)
                + "] msec:  "
                + "SQL: Time to update event in PersistentEvent database table.");
      }
    }
  }
  /**
   * The message obtained from the queue was successfully processed. The corresponding message in
   * the message table gets updated to have a status of "Sent".
   *
   * <p>The message then gets deleted from the queue.
   *
   * @param message The message being processed.
   * @param queue The queue to which this message belongs.
   * @throws QueueException If processing fails.
   */
  public void handleSuccess(QueueMessage queueMessage, MessageQueue queue) throws QueueException {

    if (!(queueMessage instanceof NPACMessageType)) {

      Debug.error(
          "NPAC consumer policy received a message "
              + "that is not of type: "
              + NPACMessageType.class.getName());

    } else {

      NPACMessageType message = (NPACMessageType) queueMessage;

      // update message status to "Sent"

      // construct update statement
      String update = "UPDATE " + message.getTableName() + UPDATE_CLAUSE;

      try {

        Connection connection = DBInterface.acquireConnection();

        try {

          PreparedStatement stmt = null;

          try {

            stmt = connection.prepareStatement(update);

            int invokeID = message.getInvokeID().intValue();
            Debug.log(
                Debug.DB_STATUS,
                "Updating status of message with invoke ID ["
                    + invokeID
                    + "] to 'Sent' using statement:\n"
                    + update);

            stmt.setInt(1, invokeID);

            stmt.execute();
            connection.commit();

          } catch (Exception ex) {
            Debug.error("Could not update status for message:\n" + message.describe() + "\n" + ex);
          } finally {

            try {
              if (stmt != null) {
                stmt.close();
              }
            } catch (Exception ex) {
              Debug.error("Could not close statement: " + ex);
            }
          }

        } finally {

          try {
            DBInterface.releaseConnection(connection);
          } catch (Exception ex) {
            Debug.error("Could not release database connection: " + ex);
          }
        }

      } catch (DatabaseException dbex) {

        Debug.error(
            "Could not update status for message:\n"
                + message.describe()
                + ". A database connection could not be acquired: "
                + dbex);
      }
    }

    // This is used in the where clause to identify which message
    // should be updated. All we need is the message's ID
    // value to uniquely identify it.
    NPACMessageType whereMessage = new NPACMessageType();
    Map whereValues = new HashMap();
    whereValues.put(ID_COL, queueMessage.getValues().get(ID_COL));
    whereMessage.setValues(whereValues);

    // update the status of the message to "sent"
    NPACMessageType newMessage = new NPACMessageType();

    if (Debug.isLevelEnabled(Debug.MSG_STATUS)) {
      Debug.log(
          Debug.MSG_STATUS,
          "NPACConsumerPolicy, handleSuccess(), Before removing REGION ID... values :-"
              + queueMessage.getValues());
    }

    Map value = queueMessage.getValues();
    if (value.containsKey("REGIONID")) {
      value.remove("REGIONID");
    }
    newMessage.setValues(value);

    if (Debug.isLevelEnabled(Debug.MSG_STATUS)) {
      Debug.log(
          Debug.MSG_STATUS,
          "NPACConsumerPolicy, handleSuccess(), After removing region id, values :-"
              + newMessage.getValues());
    }
    newMessage.setStatus(SOAConstants.SENT_STATUS);
    queue.update(whereMessage, newMessage);
  }
  /*
   * (non-Javadoc)
   *
   * @see com.nightfire.comms.servicemgr.ServerManagerBase#initialize()
   */
  @Override
  public void initialize() throws ProcessingException {

    if (Debug.isLevelEnabled(Debug.NORMAL_STATUS))
      Debug.log(Debug.NORMAL_STATUS, "initializing poll comm server : " + getType());

    // read configuration from repository
    // String categoryConfig =
    // ServerManagerFactory.getInstance().getConfigCategory(getType());
    String categoryConfig = ServerManagerFactory.MGR_CONFIG_CATEGORY;
    // String metaConfig =
    // ServerManagerFactory.getInstance().getConfigMeta(getType());

    List<String> configCategory = ServerManagerFactory.getInstance().getConfigCategory(getType());
    XMLMessageParser fileParser = null;

    Document aggregatedDoc;
    try {
      aggregatedDoc = XMLLibraryPortabilityLayer.getNewDocument(getType(), null);
    } catch (MessageException me) {
      Debug.logStackTrace(me);
      throw new ProcessingException(me);
    }

    ServiceIdFilter idFilter = new ServiceIdFilter();
    for (String fileNm : configCategory) {

      String xmlDescription;
      try {
        xmlDescription = RepositoryManager.getInstance().getMetaData(categoryConfig, fileNm);
        fileParser = new XMLMessageParser(xmlDescription);
        fileParser = idFilter.getFilteredDOM(fileParser, ServiceMgrConsts.POLL_COMM_SERVER_CONFIG);
        Element document = fileParser.getDocument().getDocumentElement();
        Node node = aggregatedDoc.importNode(document, true);
        aggregatedDoc.getDocumentElement().appendChild(node);

      } catch (Exception e) {
        Debug.error(
            "Unable to load and parse configuration from repository "
                + categoryConfig
                + " "
                + fileNm);
        throw new ProcessingException(e);
      }

      NodeList list = fileParser.getDocument().getElementsByTagName(ELEM_POLL_COMM_SERVER);
      for (int Ix = 0; Ix < list.getLength(); Ix++) {
        if (list.item(Ix) != null) {
          Element pollCommServerElement = (Element) list.item(Ix);

          String id, key, value, start, pollCommServerType;

          id =
              getConfigurationValue(
                  pollCommServerElement, ConfigType.ATTRIBUTE, ATTR_COMM_SERVER_ID, null);
          key =
              getConfigurationValue(
                  pollCommServerElement, ConfigType.ELEMENT, ELEM_COMM_SERVER_KEY, null);
          value =
              getConfigurationValue(
                  pollCommServerElement, ConfigType.ELEMENT, ELEM_COMM_SERVER_TYPE, null);
          start =
              getConfigurationValue(
                  pollCommServerElement, ConfigType.ATTRIBUTE, ATTR_COMM_SERVER_START, "true");
          pollCommServerType =
              getConfigurationValue(pollCommServerElement, ConfigType.ATTRIBUTE, "type", null);

          if (!StringUtils.hasValue(id)
              || !StringUtils.hasValue(key)
              || !StringUtils.hasValue(value)) {
            Debug.log(
                Debug.XML_ERROR,
                "Could not configure Poll Comm Server since mandatory property are not configured");
            Debug.log(Debug.DB_ERROR, ELEM_POLL_COMM_SERVER + "=" + getType());
            Debug.log(Debug.DB_ERROR, ATTR_COMM_SERVER_ID + "=" + id);
            Debug.log(Debug.DB_ERROR, ELEM_COMM_SERVER_KEY + "=" + key);
            Debug.log(Debug.DB_ERROR, ELEM_COMM_SERVER_TYPE + "=" + value);

            // skip to initialize FTP Poller server if not
            // configured correctly.
            continue;
          }

          boolean startFlag = "false".equalsIgnoreCase(start) ? false : true;

          CommServerConf commServerConf = new CommServerConf(key, value, pollCommServerType);
          commServerConf.setStarted(startFlag);

          NodeList paramList = pollCommServerElement.getElementsByTagName(ELEM_PARAM);
          if (paramList != null) {
            for (int i = 0; i < paramList.getLength(); i++) {
              Element paramElement = (Element) paramList.item(i);

              String paramNm =
                  getConfigurationValue(paramElement, ConfigType.ATTRIBUTE, "name", null);

              String paramVal =
                  getConfigurationValue(paramElement, ConfigType.ATTRIBUTE, "value", null);

              commServerConf.setParamValue(paramNm, paramVal);

              if (Debug.isLevelEnabled(Debug.OBJECT_LIFECYCLE))
                Debug.log(
                    Debug.OBJECT_LIFECYCLE,
                    "Setting param [" + paramNm + "] value [" + paramVal + "]");
            }
          }

          if (id == null)
            Debug.log(
                Debug.MSG_ERROR, "Could not initialize poll comm server with id [" + id + "].");
          else commServerConfMap.put(id, commServerConf);
        }
      }
    }

    try {
      parser = new XMLMessageParser(aggregatedDoc);
    } catch (Exception e) {
      throw new ProcessingException("Unable to create XMLMessageParser object " + e.getMessage());
    }

    if (Debug.isLevelEnabled(Debug.NORMAL_STATUS))
      Debug.log(
          Debug.NORMAL_STATUS,
          "done initializing poll comm servers of type [" + getType() + "].. ");
  }
  /**
   * If NF_HEADER_LOCATION_PROP exists in context use as the header to forward to Gateway. If
   * IS_ASYNCHRONOUS_PROP exists in context then use that value to call either processAsync or
   * processSync
   *
   * @param input MessageObject containing the value to be processed *
   * @param mpcontext The context
   * @return Optional NVPair containing a Destination name and a MessageObject, or null if none.
   * @exception ProcessingException Thrown if processing fails.
   * @exception MessageException Thrown if bad message.
   */
  public NVPair[] process(MessageProcessorContext ctx, MessageObject input)
      throws MessageException, ProcessingException {
    if (input == null) return null;

    try {
      serverName = getRequiredProperty(ctx, input, SERVER_NAME_PROP);
    } catch (MessageException me) {
      throw new ProcessingException(me.getMessage());
    }

    if (StringUtils.hasValue(headerLocation)) {
      try {
        header = getString(headerLocation, ctx, input);
      } catch (MessageException me) {
        throw new ProcessingException(me.getMessage());
      }
    }

    if (StringUtils.hasValue(isAsyncLocation)) {
      try {
        isAsync = StringUtils.getBoolean(getString(isAsyncLocation, ctx, input));
      } catch (FrameworkException fe) {
        throw new ProcessingException(
            "Value of " + IS_ASYNCHRONOUS_LOCATION_PROP + " is not TRUE/FALSE. " + fe.getMessage());
      }
    }

    // Fetch the alternate Orb Address, if one exists at the specified context location
    if (StringUtils.hasValue(orbAgentAddrLocation)) {
      try {
        if (exists(orbAgentAddrLocation, ctx, input, true)) {
          orbAgentAddr = getString(orbAgentAddrLocation, ctx, input);
          if (Debug.isLevelEnabled(Debug.MSG_STATUS))
            Debug.log(
                Debug.MSG_STATUS,
                "RequestHandlerClient:: alternate orb exists with orb agent address ["
                    + orbAgentAddr
                    + "]");
        }
      } catch (MessageException me) {
        throw new ProcessingException(me.getMessage());
      }
    }

    // Fetch the alternate Orb Port, if one exists at the specified context location
    if (StringUtils.hasValue(orbAgentPortLocation)) {
      try {
        if (exists(orbAgentPortLocation, ctx, input, true)) {
          orbAgentPort = getString(orbAgentPortLocation, ctx, input);
          if (Debug.isLevelEnabled(Debug.MSG_STATUS))
            Debug.log(
                Debug.MSG_STATUS,
                "RequestHandlerClient:: alternate orb exists with orb agent port ["
                    + orbAgentPort
                    + "]");
        }
      } catch (MessageException me) {
        throw new ProcessingException(me.getMessage());
      }
    }

    String msg = input.getString();

    try {
      try {
        return (formatNVPair(makeClientCall(serverName, false, header, msg)));
      } catch (Exception e) {
        // Any of the following exceptions indicate that the failure might be due to
        // a CORBA communications issue (stale object reference) that should be retried.
        if ((e instanceof org.omg.CORBA.OBJECT_NOT_EXIST)
            || (e instanceof org.omg.CORBA.TRANSIENT)
            || (e instanceof org.omg.CORBA.COMM_FAILURE)
            || (e instanceof org.omg.CORBA.INV_OBJREF)
            || (e instanceof org.omg.CORBA.UNKNOWN)) {
          Debug.warning(
              "Caught the following CORBA communication error, so retrying:\n"
                  + e.toString()
                  + "\n"
                  + Debug.getStackTrace(e));

          return (formatNVPair(makeClientCall(serverName, true, header, msg)));
        } else {
          // It's not a communication exception indicating that retry is recommended,
          // so just re-throw it.
          throw e;
        }
      }
    } catch (Exception e) {
      if (e instanceof InvalidDataException) {
        Debug.error(e.toString() + "\n" + Debug.getStackTrace(e));

        throw new MessageException(((InvalidDataException) e).errorMessage);
      } else {
        Debug.error(e.toString() + "\n" + Debug.getStackTrace(e));

        throw new ProcessingException(e.toString());
      }
    }
  }