/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.concurrent;

import edu.emory.mathcs.util.concurrent.*;
import edu.emory.mathcs.backport.java.util.concurrent.*;

/**
 * Variant of {@link AsyncTask} that overrides method
 * {@link edu.emory.mathcs.util.concurrent.AsyncTask#createPerformer
 *  createPerformer} so that
 * it always runs with a fixed access control context and
 * {@link ThreadContext thread context} inherited from the caller of
 * createPerformer method. This class is intended primarily for subclassing.
 * Typical usage scenario in subclasses is to create a new instance and
 * call <code>createPerformer</code>. As a result, one obtains a runnable that
 * will perform assigned task with the same access permissions,
 * {@link DelegatableThreadLocal}s, and the context class loader, regardless
 * of the thread that actually executes the runnable.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */
public class SecureAsyncTask extends AsyncTask {

    protected SecureAsyncTask() {
        super();
    }

    protected SecureAsyncTask(Callback cb) {
        super(cb);
    }

    /**
     * Creates a runnable that will execute specified call and then mark this
     * AsyncTask with the result of that call. The runnable will run
     * with access control context and {@link ThreadContext} inherited from
     * this method's invoker, regardless of the thread that will actually
     * execute it.
     * If the call completes successfully, this AsyncTask is marked
     * completed with the result returned by the call. If the call
     * throws an exception, the AsyncTask is marked as failed with
     * cause being that exception. The stack trace of the thread in which
     * the performer is created is appended to the failure cause stack
     * trace unless the disableStackTraces parameter is set to false.
     * <p>
     * This method is intended to be used by subclasses. Runnable
     * returned from this method should be executed only once -- subsequent
     * execution attempts will fail due to "task already completed" condition.
     *
     * @param call the call to execute
     * @param disableStackTraces if true, does not append invoker stack trace
     *        to traces of exceptions thrown during execution of the runnable
     * @return runnable that will execute specified call and
     *         set the result of this AsyncTask upon completion
     */
    protected Runnable createPerformer(Callable call, boolean disableStackTraces) {
        return super.createPerformer(ExecutorUtils.delegatedCallable(call), disableStackTraces);
    }

    /**
     * Schedules specified task with given executor, and returns
     * completion handle that can be used to access the result or to cancel
     * the task. The task will run
     * with access control context and {@link ThreadContext} inherited from
     * this method's invoker, regardless of the thread that will actually
     * execute it.
     * Later, if task completes successfully, the handle is marked
     * completed with the result that has been returned from the task.
     * If the task ends with an exception, the handle is marked
     * failed with cause being that exception. In such case, as a debugging
     * aid, current stack trace (that is, that of this method's invoker) is
     * appended to the original stack trace.
     *
     * @param executor the executor to use
     * @param call the task to schedule
     * @return completion handle that can be used to access the result or
     *         cancel the task
     */
    public static AsyncTask start(final Executor executor, final Callable call)
    {
        return start(executor, call, null);
    }

    /**
    * Schedules specified task with given executor, and returns
    * completion handle that can be used to access the result or to cancel
    * the task. The task will run
    * with access control context and {@link ThreadContext} inherited from
    * this method's invoker, regardless of the thread that will actually
    * execute it.
    * Later, if task completes successfully, the handle is marked
    * completed with the result that has been returned from the task.
    * If the task ends with an exception, the handle is marked
    * failed with cause being that exception. In such case, as a debugging
    * aid, current stack trace (that is, that of this method's invoker) is
    * appended to the original stack trace.
    *
    * @param executor the executor to use
    * @param call the task to schedule
    * @param cb callback to invoke upon completion
    * @return completion handle that can be used to access the result or
    *         cancel the task
    */
   public static AsyncTask start(final Executor executor, final Callable call,
                                  final Callback cb)
    {
        return start(executor, call, cb, false);
    }

    /**
     * Schedules specified task with given executor, and returns
     * completion handle that can be used to access the result or to cancel
     * the task. The task will run
     * with access control context and {@link ThreadContext} inherited from
     * this method's invoker, regardless of the thread that will actually
     * execute it.
     * Later, if task completes successfully, the handle is marked
     * completed with the result that has been returned from the task.
     * If the task ends with an exception, the handle is marked
     * failed with cause being that exception. In such case, as a debugging
     * aid, current stack trace (that is, that of this method's invoker) is
     * appended to the original stack trace unless
     * the disableStackTraces parameter is set to false
     *
     * @param executor the executor to use
     * @param call the task to schedule
     * @param cb callback to invoke upon completion
     * @param disableStackTraces if true, does not append invoker stack trace
     *        to traces of exceptions thrown during task execution
     * @return completion handle that can be used to access the result or
     *         cancel the task
     */
    public static AsyncTask start(final Executor executor, final Callable call,
                                  final Callback cb, boolean disableStackTraces)
    {
        final SecureAsyncTask task = new SecureAsyncTask(cb);
        executor.execute(task.createPerformer(call, disableStackTraces));
        return task;
    }

}
