GlobalTimerService and DisposableCommandService running in CMT
aartigao Apr 1, 2014 5:33 AMHi community,
We have some questions regarding the use of GlobalTimerService running inside Container Managed Transactions (using ContainerManagedTransactionManager as environment transaction manager). We post this just to know if we're doing something bad or if it's a "bug", before opening a JIRA issue. Here we go:
We are running JBPM through EJB service layer (it's mandatory for us to adhere to global transactions, so we can't use Bean Managed Transactions). After https://issues.jboss.org/browse/JBPM-4270 if we run BPM process without timers, all goes smooth... but if we use timers two problems arise:
- Wheter we use ThreadPoolSchedulerService or QuartzSchedulerService, since they span custom threads that doesn't have J2EE context, no transaction is present in the thread, causing ContainerManagedTransactionManager to fail at transaction synchronization. We solved this problem implementing an in-house solution of GlobalSchedulerService that uses EJB timers to execute jobs inside CMT (probably Quartz would work with the lastest release and some configuration/implementation but our solution solves runtime execution in clustering without the need of creating Quartz tables for every different domain, and defining quartz.properties for every domain-node). We think that this doesn't break the code, because GlobalSchedulerService is pluggable, and everyone can implement their own custom solution. Are we right?
- GlobalTimerService uses DisposableCommandService. Inside GlobalJpaTimerJobInstance we have the following snippet:
public Void call() throws Exception { CommandService commandService = null; try { JDKCallableJobCommand command = new JDKCallableJobCommand( this ); if (scheduler == null) { scheduler = (InternalSchedulerService) TimerServiceRegistry.getInstance().get(timerServiceId); } commandService = ((GlobalTimerService) scheduler).getCommandService(getJobContext()); commandService.execute( command ); GlobalJPATimerJobFactoryManager timerService = ((GlobalJPATimerJobFactoryManager)((GlobalTimerService) scheduler).getTimerJobFactoryManager()); timerService.removeTimerJobInstance(((DefaultJobHandle)getJobHandle()).getTimerJobInstance()); return null; } catch( Exception e ) { e.printStackTrace(); throw e; } finally { if (commandService != null && commandService instanceof DisposableCommandService) { ((DisposableCommandService) commandService).dispose(); } } }
In finally clause it forces the disposition of CommandService, who disposes the RuntimeEngine. This disposition causes errors, because after transaction completion (remember CMT) it tries to dispose again a session that was previously disposed.
We are not sure if this is the expected behaviour... do we really need a DisposableCommandService? Or perhaps DisposableCommandService would check first the transaction manager and dispose only if it's not a ContainerManagedTransactionManager?
Only for checking purposes, we commented the finally clause, and worked well (the session was disposed after transaction) but before opening a JIRA or submitting a patch we would like to comment this situation with core developers.
Many thanks for your attention.