Executors
The ColdBox AsyncManager will allow you to register and manage different types of executors that can execute your very own tasks! Each executor acts as a singleton and can be configured uniquely. (See: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html)
You can also create executors on the fly so your async futures can use them as well for ONLY that execution stage.
Nice video explaining the Java Executor Service: https://www.youtube.com/watch?v=6Oo-9Can3H8&t=2s

Executor Types

The types that we currently support are:
    fixed : By default it will build one with 20 threads on it. Great for multiple task execution and worker processing.
Fixed Thread
    single : A great way to control that submitted tasks will execute in the order of submission much like a FIFO queue (First In First Out).
Single Thread
    cached : An unbounded pool where the number of threads will grow according to the tasks it needs to service. The threads are killed by a default 60 second timeout if not used and the pool shrinks back to 0.
Cached Executor
    scheduled : A pool to use for scheduled tasks that can run one time or periodically. The default scheduled task queue has 20 threads for processing.
Scheduled Executor
Please note that each executor is unique in its way of operation, so make sure you read about each type in the JavaDocs or watch this amazing video: https://www.youtube.com/watch?v=sIkG0X4fqs4

Executor Methods

Here are the methods you can use for registering and managing singleton executors in your application:
    newExecutor() : Create and register an executor according to passed arguments. If the executor exists, just return it.
    newScheduledExecutor() : Create a scheduled executor according to passed arguments. Shortcut to newExecutor( type: "scheduled" )
    newSingleExecutor() : Create a single thread executor. Shortcut to newExecutor( type: "single", threads: 1 )
    newCachedExecutor() : Create a cached thread executor. Shortcut to newExecutor( type: "cached" )
    getExecutor() : Get a registered executor registerd in this async manager
    getExecutorNames() : Get the array of registered executors in the system
    hasExecutor() : Verify if an executor exists
    deleteExecutor() : Delete an executor from the registry, if the executor has not shutdown, it will shutdown the executor for you using the shutdownNow() event
    shutdownExecutor() : Shutdown an executor or force it to shutdown, you can also do this from the Executor themselves. If an un-registered executor name is passed, it will ignore it
    shutdownAllExecutors() : Shutdown all registered executors in the system
    getExecutorStatusMap() : Returns a structure of status maps for every registered executor in the manager. This is composed of tons of stats about the executor.
And here are the full signatures:
1
/**
2
* Creates and registers an Executor according to the passed name, type and options.
3
* The allowed types are: fixed, cached, single, scheduled with fixed being the default.
4
*
5
* You can then use this executor object to submit tasks for execution and if it's a
6
* scheduled executor then actually execute scheduled tasks.
7
*
8
* Types of Executors:
9
* - fixed : By default it will build one with 20 threads on it. Great for multiple task execution and worker processing
10
* - single : A great way to control that submitted tasks will execute in the order of submission: FIFO
11
* - cached : An unbounded pool where the number of threads will grow according to the tasks it needs to service. The threads are killed by a default 60 second timeout if not used and the pool shrinks back
12
* - scheduled : A pool to use for scheduled tasks that can run one time or periodically
13
*
14
* @see https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
15
*
16
* @name The name of the executor used for registration
17
* @type The type of executor to build fixed, cached, single, scheduled
18
* @threads How many threads to assign to the thread scheduler, default is 20
19
* @debug Add output debugging
20
* @loadAppContext Load the CFML App contexts or not, disable if not used
21
*
22
* @return The ColdBox Schedule class to work with the schedule: coldbox.system.async.tasks.Executor
23
*/
24
Executor function newExecutor(
25
required name,
26
type = "fixed",
27
numeric threads = this.$executors.DEFAULT_THREADS,
28
boolean debug = false,
29
boolean loadAppContext = true
30
)
31
32
/**
33
* Shortcut to newExecutor( type: "scheduled" )
34
*/
35
Executor function newScheduledExecutor(
36
required name,
37
numeric threads = this.$executors.DEFAULT_THREADS,
38
boolean debug = false,
39
boolean loadAppContext = true
40
)
41
42
/**
43
* Shortcut to newExecutor( type: "single", threads: 1 )
44
*/
45
Executor function newSingleExecutor(
46
required name,
47
boolean debug = false,
48
boolean loadAppContext = true
49
)
50
51
/**
52
* Shortcut to newExecutor( type: "cached" )
53
*/
54
Executor function newCachedExecutor(
55
required name,
56
numeric threads = this.$executors.DEFAULT_THREADS,
57
boolean debug = false,
58
boolean loadAppContext = true
59
)
60
61
/**
62
* Get a registered executor registerd in this async manager
63
*
64
* @name The executor name
65
*
66
* @throws ExecutorNotFoundException
67
* @return The executor object: coldbox.system.async.tasks.Executor
68
*/
69
Executor function getExecutor( required name )
70
71
/**
72
* Get the array of registered executors in the system
73
*
74
* @return Array of names
75
*/
76
array function getExecutorNames()
77
78
/**
79
* Verify if an executor exists
80
*
81
* @name The executor name
82
*/
83
boolean function hasExecutor( required name )
84
85
/**
86
* Delete an executor from the registry, if the executor has not shutdown, it will shutdown the executor for you
87
* using the shutdownNow() event
88
*
89
* @name The scheduler name
90
*/
91
AsyncManager function deleteExecutor( required name )
92
93
/**
94
* Shutdown an executor or force it to shutdown, you can also do this from the Executor themselves.
95
* If an un-registered executor name is passed, it will ignore it
96
*
97
* @force Use the shutdownNow() instead of the shutdown() method
98
*/
99
AsyncManager function shutdownExecutor( required name, boolean force = false )
100
101
/**
102
* Shutdown all registered executors in the system
103
*
104
* @force By default (false) it gracefullly shuts them down, else uses the shutdownNow() methods
105
*
106
* @return AsyncManager
107
*/
108
AsyncManager function shutdownAllExecutors( boolean force = false )
109
110
/**
111
* Returns a structure of status maps for every registered executor in the
112
* manager. This is composed of tons of stats about the executor
113
*
114
* @name The name of the executor to retrieve th status map ONLY!
115
*
116
* @return A struct of metadata about the executor or all executors
117
*/
118
struct function getExecutorStatusMap( name )
Copied!

