Coverage Report - org.mule.transport.AbstractPollingMessageReceiver
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractPollingMessageReceiver
0%
0/55
0%
0/14
0
 
 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.construct.FlowConstruct;
 11  
 import org.mule.api.endpoint.InboundEndpoint;
 12  
 import org.mule.api.lifecycle.CreateException;
 13  
 import org.mule.api.transport.Connector;
 14  
 import org.mule.config.i18n.CoreMessages;
 15  
 import org.mule.util.ObjectUtils;
 16  
 
 17  
 import java.util.HashMap;
 18  
 import java.util.Iterator;
 19  
 import java.util.Map;
 20  
 
 21  
 import edu.emory.mathcs.backport.java.util.concurrent.Future;
 22  
 import edu.emory.mathcs.backport.java.util.concurrent.RejectedExecutionException;
 23  
 import edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService;
 24  
 import edu.emory.mathcs.backport.java.util.concurrent.ScheduledFuture;
 25  
 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
 26  
 
 27  
 /**
 28  
  * <code>AbstractPollingMessageReceiver</code> implements a base class for polling
 29  
  * message receivers. The receiver provides a {@link #poll()} method that implementations
 30  
  * must implement to execute their custom code. Note that the receiver will not poll if
 31  
  * the associated connector is not started.
 32  
  */
 33  
 public abstract class AbstractPollingMessageReceiver extends AbstractMessageReceiver
 34  
 {
 35  
     public static final long DEFAULT_POLL_FREQUENCY = 1000;
 36  0
     public static final TimeUnit DEFAULT_POLL_TIMEUNIT = TimeUnit.MILLISECONDS;
 37  
 
 38  
     public static final long DEFAULT_STARTUP_DELAY = 1000;
 39  
 
 40  0
     private long frequency = DEFAULT_POLL_FREQUENCY;
 41  0
     private TimeUnit timeUnit = DEFAULT_POLL_TIMEUNIT;
 42  
 
 43  
     // @GuardedBy(itself)
 44  0
     protected final Map<ScheduledFuture, PollingReceiverWorker> schedules = new HashMap<ScheduledFuture, PollingReceiverWorker>();
 45  
 
 46  
     public AbstractPollingMessageReceiver(Connector connector,
 47  
                                           FlowConstruct flowConstruct,
 48  
                                           final InboundEndpoint endpoint) throws CreateException
 49  
     {
 50  0
         super(connector, flowConstruct, endpoint);
 51  0
     }
 52  
 
 53  
     @Override
 54  
     protected void doStart() throws MuleException
 55  
     {
 56  
         try
 57  
         {
 58  0
             this.schedule();
 59  
         }
 60  0
         catch (Exception ex)
 61  
         {
 62  0
             this.stop();
 63  0
             throw new CreateException(CoreMessages.failedToScheduleWork(), ex, this);
 64  0
         }
 65  0
     }
 66  
 
 67  
     @Override
 68  
     protected void doStop() throws MuleException
 69  
     {
 70  0
         this.unschedule();
 71  0
     }
 72  
 
 73  
     /**
 74  
      * This method registers this receiver for periodic polling ticks with the connectors
 75  
      * scheduler. Subclasses can override this in case they want to handle their polling
 76  
      * differently.
 77  
      *
 78  
      * @throws RejectedExecutionException
 79  
      * @throws NullPointerException
 80  
      * @throws IllegalArgumentException
 81  
      * @see ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)
 82  
      */
 83  
     protected void schedule()
 84  
             throws RejectedExecutionException, NullPointerException, IllegalArgumentException
 85  
     {
 86  0
         synchronized (schedules)
 87  
         {
 88  
             // we use scheduleWithFixedDelay to prevent queue-up of tasks when
 89  
             // polling takes longer than the specified frequency, e.g. when the
 90  
             // polled database or network is slow or returns large amounts of
 91  
             // data.
 92  0
             PollingReceiverWorker pollingReceiverWorker = this.createWork();
 93  0
             ScheduledFuture schedule = connector.getScheduler().scheduleWithFixedDelay(
 94  
                     new PollingReceiverWorkerSchedule(pollingReceiverWorker), DEFAULT_STARTUP_DELAY,
 95  
                     this.getFrequency(), this.getTimeUnit());
 96  0
             schedules.put(schedule, pollingReceiverWorker);
 97  
 
 98  0
             if (logger.isDebugEnabled())
 99  
             {
 100  0
                 logger.debug(ObjectUtils.identityToShortString(this) + " scheduled "
 101  
                              + ObjectUtils.identityToShortString(schedule) + " with " + frequency
 102  
                              + " " + getTimeUnit() + " polling frequency");
 103  
             }
 104  0
         }
 105  0
     }
 106  
 
 107  
     /**
 108  
      * This method cancels the schedules which were created in {@link #schedule()}.
 109  
      *
 110  
      * @see Future#cancel(boolean)
 111  
      */
 112  
     protected void unschedule()
 113  
     {
 114  0
         synchronized (schedules)
 115  
         {
 116  
             // cancel our schedules gently: do not interrupt when polling is in progress
 117  0
             for (Iterator<ScheduledFuture> i = schedules.keySet().iterator(); i.hasNext();)
 118  
             {
 119  0
                 ScheduledFuture schedule = i.next();
 120  0
                 schedule.cancel(false);
 121  
                 // Wait until in-progress PollingRecevierWorker completes.
 122  0
                 int shutdownTimeout = connector.getMuleContext().getConfiguration().getShutdownTimeout();
 123  0
                 PollingReceiverWorker worker = schedules.get(schedule);
 124  0
                 for (int elapsed = 0; worker.isRunning() && elapsed < shutdownTimeout; elapsed += 50)
 125  
                 {
 126  
                     try
 127  
                     {
 128  0
                         Thread.sleep(50);
 129  
                     }
 130  0
                     catch (InterruptedException e)
 131  
                     {
 132  0
                         logger.warn(
 133  
                                 ObjectUtils.identityToShortString(this) + "  interrupted while waiting for poll() to complete as part of message receiver stop.",
 134  
                                 e);
 135  0
                         break;
 136  0
                     }
 137  
                 }
 138  0
                 i.remove();
 139  
 
 140  0
                 if (logger.isDebugEnabled())
 141  
                 {
 142  0
                     logger.debug(ObjectUtils.identityToShortString(this) + " cancelled polling schedule: "
 143  
                                  + ObjectUtils.identityToShortString(schedule));
 144  
                 }
 145  0
             }
 146  0
         }
 147  0
     }
 148  
 
 149  
     public void disableNativeScheduling()
 150  
     {
 151  0
         this.unschedule();
 152  0
     }
 153  
 
 154  
     protected PollingReceiverWorker createWork()
 155  
     {
 156  0
         return new PollingReceiverWorker(this);
 157  
     }
 158  
 
 159  
     public long getFrequency()
 160  
     {
 161  0
         return frequency;
 162  
     }
 163  
 
 164  
     // TODO a nifty thing would be on-the-fly adjustment (via JMX?) of the
 165  
     // polling frequency by rescheduling without explicit stop()
 166  
     public void setFrequency(long value)
 167  
     {
 168  0
         if (value <= 0)
 169  
         {
 170  0
             frequency = DEFAULT_POLL_FREQUENCY;
 171  
         }
 172  
         else
 173  
         {
 174  0
             frequency = value;
 175  
         }
 176  0
     }
 177  
 
 178  
     public TimeUnit getTimeUnit()
 179  
     {
 180  0
         return timeUnit;
 181  
     }
 182  
 
 183  
     public void setTimeUnit(TimeUnit timeUnit)
 184  
     {
 185  0
         this.timeUnit = timeUnit;
 186  0
     }
 187  
     
 188  
     /**
 189  
      * The preferred number of messages to process in the current batch. We need to
 190  
      * drain the queue quickly, but not by slamming the workManager too hard. It is
 191  
      * impossible to determine this more precisely without proper load
 192  
      * statistics/feedback or some kind of "event cost estimate". Therefore we just
 193  
      * try to use half of the receiver's workManager, since it is shared with
 194  
      * receivers for other endpoints. TODO make this user-settable
 195  
      * 
 196  
      * @param available the number if messages currently available to be processed
 197  
      */
 198  
     protected int getBatchSize(int available)
 199  
     {
 200  0
         if (available <= 0)
 201  
         {
 202  0
             return 0;
 203  
         }
 204  
 
 205  0
         int maxThreads = connector.getReceiverThreadingProfile().getMaxThreadsActive();
 206  0
         return Math.max(1, Math.min(available, ((maxThreads / 2) - 1)));
 207  
     }
 208  
 
 209  
     public abstract void poll() throws Exception;
 210  
 
 211  
 }