Description
In Camunda and the Data Sharing Framework (DSF), Spring beans that act as delegates, execution listeners, or task listeners should usually be configured with prototype scope (SCOPE_PROTOTYPE). If the @Scope annotation is omitted, Spring defaults to singleton. That can be acceptable for strictly stateless classes; as soon as someone adds mutable instance state, the risk of race conditions and hard-to-debug concurrency bugs rises sharply—often only visible under load.
Proposed solution
Extend the existing SpringConfigurationLinter so it reuses the current pipeline:
- Configuration discovery via
ProcessPluginDefinition#getSpringConfigurations() (registered @Configuration classes).
- BPMN reference collection from plugin BPMN files (
camunda:class on service/send tasks, message events, execution/task listeners, etc.).
- Bean coverage by matching each referenced class to a
@Bean method’s return type on those registered configurations.
- Scope check on the covering
@Bean method, plus a mutable-field check on the BPMN-referenced implementation class.
For each covered BPMN reference, emit:
- SUCCESS –
@Scope is prototype (e.g. ConfigurableBeanFactory.SCOPE_PROTOTYPE or @Scope("prototype")). No further scope items for that reference in this pass.
- ERROR (once) – The bean is effectively singleton (missing
@Scope or explicit non-prototype scope such as singleton) and the referenced class has mutable instance fields (not static, not final).
- WARN – The covering
@Bean has no @Scope (Spring’s default is singleton, which is risky for Camunda hooks unless you are very intentional), or @Scope is explicitly set to a non-prototype value (typically singleton) so the developer consciously acknowledges singleton semantics.
Why this matters
The rule is a safety net for DSF process plugins: it stays aligned with how DSF actually wires beans (only configurations returned by getSpringConfigurations()), and it surfaces dangerous combinations—singleton or default-singleton scope plus mutable instance state—before they bite in production.
Example scenarios
Scenario 1: Missing @Scope → WARN (and ERROR if the delegate/listener class has mutable instance fields)
@Bean
// Missing @Scope annotation
public SetCorrelationKeyListener setCorrelationKeyListener() {
return new SetCorrelationKeyListener(api);
}
Scenario 2: Explicit singleton → WARN (and ERROR if mutable instance fields exist)
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // or @Scope("singleton")
public SetCorrelationKeyListener setCorrelationKeyListener() {
return new SetCorrelationKeyListener(api);
}
Scenario 3: Prototype (recommended) → SUCCESS
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // or @Scope("prototype")
public SetCorrelationKeyListener setCorrelationKeyListener() {
return new SetCorrelationKeyListener(api);
}
Description
In Camunda and the Data Sharing Framework (DSF), Spring beans that act as delegates, execution listeners, or task listeners should usually be configured with prototype scope (
SCOPE_PROTOTYPE). If the@Scopeannotation is omitted, Spring defaults to singleton. That can be acceptable for strictly stateless classes; as soon as someone adds mutable instance state, the risk of race conditions and hard-to-debug concurrency bugs rises sharply—often only visible under load.Proposed solution
Extend the existing
SpringConfigurationLinterso it reuses the current pipeline:ProcessPluginDefinition#getSpringConfigurations()(registered@Configurationclasses).camunda:classon service/send tasks, message events, execution/task listeners, etc.).@Beanmethod’s return type on those registered configurations.@Beanmethod, plus a mutable-field check on the BPMN-referenced implementation class.For each covered BPMN reference, emit:
@Scopeis prototype (e.g.ConfigurableBeanFactory.SCOPE_PROTOTYPEor@Scope("prototype")). No further scope items for that reference in this pass.@Scopeor explicit non-prototype scope such assingleton) and the referenced class has mutable instance fields (notstatic, notfinal).@Beanhas no@Scope(Spring’s default is singleton, which is risky for Camunda hooks unless you are very intentional), or@Scopeis explicitly set to a non-prototype value (typicallysingleton) so the developer consciously acknowledges singleton semantics.Why this matters
The rule is a safety net for DSF process plugins: it stays aligned with how DSF actually wires beans (only configurations returned by
getSpringConfigurations()), and it surfaces dangerous combinations—singleton or default-singleton scope plus mutable instance state—before they bite in production.Example scenarios
Scenario 1: Missing
@Scope→ WARN (and ERROR if the delegate/listener class has mutable instance fields)Scenario 2: Explicit singleton → WARN (and ERROR if mutable instance fields exist)
Scenario 3: Prototype (recommended) → SUCCESS