AnsweredAssumed Answered

Activiti doesn't submit to rollback

Question asked by jolo_ on Aug 24, 2013
Latest reply on Sep 3, 2013 by jbarrez
I wrote the simplest demo: only one hundred strokes of code. In this demo I use Activiti with JPA, pass JPATransactionManager to activiti's processEngineDefinition. All configurations of Spring context and JPA I've got from "Activiti in Action" sample code. So, there is no errors in my configurations.

What the statement:
I waste a lot of time trying to rollback variables that were put to process execution context, and no result.
I really concluded that rollback Activiti's process changes is impossible with the help of transaction, only errors.
If you proceed execution.setVariable and rollback transaction, the variable will save into next tables:
1. ACT_GE_BYTEARRAY
2. ACT_HI_DETAIL
3. ACT_HI_VARINST
4. ACT_RU_VARABLE

Can you refute my statement? Can you say why it doesn't work?

P.S.
The demo you can clone from https://github.com/JOLO-/activiti-jpa-spring-transaction-demo.git
In this demo the whole experiment occurs in CreateClientService class. There I proceed saving and transaction rollback.
And I use PostgreSQL, so you need also to install it.

Addition:
If for some reason you don't want to clone the demo, I provide you the full source code

applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context-3.0.xsd
                           http://www.springframework.org/schema/tx      
                           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <tx:annotation-driven/>
    <context:component-scan base-package="name.krestjaninoff.activiti.hello" />

    <bean id="activitiDataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://localhost:5432/postgres" />
        <property name="username" value="postgres" />
        <property name="password" value="postgres" />
    </bean>

    <bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
        <property name="persistenceXmlLocation">
            <value>classpath:jpa-persistence.xml</value>
        </property>
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitManager" ref="persistenceUnitManager" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <property name="databaseType" value="postgres" />
        <property name="dataSource" ref="activitiDataSource" />
        <property name="transactionManager" ref="transactionManager" />
        <property name="databaseSchemaUpdate" value="update" />
        <property name="jpaEntityManagerFactory" ref="entityManagerFactory" />
        <property name="jpaHandleTransaction" value="true" />
        <property name="jpaCloseEntityManager" value="true" />
        <property name="deploymentResources" value="classpath*:process/simplest_process.bpmn20.xml" />
        <property name="jobExecutorActivate" value="false" />
        <property name="history" value="full"/>
    </bean>

    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <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"/>
</beans>


jpa-persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
   <persistence-unit name="Client" transaction-type="RESOURCE_LOCAL">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <class>name.krestjaninoff.activiti.hello.db.Client</class>
      <exclude-unlisted-classes>true</exclude-unlisted-classes>
      <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.ProgressDialect" />
         <property name="hibernate.hbm2ddl.auto" value="update" />
         <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
         <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/postgres" />
         <property name="hibernate.connection.username" value="postgres" />
         <property name="hibernate.connection.password" value="postgres" />
            <property name="hibernate.show_sql" value="true"/>
      </properties>
   </persistence-unit>
</persistence>


Main.java

package name.krestjaninoff.activiti.hello;

import org.activiti.engine.RuntimeService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

   public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        RuntimeService runtimeService = (RuntimeService) applicationContext.getBean("runtimeService");
        runtimeService.startProcessInstanceByKey("simplestProcess");
    }
}


Engine.java

package name.krestjaninoff.activiti.hello.core;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Component;

@Component
public class Engine {

    @Autowired
    @Qualifier("transactionManager")
    private JpaTransactionManager transactionManager;

    public JpaTransactionManager getTransactionManager() {
        return transactionManager;
    }
}


CreateClientService.java

package name.krestjaninoff.activiti.hello.process;

import name.krestjaninoff.activiti.hello.core.Engine;
import name.krestjaninoff.activiti.hello.db.Client;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.hibernate.Session;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.persistence.EntityManager;

@Service
public class CreateClientService implements JavaDelegate {

   public void execute(DelegateExecution execution) throws Exception {
        Client client = new Client();
        client.setFirstName("Rodrigo");

        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Engine engine = (Engine) applicationContext.getBean("engine");
        JpaTransactionManager transactionManager = engine.getTransactionManager();
        EntityManager em = transactionManager.getEntityManagerFactory().createEntityManager();

        em.getTransaction().begin();
        em.persist(client);
        execution.setVariable("client", client);
        execution.setVariable("clientName", client.getFirstName());
        em.getTransaction().rollback();
   }
}


Client.java

package name.krestjaninoff.activiti.hello.db;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "Client")
public class Client implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id")
    private long id;

    @Column(name = "firstName")
    private String firstName;

    public Client() {}

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}


simplest_process.bpmn20.xml

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="simplestProcess" name="Simplest Process">
    <startEvent id="startevent" name="Start"></startEvent>
    <sequenceFlow sourceRef="startevent" targetRef="servicetask"/>
    <serviceTask id="servicetask" name="GenerateData" activiti:class="name.krestjaninoff.activiti.hello.process.CreateClientService"></serviceTask>
    <sequenceFlow sourceRef="servicetask" targetRef="usertask"/>
    <userTask id="usertask" name="Wait a minute"/>
    <sequenceFlow sourceRef="usertask" targetRef="endevent"/>
    <endEvent id="endevent" name="End"></endEvent>
  </process>
</definitions>

Outcomes