Class AsyncTaskManager<R>
- Type Parameters:
R
- asynchronous task result type
VaadinSession
lock) entirely within the context of a locked VaadinSession
.
Instances of this class manage some background activity or task that is initiated from within a VaadinSession
.
The task runs asynchronously in the background without holding the VaadinSession
lock. Once completed, the
result is reported back to the locked VaadinSession
and the configured
result consumer, if any.
Only one such task is allowed to be executing at any given time: if a second task is started while an existing task is
still in progress, the first task is automatically canceled. Tasks are initiated via startTask()
and may be canceled at any time via cancelTask()
.
Results returned from successful task executions are delivered to the configured result consumer, if any.
Safety Guarantees
This class handles all required synchronization and locking. It guarantees that at most one background task
can be executing at a time, that all operations are atomic, that listener notifications are delivered in proper order,
and that no race conditions can occur. For example, if a background task tries to report back at the same time a Vaadin
thread invokes cancelTask()
, then the task will always appear to have either completed successfully or been canceled.
Instances bind to the current VaadinSession
at construction time and may only be used with that session.
If any method is invoked with the wrong VaadinSession
locking state, an immediate exception is thrown.
Therefore, thread safety is not only provided but enforced.
If a current UI exists when startTask(org.dellroad.stuff.vaadin24.util.AsyncTask<? extends R>)
is invoked, it will also be restored
(along with the current VaadinSession
) when any corresponding callbacks are invoked, unless it has since
been detached.
Event Notifications
Instances support event notification via addAsyncTaskStatusChangeListener()
.
All notifications are delivered within the context of the locked VaadinSession
.
On task start, a STARTED
notification is generated. When the task finishes,
the outcome - one of: COMPLETED
,
CANCELED
, or FAILED
- is reported.
Proper ordering of event notifications is guaranteed:
- Exactly one
STARTED
notification and exactly oneCOMPLETED
,CANCELED
, orFAILED
notification will be delivered for each task initiated bystartTask()
. STARTED
notifications are always delivered before the correspondingCOMPLETED
,CANCELED
, orFAILED
notification for the same task.- The
COMPLETED
,CANCELED
, orFAILED
notification for a task is always delivered before theSTARTED
notification for any subsequent task. - Tasks are executed, and corresponding notifications are delivered, in the same order that they are started.
- See Also:
-
Field Summary
Modifier and TypeFieldDescriptionprotected final VaadinSession
TheVaadinSession
with which this instance is associated. -
Constructor Summary
ConstructorDescriptionDefault constructor.AsyncTaskManager
(Function<? super Runnable, ? extends Future<?>> executor) Constructor. -
Method Summary
Modifier and TypeMethodDescriptionAdd aAsyncTaskStatusChangeListener
to this instance.long
Cancels the current outstanding asynchronous task, if any, and returns its unique ID.long
Get the ID of the currently outstanding asynchronous task, if any.Get theVaadinSession
to which this instance is associated.protected void
handleTaskException
(long id, Throwable t) Invoked when an exception other thanInterruptedException
is thrown by theAsyncTask
.protected void
handleTaskResult
(long id, R result) Process the result from a successfully completed asynchronous task.protected void
invokeTask
(long id, AsyncTask<? extends R> task) Perform the asynchronous task.boolean
isBusy()
Determine whether there is an outstanding asynchronous task in progress.protected long
Get the next unique task ID.protected void
notifyListeners
(UI ui, AsyncTaskStatusChangeEvent<R> event) Notify listeners.protected boolean
reportTask
(long id, R result, Throwable exception) Report the outcome of an asynchronous task (whether successful or otherwise) back to theVaadinSession
.void
setAsyncExecutor
(Function<? super Runnable, ? extends Future<?>> executor) Configure the executor used for async tasks.void
setResultConsumer
(BiConsumer<? super Long, ? super R> resultConsumer) Configure where successful results are delivered.long
Trigger execution of a new asynchronous task.protected void
Perform the given action with the givenUI
as current, if not null and still associated with the currentVaadinSession
.
-
Field Details
-
session
TheVaadinSession
with which this instance is associated.
-
-
Constructor Details
-
AsyncTaskManager
public AsyncTaskManager()Default constructor.Caller must configure an async executor via
setAsyncExecutor()
.- Throws:
IllegalStateException
- if there is noVaadinSession
associated with the current thread
-
AsyncTaskManager
Constructor.- Parameters:
executor
- the executor used to execute async tasks, or null for none- Throws:
IllegalStateException
- if there is noVaadinSession
associated with the current thread
-
-
Method Details
-
getVaadinSession
Get theVaadinSession
to which this instance is associated.- Returns:
- this instance's
VaadinSession
, never null
-
setAsyncExecutor
Configure the executor used for async tasks.The executor must execute tasks with this instance's VaadinSession unlocked.
Note: when an in-progress task is canceled via
cancelTask()
, thenFuture.cancel()
will be invoked on theFuture
returned by the executor.- Parameters:
executor
- the thing that launches background tasks, or null for none
-
setResultConsumer
Configure where successful results are delivered.The given consumer will always be invoked with this instance's VaadinSession locked.
- Parameters:
resultConsumer
- recipient for successful task results, taking task ID and result, or null to discard task results
-
startTask
Trigger execution of a new asynchronous task.If there is already an asynchronous task in progress, this method will cancel it first. You can safely check this ahead of time via
isBusy()
; this is race-free as long as the session lock is held across both method invocations.- Parameters:
task
- performs the desired task and returns some result- Returns:
- unique ID for this task execution
- Throws:
IllegalStateException
- if this instance's VaadinSession is not locked by the current threadIllegalStateException
- if there is no executor configuredIllegalArgumentException
- iftask
is null
-
isBusy
public boolean isBusy()Determine whether there is an outstanding asynchronous task in progress.Equivalent to:
getCurrentTaskId() != 0
.- Returns:
- true if an asynchronous task is currently executing, otherwise false
- Throws:
IllegalStateException
- if the current thread is not associated with this instance's session
-
getCurrentTaskId
public long getCurrentTaskId()Get the ID of the currently outstanding asynchronous task, if any.- Returns:
- the unique ID of the current asynchronous task, if any, otherwise zero
- Throws:
IllegalStateException
- if the current thread is not associated with this instance's session
-
cancelTask
public long cancelTask()Cancels the current outstanding asynchronous task, if any, and returns its unique ID.Any currently executing asynchronous task canceled and
Future.cancel()
is invoked on it'sFuture
, which may result in the background thread being interrupted.This method guarantees that the corresponding task, if any, will have a
CANCELED
outcome.- Returns:
- the unique ID of the canceled task, if any, or zero if there is no task outstanding
- Throws:
IllegalStateException
- if the current thread is not associated with this instance's session
-
addAsyncTaskStatusChangeListener
Add aAsyncTaskStatusChangeListener
to this instance.- Parameters:
listener
- listener for notifications- Returns:
- listener registration
- Throws:
IllegalArgumentException
- iflistener
is nullIllegalStateException
- if the current thread is not associated with this instance's session
-
nextTaskId
protected long nextTaskId()Get the next unique task ID.Each invocation of this method returns a new value.
- Returns:
- unique task ID, never zero
-
invokeTask
Perform the asynchronous task.This method is invoked in the background, with this instance's session not locked.
When finished (regardless of the outcome) this method invokes
reportTask()
with this instance's session locked.- Parameters:
id
- task IDtask
- task to execute- Throws:
IllegalStateException
- if the current thread has this instance's session lockedIllegalArgumentException
- ifid
is zeroIllegalArgumentException
- iftask
is null
-
reportTask
Report the outcome of an asynchronous task (whether successful or otherwise) back to theVaadinSession
.This is invoked (indirectly) by
invokeTask()
with this instance's session locked.- Parameters:
id
- task IDresult
- task result; must be null if there was an exceptionexception
- thrown exception (InterruptedException
if interrupted), or null if there was no exception- Returns:
- true if
id
matched the current task ID, otherwise false - Throws:
IllegalStateException
- if the current thread is not associated with this instance's sessionIllegalArgumentException
- ifid
is zeroIllegalArgumentException
- ifresult
andexception
are both not null
-
notifyListeners
Notify listeners.This is invoked with this instance's session locked.
The implementation in
AsyncTaskManager
actually delivers the notifications later, in the manner ofVaadinSession.access()
.- Parameters:
ui
-UI
to make current, or null for noneevent
- status change event- Throws:
IllegalStateException
- if the current thread is not associated with this instance's sessionIllegalArgumentException
- ifevent
is zero
-
handleTaskResult
Process the result from a successfully completed asynchronous task.This is invoked with this instance's session locked.
The implementation in
AsyncTaskManager
passes the result to the configured result consumer, if any.- Parameters:
id
- task IDresult
- task result- Throws:
IllegalArgumentException
- ifresult
is nullIllegalStateException
- if the current thread is not associated with this instance's session
-
handleTaskException
Invoked when an exception other thanInterruptedException
is thrown by theAsyncTask
.Note: this method runs in the background thread and the
VaadinSession
will not be locked.The implementation in
AsyncTaskManager
just logs an error.- Parameters:
id
- the unique ID of the task that failedt
- the exception that was caught
-
withUI
Perform the given action with the givenUI
as current, if not null and still associated with the currentVaadinSession
.- Parameters:
ui
-UI
to make current, or null for noneaction
- action to perform
-