/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */
package integration;

import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The request handler that handles requests issued from clients and directly
 * interacts with the backend system.
 * <p>
 * The client only knows the dispatcher, which integrates all access to a 
 * simulated backend system.
 * <p>
 * 
 * It is responsible for client registration. Before 
 * sending requests, a client needs to register through the
 * <code>registerClientSystem</code> method to receive an unique 
 * <i>system identifier</i>, which is used to uniquely identify 
 * one client from others.
 * <p> 
 * Requests are buffered in a request queue and handled in a FIFO
 * (first-come-first-serve) order. Once a request is handled, it is 
 * removed from the request queue.
 * <p>
 * Requests are handled in separated threads.
 */
public abstract class RequestHandler {

  /**
   * Thread used to process requests.
   */
  protected Thread processRequestThread;
  
  /**
   * Request queue.
   */
  protected Vector<IntegrationRequest> requestQueue = new Vector<IntegrationRequest>();
  
  /**
   * Logger used by the request handler.
   */
  protected Logger log;
  
  /**
   * Number of request handler threads.
   */
  protected static int requestHandlerThreadCount = 0;

  public RequestHandler(Logger logger) {
    logger.log(Level.INFO, "RequestHandler initialized");
    this.log = logger;
  }

  /**
   * Should handle one request.
   */
  protected abstract void handleRequest(Object request);
  
  /**
   * Should register a client and return an unique system identifier that 
   * identifies that client. 
   * 
   * @param prefix  The system identifier prefix to be used
   * @return An unique system identifier to be used by the client
   */
  public abstract String registerClientSystem(String prefix);
  
  /**
   * Should unregister a client by releasing its unique system identifier.
   * 
   * @param systemId the client's system identifier.
   */
  public abstract void unregisterClientSystem(String systemId);
    
  /**
   * Returns the logger used by this instance.
   * 
   * @return Logger
   */
  protected Logger getLogger() {
    return this.log;
  }

  /** 
   * Empties the request queue. 
   */
  public void clearRequests() {
    requestQueue.clear();
  }

  /**
   * Adds request to the request queue.
   * 
   * If the <code>processRequestThread</code> does not exist or is 
   * dead, this method will start a new thread to process all the 
   * pending requests. Otherwise, no new thread will be started 
   * because the <code>processRequestThread</code> will process all 
   * the pending requests.
   * 
   * @param request The request acknowledged by the request handler
   */
  public synchronized void addRequest(IntegrationRequest request) {
    if (request != null) {

      // Appends to the queue.
      requestQueue.add(request);

      // If the processRequestThread not initialized or is dead, start a
      // new thread to process the request. Else do nothing because 
      // processRequestThread will automatically process all the requests in 
      // the queue.
      if ((this.processRequestThread == null)
          || (!this.processRequestThread.isAlive())) {

        Thread newThread = new Thread(new Runnable() {
          public void run() {
            processingPendingRequest();
          }
        });

        // Assign new thread
        this.processRequestThread = newThread;

        //Name it 
        processRequestThread.setName("Integration Request Handler - " + requestHandlerThreadCount++);

        // Start thread
        this.processRequestThread.start();
      } //endif
    }
  }

  /**
   * This method returns the first pending request from queue.
   */
  protected IntegrationRequest getPendingRequest() {
    IntegrationRequest req = null;
    synchronized (requestQueue) {
      if (!requestQueue.isEmpty()) {
        req = requestQueue.get(0);
        if (req != null) {

          // Remove it from the queue.
          requestQueue.remove(0);
        }
      }
    }
    return req;
  }

  /**
   * Processes all the requests in the request queue.
   * This method must be invoked from a Runnable object. 
   */
  protected void processingPendingRequest() {
    while (true) {
      IntegrationRequest req = getPendingRequest();
      if (req != null) {
        handleRequest(req);
      } else {
        break;
      }
    } //end while
  }
}