Coverage Report - org.mule.module.jbpm.Jbpm
 
Classes in this File Line Coverage Branch Coverage Complexity
Jbpm
0%
0/108
0%
0/48
0
 
 1  
 /*
 2  
  * $Id: Jbpm.java 20121 2010-11-08 17:33:48Z tcarlson $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.module.jbpm;
 12  
 
 13  
 import org.mule.api.NamedObject;
 14  
 import org.mule.api.lifecycle.Disposable;
 15  
 import org.mule.api.lifecycle.Initialisable;
 16  
 import org.mule.module.bpm.BPMS;
 17  
 import org.mule.module.bpm.MessageService;
 18  
 import org.mule.util.IOUtils;
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.io.InputStream;
 22  
 import java.util.Map;
 23  
 import java.util.Properties;
 24  
 import java.util.Set;
 25  
 
 26  
 import org.jbpm.api.Configuration;
 27  
 import org.jbpm.api.Execution;
 28  
 import org.jbpm.api.ProcessEngine;
 29  
 import org.jbpm.api.ProcessInstance;
 30  
 import org.jbpm.api.task.Task;
 31  
 import org.slf4j.Logger;
 32  
 import org.slf4j.LoggerFactory;
 33  
 
 34  
 /**
 35  
  * An implementation of Mule's generic {@link BPMS} interface for JBoss jBPM. 
 36  
  */
 37  
 public class Jbpm implements BPMS, Initialisable, Disposable, NamedObject
 38  
 {
 39  
     /** 
 40  
      * The initialized jBPM ProcessEngine. 
 41  
      */
 42  0
     protected ProcessEngine processEngine = null;
 43  
 
 44  
     /** 
 45  
      * The configuration file for jBPM, default is "jbpm.cfg.xml" if not specified. 
 46  
      */
 47  
     private String configurationResource;
 48  
     
 49  
     /** 
 50  
      * Process definitions to be loaded into jBPM at startup. 
 51  
      */
 52  
     private Properties processDefinitions;
 53  
     
 54  
     /** 
 55  
      * An optional logical name for the BPMS. 
 56  
      */
 57  
     private String name;
 58  
     
 59  
     /**
 60  
      * Indicates whether jBPM has been instantiated by the connector (false) or was
 61  
      * passed in from somewhere else (true).
 62  
      */
 63  0
     protected boolean containerManaged = true;
 64  
 
 65  
     public static final String PROCESS_ENDED = "Process has ended";
 66  
     
 67  
     /**
 68  
      * Given the multi-threaded nature of Mule, sometimes a response message will arrive to advance the 
 69  
      * process before the creation of the process has fully terminated (e.g., during in-memory unit tests).
 70  
      * After this amount of time (in ms), we stop waiting and assume there must be some other problem.  
 71  
      */
 72  
     public static final int PROCESS_CREATION_WAIT = 3000;
 73  
     
 74  0
     protected static final Logger log = LoggerFactory.getLogger(Jbpm.class);
 75  
 
 76  
     // ///////////////////////////////////////////////////////////////////////////
 77  
     // Lifecycle methods
 78  
     // ///////////////////////////////////////////////////////////////////////////
 79  
 
 80  
     /**
 81  
      * Creates the Mule wrapper for jBPM
 82  
      */
 83  
     public Jbpm()
 84  0
     {
 85  
         // empty
 86  0
     }
 87  
 
 88  
     /**
 89  
      * Creates the Mule wrapper for jBPM
 90  
      * @param configurationResource - The configuration file for jBPM, default is "jbpm.cfg.xml" if not specified.
 91  
      * @param processDefinitions - A list of process definitions to load into jBPM upon initialization.
 92  
      */
 93  
     public Jbpm(String configurationResource, Properties processDefinitions)
 94  0
     {
 95  0
         this.configurationResource = configurationResource;
 96  0
         this.processDefinitions = processDefinitions;
 97  0
     }
 98  
 
 99  
     /**
 100  
      * Creates the Mule wrapper for jBPM
 101  
      * @param The already-initialized jBPM ProcessEngine.  This is useful if you use Spring to configure your jBPM instance.
 102  
      */
 103  
     public Jbpm(ProcessEngine processEngine, Properties processDefinitions)
 104  0
     {
 105  0
         this.processEngine = processEngine;
 106  0
         this.processDefinitions = processDefinitions;
 107  0
     }
 108  
 
 109  
     public void initialise()
 110  
     {
 111  0
         if (processEngine == null)
 112  
         {
 113  0
             Configuration config = new Configuration();
 114  0
             if (configurationResource != null)
 115  
             {
 116  0
                 config.setResource(configurationResource);
 117  
             }
 118  0
             setProcessEngine(config.buildProcessEngine());  
 119  0
             containerManaged = false;
 120  
         }
 121  0
         if (processDefinitions != null)
 122  
         {
 123  0
             for (Object def : processDefinitions.values())
 124  
             {
 125  
                 try
 126  
                 {
 127  0
                     deployProcess((String) def);
 128  
                 }
 129  0
                 catch (IOException e)
 130  
                 {
 131  0
                     log.error("Unable to deploy process definition: " + e.getMessage());
 132  0
                 }
 133  
             }
 134  
         }
 135  0
     }
 136  
     
 137  
     public void dispose()
 138  
     {
 139  0
         if (!containerManaged && processEngine != null)
 140  
         {
 141  0
             processEngine.close();
 142  0
             processEngine = null;
 143  
         }
 144  0
     }
 145  
 
 146  
     public void setMessageService(MessageService msgService)
 147  
     {
 148  0
         MuleMessageService serviceProxy = processEngine.get(MuleMessageService.class);
 149  0
         serviceProxy.setMessageService(msgService);
 150  0
     }
 151  
 
 152  
     // ///////////////////////////////////////////////////////////////////////////
 153  
     // Process manipulation
 154  
     // ///////////////////////////////////////////////////////////////////////////
 155  
 
 156  
     /**
 157  
      * Start a new process.
 158  
      * 
 159  
      * @return the newly-created ProcessInstance
 160  
      */
 161  
     public Object startProcess(Object processDefinitionKey) throws Exception
 162  
     {
 163  0
         return startProcess(processDefinitionKey, null, null);
 164  
     }
 165  
     
 166  
     /**
 167  
      * Start a new process.
 168  
      * 
 169  
      * @return the newly-created ProcessInstance
 170  
      */
 171  
     public Object startProcess(Object processDefinitionKey, Object signalName, Map variables) throws Exception
 172  
     {
 173  0
         ProcessInstance processInstance = 
 174  
             processEngine.getExecutionService().startProcessInstanceByKey((String) processDefinitionKey, (Map) variables);
 175  
 
 176  0
         if (processInstance == null)
 177  
         {
 178  0
             throw new IllegalArgumentException("No process definition found for process " + processDefinitionKey);
 179  
         }
 180  
 
 181  0
         return processInstance;
 182  
     }
 183  
 
 184  
     /**
 185  
      * Advance a process instance one step.
 186  
      * 
 187  
      * @return the updated ProcessInstance
 188  
      */
 189  
     public Object advanceProcess(Object executionId) throws Exception
 190  
     {
 191  0
         return advanceProcess(executionId, null, null);
 192  
     }
 193  
 
 194  
     /**
 195  
      * Advance a process instance one step.
 196  
      * 
 197  
      * @param processId - if no transition value is provided, this is assumed to be
 198  
      *            execution id
 199  
      * @param transition - the state of the execution we're looking for
 200  
      * @param processVariables - optional process variables/parameters to set
 201  
      * @return the updated ProcessInstance
 202  
      */
 203  
     public Object advanceProcess(Object executionId, Object signalName, Map variables) throws Exception
 204  
     {
 205  0
         int waitTime = 0;
 206  0
         Execution execution = processEngine.getExecutionService().findExecutionById((String) executionId);
 207  0
         while (execution == null && waitTime < PROCESS_CREATION_WAIT)
 208  
         {
 209  
             // Given the multi-threaded nature of Mule, sometimes a response message will arrive to advance the 
 210  
             // process before the creation of the process has fully terminated (e.g., during in-memory unit tests).  
 211  
             // We delay for awhile to make sure this is not the case before giving up and throwing an exception.
 212  0
             Thread.sleep(PROCESS_CREATION_WAIT / 10);
 213  0
             waitTime += (PROCESS_CREATION_WAIT / 10);
 214  0
             execution = processEngine.getExecutionService().findExecutionById((String) executionId);
 215  
         }
 216  0
         if (execution == null)
 217  
         {
 218  0
             throw new IllegalArgumentException("No process execution found with id = " + executionId + " (it may have already terminated)");
 219  
         }
 220  
         
 221  
         String processId;
 222  0
         if (execution.getProcessInstance() != null)
 223  
         {
 224  0
             processId = execution.getProcessInstance().getId();
 225  
         }
 226  
         else
 227  
         {
 228  0
             processId = execution.getId();
 229  
         }
 230  
 
 231  
         // Set any process variables.
 232  0
         if (variables != null && !variables.isEmpty())
 233  
         {
 234  0
             processEngine.getExecutionService().setVariables((String) executionId, variables);
 235  
         }
 236  
 
 237  
         // MULE-1690
 238  0
         synchronized (this)
 239  
         {
 240  0
             processEngine.getExecutionService().signalExecutionById((String) executionId, (String) signalName, variables);
 241  0
         }
 242  
 
 243  
         // Refresh process info. from the DB
 244  0
         ProcessInstance process = processEngine.getExecutionService().findProcessInstanceById(processId);
 245  0
         if (process == null)
 246  
         {
 247  
             // The process has already ended, so we return a mock/skeleton ProcessInstance with the expected ID and state = "ended"
 248  0
             process = new EndedProcess(processId);
 249  
         }
 250  0
         return process;
 251  
     }
 252  
 
 253  
     /**
 254  
      * Update the variables for an execution.
 255  
      * 
 256  
      * @return the updated ProcessInstance
 257  
      */
 258  
     public Object updateProcess(Object executionId, Map variables) throws Exception
 259  
     {
 260  
         // Get Process ID
 261  
         String processId;
 262  0
         Execution execution = processEngine.getExecutionService().findExecutionById((String) executionId);
 263  0
         if (execution == null)
 264  
         {
 265  0
             throw new IllegalArgumentException("No process execution found with id = " + executionId + " (it may have already terminated)");
 266  
         }
 267  0
         if (execution.getProcessInstance() != null)
 268  
         {
 269  0
             processId = execution.getProcessInstance().getId();
 270  
         }
 271  
         else
 272  
         {
 273  0
             processId = execution.getId();
 274  
         }
 275  
 
 276  
         // Set any process variables.
 277  0
         if (variables != null && !variables.isEmpty())
 278  
         {
 279  0
             processEngine.getExecutionService().setVariables((String) executionId, variables);
 280  
         }
 281  
 
 282  
         // Refresh process info. from the DB
 283  0
         ProcessInstance process = processEngine.getExecutionService().findProcessInstanceById(processId);
 284  0
         if (process == null)
 285  
         {
 286  
             // The process has already ended, so we return a mock/skeleton ProcessInstance with the expected ID and state = "ended"
 287  0
             process = new EndedProcess(processId);
 288  
         }
 289  0
         return process;
 290  
     }
 291  
 
 292  
     /**
 293  
      * Delete a process instance.
 294  
      */
 295  
     public void abortProcess(Object processInstanceId) throws Exception
 296  
     {
 297  0
         processEngine.getExecutionService().endProcessInstance((String) processInstanceId, Execution.STATE_ENDED);
 298  0
     }
 299  
 
 300  
     // ///////////////////////////////////////////////////////////////////////////
 301  
     // Process status / lookup
 302  
     // ///////////////////////////////////////////////////////////////////////////
 303  
 
 304  
     public boolean isProcess(Object obj) throws Exception
 305  
     {
 306  0
         return (obj instanceof ProcessInstance);
 307  
     }
 308  
 
 309  
     public Object getId(Object process) throws Exception
 310  
     {
 311  0
         return ((ProcessInstance) process).getId();
 312  
     }
 313  
 
 314  
     public Object getState(Object process) throws Exception
 315  
     {
 316  0
         return getState((ProcessInstance) process);
 317  
     }
 318  
     
 319  
     public static String getState(ProcessInstance processInstance) throws Exception
 320  
     {
 321  0
         if (processInstance == null || processInstance.isEnded())
 322  
         {
 323  0
             return ProcessInstance.STATE_ENDED;
 324  
         }
 325  
         
 326  0
         Set activities = processInstance.findActiveActivityNames();
 327  0
         String state = null;
 328  
         // Separate concurrent paths of execution with a "/"
 329  0
         for (Object activityName : activities)
 330  
         {
 331  0
             if (state == null)
 332  
             {
 333  0
                 state = (String) activityName;
 334  
             }
 335  
             else
 336  
             {
 337  0
                 state += " / " + activityName;
 338  
             }
 339  
         }
 340  0
         return state;
 341  
     }
 342  
 
 343  
     public boolean hasEnded(Object process) throws Exception
 344  
     {
 345  0
         return process == null ? true : ((ProcessInstance) process).isEnded();
 346  
     }
 347  
 
 348  
     /**
 349  
      * Look up an already-running process instance.
 350  
      * 
 351  
      * @return the ProcessInstance
 352  
      */
 353  
     public Object lookupProcess(Object processId) throws Exception
 354  
     {
 355  0
         return processEngine.getExecutionService().findProcessInstanceById((String) processId);
 356  
     }
 357  
 
 358  
     // ///////////////////////////////////////////////////////////////////////////
 359  
     // Miscellaneous
 360  
     // ///////////////////////////////////////////////////////////////////////////
 361  
 
 362  
     public void deployProcess(String processDefinitionFile) throws IOException
 363  
     {
 364  0
         deployProcessFromStream(processDefinitionFile, IOUtils.getResourceAsStream(processDefinitionFile,
 365  
             getClass()));
 366  0
     }
 367  
 
 368  
     public void deployProcessFromStream(String resourceName, InputStream processDefinition)
 369  
         throws IOException
 370  
     {
 371  0
         processEngine.getRepositoryService().createDeployment()
 372  
             .addResourceFromInputStream(resourceName, processDefinition)
 373  
             .deploy();
 374  0
     }
 375  
 
 376  
     public void undeployProcess(String resource) throws Exception
 377  
     {
 378  
         // empty
 379  0
     }
 380  
     
 381  
     public void completeTask(Task task)
 382  
     {
 383  0
         completeTask(task, null, null);
 384  0
     }
 385  
 
 386  
     public void completeTask(Task task, String outcome, Map variables)
 387  
     {
 388  0
         processEngine.getTaskService().completeTask(task.getId(), outcome, variables);
 389  0
     }
 390  
 
 391  
     // ///////////////////////////////////////////////////////////////////////////
 392  
     // Getters and setters
 393  
     // ///////////////////////////////////////////////////////////////////////////
 394  
 
 395  
     public ProcessEngine getProcessEngine()
 396  
     {
 397  0
         return processEngine;
 398  
     }
 399  
 
 400  
     public void setProcessEngine(ProcessEngine processEngine)
 401  
     {
 402  0
         this.processEngine = processEngine;
 403  0
     }
 404  
 
 405  
     public String getConfigurationResource()
 406  
     {
 407  0
         return configurationResource;
 408  
     }
 409  
 
 410  
     public void setConfigurationResource(String configurationResource)
 411  
     {
 412  0
         this.configurationResource = configurationResource;
 413  0
     }
 414  
 
 415  
     public Properties getProcessDefinitions()
 416  
     {
 417  0
         return processDefinitions;
 418  
     }
 419  
 
 420  
     public void setProcessDefinitions(Properties processDefinitions)
 421  
     {
 422  0
         this.processDefinitions = processDefinitions;
 423  0
     }
 424  
 
 425  
     public void setName(String name)
 426  
     {
 427  0
         this.name = name;
 428  0
     }
 429  
 
 430  
     public String getName()
 431  
     {
 432  0
         return name;
 433  
     }
 434  
 }