View Javadoc

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