View Javadoc

1   /*
2    * $Id: VMMessageReceiver.java 20351 2010-11-25 18:01:47Z svacas $
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.transport.vm;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.api.DefaultMuleException;
15  import org.mule.api.MuleEvent;
16  import org.mule.api.MuleException;
17  import org.mule.api.MuleMessage;
18  import org.mule.api.construct.FlowConstruct;
19  import org.mule.api.endpoint.InboundEndpoint;
20  import org.mule.api.lifecycle.CreateException;
21  import org.mule.api.transport.Connector;
22  import org.mule.transport.PollingReceiverWorker;
23  import org.mule.transport.TransactedPollingMessageReceiver;
24  import org.mule.util.queue.Queue;
25  import org.mule.util.queue.QueueSession;
26  
27  import java.util.ArrayList;
28  import java.util.LinkedList;
29  import java.util.List;
30  
31  import edu.emory.mathcs.backport.java.util.concurrent.RejectedExecutionException;
32  
33  /**
34   * <code>VMMessageReceiver</code> is a listener for events from a Mule service which then simply passes
35   * the events on to the target service.
36   */
37  public class VMMessageReceiver extends TransactedPollingMessageReceiver
38  {
39  
40      private VMConnector connector;
41  
42      public VMMessageReceiver(Connector connector, FlowConstruct flowConstruct, InboundEndpoint endpoint)
43          throws CreateException
44      {
45          super(connector, flowConstruct, endpoint);
46          this.setReceiveMessagesInTransaction(endpoint.getTransactionConfig().isTransacted());
47          this.connector = (VMConnector) connector;
48      }
49  
50      /*
51       * We only need to start scheduling this receiver if event queueing is enabled on the connector; otherwise
52       * events are delivered via onEvent/onCall.
53       */
54      @Override
55      protected void schedule() throws RejectedExecutionException, NullPointerException, IllegalArgumentException
56      {
57          if (!endpoint.getExchangePattern().hasResponse())
58          {
59              super.schedule();
60          }
61      }
62  
63      @Override
64      protected void doDispose()
65      {
66          // template method
67      }
68  
69      @Override
70      protected void doConnect() throws Exception
71      {
72          if (!endpoint.getExchangePattern().hasResponse())
73          {
74              // Ensure we can create a vm queue
75              QueueSession queueSession = connector.getQueueSession();
76              Queue q = queueSession.getQueue(endpoint.getEndpointURI().getAddress());
77              if (logger.isDebugEnabled())
78              {
79                  logger.debug("Current queue depth for queue: " + endpoint.getEndpointURI().getAddress() + " is: "
80                               + q.size());
81              }
82          }
83      }
84  
85      @Override
86      protected void doDisconnect() throws Exception
87      {
88          // template method
89      }
90  
91      public void onMessage(MuleMessage message) throws MuleException
92      {
93          // Rewrite the message to treat it as a new message
94          MuleMessage newMessage = new DefaultMuleMessage(message.getPayload(), message, connector.getMuleContext());
95          routeMessage(newMessage);
96      }
97  
98      public MuleMessage onCall(MuleMessage message) throws MuleException
99      {
100         try
101         {
102             // Rewrite the message to treat it as a new message
103             MuleMessage newMessage = message.createInboundMessage();
104             MuleEvent event =  routeMessage(newMessage);
105             MuleMessage returnedMessage = event == null ? null : event.getMessage();
106             if (returnedMessage != null)
107             {
108                 returnedMessage = returnedMessage.createInboundMessage();
109             }
110             return returnedMessage;
111         }
112         catch (Exception e)
113         {
114             throw new DefaultMuleException(e);
115         }
116     }
117 
118     /**
119      * It's impossible to process all messages in the receive transaction
120      */
121     @Override
122     protected List<MuleMessage> getMessages() throws Exception
123     {
124         if (isReceiveMessagesInTransaction())
125         {
126             MuleEvent message = getFirstMessage();
127             if (message == null)
128             {
129                 return null;
130             }
131             
132             List<MuleMessage> messages = new ArrayList<MuleMessage>(1);
133             messages.add(message.getMessage());
134             return messages;
135         }
136         else
137         {
138             return getFirstMessages();
139         }
140     }
141     
142     protected List<MuleMessage> getFirstMessages() throws Exception
143     {
144         // The queue from which to pull events
145         QueueSession qs = connector.getQueueSession();
146         Queue queue = qs.getQueue(endpoint.getEndpointURI().getAddress());
147 
148         // The list of retrieved messages that will be returned
149         List<MuleMessage> messages = new LinkedList<MuleMessage>();
150 
151         /*
152          * Determine how many messages to batch in this poll: we need to drain the queue quickly, but not by
153          * slamming the workManager too hard. It is impossible to determine this more precisely without proper
154          * load statistics/feedback or some kind of "event cost estimate". Therefore we just try to use half
155          * of the receiver's workManager, since it is shared with receivers for other endpoints.
156          */
157         int maxThreads = connector.getReceiverThreadingProfile().getMaxThreadsActive();
158         // also make sure batchSize is always at least 1
159         int batchSize = Math.max(1, Math.min(queue.size(), ((maxThreads / 2) - 1)));
160 
161         // try to get the first event off the queue
162         MuleEvent message = (MuleEvent) queue.poll(connector.getQueueTimeout());
163 
164         if (message != null)
165         {
166             // keep first dequeued event
167             messages.add(message.getMessage());
168 
169             // keep batching if more events are available
170             for (int i = 0; i < batchSize && message != null; i++)
171             {
172                 message = (MuleEvent)queue.poll(0);
173                 if (message != null)
174                 {
175                     messages.add(message.getMessage());
176                 }
177             }
178         }
179 
180         // let our workManager handle the batch of events
181         return messages;
182     }
183     
184     protected MuleEvent getFirstMessage() throws Exception
185     {
186         // The queue from which to pull events
187         QueueSession qs = connector.getQueueSession();
188         Queue queue = qs.getQueue(endpoint.getEndpointURI().getAddress());
189         // try to get the first event off the queue
190         return (MuleEvent) queue.poll(connector.getQueueTimeout());
191     }
192 
193     @Override
194     protected void processMessage(Object msg) throws Exception
195     {
196         MuleMessage message = (MuleMessage) msg;
197 
198         // Rewrite the message to treat it as a new message
199         MuleMessage newMessage = message.createInboundMessage();
200         routeMessage(newMessage);
201     }
202 
203     /*
204      * We create our own "polling" worker here since we need to evade the standard scheduler.
205      */
206     @Override
207     protected PollingReceiverWorker createWork()
208     {
209         return new VMReceiverWorker(this);
210     }
211     
212     /*
213      * Even though the VM transport is "polling" for messages, the nonexistent cost of accessing the queue is
214      * a good reason to not use the regular scheduling mechanism in order to both minimize latency and
215      * maximize throughput.
216      */
217     protected static class VMReceiverWorker extends PollingReceiverWorker
218     {
219 
220         public VMReceiverWorker(VMMessageReceiver pollingMessageReceiver)
221         {
222             super(pollingMessageReceiver);
223         }
224 
225         @Override
226         protected void poll() throws Exception
227         {
228             /*
229              * We simply run our own polling loop all the time as long as the receiver is started. The
230              * blocking wait defined by VMConnector.getQueueTimeout() will prevent this worker's receiver
231              * thread from busy-waiting.
232              */
233             while (this.getReceiver().isConnected())
234             {
235                 super.poll();
236             }
237         }
238     }
239 
240 }