View Javadoc

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