I stumbled upon an inability to access spring beans in an Input/Output Mapping Script. The example is as shown below:
Apparently I am not able to access the spring bean in an Input/Output mapping script, although the bean is wired correctly and I am able to access it throughout the process.
The process engine test is linked below:
Spring Bean Input/Output Access
Hi Robert,
I think accessing beans (CDI or Spring) from a script is a missing feature, see https://app.camunda.com/jira/browse/CAM-4222. You can consider implementing the interfaces org.camunda.bpm.engine.impl.scripting.engine.ResolverFactory
and org.camunda.bpm.engine.impl.scripting.engine.Resolver
in a way that accesses a Spring application context. Then register the resolver factory with the engine via the configuration property resolverFactories
. If you like, you can also provide this feature as a pull request.
Cheers,
Thorben
Hi Thorben,
thanks, I will consider this solution.
Kind Regards
Robert
Here is a small example, for springboot, with an embedded process engine!
This is the bean for testing:
@Component
public class Testbean {
private String name = "Hello from testbean!";
public String getName() {
return name;
}
}
The resolver:
import org.camunda.bpm.engine.impl.scripting.engine.Resolver;
import org.springframework.context.ApplicationContext;
public class SpringResolver implements Resolver {
protected ApplicationContext applicationContext;
private static final Set<String> exposedBeans = new HashSet<String>(
Arrays.asList("testbean"));
public SpringResolver(ApplicationContext applicationContext) {
ensureNotNull("applicationContext", applicationContext);
this.applicationContext = applicationContext;
}
public boolean containsKey(Object key) {
return exposedBeans.contains((String)key);
}
public Object get(Object key) {
return applicationContext.getBean((String)key);
}
public Set<String> keySet() {
return exposedBeans;
}
}
The resolver factory:
import org.camunda.bpm.engine.delegate.VariableScope;
import org.camunda.bpm.engine.impl.scripting.engine.Resolver;
import org.camunda.bpm.engine.impl.scripting.engine.ResolverFactory;
import org.springframework.context.ApplicationContext;
public class SpringResolverFactory implements ResolverFactory {
private ApplicationContext applicationContext;
public SpringResolverFactory(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public Resolver createResolver(VariableScope variableScope) {
return new SpringResolver(applicationContext);
}
}
Configure the resolver factory with plugin-configuration:
public class SpringResolverPlugin implements ProcessEnginePlugin {
private ApplicationContext applicationContext;
public SpringBeanResolverPlugin(
ApplicationContext applicationContext) {
super();
this.applicationContext = applicationContext;
}
@Override
public void preInit(
ProcessEngineConfigurationImpl processEngineConfiguration) {}
@Override
public void postInit(
ProcessEngineConfigurationImpl processEngineConfiguration) {
processEngineConfiguration
.getResolverFactories()
.add(new SpringResolverFactory(applicationContext));
}
@Override
public void postProcessEngineBuild(ProcessEngine processEngine) {}
}
And set the plugin into the spring context:
@Bean
public ProcessEnginePlugin springResolverPlugin(
ApplicationContext applicationContext) {
return new SpringResolverPlugin(applicationContext);
}
At last rty it out from javascript, from any script-task:
print(testbean.name)
It is better to enumerate only the needed beans in a HashSet (exposedBeans),
because the resolver’s
containsKey(Object key)
gets called very often by the system!
But if you really want get access to the ~600 beans in your container (slower because containsKey):
public class SpringResolver implements Resolver {
protected ApplicationContext applicationContext;
public SpringResolver(ApplicationContext applicationContext) {
ensureNotNull("applicationContext", applicationContext);
this.applicationContext = applicationContext;
}
public boolean containsKey(Object key) {
try {
return applicationContext.getBean((String) key) != null;
} catch (Exception ex) {
return false;
}
}
public Object get(Object key) {
return applicationContext.getBean((String) key);
}
public Set<String> keySet() {
String[] beannames = applicationContext.getBeanDefinitionNames();
return new HashSet<String>(Arrays.asList(beannames));
}
}
Optimized
public class SpringResolver implements Resolver {
private ApplicationContext applicationContext;
private Set<String> beannames;
public SpringResolver(ApplicationContext applicationContext) {
ensureNotNull("applicationContext", applicationContext);
this.applicationContext = applicationContext;
this.beannames = new HashSet<String>(
Arrays.asList(
applicationContext.getBeanDefinitionNames()));
}
public boolean containsKey(Object key) {
return beannames.contains((String)key);
}
public Object get(Object key) {
return applicationContext.getBean((String) key);
}
public Set<String> keySet() {
return beannames;
}
}
1 Like
This could be a great solution. I’m working on it. What is ‘ensureNotNull’ method ?
I notice this isn’t working for a shared process engine (the spring jars are not visible from tomcat lib folder). There’s some workaround for that?
Hallo!
ensureNotNull is a static import:
import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull;
It is in the spring jar’s, sorry, i have no experience with the shared engine, we are using embedded…