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