-
Bug Report
-
Resolution: Fixed
-
L3 - Default
-
7.16.0, 7.17.0
-
None
Environment (Required on creation):
Spring Boot and Camunda Spring Boot Starter.
Description (Required on creation; please attach any relevant screenshots, stacktraces, log files, etc. to the ticket):
A process that has a Timer (or anything async) that ends up executing a Script Task with Javascript using GraalJS as part of the job, while having a Spring Bean that is request scoped, will throw the following exception:
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.3.18.jar:5.3.18] at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:42) ~[spring-web-5.3.18.jar:5.3.18] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:371) ~[spring-beans-5.3.18.jar:5.3.18] ... 99 common frames omitted
As this is ran in a job that is being processed by Camunda, this thread is not web related, so any Request scoped beans won't be accessible.
Steps to reproduce (Required on creation):
- Create a Spring Boot project with Camunda Spring Boot starter
- Add attached sample.bpmn
- Create a random bean that has @RequestScope
- Start process instance and wait till timer picks up job, exception above will be thrown.
Observed Behavior (Required on creation):
The Camunda Spring Boot starter adds a SpringBeansResolverFactory which will add all beans in the application context as a context for all scripts to be used.
When executing javascript with GraalJS, Camunda will eventually call evaluate() on the ScriptEngine, which in this case is GraalJS and passes along the ScriptBindings (Camunda implementation of Bindings) to GraalJSScriptEngine which will eventually call getOrCreateGraalJSBindings. This method basically moves the Camunda Script Bindings to an internal object of GraalJS, the way they do it is using putAll(). This putAll uses the entrySet() method, which is implemented on the Camunda's ScriptBindings which ends up calling calculateBindingMap() method.
Now, if you have a process instance that ends up executing a Script Task as part of a Job (timer, async before, etc) AND have a Spring bean set with @RequestScope, it will throw the above mentioned exception. Which in this case is happening because of calculateBindingMap() is retrieving the value of all beans.
Expected behavior (Required on creation):
The SpringBeansResolverFactory should only include all beans that are of scope Singleton.
Root Cause (Required on prioritization):
The Root Cause for this is the SpringBeansResolverFactory, as its adding all beans regardless of scope to the bindings for the script engine.