Single Use Executors

If you just want to use executors for different completion stages and then discard them, you can easily do so by using the $executors public component in the AsyncManager. This object can be found at: coldbox.system.async.util.Executors:
1
asyncManager.$executors.{creationMethod}()
Copied!
The available creation methods are:
1
this.DEFAULT_THREADS = 20;
2
/**
3
* Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue.
4
*
5
* @see https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
6
*
7
* @threads The number of threads in the pool, defaults to 20
8
*
9
* @return ExecutorService: The newly created thread pool
10
*/
11
function newFixedThreadPool( numeric threads = this.DEFAULT_THREADS )
12
13
/**
14
* Creates a thread pool that creates new threads as needed, but will
15
* reuse previously constructed threads when they are available.
16
*
17
* @see https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
18
*
19
* @return ExecutorService: The newly created thread pool
20
*/
21
function newCachedThreadPool()
22
23
/**
24
* Creates a thread pool that can schedule commands to run after a given delay,
25
* or to execute periodically.
26
*
27
* @see https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html
28
*
29
* @corePoolSize The number of threads to keep in the pool, even if they are idle, default is 20
30
*
31
* @return ScheduledExecutorService: The newly created thread pool
32
*/
33
function newScheduledThreadPool( corePoolSize = this.DEFAULT_THREADS )
Copied!
This way you can use them and discard them upon further processing.
1
// Create an IO bound pool with 100 threads to process my data
2
var dataQueue = [ data, data ... data1000 ];
3
var results = asyncManager
4
.newFuture( executor : asyncManager.$executors.newFixedThreadPool( 100 ) )
5
.allApply( dataQueue, ( item ) => dataService.process( item ) );
6
7
// Create two types of thread pools: 1) CPU intesive, 2) IO Intensive
8
var cpuBound = asyncManager.$executors.newFixedThreadPool( 4 );
9
var ioBound = asyncManager.$executors.newFixedThreadPool( 50 );
10
11
// Process an order with different executor pools
12
newFuture( () => orderService.getOrder(), ioBound )
13
.thenAsync( (order) => enrichOrder( order ), cpuBound )
14
.thenAsync( (order) => performPayment( order ), ioBound )
15
.thenAsync( (order) => dispatchOrder( order ), cpuBound )
16
.thenAsync( (order) => sendConfirmation( order ), ioBound );
Copied!

ColdBox Task Scheduler

Every ColdBox application will register a task scheduler called coldbox-tasks which ColdBox internal services can leverage it for tasks and schedules. It is configured with 20 threads and using the scheduled type. You can leverage if you wanted to for your own tasks as well.
1
property name="scheduler" inject="executor:coldbox-tasks";
2
3
async().getExecutor( "coldbox-tasks" );
Copied!

ColdBox Application Executor Registration

We have also added a new configuration struct called executors which you can use to register global executors or per-module executors.
1
// In your config/ColdBox.cfc
2
function configure(){
3
4
executors = {
5
"name" : {
6
"type" : "fixed",
7
"threads" : 100
8
}
9
};
10
11
}
Copied!
Each executor registration is done as a struct with the name of the executor and at most two settings:
    type : The executor type you want to register
    threads : The number of threads to assign to the executor

ModuleConfig.cfc

You can also do the same at a per-module level in your module's ModuleConfig.cfc.
1
// In your ModuleConfig.cfc
2
function configure(){
3
4
executors = {
5
"name" : {
6
"type" : "fixed",
7
"threads" : 100
8
}
9
};
10
11
}
Copied!
ColdBox will register your executors upon startup and un-register and shut them down when the module is unloaded.

WireBox Injection DSL

We have extended WireBox so you can inject registered executors using the following DSL: executors:{name}
1
// Inject an executor called `taskScheduler` the same name as the property
2
property name="taskScheduler" inject="executor";
3
// Inject the `coldbox-tasks` as `taskScheduler`
4
property name="taskScheduler" inject="executor:coldbox-tasks";
Copied!
Last modified 1yr ago