The error “Unknown property used in expression: ${userValidationDelegate}. Cause: Cannot resolve identifier ‘userValidationDelegate’” indicates that Camunda cannot find your Spring bean when trying to resolve the delegate expression. Here’s a comprehensive solution to resolve this issue.
Root Cause Analysis
The problem occurs when Camunda’s process engine cannot access the Spring application context where your bean is registered. This typically happens due to context switching issues between the Camunda engine and your Spring Boot application.
Solution Steps
1. Verify Your Java Delegate Implementation
Ensure your delegate class is properly implemented:
@Slf4j
@Component("userValidationDelegate")
public class UserValidationDelegate implements JavaDelegate {
@Value("${configuration.userConfirmationUrl}")
private String userConfirmationUrl;
@Override
public void execute(DelegateExecution execution) throws Exception {
// Your business logic here
log.info("Executing user validation delegate");
// Process your logic and set variables if needed
// execution.setVariable("result", someValue);
}
}
Key points:
- The class must implement
org.camunda.bpm.engine.delegate.JavaDelegate
- Use
@Component
with the exact name you reference in your BPMN
- Keep the delegate stateless and avoid storing data in fields
2. Configure Camunda Spring Boot Integration
Add the following configuration to ensure proper Spring context integration:
camunda:
bpm:
process-engine-name: default
application:
enabled: true
database:
schema-name: camunda
table-prefix: camunda.
schema-update: true
history-level: FULL
admin-user:
id: ${COCKPIT_USER}
password: ${COCKPIT_PASSWORD}
authorization:
enabled: true
job-execution:
deployment-aware: true
webapp:
index-redirect-enabled: true
3. Create a Process Application Class
Create a @ProcessApplication
annotated class to ensure proper context registration:
@SpringBootApplication
@EnableProcessApplication
public class CamundaApplication {
public static void main(String[] args) {
SpringApplication.run(CamundaApplication.class, args);
}
}
Or create a separate process application class:
@ProcessApplication("my-process-application")
@Component
public class MyProcessApplication extends SpringBootProcessApplication {
// This ensures proper context switching
}
4. Alternative Configuration Approach
If the above doesn’t work, try using @Service
instead of @Component
and ensure component scanning:
@Service("userValidationDelegate")
public class UserValidationDelegate implements JavaDelegate {
// Implementation
}
Add explicit component scanning to your main application class:
@SpringBootApplication
@ComponentScan(basePackages = {"your.package.path"})
@EnableProcessApplication
public class CamundaApplication {
// Main method
}
5. Verify BPMN Configuration
Ensure your BPMN service task is correctly configured:
<bpmn:serviceTask id="Task_CheckUser"
name="Check if User is valid"
camunda:asyncBefore="true"
camunda:delegateExpression="${userValidationDelegate}">
<bpmn:extensionElements>
<camunda:failedJobRetryTimeCycle>R3/PT5M</camunda:failedJobRetryTimeCycle>
</bpmn:extensionElements>
<bpmn:incoming>Flow_To_UserCheck</bpmn:incoming>
<bpmn:outgoing>Flow_To_IsUserValid</bpmn:outgoing>
</bpmn:serviceTask>
6. Debugging Steps
To troubleshoot further:
-
Check bean registration: Add logging to verify your bean is being created:
@PostConstruct
public void init() {
log.info("UserValidationDelegate bean initialized");
}
-
Verify Spring context: Add a @Component
that lists all beans at startup:
@Component
public class BeanLister implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
String[] beanNames = event.getApplicationContext().getBeanDefinitionNames();
log.info("Available beans: {}", Arrays.toString(beanNames));
}
}
-
Test with Java class reference: Temporarily change your BPMN to use camunda:class
instead of camunda:delegateExpression
to verify the class works:
camunda:class="com.yourpackage.UserValidationDelegate"
Common Pitfalls to Avoid
- Deployment timing: The bean must be available when the process is executed, not just when deployed[3]
- Context switching: Ensure your process application is properly registered to maintain Spring context[2]
- Classpath issues: Verify all dependencies are available at runtime[3]
- Async execution: When using
camunda:asyncBefore="true"
, ensure the job executor has access to the Spring context[5]
Alternative Workaround
If the issue persists, consider using a service locator pattern:
@Component("userValidationDelegate")
public class UserValidationDelegate implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) throws Exception {
// Get Spring context manually if needed
ApplicationContext context = SpringContextHolder.getApplicationContext();
YourService service = context.getBean(YourService.class);
// Use the service
}
}
This solution addresses the core issue of Spring bean resolution in Camunda delegate expressions and provides multiple approaches to ensure your delegate is properly recognized by the process engine.