
import java.util.*;

/*
 * accepts new requests and processes them with its associated
 * ref: http://www.informit.com/guides/content.asp?g=java&seqNum=237&rl=1
 */


public class ThreadPool{
	private LinkedList queue = new LinkedList();
	private int maxQueueLength;
	private int minThreads;
	private int maxThreads;
	private int currentThreads = 0;
	private String requestHandlerClassName;
	private List threadPool = new ArrayList();
	private boolean running = true;
	
	
	public ThreadPool( String requestHandlerClassName,
			int maxQueueLength,
			int minThreads,
			int maxThreads )
	{
		// Initialize the parameters
		this.requestHandlerClassName = requestHandlerClassName;
		this.maxQueueLength = maxQueueLength;
		this.minThreads = minThreads;
		this.maxThreads = maxThreads;
		this.currentThreads = this.minThreads;

		// Create the minimum number of threads
		for( int i=0; i<this.minThreads; i++ )
		{
			RequestThread thread = new RequestThread( this, i, requestHandlerClassName);
			thread.start();
			this.threadPool.add( thread );
		}
	}

	/**
	 * Returns the name of the RequestHandler implementation class
	 */
	public String getRequestHandlerClassName()
	{
		return this.requestHandlerClassName;
	}

	/**
	 * Adds a new object to the end of the queue
	 * 
	 * @param o     Adds the specified object to the Request Queue
	 * @throws Exception 
	 */
	public synchronized void add( Object o) throws Exception
	{
		// Validate that we have room of the object before we add it to the queue
		if( queue.size() > this.maxQueueLength )
		{
			throw new Exception( "The Request Queue is full. Max size = " + this.maxQueueLength );
		}

		// Add the new object to the end of the queue
		queue.addLast( o );

		// See if we have an available thread to process the request
		boolean availableThread = false;
		for( Iterator i=this.threadPool.iterator(); i.hasNext(); )
		{
			RequestThread rt = ( RequestThread )i.next();
			if( !rt.isProcessing() )
			{
				availableThread = true;
				break;
			}
		}

		// See if we have an available thread
		if( !availableThread )
		{
			if( this.currentThreads < this.maxThreads )
			{
				RequestThread thread = new RequestThread( this, currentThreads++, this.requestHandlerClassName );
				thread.start();
				this.threadPool.add( thread );
			}
		}

		// Wake someone up
		notifyAll();
	}

	/**
	 * Returns the first object in the queue
	 */
	public synchronized Object getNextObject()
	{
		// Setup waiting on the Request Queue
		while( queue.isEmpty() )
		{
			try
			{
				if( !running )
				{
					// Exit criteria for stopping threads
					return null;
				}
				wait();
			}
			catch( InterruptedException ie ) {}
		}

		// Return the item at the head of the queue
		return queue.removeFirst();
	}

	/**
	 * Shuts down the request queue and kills all of the request threads
	 */
	public synchronized void shutdown()
	{
		// Mark the queue as not running so that we will free up our request threads
		this.running = false;

		// Tell each thread to kill itself
		for( Iterator i=this.threadPool.iterator(); i.hasNext(); )
		{
			RequestThread rt = ( RequestThread )i.next();
			rt.killThread();
		}

		// Wake up all threads and let them die
		notifyAll();
	}
}                                  