/** * Created on Feb 20, 2006 * * $Id: JbpmTemplate.java,v 1.6 2006/09/15 13:32:06 costin Exp $ * $Revision: 1.6 $ */ package org.springmodules.workflow.jbpm31; import java.sql.SQLException; import java.util.List; import org.hibernate.HibernateException; import org.hibernate.Session; import org.jbpm.JbpmConfiguration; import org.jbpm.JbpmContext; import org.jbpm.JbpmException; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.def.Transition; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.graph.exe.Token; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.HibernateTemplate; /** * Jbpm 3.1 Template. Requires a jbpmConfiguration and accepts also a * hibernateTemplate and processDefinition. Jbpm Persistence Service can be * managed by Spring through the given HibernateTemplate, allowing jBPM to work * with a user configured session factory and thread-bound session depending on * the HibernateTemplate settings. However, due to the nature of jBPM * architecture, on each execute the jbpmContext will try to close the user * Hibernate session which is undesireable when working with a thread-bound * session or a transaction. One can overcome this undesired behavior by setting * 'exposeNative' property on the HibernateTemplate to false (default). * * @see org.springframework.orm.hibernate3.HibernateTemplate * @author Costin Leau * */ public class JbpmTemplate extends JbpmAccessor implements JbpmOperations { // TODO: persistence is not always required /** * Optional process definition. */ private ProcessDefinition processDefinition; /** * Required if jBPM has persistence services. */ private HibernateTemplate hibernateTemplate; /** * Boolean used to determine if the persistence service is used or not. If * so, hibernateTemplate will be required and used internally. */ private boolean hasPersistenceService; /** * jBPM context name. defaults to null which is equivalent to * JbpmContext.DEFAULT_JBPM_CONTEXT_NAME */ private String contextName = JbpmContext.DEFAULT_JBPM_CONTEXT_NAME; /** * Execute the action specified by the given action object within a * JbpmSession. * * @param callback * @return */ public Object execute(final JbpmCallback callback) { final JbpmContext context = getContext(); try { // use the hibernateTemplate is present and if needed if (hibernateTemplate != null && hasPersistenceService) { // use hibernate template return hibernateTemplate.execute(new HibernateCallback() { /** * @see org.springframework.orm.hibernate3.HibernateCallback#doInHibernate(org.hibernate.Session) */ public Object doInHibernate(Session session) throws HibernateException, SQLException { // inject the session in the context context.setSession(session); return callback.doInJbpm(context); } }); } // plain callback invocation (no template w/ persistence) return callback.doInJbpm(context); } catch (JbpmException ex) { throw convertJbpmException(ex); } finally { releaseContext(context); } } /** * Hook for subclasses for adding custom behavior. * * @param jbpmContext */ protected void releaseContext(JbpmContext jbpmContext) { jbpmContext.close(); } /** * Hook for subclasses for adding custom behavior. * * @return created of fetched from the thread jbpm context. */ protected JbpmContext getContext() { JbpmContext context = jbpmConfiguration.createJbpmContext(contextName); return context; } /** * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); // see if persistence is required // we don't have any other way to get the services then by creating a // jbpm context // secured in try/finally block JbpmContext dummy = getContext(); try { if (JbpmUtils.hasPersistenceService(dummy)) { hasPersistenceService = true; logger.debug("jBPM persistence service present"); } if (hibernateTemplate != null) logger.debug("hibernateTemplate present - jBPM persistence service will be managed by Spring"); else { if (dummy.getSessionFactory() != null) { logger.debug("creating hibernateTemplate based on jBPM SessionFactory"); hibernateTemplate = new HibernateTemplate(dummy.getSessionFactory()); } else logger.debug("hibernateTemplate missing - jBPM will handle its own persistence"); } } finally { dummy.close(); } } public JbpmTemplate() { } public JbpmTemplate(JbpmConfiguration jbpmConfiguration) { this.jbpmConfiguration = jbpmConfiguration; } public JbpmTemplate(JbpmConfiguration jbpmConfiguration, ProcessDefinition processDefinition) { this.jbpmConfiguration = jbpmConfiguration; this.processDefinition = processDefinition; } /** * @return Returns the contextName. */ public String getContextName() { return contextName; } /** * @param contextName The contextName to set. */ public void setContextName(String contextName) { this.contextName = contextName; } /** * @return Returns the hibernateTemplate. */ public HibernateTemplate getHibernateTemplate() { return hibernateTemplate; } /** * @param hibernateTemplate The hibernateTemplate to set. */ public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } /** * @return Returns the processDefinition. */ public ProcessDefinition getProcessDefinition() { return processDefinition; } /** * @param processDefinition The processDefinition to set. */ public void setProcessDefinition(ProcessDefinition processDefinition) { this.processDefinition = processDefinition; } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findProcessInstance(java.lang.Long) */ public ProcessInstance findProcessInstance(final Long processInstanceId) { return (ProcessInstance) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { return context.getGraphSession().loadProcessInstance(processInstanceId.longValue()); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findProcessInstances() */ public List findProcessInstances() { return (List) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { return context.getGraphSession().findProcessInstances(processDefinition.getId()); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findPooledTaskInstances(java.lang.String) */ public List findPooledTaskInstances(final String actorId) { return (List) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { return context.getTaskMgmtSession().findPooledTaskInstances(actorId); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findPooledTaskInstances(java.util.List) */ public List findPooledTaskInstances(final List actorIds) { return (List) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { return context.getTaskMgmtSession().findPooledTaskInstances(actorIds); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findTaskInstances(java.lang.String) */ public List findTaskInstances(final String actorId) { return (List) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { return context.getTaskMgmtSession().findTaskInstances(actorId); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findTaskInstances(java.lang.String[]) */ public List findTaskInstances(final String[] actorIds) { return (List) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { return context.getTaskMgmtSession().findTaskInstances(actorIds); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findTaskInstances(java.util.List) */ public List findTaskInstances(final List actorIds) { return (List) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { return context.getTaskMgmtSession().findTaskInstances(actorIds); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findTaskInstancesByToken(org.jbpm.graph.exe.Token) */ public List findTaskInstancesByToken(Token token) { return findTaskInstancesByToken(token.getId()); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#findTaskInstancesByToken(long) */ public List findTaskInstancesByToken(final long tokenId) { return (List) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { return context.getTaskMgmtSession().findTaskInstancesByToken(tokenId); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#signal(org.jbpm.graph.exe.ProcessInstance) */ public void signal(final ProcessInstance processInstance) { execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { processInstance.signal(); return null; } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#saveProcessInstance(org.jbpm.graph.exe.ProcessInstance) */ public Long saveProcessInstance(final ProcessInstance processInstance) { return (Long) execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { context.save(processInstance); return new Long(processInstance.getId()); } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#signal(org.jbpm.graph.exe.ProcessInstance, * java.lang.String) */ public void signal(final ProcessInstance processInstance, final String transitionId) { execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { processInstance.signal(transitionId); return null; } }); } /** * @see org.springmodules.workflow.jbpm31.JbpmOperations#signal(org.jbpm.graph.exe.ProcessInstance, * org.jbpm.graph.def.Transition) */ public void signal(final ProcessInstance processInstance, final Transition transition) { execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { processInstance.signal(transition); return null; } }); throw new UnsupportedOperationException(); } /** * Signals a specific token in a process instance. Used to progress through execution paths other than the main one. * If the token could not be found, the root token is signaled (main execution path). * * @param processInstance process instance to progress through * @param tokenName name of the token to signal */ public void signalToken(final ProcessInstance processInstance, final String tokenName) { execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { Token token = processInstance.getRootToken().findToken(tokenName); if (token == null) { processInstance.signal(); } else { token.signal(); } return null; } }); } /** * Signals a specific token with the given transition to take. * * @see #signalToken(ProcessInstance, String) * @param processInstance process instance to progress through * @param tokenName name of the token to signal * @param transitionId transition to take in the execution path */ public void signalToken(final ProcessInstance processInstance, final String tokenName, final String transitionId) { execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { Token token = processInstance.getRootToken().findToken(tokenName); if (token == null) { processInstance.signal(transitionId); } else { token.signal(transitionId); } return null; } }); } }