public class ValidationExecutor
extends java.util.concurrent.ThreadPoolExecutor
implements java.util.concurrent.RejectedExecutionHandler
ThreadPoolExecutor
specialized in dealing with
EntityValidationThread
s and validation tasks (see
EntityValidationTaskBase
). This implementation creates a thread pool
containing just one thread, meaning all validation tasks are run one after
another on that one thread. Especially for Level-3 validation tasks this is
probably exactly what you want. These tasks are run upon CRUD events, and you
don't want the database to be crawled to validate entire object graphs every
time a CRUD event takes place, especially since one CRUD operation may be
meant to cancel or correct a previous CRUD operation (e.g. a user of the
taxonomic editor may realize he/she did something wrong and then quickly
correct it).
Although a ValidationExecutor
sets up a thread pool containing just a
single thread, it does not logically or functionally depend on the
thread pool containing at most one thread. Thus, should performance become an
issue, and concurrency the solution, increasing the pool size is still an
option. For example, Level-2 validation tasks might be quite amenable to
being executed concurrently.
The reason we extend ThreadPoolExecutor
rather than simply use
Executors.newSingleThreadExecutor()
is that we need access to the
threads in the thread pool for the reason indicated above: if an entity
annotated with Level-2 or Level-3 validation constraints is updated, it will
be validated on the validation thread. However, if it is quickly thereafter
updated again, you really would like to terminate the first validation if
it's still running. After all, it doesn't make sense to validate an entity in
a state that it no longer has. For Level-2 validations this may not be so
important, because they are likely to run fast. But for Level-3 validations
you want to prevent needless queueing and execution of long-running tasks.
Thus, you really would like to know which entity is being validated on the
validation thread. The ThreadPoolExecutor
provides a
beforeExecute(Thread, Runnable)
method, passing us the thread and
the task that it is about to run. This allows us to track the threads in the
thread pool.
If the ValidationExecutor
detects that a validation task enters the
task queue that will validate the same entity as the entity currently being
validated on the validation thread, it will call
EntityValidationThread.setTerminationRequested(boolean)
. This gives
the ConstraintValidator
running in the validation thread a chance to
terminate itself:
if(Thread.currentThread() instanceof EntityValidationThread) {
EntityValidationThread evt = (EntityValidationThread) Thread.currentThread();
if(evt.isTerminationRequested()) {
// Stop with what I am doing
}
}
Constraint validators are free to include this logic or not. If they know
themselves to be short-lived it may not be worth it. But if they potentially
take a lot of time to complete, they can and and probably should include this
logic to prevent needless queueing and queue overruns. This would make them
dependent, though, on at least the EntityValidationThread
class, so
there are some architectural issues here.
java.util.concurrent.ThreadPoolExecutor.AbortPolicy, java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy, java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy, java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
Constructor and Description |
---|
ValidationExecutor()
Creates a
ValidationExecutor with a task queue size of 1000. |
ValidationExecutor(int taskQueueSize)
Creates a
ValidationExecutor with a custom task queue size. |
Modifier and Type | Method and Description |
---|---|
protected void |
beforeExecute(java.lang.Thread thread,
java.lang.Runnable runnable) |
void |
rejectedExecution(java.lang.Runnable r,
java.util.concurrent.ThreadPoolExecutor executor)
Implements the one method from
RejectedExecutionHandler , which is
called in case of task queue overruns. |
void |
setMaximumPoolSize(int maximumPoolSize)
Overrides method from
ThreadPoolExecutor to prevent thread pool
size from being altered. |
afterExecute, allowCoreThreadTimeOut, allowsCoreThreadTimeOut, awaitTermination, execute, finalize, getActiveCount, getCompletedTaskCount, getCorePoolSize, getKeepAliveTime, getLargestPoolSize, getMaximumPoolSize, getPoolSize, getQueue, getRejectedExecutionHandler, getTaskCount, getThreadFactory, isShutdown, isTerminated, isTerminating, prestartAllCoreThreads, prestartCoreThread, purge, remove, setCorePoolSize, setKeepAliveTime, setRejectedExecutionHandler, setThreadFactory, shutdown, shutdownNow, terminated, toString
public ValidationExecutor()
ValidationExecutor
with a task queue size of 1000. Thus
there can be at most 1000 pending validations. Thereafter newly submitted
validation tasks will simply be discarded. See
rejectedExecution(Runnable, ThreadPoolExecutor)
.public ValidationExecutor(int taskQueueSize)
ValidationExecutor
with a custom task queue size.taskQueueSize
- public void rejectedExecution(java.lang.Runnable r, java.util.concurrent.ThreadPoolExecutor executor)
RejectedExecutionHandler
, which is
called in case of task queue overruns. Because Level-2 and Level-3
validations may not obstruct the CRUD events that triggered them, or
impair the stability of the system as a whole, this method only writes an
error message to the log4j log file. Thus, task queue overruns may cause
Level-2 and/or Level-3 constraint violations to creep into the database.
And thus, some other, batch-like process needs to crawl the entire
database in search of Level-2 and Level-3 constraint violations every
once in a while.rejectedExecution
in interface java.util.concurrent.RejectedExecutionHandler
public void setMaximumPoolSize(int maximumPoolSize)
ThreadPoolExecutor
to prevent thread pool
size from being altered. Will throw a RuntimeException. Future versions
could abandon this restriction once it has become clear that concurrent
execution of Level-2 and/or Level-3 validations constitutes no problem
and may solve performance problems.setMaximumPoolSize
in class java.util.concurrent.ThreadPoolExecutor
protected void beforeExecute(java.lang.Thread thread, java.lang.Runnable runnable)
beforeExecute
in class java.util.concurrent.ThreadPoolExecutor
Copyright © 2007-2020 EDIT. All Rights Reserved.