Package org.gephi.utils.longtask.api

Source Code of org.gephi.utils.longtask.api.LongTaskExecutor$RunningLongTask

/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org

This file is part of Gephi.

Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.utils.longtask.api;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.gephi.utils.longtask.spi.LongTask;
import org.gephi.utils.progress.ProgressTicket;
import org.gephi.utils.progress.ProgressTicketProvider;
import org.openide.util.Cancellable;
import org.openide.util.Lookup;

/**
* Portable long-task executor, that supports synchronous and asynchronous
* execution, progress, cancellation and error management.
* <p>
* Note that only one task can be executed by the executor at one time.
*
* @author Mathieu Bastian
* @see LongTask
*/
public final class LongTaskExecutor {

    private final boolean inBackground;
    private boolean interruptCancel;
    private final long interruptDelay;
    private final String name;
    private RunningLongTask runningTask;
    private ExecutorService executor;
    private Timer cancelTimer;
    private LongTaskListener listener;
    private LongTaskErrorHandler errorHandler;
    private LongTaskErrorHandler defaultErrorHandler;

    /**
     * Creates a new long task executor.
     * @param doInBackground when <code>true</code>, the task will be executed in a separate thread
     * @param name the name of the executor, used to recognize threads by names
     * @param interruptDelay number of seconds to wait before calling <code>Thread.interrupt()</code> after a cancel request
     */
    public LongTaskExecutor(boolean doInBackground, String name, int interruptDelay) {
        this.inBackground = doInBackground;
        this.name = name;
        this.interruptCancel = true;
        this.interruptDelay = interruptDelay * 1000;
    }

    /**
     * Creates a new long task executor.
     * @param doInBackground doInBackground when <code>true</code>, the task will be executed in a separate thread
     * @param name the name of the executor, used to recognize threads by names
     */
    public LongTaskExecutor(boolean doInBackground, String name) {
        this(doInBackground, name, 0);
        this.interruptCancel = false;
    }

    /**
     * Creates a new long task executor.
     * @param doInBackground doInBackground when <code>true</code>, the task will be executed in a separate thread
     */
    public LongTaskExecutor(boolean doInBackground) {
        this(doInBackground, "LongTaskExecutor");
    }

    /**
     * Execute a long task with cancel and progress support. Task can be <code>null</code>.
     * In this case <code>runnable</code> will be executed normally, but without
     * cancel and progress support.
     * @param task the task to be executed, can be <code>null</code>.
     * @param runnable the runnable to be executed
     * @param taskName the name of the task, is displayed in the status bar if available
     * @param errorHandler error handler for exception retrieval during execution
     * @throws NullPointerException if <code>runnable</code> or <code>taskName</code> is null
     * @throws IllegalStateException if a task is still executing at this time
     */
    public void execute(LongTask task, final Runnable runnable, String taskName, LongTaskErrorHandler errorHandler) {
        if (runnable == null || taskName == null) {
            throw new NullPointerException();
        }
        if (runningTask != null) {
            throw new IllegalStateException("A task is still executing");
        }
        if (executor == null) {
            this.executor = new ThreadPoolExecutor(0, 1, 15, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory());
        }
        if (errorHandler != null) {
            this.errorHandler = errorHandler;
        }
        runningTask = new RunningLongTask(task, runnable, taskName);
        if (inBackground) {
            runningTask.future = executor.submit(runningTask);
        } else {
            runningTask.run();
        }
    }

    /**
     * Execute a long task with cancel and progress support. Task can be <code>null</code>.
     * In this case <code>runnable</code> will be executed normally, but without
     * cancel and progress support.
     * @param task the task to be executed, can be <code>null</code>.
     * @param runnable the runnable to be executed
     * @throws NullPointerException if <code>runnable</code> is null
     * @throws IllegalStateException if a task is still executing at this time
     */
    public void execute(LongTask task, Runnable runnable) {
        execute(task, runnable, "", null);
    }

