/*
 * JBoss, the OpenSource EJB server
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.util;

/**
 * This class runs in a single thread {@link TimerTask}s that have been 
 * scheduled. <p>
 * A similar class is present in java.util package of jdk version >= 1.3;
 * for compatibility with jdk 1.2 it is reimplemented here.
 *
 * @see TimerTask
 * @author Simone Bordet (simone.bordet@compaq.com)
 * @version $Revision: 1.2 $
 */
public class TimerQueue 
	extends WorkerQueue
{
	// Constants -----------------------------------------------------

	// Attributes ----------------------------------------------------
	/* This member is accessed only from putJob and getJob, that will 
	   then guarantee necessary synchronization on it */
	private Heap m_heap;

	// Static --------------------------------------------------------

	// Constructors --------------------------------------------------
	/**
	 * Creates a new timer queue with default thread name of "TimerTask Thread"
	 */
	public TimerQueue() 
	{
		this("TimerTask Thread");
	}
	/**
	 * Creates a new timer queue with the specified thread name
	 */
	public TimerQueue(String threadName) 
	{
		super(threadName);
		m_heap = new Heap();
	}

	// Public --------------------------------------------------------
	/**
	 * Schedules the given TimerTask for immediate execution.
	 */
	public void schedule(TimerTask t) 
	{
		schedule(t, 0);
	}
	/**
	 * Schedule the given TimerTask to be executed after <code>delay</code>
	 * milliseconds.
	 */
	public void schedule(TimerTask t, long delay) 
	{
		if (t == null) throw new IllegalArgumentException("Can't schedule a null TimerTask");
		if (delay < 0) delay = 0;
		t.setNextExecutionTime(System.currentTimeMillis() + delay);
		putJob(t);
	}

	// Z implementation ----------------------------------------------

	// WorkerQueue overrides ---------------------------------------------------
	protected void putJobImpl(Executable task) 
	{
		m_heap.insert(task);
		((TimerTask)task).setState(TimerTask.SCHEDULED);
		notifyAll();
	}
	protected Executable getJobImpl() throws InterruptedException
	{
		while (m_heap.peek() == null) 
		{
			wait();
		}
		TimerTask task = (TimerTask)m_heap.extract();
		switch (task.getState())
		{
			case TimerTask.CANCELLED:
			case TimerTask.EXECUTED:
				return getJobImpl();
			case TimerTask.NEW:
			case TimerTask.SCHEDULED:
				return task;
			default:
				throw new IllegalStateException("TimerTask has an illegal state");
		}
	}
	protected Runnable createQueueLoop() 
	{
		return new TimerTaskLoop();
	}
	protected void clear() 
	{
		super.clear();
		synchronized (this) 
		{
			m_heap.clear();
		}
	}

	// Package protected ---------------------------------------------

	// Protected -----------------------------------------------------

	// Private -------------------------------------------------------

	// Inner classes -------------------------------------------------
	/**
	 * Class that loops getting the next job to be executed and then 
	 * executing it, in the timer task thread.
	 */
	protected class TimerTaskLoop implements Runnable 
	{
		public void run() 
		{
			try
			{
				while (true) 
				{
					try 
					{
						// Wait for the first job
						TimerTask task = (TimerTask)getJob();
						long now = System.currentTimeMillis();
						long executionTime = task.getNextExecutionTime();
						long timeToWait = executionTime - now;
						boolean runTask = timeToWait <= 0;
						if (!runTask)
						{
							// Entering here when a new job is scheduled but
							// it's not yet time to run the first one;
							// the job was extracted from the heap, reschedule
							putJob(task);
							Object mutex = TimerQueue.this;
							synchronized (mutex)
							{
								// timeToWait is always strictly > 0, so I don't wait forever
								mutex.wait(timeToWait);
							}
						}
						else
						{
							if (task.isPeriodic())
							{
								// Reschedule with the new time
								task.setNextExecutionTime(executionTime + task.getPeriod());
								putJob(task);
							}
							else
							{
								// The one-shot task is already removed from 
								// the heap, mark it as executed
								task.setState(TimerTask.EXECUTED);
							}
							// Run it !
							task.execute();
						}
					}
					catch (InterruptedException x)
					{
						// Exit the thread
						break;
					}
					catch (Exception x)
					{
						x.printStackTrace();
						// and go on with the next job
					}
				}
			}
			finally	{clear();}
		}
	}
}
