View Javadoc

1   /*
2    * $Id: ProcessMessageDispatcher.java 10961 2008-02-22 19:01:02Z 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;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleMessage;
16  import org.mule.api.config.MuleProperties;
17  import org.mule.api.endpoint.OutboundEndpoint;
18  import org.mule.api.transport.DispatchException;
19  import org.mule.config.i18n.MessageFactory;
20  import org.mule.transport.AbstractMessageDispatcher;
21  import org.mule.transport.NullPayload;
22  import org.mule.util.StringUtils;
23  
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.Map;
27  
28  /**
29   * Initiates or advances a workflow process from an outgoing Mule event.
30   */
31  public class ProcessMessageDispatcher extends AbstractMessageDispatcher
32  {
33      private ProcessConnector connector;
34  
35      public ProcessMessageDispatcher(OutboundEndpoint endpoint)
36      {
37          super(endpoint);
38          this.connector = (ProcessConnector)endpoint.getConnector();
39      }
40  
41      /**
42       * Performs a synchronous action on the BPMS.
43       * 
44       * @return an object representing the new state of the process
45       */
46      public MuleMessage doSend(MuleEvent event) throws Exception
47      {
48          Object process = processAction(event);
49  
50          if (process != null)
51          {
52              MuleMessage msg = new DefaultMuleMessage(process);
53              msg.setProperty(ProcessConnector.PROPERTY_PROCESS_ID, connector.getBpms().getId(process));
54              return msg;
55          }
56          else
57          {
58              throw new DispatchException(MessageFactory
59                  .createStaticMessage("Synchronous process invocation must return the new process state."),
60                  event.getMessage(), event.getEndpoint());
61          }
62      }
63  
64      /**
65       * Performs an asynchronous action on the BPMS.
66       */
67      public void doDispatch(MuleEvent event) throws Exception
68      {
69          processAction(event);
70      }
71  
72      protected Object processAction(MuleEvent event) throws Exception
73      {
74          // An object representing the new state of the process
75          Object process = null;
76  
77          // Create a map of process variables based on the message properties.
78          Map processVariables = new HashMap();
79          if (event != null)
80          {
81              String propertyName;
82              for (Iterator iterator = event.getMessage().getPropertyNames().iterator(); iterator.hasNext();)
83              {
84                  propertyName = (String)iterator.next();
85                  processVariables.put(propertyName, event.getMessage().getProperty(propertyName));
86              }
87  
88              Object payload = event.transformMessage();
89              if (payload != null && !(payload instanceof NullPayload))
90              {
91                  // Store the message's payload as a process variable.
92                  processVariables.put(ProcessConnector.PROCESS_VARIABLE_INCOMING, payload);
93  
94                  // Store the endpoint on which the message was received as a process
95                  // variable.
96                  String originatingEndpoint = event.getMessage().getStringProperty(
97                      MuleProperties.MULE_ORIGINATING_ENDPOINT_PROPERTY, null);
98                  if (StringUtils.isNotEmpty(originatingEndpoint))
99                  {
100                     processVariables.put(ProcessConnector.PROCESS_VARIABLE_INCOMING_SOURCE,
101                         originatingEndpoint);
102                 }
103             }
104         }
105 
106         // Retrieve the parameters
107         Object processType = event.getProperty(ProcessConnector.PROPERTY_PROCESS_TYPE, /* exhaustiveSearch */
108         true);
109         processVariables.remove(ProcessConnector.PROPERTY_PROCESS_TYPE);
110 
111         // TODO MULE-1220 The processId for BPM is sort of like a session and so we could probably use
112         // Mule's SessionHandler interface for managing this.  
113         Object processId;
114         String processIdField = connector.getProcessIdField();
115         if (StringUtils.isNotEmpty(processIdField))
116         {
117             processId = event.getProperty(processIdField, /* exhaustiveSearch */false);
118         }
119         // If processId is explicitly set for the message, this overrides the
120         // processIdField.
121         processId = event.getProperty(ProcessConnector.PROPERTY_PROCESS_ID, /* exhaustiveSearch */true);
122         processVariables.remove(ProcessConnector.PROPERTY_PROCESS_ID);
123 
124         // Default action is "advance"
125         String action = event.getMessage().getStringProperty(ProcessConnector.PROPERTY_ACTION,
126             ProcessConnector.ACTION_ADVANCE);
127         processVariables.remove(ProcessConnector.PROPERTY_ACTION);
128 
129         Object transition = event.getMessage().getProperty(ProcessConnector.PROPERTY_TRANSITION);
130         processVariables.remove(ProcessConnector.PROPERTY_TRANSITION);
131 
132         // Decode the URI, for example:
133         // bpm://testProcess/4561?action=advance
134         String temp;
135         temp = event.getEndpoint().getEndpointURI().getHost();
136         if (StringUtils.isNotEmpty(temp))
137         {
138             processType = temp;
139         }
140         temp = event.getEndpoint().getEndpointURI().getPath();
141         if (StringUtils.isNotEmpty(temp))
142         {
143             // Strip the leading "/" from the path.
144             if (temp.startsWith("/"))
145             {
146                 temp = StringUtils.right(temp, temp.length() - 1);
147             }
148             // If there are any remaining "/", we don't know what to do with them.
149             if (temp.indexOf("/") != -1)
150             {
151                 throw new IllegalArgumentException("Unexpected format in the path of the URL: " + temp);
152             }
153             processId = temp;
154         }
155 
156         // //////////////////////////////////////////////////////////////////////
157 
158         // Start a new process.
159         if (processId == null || action.equals(ProcessConnector.ACTION_START))
160         {
161             if (processType != null)
162             {
163                 process = connector.getBpms().startProcess(processType, transition, processVariables);
164                 if ((process != null) && logger.isInfoEnabled())
165                 {
166                     logger.info("New process started, ID = " + connector.getBpms().getId(process));
167                 }
168             }
169             else
170             {
171                 throw new IllegalArgumentException("Process type is missing, cannot start a new process.");
172             }
173         }
174 
175         // Don't advance the process, just update the process variables.
176         else if (action.equals(ProcessConnector.ACTION_UPDATE))
177         {
178             if (processId != null)
179             {
180                 process = connector.getBpms().updateProcess(processId, processVariables);
181                 if ((process != null) && logger.isInfoEnabled())
182                 {
183                     logger.info("Process variables updated, ID = " + connector.getBpms().getId(process));
184                 }
185             }
186             else
187             {
188                 throw new IllegalArgumentException("Process ID is missing, cannot update process.");
189             }
190         }
191 
192         // Abort the running process (end abnormally).
193         else if (action.equals(ProcessConnector.ACTION_ABORT))
194         {
195             if (processId != null)
196             {
197                 connector.getBpms().abortProcess(processId);
198                 process = NullPayload.getInstance();
199                 logger.info("Process aborted, ID = " + processId);
200             }
201             else
202             {
203                 throw new IllegalArgumentException("Process ID is missing, cannot abort process.");
204             }
205         }
206 
207         // Advance the already-running process one step.
208         else
209         {
210             if (processId != null)
211             {
212                 process = connector.getBpms().advanceProcess(processId, transition, processVariables);
213                 if ((process != null) && logger.isInfoEnabled())
214                 {
215                     logger.info("Process advanced, ID = " + connector.getBpms().getId(process)
216                                     + ", new state = " + connector.getBpms().getState(process));
217                 }
218             }
219             else
220             {
221                 throw new IllegalArgumentException("Process ID is missing, cannot advance process.");
222             }
223         }
224 
225         return process;
226     }
227 
228     protected void doConnect() throws Exception
229     {
230         /* nop */
231     }
232 
233     protected void doDisconnect() throws Exception
234     {
235         /* nop */
236     }
237 
238     protected void doDispose()
239     {
240         /* nop */
241     }
242 
243 }