cancel
Showing results for 
Search instead for 
Did you mean: 

Activiti Error Handling

fritz128
Champ in-the-making
Champ in-the-making
What I have:
Non-Activiti processes (saving to DB) in Activiti Service Task. I want to rollback Activiti context on error

What a problem:
Activiti context doesn't rollback.

My code:]
In this service task I create new client entity and persist it. Client entity has Unique constraint, so the try{} section may fail. When it happens, I see in TaskTwo that execution.getVariable("client") != null


@Service
public class AddClientService implements ActivityBehavior {

    @Override
    public void execute(ActivityExecution execution) throws Exception {
        PvmTransition transition = null;

        Client client = new Client();
        client.setFirstName("John");
        client.setLastName("Smith"); // Have unique constraint

        execution.setVariable("client", client); // need to be rollbacked in case of error

        Engine engine = Engine.getInstance();
        SessionFactory sessionFactory = engine.getHibernateSessionFactory();
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        try {
            session.save(client);
            session.getTransaction().commit();
            transition = execution.getActivity().findOutgoingTransition("toTaskOneFlow");
        } catch (Exception ex) {
            transition = execution.getActivity().findOutgoingTransition("toTaskTwoFlow");
        } finally {
            session.close();
        }
        execution.take(transition);
    }
}


Questions:
1. Why execution.getVariable("client") != null in the next task after error happens?
2. Do I need manually remove all variables from Activiti context after error?
3. May be it is better to use compensation end event in my case?
10 REPLIES 10

jbarrez
Star Contributor
Star Contributor
I don't think any of your questions are asking the correct thing. The question is whether the hibernate session is running  in the same transaction as the activiti one. How did you configure that

How can you know that this:

<code>
Engine engine = Engine.getInstance();
SessionFactory sessionFactory = engine.getHibernateSessionFactory();
</code>

is actually in the same transaction

fritz128
Champ in-the-making
Champ in-the-making
Thanks for reply

I need to rollback only Activiti's context.
<ul>
<li>Can I make it work without common transaction manager for Activiti and Hibernate? </li>
<li>How can I check that Activiti session factory == Engine.getInstance().getHibernateSessionFactory()? I mean some Activiti's method to get Activiti session factory or where to look at in debug. Please more details</li>
</ul>

frederikherema1
Star Contributor
Star Contributor
1) You cannot make this work (reliably) without having a shared transaction-manager for activiti and your own entities, regardless of the technology (hibernat, jpa, plain JDBC, …).

2) Activiti doesn't use hibernate for persistence…

Yes, but there are many examples in an internet, where Hibernate's trasaction manager bean are passed to ProcessEngineConfig bean. Is it deliberately wrong way?

Please, see my question http://forums.activiti.org/content/pass-hibernatesessionfactory-activiti-engine-configs

frederikherema1
Star Contributor
Star Contributor
If you're using the hibernate transaction manager as transaction-manager for Activiti, both activiti and your DB-stuff will get rollbacked when transaction rolls back (after error in hibernate or error in activiti execution).

If you DO NOT want your transaction to roll-back when something happens in activiti, don't use the same transaction-manager. However, in case that the activiti-transaction is comitted (and contains a reference to hibernate-entities you created) AND after that, the hibernate-transaction rolls back, you end up with variables in activiti referencing an unexisting entity in your DB. Not sure what the actual usacase is to NOT synchronize the transactions?

The point is that when I pass HibernateTransactionManager to both Activiti and application, it DOESN'T WORK. There are a lot of topics in the internet that  people face the same problem. It is very common issue. And there're any working example (I search a lot) that can confirm your words.

Can you provide me a very simple working demo with Activiti + Spring + common HibernateTransactionManager + desirable with Postgres? PLEASE!

P.S.
In debug I can see that in service tasks Activiti call Context.getCommandContext().getDbSqlSession() that returns org.activiti.engine.impl.db.DbSqlSession instead of hibernate session

see the last comment in http://forums.activiti.org/content/pass-hibernatesessionfactory-activiti-engine-configs question

frederikherema1
Star Contributor
Star Contributor
About the org.activiti.engine.impl.db.DbSqlSession versus the hibernate session: a hibernate-session is used to get Hibernate-entities from. The org.activiti.engine.impl.db.DbSqlSession is an internal session in activiti which gives access to SQL-functionality on top of mybatis. So the  hibernate session is really not relevant.

The only thing that is relevant is that, in case the HibernateTransactionManager is used, the underlying datasource returns the same SQL-connection as is used for the hibernate-operations. Can you please share your process-engine configuration (spring-context) so we can validate what settings you're using to accomplish this.

There are a lot of topics in the internet that people face the same problem
Do you mean specifically using activity and hibernate together? Because in Alfresco 3.4.X, activiti was running with the hibernate transaction manager (as all entities USED to be hibernate in alfresco) without any issues and unit-tests to back that statement up…

pkonyves
Champ in-the-making
Champ in-the-making
Hi, I have a question regarding activiti errors and transactions.

As the userguide says, activities are executed within a DB transaction. If  techincal (RuntimeException) is thrown, then the transaction is rolled back leaving the process instance unchanged. Is this the case also when I throw BpmnError?

My problem is that I cannot pass any meaningful error message to a user when an exception occurs, because the db transaction is rolled back, any process variable changes are unmade too. Db transaction must stay rolled back by the way, so that is ok to me. My only problem is how to store a message to show the user after an error?

Error boundary event would be good, but all the information I can pass is the error code which is not enough.

Obviously I could set variables in a new JTA transaction (I use JTA), but it's very inconvenient. Is there some support for my case?

<code>
CommandExecutor commandExecutor = ((ProcessEngineImpl)processEngine).getProcessEngineConfiguration().getCommandExecutor();
      CommandConfig config = new CommandConfig().transactionRequiresNew();
      commandExecutor.execute(config, new Command<Object>() {
        public Object execute(CommandContext commandContext) {
          // do my sh*t here
          return null;
        }
      });
</code>

or using JTA transaction:
<code>
JtaProcessEngineConfiguration jtaProcessEngineConfiguration = (JtaProcessEngineConfiguration) processEngineCfg;
TransactionManager tm = jtaProcessEngineConfiguration.getTransactionManager();
tm.suspend();
tm.begin();
// do my sh*t
tm.commit();
tm.resume(tx);
</code>

trademak
Star Contributor
Star Contributor
When you are invoking the process instance synchronously and it executes synchronously you'll get the exception back in your calling application. And you can act on that based on the exception details. Doesn't that work for you?