View Javadoc

1   /*
2    * $Id: Process.java 20385 2010-11-29 20:25:26Z 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.module.bpm;
12  
13  import org.mule.DefaultMuleEvent;
14  import org.mule.DefaultMuleMessage;
15  import org.mule.MessageExchangePattern;
16  import org.mule.RequestContext;
17  import org.mule.api.MuleContext;
18  import org.mule.api.MuleEvent;
19  import org.mule.api.MuleException;
20  import org.mule.api.MuleMessage;
21  import org.mule.api.config.MuleProperties;
22  import org.mule.api.construct.FlowConstruct;
23  import org.mule.api.endpoint.EndpointBuilder;
24  import org.mule.api.endpoint.OutboundEndpoint;
25  import org.mule.api.lifecycle.Disposable;
26  import org.mule.api.lifecycle.Initialisable;
27  import org.mule.api.lifecycle.InitialisationException;
28  import org.mule.api.transport.DispatchException;
29  import org.mule.api.transport.PropertyScope;
30  import org.mule.config.i18n.MessageFactory;
31  import org.mule.session.DefaultMuleSession;
32  import org.mule.transport.NullPayload;
33  import org.mule.util.StringUtils;
34  
35  import java.util.HashMap;
36  import java.util.Map;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  /**
42   * A business process definition.
43   */
44  public class Process implements Initialisable, Disposable, MessageService
45  {
46      /** The underlying BPMS */
47      private final BPMS bpms;
48  
49      /** The logical name of the process.  This is used to look up the running process instance from the BPMS. */
50      private final String name;
51  
52      /** The resource containing the process definition.  This will be used to deploy the process to the BPMS. */
53      private final String resource;
54  
55      /** This field will be used to correlate messages with processes. */
56      protected final String processIdField;
57  
58      protected MuleContext muleContext;
59  
60      /** Needed for exception handling. */
61      private FlowConstruct flowConstruct;
62  
63      public static final String BPM_PROPERTY_PREFIX = "BPM_";
64      
65      public static final String PROPERTY_ENDPOINT = 
66          MuleProperties.PROPERTY_PREFIX + BPM_PROPERTY_PREFIX + "ENDPOINT";
67      public static final String PROPERTY_PROCESS_TYPE = 
68          MuleProperties.PROPERTY_PREFIX + BPM_PROPERTY_PREFIX + "PROCESS_TYPE";
69      public static final String PROPERTY_PROCESS_ID = 
70          MuleProperties.PROPERTY_PREFIX + BPM_PROPERTY_PREFIX + "PROCESS_ID";
71      public static final String PROPERTY_ACTION = 
72          MuleProperties.PROPERTY_PREFIX + BPM_PROPERTY_PREFIX + "ACTION";
73      public static final String PROPERTY_TRANSITION = 
74          MuleProperties.PROPERTY_PREFIX + BPM_PROPERTY_PREFIX + "TRANSITION";
75      public static final String PROPERTY_PROCESS_STARTED = 
76          MuleProperties.PROPERTY_PREFIX + BPM_PROPERTY_PREFIX + "STARTED";
77      
78      public static final String ACTION_START = "start";
79      public static final String ACTION_ADVANCE = "advance";
80      public static final String ACTION_UPDATE = "update";
81      public static final String ACTION_ABORT = "abort";
82      
83      public static final String PROCESS_VARIABLE_INCOMING = "incoming";
84      public static final String PROCESS_VARIABLE_INCOMING_SOURCE = "incomingSource";
85      public static final String PROCESS_VARIABLE_DATA = "data";
86  
87      protected transient Log logger = LogFactory.getLog(getClass());
88  
89      public Process(BPMS bpms, String name, String resource, FlowConstruct flowConstruct, MuleContext muleContext)
90      {
91          this(bpms, name, resource, null, flowConstruct, muleContext);
92      }
93  
94      public Process(BPMS bpms, String name, String resource, String processIdField, FlowConstruct flowConstruct, MuleContext muleContext)
95      {
96          this.bpms = bpms;
97          this.name = name;
98          this.resource = resource;
99          this.processIdField = processIdField;
100         this.flowConstruct = flowConstruct;
101         this.muleContext = muleContext;
102     }
103 
104     public void initialise() throws InitialisationException
105     {
106         try
107         {
108             bpms.deployProcess(resource);
109         }
110         catch (Exception e)
111         {
112             throw new InitialisationException(e, this);
113         }
114     }
115 
116     public void dispose()
117     {
118         try
119         {
120             bpms.undeployProcess(resource);
121         }
122         catch (Exception e)
123         {
124             logger.warn(e.getMessage());
125         }
126     }
127 
128     protected Object processAction(MuleEvent event) throws Exception
129     {
130         // An object representing the new state of the process
131         Object process;
132 
133         // Create a map of process variables based on the message properties.
134         Map processVariables = new HashMap();
135         if (event != null)
136         {
137             populateProcessVariables(event, processVariables, PropertyScope.INVOCATION);
138             populateProcessVariables(event, processVariables, PropertyScope.INBOUND);
139 
140             Object payload = event.getMessage().getPayload();
141             if (payload != null && !(payload instanceof NullPayload))
142             {
143                 // Store the message's payload as a process variable.
144                 processVariables.put(PROCESS_VARIABLE_INCOMING, payload);
145 
146                 // Store the endpoint on which the message was received as a process variable.
147                 String originatingEndpoint = event.getMessage().getInboundProperty(MuleProperties.MULE_ORIGINATING_ENDPOINT_PROPERTY);
148                 if (StringUtils.isNotEmpty(originatingEndpoint))
149                 {
150                     processVariables.put(PROCESS_VARIABLE_INCOMING_SOURCE, originatingEndpoint);
151                 }
152             }
153         }
154 
155         String processIdField = getProcessIdField();
156         if (StringUtils.isEmpty(processIdField))
157         {
158             processIdField = PROPERTY_PROCESS_ID;
159         }
160 
161         Object processId;
162         processId = event.getMessage().getSessionProperty(processIdField);
163         if (processId == null)
164         {
165             processId = event.getMessage().getInvocationProperty(processIdField); 
166         }
167         if (processId == null)
168         {
169             processId = event.getMessage().getInboundProperty(processIdField); 
170         }
171         processVariables.remove(processIdField);
172 
173         // Default action is "advance"
174         String action = event.getMessage().getInvocationProperty(PROPERTY_ACTION, ACTION_ADVANCE);
175         processVariables.remove(PROPERTY_ACTION);
176 
177         Object transition = event.getMessage().getInvocationProperty(PROPERTY_TRANSITION);
178         processVariables.remove(PROPERTY_TRANSITION);
179 
180         // //////////////////////////////////////////////////////////////////////
181 
182         logger.debug("Message received: payload = " + event.getMessage().getPayload().getClass().getName() + " processType = " + name + " processId = " + processId + " action = " + action);
183         
184         // Start a new process.
185         if (processId == null || action.equals(ACTION_START))
186         {
187             process = getBpms().startProcess(name, transition, processVariables);
188             if ((process != null) && logger.isInfoEnabled())
189             {
190                 logger.info("New process started, ID = " + getBpms().getId(process));
191             }
192         }
193 
194         // Don't advance the process, just update the process variables.
195         else if (action.equals(ACTION_UPDATE))
196         {
197             if (processId != null)
198             {
199                 process = getBpms().updateProcess(processId, processVariables);
200                 if ((process != null) && logger.isInfoEnabled())
201                 {
202                     logger.info("Process variables updated, ID = " + getBpms().getId(process));
203                 }
204             }
205             else
206             {
207                 throw new IllegalArgumentException("Process ID is missing, cannot update process.");
208             }
209         }
210 
211         // Abort the running process (end abnormally).
212         else if (action.equals(ACTION_ABORT))
213         {
214             if (processId != null)
215             {
216                 getBpms().abortProcess(processId);
217                 process = NullPayload.getInstance();
218                 logger.info("Process aborted, ID = " + processId);
219             }
220             else
221             {
222                 throw new IllegalArgumentException("Process ID is missing, cannot abort process.");
223             }
224         }
225 
226         // Advance the already-running process one step.
227         else
228         {
229             if (processId != null)
230             {
231                 process = getBpms().advanceProcess(processId, transition, processVariables);
232                 if ((process != null) && logger.isInfoEnabled())
233                 {
234                     logger.info("Process advanced, ID = " + getBpms().getId(process)
235                                     + ", new state = " + getBpms().getState(process));
236                 }
237             }
238             else
239             {
240                 throw new IllegalArgumentException("Process ID is missing, cannot advance process.");
241             }
242         }
243 
244         return process;
245     }
246 
247     protected void populateProcessVariables(MuleEvent event, Map processVariables, PropertyScope propertyScope)
248     {
249         for (String propertyName : event.getMessage().getPropertyNames(propertyScope))
250         {
251             // The session property can become rather large and causes problems with DB persistence.
252             if (!propertyName.equals(MuleProperties.MULE_SESSION_PROPERTY))
253             {
254                 processVariables.put(propertyName, event.getMessage().getProperty(propertyName, propertyScope));
255             }
256         }
257     }
258 
259     public MuleMessage generateMessage(String endpoint, Object payload, Map messageProperties, MessageExchangePattern exchangePattern) throws MuleException
260     {
261         MuleMessage message;
262         if (payload instanceof MuleMessage)
263         {
264             message = (MuleMessage) payload;
265         }
266         else
267         {
268             message = new DefaultMuleMessage(payload, muleContext);
269         }
270         message.addProperties(messageProperties, PropertyScope.INBOUND);
271         message.addProperties(messageProperties, PropertyScope.INVOCATION);
272 
273         //TODO should probably cache this
274         EndpointBuilder endpointBuilder = muleContext.getEndpointFactory().getEndpointBuilder(endpoint);
275         endpointBuilder.setExchangePattern(exchangePattern);
276         OutboundEndpoint ep = endpointBuilder.buildOutboundEndpoint();
277        
278         DefaultMuleEvent event = new DefaultMuleEvent(message, ep, new DefaultMuleSession(flowConstruct, muleContext));
279 
280         // Set correlation properties in SESSION scope so that they get propagated to response messages.
281         RequestContext.setEvent(event);
282         if (messageProperties.get(PROPERTY_PROCESS_TYPE) != null)
283         {
284             event.getMessage().setSessionProperty(PROPERTY_PROCESS_TYPE, messageProperties.get(PROPERTY_PROCESS_TYPE));
285         }
286         if (messageProperties.get(PROPERTY_PROCESS_ID) != null)
287         {
288             event.getMessage().setSessionProperty(PROPERTY_PROCESS_ID, messageProperties.get(PROPERTY_PROCESS_ID));
289         }
290         
291         MuleEvent resultEvent = ep.process(event);
292         
293         MuleMessage response = null;
294         if (resultEvent != null)
295         {
296             response = resultEvent.getMessage();
297             if (response.getExceptionPayload() != null)
298             {
299                 throw new DispatchException(MessageFactory.createStaticMessage("Unable to send or route message"), event, ep, response.getExceptionPayload().getRootException());
300             }
301         }        
302         return response;
303     }
304 
305     public String getProcessIdField()
306     {
307         return processIdField;
308     }
309 
310     public BPMS getBpms()
311     {
312         return bpms;
313     }
314 
315     public String getResource()
316     {
317         return resource;
318     }
319 
320     public String getName()
321     {
322         return name;
323     }
324 }
325 
326