Задачи с автоматически подключаемыми компонентами Spring в Activiti

У меня возникают проблемы с использованием возможностей автоматического связывания Spring в Activiti Explorer при вызове метода службы из задачи. Идея состоит в том, чтобы сервисная задача вызывала один из методов bean-компонента Spring @Service для сохранения данных с использованием @Autowired JPARepository.

Проблема в том, что после выполнения служебной задачи я получаю исключение нулевого указателя в результате неправильного создания экземпляра @Autowired Repository в myService.

Тогда мой вопрос: как я могу правильно вызвать bean-компонент Spring из службы задач?

Метод JavaDelegate не работает с Spring, и Я попытался использовать подход «Выражение», как было предложено здесь безрезультатно.

Вот код метода запуска Service Task, который запускается как:

activiti:expression="${testServiceTask.doSomething()}"

// вызываемый класс Java

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

    }

Вот мой сервис:

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
}

То же самое работает без проблем при вызове из нашего веб-приложения (вызов службы как @ManagedProperty работает нормально), поэтому конфигурация встроенного проекта выглядит нормально.

А вот файл applicationContext Activiti Explorer:

<?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

Заранее спасибо,

Гастон


person Gaston    schedule 18.11.2012    source источник


Ответы (3)


Вы пытались использовать решетку вместо доллара?

activiti:expression="#{testServiceTask.doSomething()}"

Это работает в моей конфигурации Spring + Activiti

person David    schedule 27.11.2012
comment
Спасибо за предложение, но, похоже, моя проблема заключалась в том, как я подошел к использованию Spring в Activiti. См. Ответ. - person Gaston; 02.12.2012

В итоге это сработало:

ClassPathXmlApplicationContext cpx = new ClassPathXmlApplicationContext("classpath:applicationContextActiviti.xml");
TestServiceDummy = (TestServiceDummy) cpx.getBean("testServiceDummy");

Где applicationContextActiviti — это настраиваемый файл конфигурации appContext с объявленной службой TestServiceDummy. Я использую это в классе JavaDelegate, который вызывается задачей.

По сути, из Activiti Explorer у меня не было информации о Spring Beans (если только вы не запачкаете ваши руки то есть), поэтому я пропустил @Autowired и использовал старый ручной подход к компонентам, а затем загрузил их из класса вызывающего объекта.

Просто оставлю это здесь на случай, если он надеется на будущих путешественников во времени: P

person Gaston    schedule 02.12.2012

Кажется, что testServiceTask не объявлен в файле конфигурации spring.

person jpl    schedule 12.03.2014
comment
это немного кратко для ответа, тебе не кажется? - person Wim Ombelets; 12.03.2014