/********************************************************************
 * Copyright © 2006 VHA. All rights reserved
 ********************************************************************/

package gov.va.med.fw.service.support;

import gov.va.med.fw.service.AbstractComponent;

/**
 * Allows for generic invocation of a task on a spawned child thread.  The current
 * thread can be configured to block or not block.  
 * 
 * Created Apr 11, 2006 1:45:49 PM
 * 
 * @author DNS   BOHMEG
 */
public class SpawnedThreadInvoker extends AbstractComponent {
	protected static int STATUS_SUCCESS = 0;

	protected static int STATUS_FAILURE = 1;

	protected static int STATUS_TIMEOUT = 2;

	protected int threadStatus;

	protected Throwable threadError;

	protected Object threadResult;

	protected Thread t;

	private int timeout = 120000;  // default
	
	private boolean blocking;

	public SpawnedThreadInvoker(SpawnedThreadTask task) {
		this(task, false);
	}

	public SpawnedThreadInvoker(SpawnedThreadTask task, boolean blocking) {
		t = new SpawnedThread(task, this);
		this.blocking = blocking;
	}
	
	public void execute() {
		if(blocking) {
			synchronized (t) {
				t.start();
				try {
					t.wait(timeout);
				} catch (InterruptedException e) {
					throw new RuntimeException(
							"Spawned thread has been interrupted, should not occur",
							e);
				}
			}
		} else {
			t.start();
		}
	}
	
	protected Thread getCurrentThread()
	{
	    return t;
	}	

	public void abort() {
		if(t != null)
			t.interrupt();
	}
	
	/**
	 * @return Returns the timeout.
	 */
	public int getTimeout() {
		return timeout;
	}

	/**
	 * @param timeout
	 *            The timeout to set.
	 */
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	public boolean isTimedOut() {
		return threadStatus == STATUS_TIMEOUT;
	}

	public boolean isFailed() {
		return threadStatus == STATUS_FAILURE;
	}

	public Throwable getError() {
		return threadError;
	}

	public Object getResult() {
		return threadResult;
	}

	/**
	 * @return Returns the blocking.
	 */
	public boolean isBlocking() {
		return blocking;
	}

	/**
	 * @param blocking The blocking to set.
	 */
	public void setBlocking(boolean blocking) {
		this.blocking = blocking;
	}
}

class SpawnedThread extends Thread {
	private SpawnedThreadTask task;

	private SpawnedThreadInvoker invoker;

	public SpawnedThread(SpawnedThreadTask task, SpawnedThreadInvoker invoker) {
		this.task = task;
		this.invoker = invoker;
	}

	public void run() {
		try {
			invoker.threadStatus = SpawnedThreadInvoker.STATUS_TIMEOUT;
			invoker.threadResult = task.execute();
			invoker.threadStatus = SpawnedThreadInvoker.STATUS_SUCCESS;
		} catch (Throwable t) {
			invoker.threadStatus = SpawnedThreadInvoker.STATUS_FAILURE;
			invoker.threadError = t;
		} finally {
			synchronized (this) {
				notify();
			}
			invoker.t = null;
		}
	}
}
