Coverage Report - org.mule.routing.requestreply.AbstractAsyncRequestReplyRequester
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractAsyncRequestReplyRequester
0%
0/72
0%
0/24
0
AbstractAsyncRequestReplyRequester$InternalAsyncReplyMessageProcessor
0%
0/16
0%
0/8
0
 
 1  
 /*
 2  
  * $Id: AbstractAsyncRequestReplyRequester.java 20637 2010-12-11 02:32:12Z dfeist $
 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.routing.requestreply;
 12  
 
 13  
 import org.mule.OptimizedRequestContext;
 14  
 import org.mule.api.MuleEvent;
 15  
 import org.mule.api.MuleException;
 16  
 import org.mule.api.MuleMessageCollection;
 17  
 import org.mule.api.construct.FlowConstruct;
 18  
 import org.mule.api.construct.FlowConstructAware;
 19  
 import org.mule.api.processor.MessageProcessor;
 20  
 import org.mule.api.processor.RequestReplyRequesterMessageProcessor;
 21  
 import org.mule.api.routing.ResponseTimeoutException;
 22  
 import org.mule.api.source.MessageSource;
 23  
 import org.mule.config.i18n.CoreMessages;
 24  
 import org.mule.context.notification.RoutingNotification;
 25  
 import org.mule.processor.AbstractInterceptingMessageProcessor;
 26  
 import org.mule.util.ObjectUtils;
 27  
 import org.mule.util.concurrent.Latch;
 28  
 
 29  
 import java.util.Map;
 30  
 
 31  
 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
 32  
 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
 33  
 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
 34  
 
 35  
 import org.apache.commons.collections.buffer.BoundedFifoBuffer;
 36  
 
 37  0
 public abstract class AbstractAsyncRequestReplyRequester extends AbstractInterceptingMessageProcessor
 38  
     implements RequestReplyRequesterMessageProcessor, FlowConstructAware
 39  
 {
 40  
     public static final int MAX_PROCESSED_GROUPS = 50000;
 41  
 
 42  0
     protected volatile long timeout = -1;
 43  0
     protected volatile boolean failOnTimeout = true;
 44  
     protected MessageSource replyMessageSource;
 45  
     protected FlowConstruct flowConstruct;
 46  0
     private final MessageProcessor internalAsyncReplyMessageProcessor = new InternalAsyncReplyMessageProcessor();
 47  
 
 48  0
     @SuppressWarnings("unchecked")
 49  
     protected final Map<String, Latch> locks = new ConcurrentHashMap();
 50  
     
 51  0
     protected final ConcurrentMap responseEvents = new ConcurrentHashMap();
 52  0
     protected final Object processedLock = new Object();
 53  
     // @GuardedBy processedLock
 54  0
     protected final BoundedFifoBuffer processed = new BoundedFifoBuffer(MAX_PROCESSED_GROUPS);
 55  
 
 56  
     public MuleEvent process(MuleEvent event) throws MuleException
 57  
     {
 58  0
         if (replyMessageSource == null)
 59  
         {
 60  0
             return processNext(event);
 61  
         }
 62  
         else
 63  
         {
 64  0
             locks.put(getAsyncReplyCorrelationId(event), new Latch());
 65  
 
 66  0
             sendAsyncRequest(event);
 67  
 
 68  0
             return receiveAsyncReply(event);
 69  
         }
 70  
     }
 71  
 
 72  
     public void setTimeout(long timeout)
 73  
     {
 74  0
         this.timeout = timeout;
 75  0
     }
 76  
 
 77  
     public void setFailOnTimeout(boolean failOnTimeout)
 78  
     {
 79  0
         this.failOnTimeout = failOnTimeout;
 80  0
     }
 81  
 
 82  
     public void setReplySource(MessageSource messageSource)
 83  
     {
 84  0
         verifyReplyMessageSource(messageSource);
 85  0
         replyMessageSource = messageSource;
 86  0
         messageSource.setListener(internalAsyncReplyMessageProcessor);
 87  0
     }
 88  
 
 89  
     protected void verifyReplyMessageSource(MessageSource messageSource)
 90  
     {
 91  
         // template method
 92  0
     }
 93  
 
 94  
     protected String getAsyncReplyCorrelationId(MuleEvent event)
 95  
     {
 96  0
         if (event.getMessage() instanceof MuleMessageCollection)
 97  
         {
 98  0
             return event.getMessage().getCorrelationId();
 99  
         }
 100  
         else
 101  
         {
 102  0
             return event.getFlowConstruct().getMessageInfoMapping().getCorrelationId(event.getMessage());
 103  
         }
 104  
     }
 105  
 
 106  
     protected void sendAsyncRequest(MuleEvent event) throws MuleException
 107  
     {
 108  0
         processNext(event);
 109  0
     }
 110  
     
 111  
     protected MuleEvent receiveAsyncReply(MuleEvent event) throws ResponseTimeoutException
 112  
     {
 113  0
         String asyncReplyCorrelationId = getAsyncReplyCorrelationId(event);
 114  0
         Latch asyncReplyLatch = locks.get(asyncReplyCorrelationId);
 115  
         // flag for catching the interrupted status of the Thread waiting for a
 116  
         // result
 117  0
         boolean interruptedWhileWaiting = false;
 118  0
         boolean resultAvailable = false;
 119  0
         MuleEvent result = null;
 120  
 
 121  
         try
 122  
         {
 123  0
             if (logger.isDebugEnabled())
 124  
             {
 125  0
                 logger.debug("Waiting for async reply message with id: " + asyncReplyCorrelationId);
 126  
             }
 127  
             // how long should we wait for the lock?
 128  0
             if (timeout <= 0)
 129  
             {
 130  0
                 asyncReplyLatch.await();
 131  0
                 resultAvailable = true;
 132  
             }
 133  
             else
 134  
             {
 135  0
                 resultAvailable = asyncReplyLatch.await(timeout, TimeUnit.MILLISECONDS);
 136  
             }
 137  0
             if (!resultAvailable)
 138  
             {
 139  0
                 postLatchAwait(asyncReplyCorrelationId);
 140  0
                 resultAvailable = asyncReplyLatch.getCount() == 0;
 141  
             }
 142  
         }
 143  0
         catch (InterruptedException e)
 144  
         {
 145  0
             interruptedWhileWaiting = true;
 146  
         }
 147  
         finally
 148  
         {
 149  0
             locks.remove(asyncReplyCorrelationId);
 150  0
             result = (MuleEvent) responseEvents.remove(asyncReplyCorrelationId);
 151  0
             if (interruptedWhileWaiting)
 152  
             {
 153  0
                 Thread.currentThread().interrupt();
 154  
             }
 155  
         }
 156  
 
 157  0
         if (interruptedWhileWaiting)
 158  
         {
 159  0
             Thread.currentThread().interrupt();
 160  
         }
 161  
 
 162  0
         if (resultAvailable)
 163  
         {
 164  0
             if (result == null)
 165  
             {
 166  
                 // this should never happen, just using it as a safe guard for now
 167  0
                 throw new IllegalStateException("Response MuleEvent is null");
 168  
             }
 169  
             // Copy event because the async-reply message was received by a different
 170  
             // receiver thread (or the senders dispatcher thread in case of vm
 171  
             // with queueEvents="false") and the current thread may need to mutate
 172  
             // the even. See MULE-4370
 173  0
             return OptimizedRequestContext.criticalSetEvent(result);
 174  
         }
 175  
         else
 176  
         {
 177  0
             addProcessed(asyncReplyCorrelationId);
 178  
 
 179  0
             if (failOnTimeout)
 180  
             {
 181  0
                 event.getMuleContext()
 182  
                     .fireNotification(
 183  
                         new RoutingNotification(event.getMessage(), null,
 184  
                             RoutingNotification.ASYNC_REPLY_TIMEOUT));
 185  
 
 186  0
                 throw new ResponseTimeoutException(CoreMessages.responseTimedOutWaitingForId((int) timeout,
 187  
                     asyncReplyCorrelationId), event, null);
 188  
             }
 189  
             else
 190  
             {
 191  0
                 return null;
 192  
             }
 193  
         }
 194  
     }
 195  
 
 196  
     protected void postLatchAwait(String asyncReplyCorrelationId)
 197  
     {
 198  
         // Template method
 199  0
     }
 200  
 
 201  
     protected void addProcessed(Object id)
 202  
     {
 203  0
         synchronized (processedLock)
 204  
         {
 205  0
             if (processed.isFull())
 206  
             {
 207  0
                 processed.remove();
 208  
             }
 209  0
             processed.add(id);
 210  0
         }
 211  0
     }
 212  
 
 213  
     protected boolean isAlreadyProcessed(Object id)
 214  
     {
 215  0
         synchronized (processedLock)
 216  
         {
 217  0
             return processed.contains(id);
 218  0
         }
 219  
     }
 220  
 
 221  0
     class InternalAsyncReplyMessageProcessor implements MessageProcessor
 222  
     {
 223  
         public MuleEvent process(MuleEvent event) throws MuleException
 224  
         {
 225  0
             String messageId = getAsyncReplyCorrelationId(event);
 226  
 
 227  0
             if (isAlreadyProcessed(messageId))
 228  
             {
 229  0
                 if (logger.isDebugEnabled())
 230  
                 {
 231  0
                     logger.debug("An event was received for an event group that has already been processed, "
 232  
                                  + "this is probably because the async-reply timed out. Correlation Id is: "
 233  
                                  + messageId + ". Dropping event");
 234  
                 }
 235  
                 // Fire a notification to say we received this message
 236  0
                 event.getMuleContext().fireNotification(
 237  
                     new RoutingNotification(event.getMessage(), event.getEndpoint()
 238  
                         .getEndpointURI()
 239  
                         .toString(), RoutingNotification.MISSED_ASYNC_REPLY));
 240  0
                 return null;
 241  
             }
 242  
 
 243  0
             addProcessed(messageId);
 244  0
             MuleEvent previousResult = (MuleEvent) responseEvents.putIfAbsent(messageId, event);
 245  0
             if (previousResult != null)
 246  
             {
 247  
                 // this would indicate that we need a better way to prevent
 248  
                 // continued aggregation for a group that is currently being
 249  
                 // processed. Can this actually happen?
 250  0
                 throw new IllegalStateException("Detected duplicate result message with id: " + messageId);
 251  
             }
 252  0
             Latch l = locks.get(messageId);
 253  0
             if (l != null)
 254  
             {
 255  0
                 l.countDown();
 256  
             }
 257  
             else
 258  
             {
 259  0
                 logger.warn("Unexpected  message with id " + messageId
 260  
                             + " received.   This message will be discarded.");
 261  
             }
 262  0
             return null;
 263  
         }
 264  
     }
 265  
 
 266  
     @Override
 267  
     public String toString()
 268  
     {
 269  0
         return ObjectUtils.toString(this);
 270  
     }
 271  
     
 272  
     public void setFlowConstruct(FlowConstruct flowConstruct)
 273  
     {
 274  0
         this.flowConstruct = flowConstruct;
 275  0
     }
 276  
 }