View Javadoc

1   /*
2    * $Id: VMMessageReceiver.java 10063 2007-12-11 11:57:03Z holger $
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.providers.vm;
12  
13  import org.mule.impl.MuleMessage;
14  import org.mule.providers.PollingReceiverWorker;
15  import org.mule.providers.TransactedPollingMessageReceiver;
16  import org.mule.umo.UMOComponent;
17  import org.mule.umo.UMOEvent;
18  import org.mule.umo.UMOException;
19  import org.mule.umo.UMOMessage;
20  import org.mule.umo.endpoint.UMOEndpoint;
21  import org.mule.umo.lifecycle.InitialisationException;
22  import org.mule.umo.provider.UMOConnector;
23  import org.mule.util.queue.Queue;
24  import org.mule.util.queue.QueueSession;
25  
26  import java.util.LinkedList;
27  import java.util.List;
28  
29  import edu.emory.mathcs.backport.java.util.concurrent.RejectedExecutionException;
30  
31  /**
32   * <code>VMMessageReceiver</code> is a listener for events from a Mule component which
33   * then simply passes the events on to the target component.
34   */
35  public class VMMessageReceiver extends TransactedPollingMessageReceiver
36  {
37  
38      private VMConnector connector;
39      private final Object lock = new Object();
40  
41      public VMMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
42          throws InitialisationException
43      {
44          super(connector, component, endpoint);
45          this.setReceiveMessagesInTransaction(endpoint.getTransactionConfig().isTransacted());
46          this.connector = (VMConnector) connector;
47      }
48  
49      /*
50       * We only need to start scheduling this receiver if event queueing is enabled on the
51       * connector; otherwise events are delivered via onEvent/onCall.
52       */
53      // @Override
54      protected void schedule()
55          throws RejectedExecutionException, NullPointerException, IllegalArgumentException
56      {
57          if (connector.isQueueEvents())
58          {
59              super.schedule();
60          }
61      }
62  
63      protected void doDispose()
64      {
65          // template method
66      }
67  
68      protected void doConnect() throws Exception
69      {
70          if (connector.isQueueEvents())
71          {
72              // Ensure we can create a vm queue
73              QueueSession queueSession = connector.getQueueSession();
74              Queue q = queueSession.getQueue(endpoint.getEndpointURI().getAddress());
75              if (logger.isDebugEnabled())
76              {
77                  logger.debug("Current queue depth for queue: " + endpoint.getEndpointURI().getAddress()
78                               + " is: " + q.size());
79              }
80          }
81      }
82  
83      protected void doDisconnect() throws Exception
84      {
85          // template method
86      }
87  
88      public void onEvent(UMOEvent event) throws UMOException
89      {
90          /*
91           * TODO HH: review: onEvent can only be called by the VMMessageDispatcher - why is
92           * this lock here and do we still need it? what can break if this receiver is run
93           * concurrently by multiple dispatchers?
94           */
95          UMOMessage msg = new MuleMessage(event.getTransformedMessage(), event.getMessage());
96          synchronized (lock)
97          {
98              routeMessage(msg);
99          }
100     }
101 
102     public Object onCall(UMOEvent event) throws UMOException
103     {
104         UMOMessage msg = new MuleMessage(event.getTransformedMessage(), event.getMessage());
105         return routeMessage(msg, event.isSynchronous());
106     }
107 
108     protected List getMessages() throws Exception
109     {
110         // The queue from which to pull events
111         QueueSession qs = connector.getQueueSession();
112         Queue queue = qs.getQueue(endpoint.getEndpointURI().getAddress());
113 
114         // The list of retrieved messages that will be returned
115         List messages = new LinkedList();
116 
117         /*
118          * Determine how many messages to batch in this poll: we need to drain the queue
119          * quickly, but not by slamming the workManager too hard. It is impossible to
120          * determine this more precisely without proper load statistics/feedback or some
121          * kind of "event cost estimate". Therefore we just try to use half of the
122          * receiver's workManager, since it is shared with receivers for other endpoints.
123          */
124         int maxThreads = connector.getReceiverThreadingProfile().getMaxThreadsActive();
125         // also make sure batchSize is always at least 1
126         int batchSize = Math.max(1, Math.min(queue.size(), ((maxThreads / 2) - 1)));
127 
128         // try to get the first event off the queue
129         UMOEvent event = (UMOEvent) queue.poll(connector.getQueueTimeout());
130 
131         if (event != null)
132         {
133             // keep first dequeued event
134             messages.add(event);
135 
136             // keep batching if more events are available
137             for (int i = 0; i < batchSize && event != null; i++)
138             {
139                 event = (UMOEvent) queue.poll(0);
140                 if (event != null)
141                 {
142                     messages.add(event);
143                 }
144             }
145         }
146 
147         // let our workManager handle the batch of events
148         return messages;
149     }
150 
151     protected void processMessage(Object msg) throws Exception
152     {
153         // getMessages() returns UMOEvents
154         UMOEvent event = (UMOEvent) msg;
155         UMOMessage message = new MuleMessage(event.getTransformedMessage(), event.getMessage());
156         routeMessage(message);
157     }
158 
159     /*
160      * We create our own "polling" worker here since we need to evade the standard scheduler.
161      */
162     // @Override
163     protected PollingReceiverWorker createWork()
164     {
165         return new VMReceiverWorker(this);
166     }
167 
168     /*
169      * Even though the VM transport is "polling" for messages, the nonexistent cost of
170      * accessing the queue is a good reason to not use the regular scheduling mechanism in
171      * order to both minimize latency and maximize throughput.
172      */
173     protected static class VMReceiverWorker extends PollingReceiverWorker
174     {
175 
176         public VMReceiverWorker(VMMessageReceiver pollingMessageReceiver)
177         {
178             super(pollingMessageReceiver);
179         }
180 
181         public void run()
182         {
183             /*
184              * We simply run our own polling loop all the time as long as the receiver is
185              * started. The blocking wait defined by VMConnector.getQueueTimeout() will
186              * prevent this worker's receiver thread from busy-waiting.
187              */
188             while (this.getReceiver().isConnected())
189             {
190                 super.run();
191             }
192         }
193     }
194 
195 }