You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
As a user, I want to register interceptors for service task workers so I can add cross-cutting concerns without replacing the delivery layer #278
I want to add cross-cutting concerns (logging, APM/OpenTelemetry tracing, auditing)
around every service task worker execution without having to replace the entire ExternalServiceTaskDelivery
bean or rely on Spring AOP (which cannot easily access TaskInformation and variables).
The library supports two registration paths:
Annotation-based (@ProcessEngineWorker) via process-engine-worker
There is no first-class interceptor concept. To intercept worker execution, users must either:
Replace the entire ExternalServiceTaskDelivery bean (large surface area, error-prone, engine-specific)
Use Spring AOP (hard or impossible to access TaskInformation + variables + topic name)
Implement a custom ParameterResolutionStrategy or ResultResolutionStrategy (worker-lib only, no lifecycle hooks)
Wanted Behaviour
A ServiceTaskInterceptor interface that allows users to register one or more Spring beans to be applied
around every service task execution — regardless of whether the subscription was created via @ProcessEngineWorker or via a manual subscribeForTask() call.
Proposed API:
fun interfaceServiceTaskInterceptor {
funintercept(context:ServiceTaskInterceptorContext, chain:ServiceTaskInterceptorChain)
}
data classServiceTaskInterceptorContext(
valtaskInformation:TaskInformation,
valpayload:Map<String, Any?>,
valtaskDescriptionKey:String?,
valtaskType:TaskType
)
fun interfaceServiceTaskInterceptorChain {
funproceed()
}
The right interception point is inside AbstractTaskSubscriptionApiImpl.subscribeForTask() — wrapping the action TaskHandler before storing the TaskSubscriptionHandle. This makes it transparent to all delivery
implementations (pull or push) and covers both registration paths automatically. Interceptors are ordered
via @Order/Ordered. When no interceptors are registered, behaviour is identical to today (zero overhead).
Possible Workarounds
Provide a custom ExternalServiceTaskDelivery bean (engine-specific, complex, replaces entire delivery layer)
Spring AOP on @ProcessEngineWorker methods (does not work for manual TaskHandler registrations; TaskInformation and variables are not directly accessible as method parameters in all cases)
Custom ParameterResolutionStrategy (worker-lib only, no pre/post hooks around the actual execution)
I believe you plan is too close to the implementation. The API has no dependencies to Spring at all. Please let define the scope properly. If it has to be Spring, we might want to implement this inside the starters of adapters or inside of the worker.
Another question is: what does the term interceptor means to you? what does it intercept? Given I register an interceptor when does it get called by the adapter implementation?
You title says "interceptor for a service task worker" - a worker is a concept only present in the process-engine-worker but it doesn't exist on the API layer.
Please try to provide more use case context (and let the technical implementation ideas out on the description of the requirements).
The code-example above is a bit miss-leading in case of spring-usage. My first naive suggestion was somehow like that:
engine-api: holds types & probably a generic class (without spring) that handles the execution of the chain (from the idea, comparable with ´InMemSubscriptionRepository`)
user-application: defines interceptors
adapter(s): use engine-api & call the interceptors before executing the job
Drafted a proof of concept in #313 that realizes exactly the split I described above: a Spring-freeTaskHandlerInterceptor in the API (types + chain execution, analogous to InMemSubscriptionRepository), with the actual wiring left to the adapter starters — no "worker" concept on the API layer.
The concrete value: it lets users hang cross-cutting concerns like logging, APM/OpenTelemetry tracing or auditing around every task execution — covering both @ProcessEngineWorker and manual subscribeForTask — without replacing the delivery layer or fighting AOP.
@zambrovski would appreciate your take on the naming/scope before I flesh out the adapter wiring.
to make the use case more concrete: this comes from a production setup I work on (CIB Seven embedded, ~185 @ProcessEngineWorkers).
Around every external task execution we need a root tracing span (topic, task id, process instance, definition key, activity id), activity id and activity instance id in the MDC of the executing thread, and an audit log line with the variables. Today the only way to get there is a full copy of EmbeddedPullServiceTaskDelivery, so every upstream fix has to be ported by hand (e.g. bpm-crafters/process-engine-adapters-cib-seven#125). That copy is what this proposal should make unnecessary.
#313 already covers most of it: the interceptor runs on the executing thread, wraps the whole handler including completion, and gets payload, topic and meta. To fully replace the copy, one could also think about those two (here or in separate)
Outcome: a span has to be marked as failed, but process-engine-worker catches worker exceptions inside the handler and fails the task or completes it by BPMN error itself. Right now an interceptor always sees a normal return.
activityInstanceId in TaskInformation.meta, so log correlation per activity instance no longer needs the raw engine task.
Activity
emaarco commentedon Feb 17, 2026
what do you think about this @zambrovski. any other ideas on how to solve an issue like this in the current implementation of the lib?
zambrovski commentedon Feb 24, 2026
I believe you plan is too close to the implementation. The API has no dependencies to Spring at all. Please let define the scope properly. If it has to be Spring, we might want to implement this inside the
starters of adapters or inside of theworker.Another question is: what does the term interceptor means to you? what does it intercept? Given I register an interceptor when does it get called by the adapter implementation?
You title says "interceptor for a service task worker" - a worker is a concept only present in the
process-engine-workerbut it doesn't exist on the API layer.Please try to provide more use case context (and let the technical implementation ideas out on the description of the requirements).
emaarco commentedon Mar 12, 2026
@zambrovski - Interceptor in this case means for me that, we have a set of classes that is executed before we execute the concrete
taskHandler. Comparable with for example the following: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-config/interceptors.htmlThe code-example above is a bit miss-leading in case of spring-usage. My first naive suggestion was somehow like that:
emaarco commentedon Sep 16, 2026
Drafted a proof of concept in #313 that realizes exactly the split I described above: a Spring-free
TaskHandlerInterceptorin the API (types + chain execution, analogous toInMemSubscriptionRepository), with the actual wiring left to the adapter starters — no "worker" concept on the API layer.The concrete value: it lets users hang cross-cutting concerns like logging, APM/OpenTelemetry tracing or auditing around every task execution — covering both
@ProcessEngineWorkerand manualsubscribeForTask— without replacing the delivery layer or fighting AOP.@zambrovski would appreciate your take on the naming/scope before I flesh out the adapter wiring.
emaarco commentedon Oct 7, 2026
to make the use case more concrete: this comes from a production setup I work on (CIB Seven embedded, ~185
@ProcessEngineWorkers).Around every external task execution we need a root tracing span (topic, task id, process instance, definition key, activity id), activity id and activity instance id in the MDC of the executing thread, and an audit log line with the variables. Today the only way to get there is a full copy of
EmbeddedPullServiceTaskDelivery, so every upstream fix has to be ported by hand (e.g. bpm-crafters/process-engine-adapters-cib-seven#125). That copy is what this proposal should make unnecessary.#313 already covers most of it: the interceptor runs on the executing thread, wraps the whole handler including completion, and gets payload, topic and meta. To fully replace the copy, one could also think about those two (here or in separate)
process-engine-workercatches worker exceptions inside the handler and fails the task or completes it by BPMN error itself. Right now an interceptor always sees a normal return.activityInstanceIdinTaskInformation.meta, so log correlation per activity instance no longer needs the raw engine task.feat(api): report task handler outcome to interceptors