3
votes

I want to use Activiti BPMN process for some database update task. My process is as follows.

Start Event-> Service Task 1 -> Service Task 2 -> Service Task 3 -> End Event

In the service implementation class of Service task 1 : I created a java.sql.Connection for MySQL database. I need to pass the same Connection object to the Service Task 2 and Service Task 3. Basically those two classes will do some insertions for the database using the same Connection object.

I tried as follows (dbConn is the the Class which contains java.sql.Connection type dbConnection)

execution.setVariable("DBConn",dbConn); 

But it gives an exception since the connection object is not serializable.

"org.activiti.engine.ActivitiException: Couldn't serialize value" 

So what is the best way to pass such non serializable variables between service tasks of a process? Or is there any way to define such common objects to multiple Service Tasks in one place and use them within service Tasks ( Something like global variables for the process)

2
Is there any particular reason why you want to pass the same connection object ?Younes Regaieg
I want to use that same connection for insertion tasks in Service task 2 and Service Task 3. The DB connection is created with setAutoCommit(false). So after the execution of Service task 3 I want to commit the transactionAnupama Pathirage
What would happen if -for some unexpected reason- the server craches and you had to restart it ?Younes Regaieg
Well that will be a problem. Can you please let me know any other way to handle DB transactions using activiti. Basically I want to create a series of DB updates and commit the transaction at the end if all transactions are successull or else roll back the all the transactions.Anupama Pathirage

2 Answers

3
votes

You can use Thradlocal in Java to pass connection object to different service tasks.

For example use a Base class like below and extend each service task from that. Then you can set the dbConnection and use whenever required by using get method.

public class BaseServiceTask
{
    public static final ThreadLocal<Connection> localConnectionContext = new ThreadLocal<Connection>();

    public static void initDBConnector(Connection dbConn)
    {
        localConnectionContext.set(dbConn);
    }

    public static Connection getDBConnector()
    {
        return localConnectionContext.get();
    }
}

Notice :
This approach assumes all service tasks are executed in the same thread, which is the case for this particular question, but once you include some user task / timer (or any asynchronous logic) this is not a viable solution anymore !

2
votes

First, you should be aware that there is absolutely no way to serialize a connection instance once it got created according to this.

The reason is that a connection uses a network resource (such as a TCP/IP socket) which uses the network stack on the machine, and eventually the machine's hardware.

Which leaves you only this alternative:

  • Setup a bean that will store the connection instances for you, let's call it myConnectionRegistry, this bean should be scoped as singleton and injected in all your java delegates (Service task implementations)
  • In the first task, you create the connection and then register it into myConnectionRegistry with something like this connectionRegistry.register(conn, wfId) which would add the connection instance to a private map ....
  • In the subsequent tasks, you retrieve your task from that same bean using a method that fetches the connection object from the private map, and throwing an exception if no connection object was registered in the map
  • Have a boundary event that gets fired on that exception and do whatever is necessary to insure data integrity (the use case i described in my comment for instance)
  • In the last Service task, un-register your connection (you should also close it) in order to prevent memory leaks
  • Make sure to take into account the db pool ... etc while designing your solution!

Cheers!