    /**
     * Cancel the current task. If the task fails to cancel itself and if an <code>interruptDelay</code> has been specified,
     * the task will be <b>interrupted</b> after <code>interruptDelay</code>. Using <code>Thread.interrupt()</code> may cause
     * hazardous behaviours and should be avoided. Therefore any task should be cancellable.
     */
    public synchronized void cancel() {
        if (runningTask != null) {
            if (runningTask.isCancellable()) {
                if (interruptCancel) {
                    if (!runningTask.cancel()) {
                        cancelTimer = new Timer(name + "_cancelTimer");
                        cancelTimer.schedule(new InterruptTimerTask(), interruptDelay);
                    }
                } else {
                    runningTask.cancel();
                }
            }
        }
    }

    /**
     * Returns <code>true</code> if the executor is executing a task.
     * @return <code>true</code> if a task is running, <code>false</code> otherwise
     */
    public boolean isRunning() {
        return runningTask != null;
    }

    /**
     * Set the listener to this executor. Only a unique listener can be set to this executor. The listener
     * is called when the task terminates normally.
     * @param listener a listener for this executor
     */
    public void setLongTaskListener(LongTaskListener listener) {
        this.listener = listener;
    }

    /**
     * Set the default error handler. Use error handlers to get errors and exceptions thrown during
     * tasks execution.
     * @param errorHandler the default error handler
     */
    public void setDefaultErrorHandler(LongTaskErrorHandler errorHandler) {
        if (errorHandler != null) {
            this.defaultErrorHandler = errorHandler;
        }
    }

    private synchronized void finished() {
        if (cancelTimer != null) {
            cancelTimer.cancel();
        }
        LongTask task = runningTask.task;
        runningTask = null;
        errorHandler = null;
        if (listener != null) {
            listener.taskFinished(task);
        }
    }

    /**
     * Inner class for associating a task to its Future instance
     */
    private class RunningLongTask implements Runnable {

        private final LongTask task;
        private final Runnable runnable;
        private Future future;
        private ProgressTicket progress;

        public RunningLongTask(LongTask task, Runnable runnable, String taskName) {
            this.task = task;
            this.runnable = runnable;
            ProgressTicketProvider progressProvider = Lookup.getDefault().lookup(ProgressTicketProvider.class);
            if (progressProvider != null) {
                this.progress = progressProvider.createTicket(taskName, new Cancellable() {

                    public boolean cancel() {
                        LongTaskExecutor.this.cancel();
                        return true;
                    }
                });
                if (task != null) {
                    task.setProgressTicket(progress);
                } else {
                    progress.start();
                }
            }
        }

        public void run() {
            try {
                runnable.run();
            } catch (Exception e) {
                LongTaskErrorHandler err = errorHandler;
                finished();
                if (progress != null) {
                    progress.finish();
                }
                if (err != null) {
                    err.fatalError(e);
                } else if (defaultErrorHandler != null) {
                    defaultErrorHandler.fatalError(e);
                } else {
                    Logger.getLogger("").log(Level.SEVERE, "", e);
                }
            }

            finished();
            if (progress != null) {
                progress.finish();
            }
        }

        public boolean cancel() {
            /*if (inBackground) {
            if (future != null && future.cancel(false)) {
            return true;
            }
            }*/
            if (task != null) {
                return task.cancel();
            }
            return false;
        }

        public boolean isCancellable() {
            if (inBackground) {
                if (!future.isCancelled()) {
                    return true;
                }
                return false;
            }
            return true;
        }
    }

    /**
     * Inner class for naming the executor service thread
     */
    private class NamedThreadFactory implements ThreadFactory {

        public Thread newThread(Runnable r) {
            return new Thread(r, name);
        }
    }

    private class InterruptTimerTask extends TimerTask {

        @Override
        public void run() {
            if (runningTask != null) {
                System.out.println("Interrupt task");
                cancelTimer = null;
                if (runningTask.progress != null) {
                    runningTask.progress.finish();
                }
                finished();
                executor.shutdownNow();
                executor = null;
            }
        }
    }
}
TOP

Related Classes of org.gephi.utils.longtask.api.LongTaskExecutor$RunningLongTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.