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.MuleException;
10  import org.mule.api.MuleMessage;
11  import org.mule.api.config.ThreadingProfile;
12  import org.mule.api.construct.FlowConstruct;
13  import org.mule.api.endpoint.InboundEndpoint;
14  import org.mule.api.lifecycle.CreateException;
15  import org.mule.api.transaction.TransactionCallback;
16  import org.mule.api.transport.Connector;
17  import org.mule.transaction.TransactionTemplate;
18  
19  import java.util.List;
20  
21  import javax.resource.spi.work.Work;
22  
23  import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
24  
25  /**
26   * The TransactedPollingMessageReceiver is an abstract receiver that handles polling
27   * and transaction management. Derived implementations of these class must be thread
28   * safe as several threads can be started at once for an improved throughput.
29   */
30  public abstract class TransactedPollingMessageReceiver extends AbstractPollingMessageReceiver
31  {
32      /** determines whether messages will be received in a transaction template */
33      private boolean receiveMessagesInTransaction = true;
34  
35      /** determines whether Multiple receivers are created to improve throughput */
36      private boolean useMultipleReceivers = true;
37  
38      public TransactedPollingMessageReceiver(Connector connector,
39                                              FlowConstruct flowConstruct,
40                                              final InboundEndpoint endpoint) throws CreateException
41      {
42          super(connector, flowConstruct, endpoint);
43          this.setReceiveMessagesInTransaction(endpoint.getTransactionConfig().isTransacted());
44      }
45  
46      /**
47       * @deprecated please use
48       *             {@link #TransactedPollingMessageReceiver(Connector, FlowConstruct, InboundEndpoint)}
49       *             instead
50       */
51      @Deprecated
52      public TransactedPollingMessageReceiver(Connector connector,
53                                              FlowConstruct flowConstruct,
54                                              final InboundEndpoint endpoint,
55                                              long frequency) throws CreateException
56      {
57          this(connector, flowConstruct, endpoint);
58          this.setFrequency(frequency);
59      }
60  
61      public boolean isReceiveMessagesInTransaction()
62      {
63          return receiveMessagesInTransaction;
64      }
65  
66      public void setReceiveMessagesInTransaction(boolean useTx)
67      {
68          receiveMessagesInTransaction = useTx;
69      }
70  
71      public boolean isUseMultipleTransactedReceivers()
72      {
73          return useMultipleReceivers;
74      }
75  
76      public void setUseMultipleTransactedReceivers(boolean useMultiple)
77      {
78          useMultipleReceivers = useMultiple;
79      }
80  
81      @Override
82      public void doStart() throws MuleException
83      {
84          // Connector property overrides any implied value
85          this.setUseMultipleTransactedReceivers(connector.isCreateMultipleTransactedReceivers());
86  
87          ThreadingProfile tp = connector.getReceiverThreadingProfile();
88          int numReceiversToStart = 1;
89  
90          if (this.isReceiveMessagesInTransaction() && this.isUseMultipleTransactedReceivers()
91                  && tp.isDoThreading())
92          {
93              numReceiversToStart = connector.getNumberOfConcurrentTransactedReceivers();
94          }
95  
96          for (int i = 0; i < numReceiversToStart; i++)
97          {
98              super.doStart();
99          }
100     }
101 
102     @Override
103     public void poll() throws Exception
104     {
105         TransactionTemplate<Object> tt = new TransactionTemplate<Object>(endpoint.getTransactionConfig(), connector.getMuleContext());
106 
107         if (this.isReceiveMessagesInTransaction())
108         {
109             // Receive messages and process them in a single transaction
110             // Do not enable threading here, but several workers
111             // may have been started
112             TransactionCallback<Object> cb = new TransactionCallback<Object>()
113             {
114                 public Object doInTransaction() throws Exception
115                 {
116                     // this is not ideal, but jdbc receiver returns a list of maps, not List<MuleMessage>
117                     List messages = getMessages();
118                     if (messages != null && messages.size() > 0)
119                     {
120                         for (Object message : messages)
121                         {
122                             processMessage(message);
123                         }
124                     }
125                     return null;
126                 }
127             };
128             tt.execute(cb);
129         }
130         else
131         {
132             // Receive messages and launch a worker for each message
133             List messages = getMessages();
134             if (messages != null && messages.size() > 0)
135             {
136                 final CountDownLatch countdown = new CountDownLatch(messages.size());
137                 for (Object message : messages)
138                 {
139                     try
140                     {
141                         this.getWorkManager().scheduleWork(
142                                 new MessageProcessorWorker(tt, countdown, message));
143                     }
144                     catch (Exception e)
145                     {
146                         countdown.countDown();
147                         throw e;
148                     }
149                 }
150                 countdown.await();
151             }
152         }
153     }
154 
155     protected class MessageProcessorWorker implements Work, TransactionCallback
156     {
157         private final TransactionTemplate tt;
158         private final Object message;
159         private final CountDownLatch latch;
160 
161         public MessageProcessorWorker(TransactionTemplate tt, CountDownLatch latch, Object message)
162         {
163             this.tt = tt;
164             this.message = message;
165             this.latch = latch;
166         }
167 
168         public void release()
169         {
170             // nothing to do
171         }
172 
173         public void run()
174         {
175             try
176             {
177                 tt.execute(this);
178             }
179             catch (Exception e)
180             {
181                 connector.getMuleContext().getExceptionListener().handleException(e);
182             }
183             finally
184             {
185                 latch.countDown();
186             }
187         }
188 
189         public Object doInTransaction() throws Exception
190         {
191             processMessage(message);
192             return null;
193         }
194 
195     }
196 
197     protected abstract List<MuleMessage> getMessages() throws Exception;
198 
199     protected abstract void processMessage(Object message) throws Exception;
200 
201 }