2012/10/19

Dispose session in CMT environment

Since jBPM5 is flexible process engine it can be deployed in various flavours. One of them is to embed it into you enterprise application running on application server, regardless of its vendor (JBoss, WebSphere, WebLogic, etc).
One option among many others is to make use of it as part of you business logic implemented as EJB. If you choose to use bean managed transaction (BMT) you do not need to take any additional steps as your business logic maintains transaction boundaries. Although when you use container managed transaction (CMT) situation is little bit different as it is container (application server) responsibility to manage transaction.

Before we jump into details what needs to be done for CMT based application, let's mention one important and common for both types (BMT and CMT) practice:

Session must be disposed outside transaction, meaning transaction must be committed/rolledback before session could be disposed.

Obviously this applies to situation when session should be disposed as part of business logic, for instance with session per process instance architecture this could be desired. But not when we have single centralized session.

If session will be disposed before transaction is completed, exception will be thrown on transaction completion, complaining that session is already disposed:

IllegalStateException
("Illegal method call. This session was previously disposed.")

Having this in mind, let's take a look at how this can be done in CMT based implementations. Since we do not control transaction how we could dispose session after transaction is completed?
A simple answer to this is to use dedicated Command that will register transaction synchronization object that will be called on transaction completion so we could safely dispose session.

Here is an example of such command's execute method:
    
public Void execute(Context context) {
       
  final StatefulKnowledgeSession ksession =     

                ((KnowledgeCommandContext)context).getStatefulKnowledgesession();
  try {
      TransactionManager tm = 

      (TransactionManager) new InitialContext().lookup(tmLookupName);
      tm.getTransaction().registerSynchronization(new Synchronization() {
               
          @Override
          public void beforeCompletion() {
              // not used here         
          }
               
          @Override
          public void afterCompletion(int arg0) {
              ksession.dispose();           
          }
      });
  } catch (Exception e) {
      e.printStackTrace();
  }  
       
    return null

}

so, instead of calling default ksession.dispose() method at the end of you business logic, simply call ksession.execute(new CMTDisposeCommand());
That will ensure session is disposed as part of transaction completion.

Here is complete CMT dispose command