/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.util;

// Framework classes
import gov.va.med.fw.service.AbstractComponent;

/**
 * A default implementation of a ThreadPool which is constructed with a given
 * number of threads.
 *  
 */
public class ThreadPool extends AbstractComponent implements Runnable {

   public final static int DEFAULT_NUMBER_OF_THREADS = 4;
   
   private TaskQueue queue = new TaskQueue();

   private boolean stopped = false;
   
   private int realizedThreadCount;

   private ThreadGroup threadGroup;

   public ThreadPool() {
      this( 4 );
   }

   public ThreadPool(int numberOfThreads) {
      this(null, numberOfThreads, Thread.NORM_PRIORITY );
   }

   /**
    * Constructor with a generic name that can be used to produce individual name
    * for each thread
    * 
    * @param numberOfThreads
    * @param name
    */
   public ThreadPool(String name, int numberOfThreads) {
	      this(name, numberOfThreads, Thread.NORM_PRIORITY );
	   }

   public ThreadPool(String name, int numberOfThreads, int threadPriority) {
      if ( numberOfThreads <= 0 || threadPriority <= 0 ) {
         throw new IllegalArgumentException("Required parameters are not passed in");
      }
      
      for (int i = 0; i < numberOfThreads; i++) {
         startThread( name!=null?(name+"-"+i):null, threadPriority );
         realizedThreadCount++;
      }
      
   }

   public void setThreadGroup(ThreadGroup threadGroup) {
      this.threadGroup = threadGroup;
   }

   public void stop() {
      stopped = true;
      // ensures while loop will finish for each thread in pool (if TaskQueue is already empty)
      for(int i=0; i<realizedThreadCount; i++) {
	      queue.add(new Runnable() {
	    	 public void run() {
	    		 logger.info("ThreadPool sentinel Runnable instance encountered");
	    	 }
	      });
      }
   }

   /**
    * Returns number of runnable object in the queue.
    */
   public int getRunnableCount() {
      return queue.size();
   }

   /**
    * Dispatch a new task onto this pool to be invoked asynchronously later
    */
   public void invokeLater(Runnable task) {
      queue.add(task);
   }

   /**
    * The method ran by the pool of background threads
    */
   public void run() {
      while (!stopped) {
         Runnable task = (Runnable) queue.remove();
         if (task != null) {
            try {
               task.run();
            }
            catch (Throwable t) {
               if (logger.isWarnEnabled()) {
                  logger.warn("Failed to execute a task", t);
               }
            }
         }
      }
      
   }
   
   public void init() {
	   queue.clearQueue(); // remove any un-processed tasks 
   }

   /** Start a new thread running */
   protected Thread startThread() {
      Thread thread = createThread();
      thread.start();
      return thread;
   }

   protected Thread startThread(String name, int priority) {
      Thread thread = createThread();
      if (name != null) {
    	  thread.setName(name);
      }
      thread.setPriority(priority);
      thread.start();
      return thread;
   }

   protected Thread createThread() {
      if (this.threadGroup != null) {
         return new Thread(this.threadGroup, this);
      }
      else {
         return new Thread(this);
      }
   }
}