/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.bpm;
import static org.jboss.seam.annotations.Install.BUILT_IN;
import javax.naming.NamingException;
import javax.transaction.RollbackException;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Unwrap;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.transaction.Transaction;
import org.jboss.seam.transaction.UserTransaction;
import org.jbpm.JbpmContext;
import org.jbpm.persistence.db.DbPersistenceServiceFactory;
import org.jbpm.svc.Services;
/**
* Manages a reference to a JbpmContext.
*
* @author <a href="mailto:steve@hibernate.org">Steve Ebersole</a>
* @author Gavin King
*/
@Scope(ScopeType.EVENT)
@Name("org.jboss.seam.bpm.jbpmContext")
@BypassInterceptors
@Install(precedence=BUILT_IN, dependencies="org.jboss.seam.bpm.jbpm")
public class ManagedJbpmContext implements Synchronization
{
private static final LogProvider log = Logging.getLogProvider(ManagedJbpmContext.class);
private JbpmContext jbpmContext;
private boolean synchronizationRegistered;
@Create
public void create() throws NamingException, RollbackException, SystemException
{
jbpmContext = Jbpm.instance().getJbpmConfiguration().createJbpmContext();
assertNoTransactionManagement();
log.debug( "created seam managed jBPM context");
}
private void assertNoTransactionManagement()
{
DbPersistenceServiceFactory dpsf = (DbPersistenceServiceFactory) jbpmContext.getJbpmConfiguration()
.getServiceFactory(Services.SERVICENAME_PERSISTENCE);
if ( dpsf.isTransactionEnabled() )
{
throw new IllegalStateException("jBPM transaction management is enabled, disable in jbpm.cfg.xml");
}
}
@Unwrap
public JbpmContext getJbpmContext() throws NamingException, RollbackException, SystemException
{
joinTransaction();
return jbpmContext;
}
private void joinTransaction() throws SystemException
{
UserTransaction transaction = Transaction.instance();
if ( !transaction.isActiveOrMarkedRollback() )
{
throw new IllegalStateException("JbpmContext may only be used inside a transaction");
}
if ( !synchronizationRegistered && !Lifecycle.isDestroying() && transaction.isActive() )
{
jbpmContext.getSession().isOpen();
try //TODO: what we really want here is if (!cmt)
{
transaction.registerSynchronization(this);
}
catch (UnsupportedOperationException uoe)
{
jbpmContext.getSession().getTransaction().registerSynchronization(this);
}
synchronizationRegistered = true;
}
}
public void beforeCompletion()
{
log.debug( "flushing seam managed jBPM context" );
/*org.jbpm.graph.exe.ProcessInstance processInstance = ProcessInstance.instance();
if (processInstance!=null)
{
jbpmContext.save(processInstance);
}*/
if ( Contexts.isBusinessProcessContextActive() )
{
//in requests that come through SeamPhaseListener,
//transactions are committed before the contexts are
//destroyed, flush here:
Contexts.getBusinessProcessContext().flush();
}
jbpmContext.getSession().flush();
log.debug( "done flushing seam managed jBPM context" );
}
public void afterCompletion(int status)
{
synchronizationRegistered = false;
if ( !Contexts.isEventContextActive() )
{
//in calls to MDBs and remote calls to SBs, the
//transaction doesn't commit until after contexts
//are destroyed, so wait until the transaction
//completes before closing the session
//on the other hand, if we still have an active
//event context, leave it open
closeContext();
}
}
@Destroy
public void destroy()
{
if ( !synchronizationRegistered )
{
//in requests that come through SeamPhaseListener,
//there can be multiple transactions per request,
//but they are all completed by the time contexts
//are dstroyed
//so wait until the end of the request to close
//the session
//on the other hand, if we are still waiting for
//the transaction to commit, leave it open
closeContext();
}
}
private void closeContext()
{
log.debug( "destroying seam managed jBPM context" );
jbpmContext.close();
log.debug( "done destroying seam managed jBPM context" );
}
public static JbpmContext instance()
{
if ( !Contexts.isEventContextActive() )
{
throw new IllegalStateException("no active event context");
}
return (JbpmContext) Component.getInstance(ManagedJbpmContext.class, ScopeType.EVENT);
}
}