/**
 * AbstractAction.java
 * Created Jan 8, 2007
 * Copyright (C) Tandberg Television 2007
 */
package com.tandbergtv.workflow.exe;

import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.jbpm.graph.exe.ExecutionContext;

import com.tandbergtv.workflow.core.CustomToken;
import com.tandbergtv.workflow.core.ProcessStatus;
import com.tandbergtv.workflow.core.graph.exe.IExecutable;

/**
 * Basic implementation of an action attached to a node.
 * 
 * @author Sahil Verma
 */
public abstract class AbstractAction implements IExecutable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private boolean aborted;
	
	private Future<Void> future;
	
	/**
	 * Creates a AbstractAction
	 */
	public AbstractAction() {
		super();
	}

	/**
	 * Aborts execution. This method is declared final because the framework needs to guarantee
	 * that implementations of this action will actually terminate. 
	 */
	public final void abort() {
		this.aborted = true;
		this.future.cancel(true);
	}

	/**
	 * Allows implementations to cleanup any resources before we exit the action
	 */
	public void cleanup() {
	}

	/* (non-Javadoc)
	 * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
	 */
	public final void execute(final ExecutionContext context) throws Exception {
		CustomToken token = (CustomToken)context.getToken();
		
		if (token.getStatus() == ProcessStatus.ERROR || token.getRequestedStatus() == ProcessStatus.ERROR)
			throw new ActionException(token + ", invalid status");
		
		ExecutorService service = Executors.newFixedThreadPool(1); 
		
		this.future = service.submit(new Callable<Void>() {
			public Void call() throws Exception {
				start(context);
				return (Void)null;
			}
		});
		
		try {
			/* Wait for the thread to complete */
			this.future.get();
		} catch (CancellationException e) {
			if (this.aborted)
				throw new ActionException(token + ", execution was aborted", e);
		} catch (ExecutionException e) {
			if (e.getCause() instanceof ActionException)
				throw (ActionException)e.getCause();
			throw new ActionException(token + ", an error occured during execution", e);
		} finally {
			service.shutdownNow();
		}
	}
	
	/**
	 * Performs the action
	 * 
	 * @param context
	 */
	protected abstract void start(ExecutionContext context);
}
