# Async Extension for Activiti & APS This library provides a way for administrators to assign asynchronous executors to specific process definitions or their activities. Activiti executes activities in either the web container's thread pool or an internal `AsyncExecutor`. There are several `AsyncExecutor` implementations available, but the default implementation is a simple thread pool. All executions originating from the web container, like REST calls, will execute the processes and activities until they reach an asynchronous element. This could be any intermediary catching elements or any element marked as "Asynchronous". At that point, the execution becomes a job, queued for execution with a single `AsyncExecutor` implementation. This extension changes that single `AsyncExecutor` into 1-to-many via start-up system properties. This project includes the base Activiti implementation in `activiti-ext` and the Alfresco Process Services enablement implementation in `activiti-app-ext`. Use both with APS and only the former with any other Activiti-based engine. ## Installation To use this extension, it depends on your Activiti implementation. ### Without APS You need to write some code to inject the `SpringJobAwareAsyncExecutor` or `PojoJobAwareAsyncExecutor` into your `ProcessEngineConfiguration`. This is very dependent on how your implementation is setup. Then you need to include this JAR in the classpath of your Activiti application. Once installed, remember to configure the extension as explained in the [Configuration](#spring-configuration) section below. ### With APS Include the JAR in the classpath of your APS application. This is best done by not changing the `activiti-app.war` file, but instead including it within the classpath using your web container configuration. For Apache Tomcat, you would add or modify the following context file: `conf/Catalina/localhost/activiti-app.xml`. Its related contents would be: ```xml ``` Notice the use of `PostResources` instead of `PreResources`. This library needs to be loaded after the web application. This is the best way to load any other extensions or customization to the Activiti App, including `JavaDelegate` implementations. If you use the `-security` switch, you will need to give this path permissions in the `catalina.policy` file: ```properties grant codeBase "file:${catalina.base}/ext/-" { permission java.security.AllPermissions } ``` ## Support Matrix | Extension | APS | | --------- | ----- | | v1.0 | v2.x+ | ## Spring Configuration This configuration is the same, whether you are using APS or the `SpringJobAwareAsyncExecutor` without APS. With the `PojoJobAwareAsyncExecutor`, configuration is defined by the developer who injected it into your application. If no related properties are supplied, the `SpringJobAwareAsyncExecutor` will act as the `DefaultAsyncJobExecutor`. This means all asynchronous executions on all process definitions will use the same `DefaultAsyncJobExecutor` and its single shared thread pool. **This is the default behavior**, so no functionality is lost; but none is gained either. To enable the feature, you will need to set some properties: | Property | Description | | --------------------------------------------------- | ----------- | | `inteligr8.async.executors.byProcessDefinitionKeys` | A comma, semi-colon, or pipe delimited list of process definition keys. A process definition key is the process ID as defined in the BPMN, which does not include version specifications. All asynchronous executions that originate in a listed process definition will use its own thread pool via its own `AsyncExecutor`. That executor is configurable as prescribed in the next table. | | `inteligr8.async.executors.byActivityIds` | A comma, semi-colon, or pipe delimited list of unique activity identifiers. An activity identifier includes the process definition key in which it is defined, combined with the activity ID of the asynchronous element. The process definition key is the process ID as defined in the BPMN, which does not include version specifications. An asynchronous element is any catching intermediary element or any activity with the "Asynchronous" flag. These executions will use their own thread pool via an `AsyncExecutor`. That executor is configurable as prescribed in the next table. | Any executions that do not match the above configurations will use the failsafe `AsyncExecutor`, which is the `DefaultAsyncJobExecutor` (or the default provided by APS). To configure each individual executor, use the properties below. Substitute `{id}` with an enumerated value used in either list in the table above. The failsafe/fallback/default executor has an `{id}` of `_fallback`. | Property | Default | | -------------------------------------------------------------------- | ------- | | `inteligr8.async.executor.{id}.corePoolSize` | `1` | | `inteligr8.async.executor.{id}.maxPoolSize` | `4` | | `inteligr8.async.executor.{id}.queueSize` | `512` | How many jobs can wait for a thread concurrently? If this is exceeded, otherwise pending jobs will be rejected and deadletter or rollback. | | `inteligr8.async.executor.{id}.keepAliveTimeMillis` | `5000` | How long should an idle thread remain in the pool before being removed. Essentially, how fast should it go from `max` to `core` size. | | `inteligr8.async.executor.{id}.defaultAsyncJobAcquireWaitTimeMillis` | `10000` | | `inteligr8.async.executor.{id}.defaultTimerJobAcquireWaitTimeMillis` | `10000` | ## Development - If you are not on APS and want to use the `SpringJobAwareAsyncExecutor`, it is a `@Primary` Spring bean of `AsyncExecutor`. You need to use it as a bean. If you construct it outside of the Spring context, it will not work. Use the `PojoJobAwareAsyncExecutor` instead. ## Examples ```ini inteligr8.async.executors.byProcessDefinitionKeys=routingProcess,generativeAiProcess inteligr8.async.executors.byActivityIds=qualityControlProcess:unpackFiles inteligr8.async.executor.routingProcess.maxCoreSize=6 inteligr8.async.executor.qualityControlProcess:unpackFiles.maxCoreSize=2 inteligr8.async.executor.qualityControlProcess:unpackFiles.queueSize=20 ```