View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport;
8   
9   import org.mule.api.MessagingException;
10  import org.mule.api.MuleContext;
11  import org.mule.api.MuleEvent;
12  import org.mule.api.MuleMessage;
13  import org.mule.api.MuleSession;
14  import org.mule.api.endpoint.InboundEndpoint;
15  import org.mule.api.transaction.Transaction;
16  import org.mule.api.transaction.TransactionCallback;
17  import org.mule.api.transaction.TransactionException;
18  import org.mule.api.transport.SessionHandler;
19  import org.mule.session.LegacySessionHandler;
20  import org.mule.session.MuleSessionHandler;
21  import org.mule.transaction.TransactionCoordination;
22  import org.mule.transaction.TransactionTemplate;
23  
24  import java.io.OutputStream;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import javax.resource.spi.work.Work;
29  
30  import org.apache.commons.lang.SerializationException;
31  
32  /**
33   * A base Worker used by Transport {@link org.mule.api.transport.MessageReceiver} implementations.
34   */
35  public abstract class AbstractReceiverWorker implements Work
36  {
37      protected List<Object> messages;
38      protected InboundEndpoint endpoint;
39      protected AbstractMessageReceiver receiver;
40      protected OutputStream out;
41  
42      public AbstractReceiverWorker(List<Object> messages, AbstractMessageReceiver receiver)
43      {
44          this(messages, receiver, null);
45      }
46  
47      public AbstractReceiverWorker(List<Object> messages, AbstractMessageReceiver receiver, OutputStream out)
48      {
49          this.messages = messages;
50          this.receiver = receiver;
51          this.endpoint = receiver.getEndpoint();
52          this.out = out;
53      }
54  
55      /**
56       * This will run the receiver logic and call {@link #release()} once {@link #doRun()} completes.
57      *
58      */
59      public final void run()
60      {
61          doRun();
62          release();
63      }
64  
65      /**
66       * The actual logic used to receive messages from the underlying transport.  The default implementation
67       * will execute the processing of messages within a TransactionTemplate.  This template will manage the
68       * transaction lifecycle for the list of messages associated with this receiver worker.
69       */
70      protected void doRun()
71      {
72          try {
73              processMessages();
74          }
75          catch (MessagingException e)
76          {
77              receiver.getFlowConstruct().getExceptionListener().handleException(e, e.getEvent());
78          }
79          catch (Exception e)
80          {
81              receiver.getConnector().getMuleContext().getExceptionListener().handleException(e);
82          }
83  
84      }
85  
86      private void processMessages() throws Exception
87      {
88          //  MuleContext is used down the line for
89          // getTransactionManager() (XaTransactionFactory) and getQueueManager() (VMTransaction)
90          final MuleContext muleContext = receiver.getConnector().getMuleContext();
91          TransactionTemplate tt = new TransactionTemplate(endpoint.getTransactionConfig(), muleContext);
92  
93          // Receive messages and process them in a single transaction
94          // Do not enable threading here, but serveral workers
95          // may have been started
96          TransactionCallback<?> cb = new TransactionCallback()
97          {
98              public Object doInTransaction() throws Exception
99              {
100                 Transaction tx = TransactionCoordination.getInstance().getTransaction();
101                 if (tx != null)
102                 {
103                     bindTransaction(tx);
104                 }
105                 List<Object> results = new ArrayList<Object>(messages.size());
106 
107                 for (Object payload : messages)
108                 {
109                     payload = preProcessMessage(payload);
110                     if (payload != null)
111                     {
112                         MuleMessage muleMessage = receiver.createMuleMessage(payload, endpoint.getEncoding());
113                         preRouteMuleMessage(muleMessage);
114 
115                         // TODO Move getSessionHandler() to the Connector interface
116                         SessionHandler handler;
117                         if (endpoint.getConnector() instanceof AbstractConnector)
118                         {
119                             handler = ((AbstractConnector) endpoint.getConnector()).getSessionHandler();
120                         }
121                         else
122                         {
123                             handler = new MuleSessionHandler();
124                         }
125                         MuleSession session;
126                         try
127                         {
128                             session = handler.retrieveSessionInfoFromMessage(muleMessage);
129                         }
130                         catch (SerializationException e)
131                         {
132                             // EE-1820 Support message headers generated by previous Mule versions
133                             session = new LegacySessionHandler().retrieveSessionInfoFromMessage(muleMessage);
134                         }
135 
136                         MuleEvent resultEvent;
137                         if (session != null)
138                         {
139                             resultEvent = receiver.routeMessage(muleMessage, session, tx, out);
140                         }
141                         else
142                         {
143                             resultEvent = receiver.routeMessage(muleMessage, tx, out);
144                         }
145                         MuleMessage result = resultEvent == null ?  null : resultEvent.getMessage();
146                         if (result != null)
147                         {
148                             payload = postProcessMessage(result);
149                             if (payload != null)
150                             {
151                                 results.add(payload);
152                             }
153                         }
154                     }
155                 }
156                 return results;
157             }
158         };
159 
160         try
161         {
162             List results = (List) tt.execute(cb);
163             handleResults(results);
164         }
165         finally
166         {
167             messages.clear();
168         }
169     }
170 
171     /**
172      * This callback is called before a message is routed into Mule and can be used by the worker to set connection
173      * specific properties to message before it gets routed
174      *
175      * @param message the next message to be processed
176      * @throws Exception
177      */
178     protected void preRouteMuleMessage(MuleMessage message) throws Exception
179     {
180         //no op
181     }
182 
183     /**
184      * Template method used to bind the resources of this receiver to the transaction.  Only transactional
185      * transports need implment this method
186      * @param tx the current transaction or null if there is no transaction
187      * @throws TransactionException
188      */
189     protected abstract void bindTransaction(Transaction tx) throws TransactionException;
190 
191     /**
192      * When Mule has finished processing the current messages, there may be zero or more messages to process
193      * by the receiver if request/response messaging is being used. The result(s) should be passed back to the callee.
194      * @param messages a list of messages.  This argument will not be null
195      * @throws Exception
196      */
197     protected void handleResults(List messages) throws Exception
198     {
199         //no op
200     }
201 
202     /**
203      * Before a message is passed into Mule this callback is called and can be used by the worker to inspect the
204      * message before it gets sent to Mule
205      * @param message the next message to be processed
206      * @return the message to be processed. If Null is returned the message will not get processed.
207      * @throws Exception
208      */
209     protected Object preProcessMessage(Object message) throws Exception
210     {
211         //no op
212         return message;
213     }
214 
215     /**
216      * If a result is returned back this method will get called before the message is added to te list of
217      * results (these are later passed to {@link #handleResults(java.util.List)})
218      * @param message the result message, this will never be null
219      * @return the message to add to the list of results. If null is returned nothing is added to the
220      * list of results
221      * @throws Exception
222      */
223     protected MuleMessage postProcessMessage(MuleMessage message) throws Exception
224     {
225         //no op
226         return message;
227     }
228 
229 
230     /**
231      * This method is called once this worker is no longer required.  Any resources *only* associated with
232      * this worker should be cleaned up here.
233      */
234     public void release()
235     {
236         // no op
237     }
238 }