/* LocalTask.java created 2007-09-12 * */ package org.signalml.task; import java.util.Arrays; import java.util.Date; import java.util.UUID; import javax.swing.event.EventListenerList; import org.apache.log4j.Logger; import org.signalml.method.ComputationException; import org.signalml.method.InputDataException; import org.signalml.method.Method; import org.signalml.method.MethodExecutionTracker; import org.signalml.method.SuspendableMethod; import org.signalml.method.TrackableMethod; import org.signalml.plugin.export.method.SvarogTask; import org.signalml.task.TaskEvent.TaskEventType; import org.springframework.core.task.TaskExecutor; /** * LocalTask class enables managing of Tasks. * It also allows to controll method execution and receive progress feedback. * * @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o. */ public class LocalTask implements SvarogTask, MethodExecutionTracker, Runnable { /** * Logger for debug info. */ protected static final Logger logger = Logger.getLogger(LocalTask.class); private static final int STATISTICS_LENGTH = 20; private Method method; private String uid; private TaskInfo info; private volatile TaskStatus status; private final Object data; private volatile Object result; private volatile Exception exception; private volatile String message; private EventListenerList listenerList = new EventListenerList(); private int[] tickerLimits = new int[0]; private int[] tickers = new int[0]; private boolean collectTickStatistics; private long[] lastTickMillis = null; private long[][] tickStatisticTimes = null; private int[][] tickStatisticTicks = null; private int[] statisticPointers; private int[] statisticLengths; private Object executionMonitorObject = new Object(); private void debug(String message) { logger.debug("TASK [" + hashCode() + "]: " + message); } /** * Creates new instance of Task with specified Method and Data. * Sets Task's UID, creation time, status and prepares tickers. * * Note that this Task will not collect tick statistics. * * @param method method to be computed by this Task * @param data arguments of the method * @throws NullPointerException when specified Method is null */ public LocalTask(Method method, Object data) { this(method,data,false); } /** * Creates a new instance of Task with specified Method and Data. * Sets Task's UID, creation time, status and prepares tickers. * * When boolean collectTickStatistics is true and method is a {@link TrackableMethod}, this * Task will collect tick statistics which enables reading progress status. * * @param method method to be computed by this Task * @param data arguments of the method * @param collectTickStatistics is true if Task should collect tick statistics * @throws NullPointerException when specified Method is null */ public LocalTask(Method method, Object data, boolean collectTickStatistics) { debug("Creating new task"); if (method == null) { throw new NullPointerException(); } this.collectTickStatistics = collectTickStatistics; this.method = method; this.data = data; uid = UUID.randomUUID().toString(); info = new TaskInfo(); info.setCreateTime(new Date()); if (method instanceof TrackableMethod) { tickerLimits = new int[((TrackableMethod) method).getTickerCount()]; tickers = new int[tickerLimits.length]; if (collectTickStatistics) { lastTickMillis = new long[tickerLimits.length]; tickStatisticTimes = new long[tickerLimits.length][STATISTICS_LENGTH]; tickStatisticTicks = new int[tickerLimits.length][STATISTICS_LENGTH]; statisticPointers = new int[tickerLimits.length]; statisticLengths = new int[tickerLimits.length]; } } else { this.collectTickStatistics = false; } if ((method instanceof SuspendableMethod) && ((SuspendableMethod) method).isDataSuspended(data)) { status = TaskStatus.SUSPENDED; debug("Created as suspended"); } else { status = TaskStatus.NEW; debug("Created as new"); } } /** * Creates a new instance of Task with specified Method, Data and TaskStatus. When boolean * collectTickStatistics is true and method is a {@link TrackableMethod}, this Task will * collect tick statistics which enables reading progress status. * * @param method method to be computed by this Task * @param data arguments of the method * @param collectTickStatistics true if Task should collect tick statistics * @param status status of this Task * @throws IllegalArgumentException when Status is not a valid Task status * @throws NullPointerException when specified Method is null */ public LocalTask(Method method, Object data, boolean collectTickStatistics, TaskStatus status) { this(method,data,collectTickStatistics); switch (status) { case FINISHED : if (method instanceof TrackableMethod) { TrackableMethod trackableMethod = (TrackableMethod) method; int tickerCount = trackableMethod.getTickerCount(); tickerLimits = new int[tickerCount]; Arrays.fill(tickerLimits, 1); tickers = new int[tickerCount]; Arrays.fill(tickers, 1); } break; case SUSPENDED : case NEW : // do nothing break; default : // error for others throw new IllegalArgumentException("Cannot create tasks in this status [" + status + "]"); } this.status = status; } /** * Returns the method that is being executed by this task. * Note that when second thread calls getMethod() on the same object, it will have to wait for the first thread to complete the call to this method. * @return the method */ @Override public Method getMethod() { synchronized (this) { return method; } } /** * Returns the result of the execution. Note that "result" here may also mean * an exception that resulted from the execution. Tasks that did not yet finish * (either normally or with exception), including those that were aborted and will * never finish do not have a result to return and will throw an exception. Use * the {@link TaskStatus#isResultAvailable()} method on the task's status to test * for result availability. * * <p>Note that when second thread calls getResult() on the same object, it will have to wait for the first thread to complete the call to this method. * * @return the result descriptor * @throws InvalidTaskStateException thrown when the task doesn't have a result (possibly yet). */ @Override public TaskResult getResult() throws InvalidTaskStateException { synchronized (this) { if (!status.isResultAvailable()) { throw new InvalidTaskStateException("error.taskStateMustBeFinishedOrError"); } return new TaskResult(uid, info, status, result, exception); } } /** * Returns the data object used as input data for the computation. * @return the data object used as input data for the computation. */ @Override public Object getData() { return data; } /** * Returns task status. * Note that when second thread calls getStatus() on the same object, it will have to wait for the first thread to complete the call to this method. * @return the status */ @Override public TaskStatus getStatus() { synchronized (this) { return status; } } /** * Returns task information object. * Note that when second thread calls getTaskInfo() on the same object, it will have to wait for the first thread to complete the call to this method. * @return the task information object. */ @Override public TaskInfo getTaskInfo() { synchronized (this) { return info; } } /** * Returns this task's unique identifier (must be unique for all tasks of all types * AND accross VMs). * Note that when second thread calls getUID() on the same object, it will have to wait for the first thread to complete the call to this method. * @return the unique identifier. */ @Override public String getUID() { synchronized (this) { return uid; } } /** * Prepares starting the execution of the task (the computation) on the new thread if specified TaskExecutor is null, otherwise by this TaskExecutor. * Note that "starting" in this case menas only to post a request for starting by setting the status to ACTIVE_WAITING. * Note that when second thread calls start() on the same object, it will have to wait for the first thread to complete the call to this method. * @param t executor of Task * @throws InvalidTaskStateException thrown when the task status doesn't allow starting */ public void start(TaskExecutor t) throws InvalidTaskStateException { synchronized (this) { if (!status.isStartable()) { throw new InvalidTaskStateException("error.taskStatusMustBeNew"); } status = TaskStatus.ACTIVE_WAITING; if (t == null) { (new Thread(this)).start(); } else { t.execute(this); } debug("Task started"); } } /** * Starts the execution of the task (the computation) on the <b> current thread</b> if the task is currently startable. * The method returns only when the computation has finished (due to being completed, aborted, suspeneded or due to an exception). * Note that when second thread calls startAndDo() on the same object, it will have to wait for the first thread to complete the call to this method. * @throws InvalidTaskStateException thrown when the task status doesn't allow starting */ public void startAndDo() throws InvalidTaskStateException { synchronized (this) { if (!status.isStartable()) { throw new InvalidTaskStateException("error.taskStatusMustBeNew"); } status = TaskStatus.ACTIVE; info.setStartTime(new Date()); fireTaskStarted(); notifyAll(); } debug("Task starting on current thread"); run(); } /** * Starts or resumes the execution of the task (the computation) on the <b>current thread</b> * if the task is currently startable or resumable. The method returns only when the computation * has finished (due to being completed, aborted, suspeneded or due to an exception). * Note that when second thread calls doIfPossible() on the same object, it will have to wait for the first thread to complete the call to this method. */ @Override public void doIfPossible() { synchronized (this) { if (!status.isStartable() && !status.isResumable()) { // not possible return; } if (status.isResumable()) { status = TaskStatus.ACTIVE; info.setResumeTime(new Date()); fireTaskResumed(); debug("Task resuming on current thread"); } else if (status.isStartable()) { status = TaskStatus.ACTIVE; info.setStartTime(new Date()); fireTaskStarted(); debug("Task starting on current thread"); } notifyAll(); } run(); } /** * Aborts the execution of this task. The task must be running or exceptions will be thrown. Note that * "aborting" in this case menas only to post a request for abortion by setting the status to * REQUESTING_ABORT. It is up to the method's compute implementation to check for this status and exit * when it is detected. * * <p>This method may also wait for the task to abort, but this may lead to the calling thread being * permanently locked if the compute method never finishes. * * <p>Note that when second thread calls abort() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param wait whether to wait for the task to abort * @throws InvalidTaskStateException thrown when the task status doesn't allow aborting */ @Override public void abort(boolean wait) throws InvalidTaskStateException { synchronized (this) { if (!status.isAbortable()) { throw new InvalidTaskStateException("error.taskNotAbortable"); } if (status.isSuspended()) { onAbort(); // abort immediately } else { status = TaskStatus.REQUESTING_ABORT; fireTaskRequestChanged(); notifyAll(); debug("Task requesting abort, wait [" + wait + "]"); if (wait) { do { try { wait(); } catch (InterruptedException ex) {} } while (!status.isAborted()); debug("Task has been aborted"); } } } } /** * Suspends the execution of this task. The task must be running and the method it runs * must be suspendalbe or exceptions will be thrown. Note that "suspending" in this case menas * only to post a request for suspension by setting the status to REQUESTING_SUSPEND. * It is up to the method's compute implementation to check for this status and exit * when it is detected. * * <p>This method may also wait for the task to suspend, but this may lead to the calling thread being * permanently locked if the compute method never finishes. * * Note that when second thread calls suspend() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param wait whether to wait for the task to suspend * @throws InvalidTaskStateException thrown when the task status doesn't allow suspending or the method * isn't suspendable */ @Override public void suspend(boolean wait) throws InvalidTaskStateException { synchronized (this) { if (!(method instanceof SuspendableMethod) || !status.isSuspendable()) { throw new InvalidTaskStateException("error.taskNotSuspendable"); } status = TaskStatus.REQUESTING_SUSPEND; fireTaskRequestChanged(); notifyAll(); debug("Task requesting suspend, wait [" + wait + "]"); if (wait) { do { try { wait(); } catch (InterruptedException ex) {} } while (!status.isSuspended()); debug("Task has been suspended"); } } } /** * Resumes the execution of this task. The task must be suspended and the method it runs * must be suspendable or exceptions will be thrown. * * Note that "resuming" in this case menas only to post a request for resumption by setting the status to ACTIVE. * It is up to the method's compute implementation to check for this status and resume when it is detected. * * <p>This method may also wait for the task to resume, but this may lead to the calling thread being * permanently locked if the compute method never finishes. * * Note that when second thread calls resume() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param t executor of this task * @throws InvalidTaskStateException thrown when the task status doesn't allow resuming or the method * isn't suspendable */ public void resume(TaskExecutor t) throws InvalidTaskStateException { synchronized (this) { if (!(method instanceof SuspendableMethod) || !status.isResumable()) { throw new InvalidTaskStateException("error.taskNotSuspendable"); } status = TaskStatus.ACTIVE; info.setResumeTime(new Date()); fireTaskResumed(); notifyAll(); if (t == null) { (new Thread(this)).start(); } else { t.execute(this); } debug("Task resumed"); } } /** * Resumes the execution of this task. The task must be suspended and the method it runs * must be suspendable or exceptions will be thrown. * * <p>Note that when second thread calls resumeAndDo() on the same object, it will have to wait for the first thread to complete the call to this method. * * @throws InvalidTaskStateException thrown when the task status doesn't allow resuming or the method * isn't suspendable */ public void resumeAndDo() throws InvalidTaskStateException { synchronized (this) { if (!(method instanceof SuspendableMethod) || !status.isResumable()) { throw new InvalidTaskStateException("error.taskNotSuspendable"); } status = TaskStatus.ACTIVE; info.setResumeTime(new Date()); fireTaskResumed(); notifyAll(); } debug("Task resuming on current thread"); run(); } /** * Method called on aborting this Task. It sets status to ABORTED and time of end of execution. */ private void onAbort() { synchronized (this) { status = TaskStatus.ABORTED; info.setEndTime(new Date()); fireTaskAborted(); notifyAll(); debug("Task aborted"); } } /** * Method called on suspension this Task. It sets status to SUSPENDED and time of suspension of execution. */ private void onSuspend() { synchronized (this) { status = TaskStatus.SUSPENDED; info.setSuspendTime(new Date()); fireTaskSuspended(); notifyAll(); debug("Task suspended"); } } /** * Method called on finish of this Task. It sets status to FINISHED if no error occured, otherwise ERROR. * It also set a time of end of execution. */ private void onFinish() { synchronized (this) { if (exception != null) { status = TaskStatus.ERROR; debug("Task finished with error"); logger.error("Task finished with error", exception); } else { status = TaskStatus.FINISHED; debug("Task finished ok"); } info.setEndTime(new Date()); fireTaskFinished(); notifyAll(); } } /** * Computes this Task result. * If any exception occurs during computation process it sets this error as result. * Possible errors: * - InputDataException when the input Data is invalid * - ComputationException when computation fails for reasons other than bad input data * or when no result is returned * */ @Override public void run() { Object result = null; Exception exception = null; debug("Task entered run"); synchronized (this) { if (status.isActiveAndWaiting()) { status = TaskStatus.ACTIVE; info.setStartTime(new Date()); fireTaskStarted(); notifyAll(); } } synchronized (executionMonitorObject) { debug("Task begins to run"); try { try { result = method.compute(data, this); } catch (InputDataException ex) { debug("Input data exception"); exception = ex; } catch (ComputationException ex) { debug("Computation exception"); exception = ex; } } catch (Throwable t) { debug("Other exception or error"); exception = new ComputationException(t); } debug("Task finished running"); } synchronized (this) { debug("Analyzing run result"); if (exception != null) { this.result = null; this.exception = exception; debug("Exception detected"); onFinish(); } else if (result != null) { this.result = result; this.exception = null; debug("Result detected"); onFinish(); } else { if (status == TaskStatus.REQUESTING_ABORT) { this.result = null; this.exception = null; debug("Abort detected"); onAbort(); } else if (status == TaskStatus.REQUESTING_SUSPEND) { this.result = null; this.exception = null; debug("Suspend detected"); onSuspend(); } else { this.result = null; this.exception = new ComputationException("error.noResult"); debug("Finish with no result - throw exception"); onFinish(); } } } } /** * Adds a listener which will be notified of task events. * Note that when second thread calls addTaskEventListener() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param listener the listener to add */ @Override public void addTaskEventListener(TaskEventListener listener) { synchronized (this) { listenerList.add(TaskEventListener.class, listener); } } /** * Removes a listener. * Note that when second thread calls removeTaskEventListener() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param listener the listener to remove */ @Override public void removeTaskEventListener(TaskEventListener listener) { synchronized (this) { listenerList.remove(TaskEventListener.class, listener); } } /** * Checks if the controlling code requests the method to abort computations. * * @return true if an abortion request is posted */ @Override public boolean isRequestingAbort() { return status.isRequestingAbort(); } /** * Checks if the controlling code requests the method to suspend computations. * * @return true if an suspention request is posted */ @Override public boolean isRequestingSuspend() { return status.isRequestingSuspend(); } /** * Retrieves the last message set by the computation code with the * {@link #setMessage(String)} method. Initially the Task has no * message and null is returned. * * @return the message or null if no message has been posted */ @Override public String getMessage() { return message; } /** * Posts a task message, which may be displayed by any controling application. * * <p>Typically this method will be called from within the {@link Method#compute} method to * indicate the current stage or status of the ongoing computation. * * <p>Note that when second thread calls setMessage() on the same object, it will have to * wait for the first thread to complete the call to this method. * * @param message the new message */ @Override public void setMessage(String message) { synchronized (this) { this.message = message; debug("Task message set to [" + message + "]"); fireTaskMessageSet(); } } /** * Returns the limits (maximum values) for the tickers associated with this task. For * methods which aren't trackable an empty array should be returned. For trackable methods * the length of the array should correspond to what is returned by * {@link TrackableMethod#getTickerCount()} for the executed method. * * <p>Note that when second thread calls getTickerLimits() on the same object, it will * have to wait for the first thread to complete the call to this method. * * @return the ticker limits */ @Override public int[] getTickerLimits() { synchronized (this) { return Arrays.copyOf(tickerLimits, tickerLimits.length); } } /** * Sets the limits (maximum values) for the tickers associated with this task. The method * should generally throw IndexOutOfBoundsException if the method is not trackable or if * the given array is longer than the ticker count for the method. * * <p>Note that when second thread calls setTickerLimits on the same object, it will * have to wait for the first thread to complete the call to this method. * @param initial the array of ticker limits */ @Override public void setTickerLimits(int[] initial) { synchronized (this) { for (int i=0; i<initial.length; i++) { tickerLimits[i] = Math.max(0, initial[i]); tickers[i] = Math.min(tickers[i], initial[i]); } if (collectTickStatistics) { clearStatistics(); } fireTaskTickerUpdated(); } } /** * Sets a single ticker limit. See {@link #setTickerLimits(int[])}. * * <p>Note that when second thread calls setTickerLimit() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param index the index of the ticker * @param limit the new limit */ @Override public void setTickerLimit(int index, int limit) { synchronized (this) { tickerLimits[index] = Math.max(0, limit); tickers[index] = Math.min(tickers[index], limit); if (collectTickStatistics) { clearStatistics(index); } fireTaskTickerUpdated(); } } /** * Returns the current values for the tickers associated with this task. For methods which * aren't trackable an empty array should be returned. For trackable methods the length of the array * should correspond to what is returned by {@link TrackableMethod#getTickerCount()} for the executed * method. * * <p>Note that when second thread calls getTickers() on the same object, it will have to wait for the first thread to complete the call to this method. * * @return the ticker values */ @Override public int[] getTickers() { synchronized (this) { return Arrays.copyOf(tickers, tickers.length); } } /** * Resets all ticker values to 0. * * <p>Note that when second thread calls resetTickers() on the same object, it will have to wait for the first thread to complete the call to this method. */ @Override public void resetTickers() { synchronized (this) { Arrays.fill(tickers, 0); if (collectTickStatistics) { clearStatistics(); } fireTaskTickerUpdated(); } } /** * Sets the current values for the tickers associated with this task. The method * should generally throw IndexOutOfBoundsException if the method is not trackable or if * the given array is longer than the ticker count for the method. * * <p>Note that when second thread calls setTickers() on the same object, it will have to * wait for the first thread to complete the call to this method. * * @param tickers an array of ticker values */ @Override public void setTickers(int[] tickers) { synchronized (this) { int oldVal; for (int i=0; i<tickers.length; i++) { oldVal = this.tickers[i]; this.tickers[i] = Math.max(0, Math.min(tickerLimits[i], tickers[i])); if (collectTickStatistics) { addToStatistics(i, oldVal, tickers[i]); } } fireTaskTickerUpdated(); } } /** * Sets a single ticker value. See {@link #setTickers(int[])}. * * <p>Note that when second thread calls setTicker() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param index the index of the ticker * @param value the new value */ @Override public void setTicker(int index, int value) { synchronized (this) { int oldVal = tickers[index]; tickers[index] = Math.max(0, Math.min(tickerLimits[index], value)); if (collectTickStatistics) { addToStatistics(index, oldVal, tickers[index]); } fireTaskTickerUpdated(); } } /** * Advances the given ticker by one. * * <p>Note that when second thread calls tick() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param index the index of the ticker */ @Override public void tick(int index) { synchronized (this) { int oldVal = tickers[index]; if (tickers[index] < tickerLimits[index]) { tickers[index]++; } if (collectTickStatistics) { addToStatistics(index, oldVal, tickers[index]); } fireTaskTickerUpdated(); } } /** * Advances the given ticker by the given value * * <p>Note that when second thread calls tick() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param index the index of the ticker * @param step the increase */ @Override public void tick(int index, int step) { synchronized (this) { int oldVal = tickers[index]; tickers[index] = Math.min(tickerLimits[index], tickers[index] + step); if (collectTickStatistics) { addToStatistics(index, oldVal, tickers[index]); } fireTaskTickerUpdated(); } } /** * Should return the expected number of seconds until given ticker is complete. This * should return <code>null</code> if the expected time is unknown or uncertain. * * <p>Note that when second thread calls getExpectedSecondsUntilComplete() on the same object, it will have to wait for the first thread to complete the call to this method. * * @param index the index of the ticker * @return expected time in seconds or <code>null</code> when unknown. */ @Override public Integer getExpectedSecondsUntilComplete(int index) { if (!collectTickStatistics) { return null; } synchronized (this) { if (statisticLengths[index] < 3) { return null; } int i; int pos = statisticPointers[index]; long totalTime = 0; int totalTicks = 0; for (i=0; i<statisticLengths[index]; i++) { pos = (STATISTICS_LENGTH + pos - 1) % STATISTICS_LENGTH; totalTime += tickStatisticTimes[index][pos]; totalTicks += tickStatisticTicks[index][pos]; } float tps = (totalTicks) / (((float) totalTime)/1000); int seconds = Math.round((tickerLimits[index]-tickers[index]) / tps); // subtract elapsed seconds -= ((System.currentTimeMillis()-lastTickMillis[index])/1000); if (seconds < 0) { seconds = 0; } return new Integer(seconds); } } private void addToStatistics(int index, int oldVal, int newVal) { if (newVal <= oldVal) { /* no advance */ return; } long millis = System.currentTimeMillis(); if (lastTickMillis[index] == 0) { lastTickMillis[index] = millis; /* no sample this time */ return; } long elapsed = millis - lastTickMillis[index]; lastTickMillis[index] = millis; int gained = newVal - oldVal; tickStatisticTimes[index][statisticPointers[index]] = elapsed; tickStatisticTicks[index][statisticPointers[index]] = gained; statisticPointers[index] = (statisticPointers[index] + 1) % STATISTICS_LENGTH; if (statisticLengths[index] < STATISTICS_LENGTH) { statisticLengths[index]++; } } private void clearStatistics() { for (int i=0; i<tickerLimits.length; i++) { clearStatistics(i); } } private void clearStatistics(int index) { lastTickMillis[index] = 0; statisticLengths[index] = 0; statisticPointers[index] = 0; Arrays.fill(tickStatisticTimes[index], 0); Arrays.fill(tickStatisticTicks[index], 0); } /** * Starts executing this Task for the first time. */ protected void fireTaskStarted() { Object[] listeners = listenerList.getListenerList(); TaskEvent taskEvent = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TaskEventListener.class) { if (taskEvent == null) { taskEvent = new TaskEvent(this, TaskEventType.TASK_STARTED, getStatus()); } ((TaskEventListener)listeners[i+1]).taskStarted(taskEvent); } } } /** * Starts executing this Task after suspending it. */ protected void fireTaskSuspended() { Object[] listeners = listenerList.getListenerList(); TaskEvent taskEvent = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TaskEventListener.class) { if (taskEvent == null) { taskEvent = new TaskEvent(this, TaskEventType.TASK_SUSPENDED, getStatus()); } ((TaskEventListener)listeners[i+1]).taskSuspended(taskEvent); } } } /** * Starts executing this Task after requesting aborting or suspense of it. */ protected void fireTaskRequestChanged() { Object[] listeners = listenerList.getListenerList(); TaskEvent taskEvent = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TaskEventListener.class) { if (taskEvent == null) { taskEvent = new TaskEvent(this, TaskEventType.TASK_REQUEST_CHANGED, getStatus()); } ((TaskEventListener)listeners[i+1]).taskRequestChanged(taskEvent); } } } /** * Starts executing this Task after resuming it. */ protected void fireTaskResumed() { Object[] listeners = listenerList.getListenerList(); TaskEvent taskEvent = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TaskEventListener.class) { if (taskEvent == null) { taskEvent = new TaskEvent(this, TaskEventType.TASK_RESUMED, getStatus()); } ((TaskEventListener)listeners[i+1]).taskResumed(taskEvent); } } } /** * Starts executing this Task after aborting of it */ protected void fireTaskAborted() { Object[] listeners = listenerList.getListenerList(); TaskEvent taskEvent = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TaskEventListener.class) { if (taskEvent == null) { taskEvent = new TaskEvent(this, TaskEventType.TASK_ABORTED, getStatus()); } ((TaskEventListener)listeners[i+1]).taskAborted(taskEvent); } } } /** * Starts executing this Task after finishing it */ protected void fireTaskFinished() { Object[] listeners = listenerList.getListenerList(); TaskEvent taskEvent = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TaskEventListener.class) { if (taskEvent == null) { taskEvent = new TaskEvent(this, TaskEventType.TASK_FINISHED, getStatus(), getResult()); } ((TaskEventListener)listeners[i+1]).taskFinished(taskEvent); } } } /** * Starts executing this Task after setting message */ protected void fireTaskMessageSet() { Object[] listeners = listenerList.getListenerList(); TaskEvent taskEvent = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TaskEventListener.class) { if (taskEvent == null) { taskEvent = new TaskEvent(this, TaskEventType.TASK_MESSAGE_SET, getStatus(), getMessage()); } ((TaskEventListener)listeners[i+1]).taskMessageSet(taskEvent); } } } /** * Starts executing this Task after updating of ticker */ protected void fireTaskTickerUpdated() { Object[] listeners = listenerList.getListenerList(); TaskEvent taskEvent = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==TaskEventListener.class) { if (taskEvent == null) { taskEvent = new TaskEvent(this, TaskEventType.TASK_TICKER_UPDATED, getStatus(), getTickerLimits(), getTickers()); } ((TaskEventListener)listeners[i+1]).taskTickerUpdated(taskEvent); } } } }