Reputation: 880
I'm having problems using Spring's autowiring capabilities in Activiti Explorer when calling a service's method from within a task. The idea is to have the Service Task call one of the methods on a Spring @Service bean in order to persist data using an @Autowired JPARepository.
Problem is, after executing the service task, I'm getting a null pointer exception as a result of the @Autowired Repository in myService not being properly instantiated.
My question then, is how can I properly call a Spring bean from the Task Service?
The JavaDelegate method does not work with Spring, and I've tried going for an "Expression" approach, as suggested here to no avail.
Here's the code for the Service Task run method, which is being run as:
activiti:expression="${testServiceTask.doSomething()}"
// the java class that's being called
public class testServiceTask {
@Autowired
private TestServiceDummy serviceDummy;
public void doSomething() {
serviceDummy.run(); // NPE here, the serviceDummy is null when called
}
// Getters and Setters for the testServiceDummy omitted for brevity
}
Here is my service's:
public interface TestServiceDummy {
public void createUser();
}
@Service(value = "testServiceDummyImpl")
@Transactional(readOnly = true)
public class TestServiceDummyImpl implements TestServiceDummy {
@Autowired
private UserRepository userRepo;
@Override
public void createUser() {
User u = new User();
userRepo.save(u);
}
// Getters and Setters for userRepo omitted for brevity
}
The same thing works without problems when called from our webapp (calling the service as a @ManagedProperty works ok) so the embedded project's config seems to be ok.
And here's Activiti Explorer's applicationContext file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.1.xsd">
<import resource="classpath*:/applicationContextCore.xml" />
<context:property-placeholder location="classpath*:jdbc.properties" />
<!-- Scan this classpath for annotated components that will be auto-registered
as Spring beans -->
<context:annotation-config /> <!-- this should take care of the @Autowiring issue -->
<!-- scan the embedded project's components -->
<context:component-scan base-package="my.project.*" />
<jpa:repositories base-package="my.project.repositories*" />
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- Automatically translate hibernate/jpa exceptions into Spring's generic
DataAccessException hierarchy for those classes annotated with Repository -->
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="testServiceDummy" class="edu.bedelias.services.TestServiceDummyImpl" />
<!-- JPA Entity Manager Factory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
<property name="packagesToScan">
<list>
<value>edu.bedelias.*</value>
</list>
</property>
<property name="jpaProperties">
<props>
<!-- set HibernateJpaVendorAdapter's behavior: 'create' = build a new
DB on each run; 'update' = modify an existing database; 'create-drop' = 'create'
and also drops tables when Hibernate closes; 'validate' = makes no changes
to the database -->
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
</props>
</property>
</bean>
<bean id="hibernateJpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClass}" />
<property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
<property name="maxStatements" value="${jdbc.maxStatements}" />
<property name="minPoolSize" value="${jdbc.minPoolSize}" />
</bean>
<!-- Transaction Manager is defined -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Hijack the current @Session scope annotation on each @Service and make
it last only for the duration of the thread -->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="session">
<bean class="org.springframework.context.support.SimpleThreadScope" />
</entry>
</map>
</property>
</bean>
<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven />
<bean id="dbProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:db.properties" />
<!-- Allow other PropertyPlaceholderConfigurer to run as well -->
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<bean id="demoDataGenerator" class="org.activiti.explorer.demo.DemoDataGenerator">
<property name="processEngine" ref="processEngine" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="true" />
<property name="customFormTypes">
<list>
<ref bean="userFormType" />
</list>
</property>
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"
destroy-method="destroy">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="repositoryService" factory-bean="processEngine"
factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine"
factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine"
factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine"
factory-method="getHistoryService" />
<bean id="managementService" factory-bean="processEngine"
factory-method="getManagementService" />
<bean id="identityService" factory-bean="processEngine"
factory-method="getIdentityService" />
<bean id="activitiLoginHandler" class="org.activiti.explorer.ui.login.DefaultLoginHandler">
<property name="identityService" ref="identityService" />
</bean>
<!-- Include the UI-related wiring. This UI context will be used in the
alfresco activiti admin UI -->
<import resource="activiti-ui-context.xml" />
<!-- Custom form types -->
<bean id="userFormType" class="org.activiti.explorer.form.UserFormType" />
If anyone is curious, the URL of the project is here: Google Code hosted project
Thanks in advance,
Gaston
Upvotes: 0
Views: 4490
Reputation: 880
This ended up working:
ClassPathXmlApplicationContext cpx = new ClassPathXmlApplicationContext("classpath:applicationContextActiviti.xml");
TestServiceDummy = (TestServiceDummy) cpx.getBean("testServiceDummy");
Where applicationContextActiviti is a custom appContext config file with the TestServiceDummy service declared. I use this within the JavaDelegate class that gets called by the task.
Basically, from Activiti Explorer I did not have visibility over Spring Beans (unless you get your hands dirty that is) so what I did was to skip the @Autowired and use the old manual approach to beans, then load them from within the caller class.
Just leaving this here in case it hopes any future time travelers :P
Upvotes: 2
Reputation: 1096
Did you try using the hash-sign instead of the dollar?
activiti:expression="#{testServiceTask.doSomething()}"
This works in my Spring+Activiti Configuration
Upvotes: 3