View Javadoc

1   /*
2    * $Id: Jbpm.java 10489 2008-01-23 17:53:38Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.bpm.jbpm;
12  
13  import org.mule.transport.bpm.BPMS;
14  import org.mule.transport.bpm.MessageService;
15  import org.mule.util.IOUtils;
16  import org.mule.util.NumberUtils;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.jbpm.JbpmConfiguration;
26  import org.jbpm.JbpmContext;
27  import org.jbpm.graph.def.ProcessDefinition;
28  import org.jbpm.graph.exe.ProcessInstance;
29  import org.jbpm.taskmgmt.exe.TaskInstance;
30  
31  /**
32   * jBPM's implementation of Mule's generic BPMS interface.
33   * This class should be set as the "bpms" property of the BPM Connector:
34   *
35   *   <connector name="jBpmConnector" className="org.mule.transport.bpm.ProcessConnector">
36   *       <properties>
37   *           <spring-property name="bpms">
38   *              <ref local="jbpm" />
39   *           </spring-property>
40   *       </properties>
41   *   </connector>
42   *
43   *   <bean id="jbpm" class="org.mule.transport.bpm.jbpm.Jbpm" destroy-method="destroy">
44   *       <spring-property name="jbpmConfiguration">
45   *           <ref local="jbpmConfig" />
46   *       </spring-property>
47   *   </bean>
48   */
49  public class Jbpm implements BPMS
50  {
51      protected static final Log logger = LogFactory.getLog(Jbpm.class);
52  
53      protected JbpmConfiguration jbpmConfiguration = null;
54  
55      /**
56       * Indicates whether jBPM has been instantiated by the connector (false) or was passed 
57       * in from somewhere else (true).
58       */
59      protected boolean containerManaged;
60      
61      // ///////////////////////////////////////////////////////////////////////////
62      // Lifecycle methods
63      // ///////////////////////////////////////////////////////////////////////////
64  
65      /**
66       * Creates the Mule wrapper for jBPM using a default configuration. 
67       */
68      public Jbpm()
69      {
70          this(JbpmConfiguration.getInstance());
71          containerManaged = false;
72      }
73  
74      /**
75       * Creates the Mule BPM wrapper based on an already-initialized jBPM instance
76       * 
77       * @param jbpmConfiguration - the already-initialized jBPM instance
78       */
79      public Jbpm(JbpmConfiguration jbpmConfiguration)
80      {
81          setJbpmConfiguration(jbpmConfiguration);
82          containerManaged = true;
83      }
84  
85      public void destroy()
86      {
87          if (!containerManaged && jbpmConfiguration != null)
88          {
89              jbpmConfiguration.close();
90              jbpmConfiguration = null;
91          }
92      }
93  
94      public void setMessageService(MessageService msgService)
95      {
96          JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
97          try
98          {
99              MuleMessageService.setMessageService(msgService);
100         }
101         finally
102         {
103             jbpmContext.close();
104         }
105     }
106     
107     // ///////////////////////////////////////////////////////////////////////////
108     // Process manipulation
109     // ///////////////////////////////////////////////////////////////////////////
110 
111     /**
112      * Start a new process.
113      * 
114      * @return the newly-created ProcessInstance
115      */
116     public synchronized Object startProcess(Object processType) throws Exception
117     {
118         return startProcess(processType, /* transition */null, /* processVariables */null);
119     }
120 
121     /**
122      * Start a new process.
123      * 
124      * @return the newly-created ProcessInstance
125      */
126     public synchronized Object startProcess(Object processType, Object transition, Map processVariables) throws Exception
127     {
128         ProcessInstance processInstance;
129 
130         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
131         try
132         {
133             ProcessDefinition processDefinition = jbpmContext.getGraphSession().findLatestProcessDefinition(
134                 (String) processType);
135             if (processDefinition == null)
136             {
137                 throw new IllegalArgumentException("No process definition found for process " + processType);
138             }
139 
140             processInstance = new ProcessInstance(processDefinition);
141 
142             // Set any process variables.
143             if (processVariables != null && !processVariables.isEmpty())
144             {
145                 processInstance.getContextInstance().addVariables(processVariables);
146             }
147             processInstance.getContextInstance().addVariables(processVariables);
148 
149             // Leave the start state.
150             processInstance.signal();
151 
152             jbpmContext.save(processInstance);
153 
154             return processInstance;
155         }
156         catch (Exception e)
157         {
158             jbpmContext.setRollbackOnly();
159             throw e;
160         }
161         finally
162         {
163             jbpmContext.close();
164         }
165     }
166 
167     /**
168      * Advance a process instance one step.
169      * 
170      * @return the updated ProcessInstance
171      */
172     public synchronized Object advanceProcess(Object processId) throws Exception
173     {
174         return advanceProcess(processId, /* transition */null, /* processVariables */null);
175     }
176 
177     /**
178      * Advance a process instance one step.
179      * 
180      * @return the updated ProcessInstance
181      */
182     public synchronized Object advanceProcess(Object processId, Object transition, Map processVariables)
183         throws Exception
184     {
185         ProcessInstance processInstance;
186 
187         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
188         try
189         {
190             // Look up the process instance from the database.
191             processInstance = jbpmContext.getGraphSession()
192                 .loadProcessInstance(NumberUtils.toLong(processId));
193 
194             if (processInstance.hasEnded())
195             {
196                 throw new IllegalStateException(
197                     "Process cannot be advanced because it has already terminated, processId = " + processId);
198             }
199 
200             // Set any process variables.
201             // Note: addVariables() will replace the old value of a variable if it
202             // already exists.
203             if (processVariables != null && !processVariables.isEmpty())
204             {
205                 processInstance.getContextInstance().addVariables(processVariables);
206             }
207 
208             // Advance the workflow.
209             if (transition != null)
210             {
211                 processInstance.signal((String) transition);
212             }
213             else
214             {
215                 processInstance.signal();
216             }
217 
218             // Save the process state back to the database.
219             jbpmContext.save(processInstance);
220 
221             return processInstance;
222         }
223         catch (Exception e)
224         {
225             jbpmContext.setRollbackOnly();
226             throw e;
227         }
228         finally
229         {
230             jbpmContext.close();
231         }
232     }
233 
234     /**
235      * Update the variables for a process instance.
236      * 
237      * @return the updated ProcessInstance
238      */
239     public synchronized Object updateProcess(Object processId, Map processVariables) throws Exception
240     {
241         ProcessInstance processInstance;
242 
243         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
244         try
245         {
246             // Look up the process instance from the database.
247             processInstance = jbpmContext.getGraphSession()
248                 .loadProcessInstance(NumberUtils.toLong(processId));
249 
250             // Set any process variables.
251             // Note: addVariables() will replace the old value of a variable if it
252             // already exists.
253             if (processVariables != null && !processVariables.isEmpty())
254             {
255                 processInstance.getContextInstance().addVariables(processVariables);
256             }
257 
258             // Save the process state back to the database.
259             jbpmContext.save(processInstance);
260 
261             return processInstance;
262         }
263         catch (Exception e)
264         {
265             jbpmContext.setRollbackOnly();
266             throw e;
267         }
268         finally
269         {
270             jbpmContext.close();
271         }
272     }
273 
274     /**
275      * Delete a process instance.
276      */
277     public synchronized void abortProcess(Object processId) throws Exception
278     {
279         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
280         try
281         {
282             jbpmContext.getGraphSession().deleteProcessInstance(NumberUtils.toLong(processId));
283         }
284         catch (Exception e)
285         {
286             jbpmContext.setRollbackOnly();
287             throw e;
288         }
289         finally
290         {
291             jbpmContext.close();
292         }
293     }
294 
295     // ///////////////////////////////////////////////////////////////////////////
296     // Process status / lookup
297     // ///////////////////////////////////////////////////////////////////////////
298 
299     public boolean isProcess(Object obj) throws Exception
300     {
301         return (obj instanceof ProcessInstance);
302     }
303 
304     public Object getId(Object process) throws Exception
305     {
306         return new Long(((ProcessInstance) process).getId());
307     }
308 
309     // By default the process is lazily-initialized so we need to open a new session to the
310     // database before calling process.getRootToken().getNode().getName()
311     public Object getState(Object process) throws Exception
312     {
313         ProcessInstance processInstance = (ProcessInstance) process;
314 
315         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
316         try
317         {
318             // Look up the process instance from the database.
319             processInstance = jbpmContext.getGraphSession()
320                 .loadProcessInstance(processInstance.getId());
321             return processInstance.getRootToken().getNode().getName();
322         }
323         finally
324         {
325             jbpmContext.close();
326         }
327     }
328 
329     public boolean hasEnded(Object process) throws Exception
330     {
331         return ((ProcessInstance) process).hasEnded();
332     }
333 
334     /**
335      * Look up an already-running process instance.
336      * 
337      * @return the ProcessInstance
338      */
339     public Object lookupProcess(Object processId) throws Exception
340     {
341         ProcessInstance processInstance;
342 
343         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
344         try
345         {
346             // Look up the process instance from the database.
347             processInstance = jbpmContext.getGraphSession()
348                 .loadProcessInstance(NumberUtils.toLong(processId));
349             return processInstance;
350         }
351         finally
352         {
353             jbpmContext.close();
354         }
355     }
356 
357     // ///////////////////////////////////////////////////////////////////////////
358     // Miscellaneous
359     // ///////////////////////////////////////////////////////////////////////////
360 
361     /**
362      * Deploy a new process definition.
363      */
364     public void deployProcess(String processDefinitionFile) throws IOException
365     {
366         deployProcessFromStream(IOUtils.getResourceAsStream(processDefinitionFile, getClass()));
367     }
368 
369     public void deployProcessFromStream(InputStream processDefinition)
370         throws IOException
371     {
372         deployProcess(ProcessDefinition.parseXmlInputStream(processDefinition));
373     }
374 
375     private void deployProcess(ProcessDefinition processDefinition)
376     {
377         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
378         try
379         {
380             jbpmContext.deployProcessDefinition(processDefinition);
381         }
382         finally
383         {
384             jbpmContext.close();
385         }
386     }
387 
388     public List/* <TaskInstance> */loadTasks(ProcessInstance process)
389     {
390         List/* <TaskInstance> */taskInstances;
391 
392         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
393         try
394         {
395             taskInstances = jbpmContext.getTaskMgmtSession().findTaskInstancesByToken(
396                 process.getRootToken().getId());
397             return taskInstances;
398         }
399         finally
400         {
401             jbpmContext.close();
402         }
403     }
404 
405     public synchronized void completeTask(TaskInstance task)
406     {
407         completeTask(task, /* transition */null);
408     }
409 
410     public synchronized void completeTask(TaskInstance task, String transition)
411     {
412         JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
413         try
414         {
415             task = jbpmContext.getTaskMgmtSession().loadTaskInstance(task.getId());
416             if (transition != null)
417             {
418                 task.end(transition);
419             }
420             else
421             {
422                 task.end();
423             }
424         }
425         finally
426         {
427             jbpmContext.close();
428         }
429     }
430 
431     // ///////////////////////////////////////////////////////////////////////////
432     // Getters and setters
433     // ///////////////////////////////////////////////////////////////////////////
434 
435     public JbpmConfiguration getJbpmConfiguration()
436     {
437         return jbpmConfiguration;
438     }
439 
440     public void setJbpmConfiguration(JbpmConfiguration jbpmConfiguration)
441     {
442         this.jbpmConfiguration = jbpmConfiguration;
443     }
444 }