Author: davsclaus
Date: Sat Apr 16 11:45:55 2011
New Revision: 1093970

URL: http://svn.apache.org/viewvc?rev=1093970&view=rev
Log:
CAMEL-1669: Throttler now uses an expression to set per period, like the other 
EIP patterns. Thanks to Ben O'Day for the patch.

Modified:
    
camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedThrottler.java
    
camel/trunk/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java
    
camel/trunk/camel-core/src/main/java/org/apache/camel/model/ThrottleDefinition.java
    
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/Throttler.java
    
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/ThrottlerTest.java
    
camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/throttler.xml
    
camel/trunk/examples/camel-example-management/src/main/resources/META-INF/spring/camel-context.xml

Modified: 
camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedThrottler.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedThrottler.java?rev=1093970&r1=1093969&r2=1093970&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedThrottler.java
 (original)
+++ 
camel/trunk/camel-core/src/main/java/org/apache/camel/management/mbean/ManagedThrottler.java
 Sat Apr 16 11:45:55 2011
@@ -22,6 +22,8 @@ import org.apache.camel.processor.Thrott
 import org.springframework.jmx.export.annotation.ManagedAttribute;
 import org.springframework.jmx.export.annotation.ManagedResource;
 
+import static org.apache.camel.builder.Builder.constant;
+
 /**
  * @version 
  */
@@ -40,12 +42,12 @@ public class ManagedThrottler extends Ma
 
     @ManagedAttribute(description = "Maximum requires per period")
     public long getMaximumRequestsPerPeriod() {
-        return getThrottler().getMaximumRequestsPerPeriod();
+        return getThrottler().getCurrentMaximumRequestsPerPeriod();
     }
 
     @ManagedAttribute(description = "Maximum requires per period")
     public void setMaximumRequestsPerPeriod(long maximumRequestsPerPeriod) {
-        getThrottler().setMaximumRequestsPerPeriod(maximumRequestsPerPeriod);
+        
getThrottler().setMaximumRequestsPerPeriodExpression(constant(maximumRequestsPerPeriod));
     }
 
     @ManagedAttribute(description = "Time period in millis")

Modified: 
camel/trunk/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java?rev=1093970&r1=1093969&r2=1093970&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java
 (original)
+++ 
camel/trunk/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java
 Sat Apr 16 11:45:55 2011
@@ -1805,11 +1805,26 @@ public abstract class ProcessorDefinitio
      * @return the builder
      */
     public ThrottleDefinition throttle(long maximumRequestCount) {
+        return 
throttle(ExpressionBuilder.constantExpression(maximumRequestCount));
+    }
+
+    /**
+     * <a href="http://camel.apache.org/throttler.html";>Throttler EIP:</a>
+     * Creates a throttler allowing you to ensure that a specific endpoint 
does not get overloaded,
+     * or that we don't exceed an agreed SLA with some external service.
+     * <p/>
+     * Will default use a time period of 1 second, so setting the 
maximumRequestCount to eg 10
+     * will default ensure at most 10 messages per second. 
+     *
+     * @param maximumRequestCount  an expression to calculate the maximum 
request count 
+     * @return the builder
+     */
+    public ThrottleDefinition throttle(Expression maximumRequestCount) {
         ThrottleDefinition answer = new 
ThrottleDefinition(maximumRequestCount);
         addOutput(answer);
         return answer;
     }
-
+    
     /**
      * <a href="http://camel.apache.org/loop.html";>Loop EIP:</a>
      * Creates a loop allowing to process the a message a number of times and 
possibly process them
@@ -2994,5 +3009,5 @@ public abstract class ProcessorDefinitio
     public String getLabel() {
         return "";
     }
-
+    
 }

Modified: 
camel/trunk/camel-core/src/main/java/org/apache/camel/model/ThrottleDefinition.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/model/ThrottleDefinition.java?rev=1093970&r1=1093969&r2=1093970&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/main/java/org/apache/camel/model/ThrottleDefinition.java
 (original)
+++ 
camel/trunk/camel-core/src/main/java/org/apache/camel/model/ThrottleDefinition.java
 Sat Apr 16 11:45:55 2011
@@ -18,15 +18,20 @@ package org.apache.camel.model;
 
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ScheduledExecutorService;
+
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlTransient;
 
+import org.apache.camel.Expression;
 import org.apache.camel.Processor;
+import org.apache.camel.builder.ExpressionBuilder;
+import org.apache.camel.model.language.ExpressionDefinition;
 import org.apache.camel.processor.Throttler;
 import org.apache.camel.spi.RouteContext;
+import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.concurrent.ExecutorServiceHelper;
 
 /**
@@ -36,33 +41,30 @@ import org.apache.camel.util.concurrent.
  */
 @XmlRootElement(name = "throttle")
 @XmlAccessorType(XmlAccessType.FIELD)
-public class ThrottleDefinition extends OutputDefinition<ThrottleDefinition> 
implements ExecutorServiceAwareDefinition<ThrottleDefinition> {
-
-    // TODO: Camel 3.0 Should extend NoOutputDefinition
+public class ThrottleDefinition extends ExpressionNode implements 
ExecutorServiceAwareDefinition<ThrottleDefinition> {
+    // TODO: Camel 3.0 Should not support outputs
 
     @XmlTransient
     private ExecutorService executorService;
     @XmlAttribute
     private String executorServiceRef;
-    @XmlAttribute(required = true)
-    private Long maximumRequestsPerPeriod;
     @XmlAttribute
     private Long timePeriodMillis;
     @XmlAttribute
     private Boolean asyncDelayed;
     @XmlAttribute
     private Boolean callerRunsWhenRejected;
-
+    
     public ThrottleDefinition() {
     }
 
-    public ThrottleDefinition(long maximumRequestsPerPeriod) {
-        this.maximumRequestsPerPeriod = maximumRequestsPerPeriod;
+    public ThrottleDefinition(Expression maximumRequestsPerPeriod) {
+        super(maximumRequestsPerPeriod);
     }
 
     @Override
     public String toString() {
-        return "Throttle[" + getMaximumRequestsPerPeriod() + " request per " + 
getTimePeriodMillis()
+        return "Throttle[" + getExpression() + " request per " + 
getTimePeriodMillis()
                + " millis -> " + getOutputs() + "]";
     }
 
@@ -73,7 +75,7 @@ public class ThrottleDefinition extends 
 
     @Override
     public String getLabel() {
-        return "" + getMaximumRequestsPerPeriod() + " per " + 
getTimePeriodMillis() + " (ms)";
+        return "" + getExpression() + " per " + getTimePeriodMillis() + " 
(ms)";
     }
 
     @Override
@@ -90,10 +92,14 @@ public class ThrottleDefinition extends 
 
         // should be default 1000 millis
         long period = getTimePeriodMillis() != null ? getTimePeriodMillis() : 
1000L;
-        Throttler answer = new Throttler(childProcessor, 
getMaximumRequestsPerPeriod(), period, scheduled);
+        Expression maxRequestsExpression = 
createMaxRequestsPerPeriodExpression(routeContext);
+
+        Throttler answer = new Throttler(childProcessor, 
maxRequestsExpression, period, scheduled);
+
         if (getAsyncDelayed() != null) {
             answer.setAsyncDelayed(getAsyncDelayed());
         }
+        
         if (getCallerRunsWhenRejected() == null) {
             // should be true by default
             answer.setCallerRunsWhenRejected(true);
@@ -103,9 +109,17 @@ public class ThrottleDefinition extends 
         return answer;
     }
 
+    private Expression createMaxRequestsPerPeriodExpression(RouteContext 
routeContext) {
+        if (getExpression() != null) {
+            if (ObjectHelper.isNotEmpty(getExpression().getExpression()) || 
getExpression().getExpressionValue() != null) {
+                return getExpression().createExpression(routeContext);
+            } 
+        } 
+        return null;
+    }
+    
     // Fluent API
     // 
-------------------------------------------------------------------------
-
     /**
      * Sets the time period during which the maximum request count is valid for
      *
@@ -124,7 +138,7 @@ public class ThrottleDefinition extends 
      * @return the builder
      */
     public ThrottleDefinition maximumRequestsPerPeriod(Long 
maximumRequestsPerPeriod) {
-        setMaximumRequestsPerPeriod(maximumRequestsPerPeriod);
+        setExpression(new 
ExpressionDefinition(ExpressionBuilder.constantExpression(maximumRequestsPerPeriod)));
         return this;
     }
 
@@ -164,14 +178,6 @@ public class ThrottleDefinition extends 
     // Properties
     // 
-------------------------------------------------------------------------
 
-    public Long getMaximumRequestsPerPeriod() {
-        return maximumRequestsPerPeriod;
-    }
-
-    public void setMaximumRequestsPerPeriod(Long maximumRequestsPerPeriod) {
-        this.maximumRequestsPerPeriod = maximumRequestsPerPeriod;
-    }
-
     public Long getTimePeriodMillis() {
         return timePeriodMillis;
     }

Modified: 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/Throttler.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/processor/Throttler.java?rev=1093970&r1=1093969&r2=1093970&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/Throttler.java 
(original)
+++ 
camel/trunk/camel-core/src/main/java/org/apache/camel/processor/Throttler.java 
Sat Apr 16 11:45:55 2011
@@ -19,7 +19,9 @@ package org.apache.camel.processor;
 import java.util.concurrent.ScheduledExecutorService;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
 import org.apache.camel.Processor;
+import org.apache.camel.util.ObjectHelper;
 
 /**
  * A <a href="http://camel.apache.org/throttler.html";>Throttler</a>
@@ -34,50 +36,58 @@ import org.apache.camel.Processor;
  */
 public class Throttler extends DelayProcessorSupport implements Traceable {
     private long maximumRequestsPerPeriod;
-    private long timePeriodMillis;
+    private Expression maxRequestsPerPeriodExpression;
+    private long timePeriodMillis = 1000;
     private volatile TimeSlot slot;
 
-    public Throttler(Processor processor, long maximumRequestsPerPeriod) {
-        this(processor, maximumRequestsPerPeriod, 1000, null);
-    }
-
-    public Throttler(Processor processor, long maximumRequestsPerPeriod, long 
timePeriodMillis, ScheduledExecutorService executorService) {
+    public Throttler(Processor processor, Expression 
maxRequestsPerPeriodExpression, long timePeriodMillis, ScheduledExecutorService 
executorService) {
         super(processor, executorService);
-        this.maximumRequestsPerPeriod = maximumRequestsPerPeriod;
-        if (maximumRequestsPerPeriod <= 0) {
-            throw new IllegalArgumentException("MaximumRequestsPerPeriod 
should be a positive number, was: " + maximumRequestsPerPeriod);
+
+        ObjectHelper.notNull(maxRequestsPerPeriodExpression, 
"maxRequestsPerPeriodExpression");
+        this.maxRequestsPerPeriodExpression = maxRequestsPerPeriodExpression;
+
+        if (timePeriodMillis <= 0) {
+            throw new IllegalArgumentException("TimePeriodMillis should be a 
positive number, was: " + timePeriodMillis);
         }
         this.timePeriodMillis = timePeriodMillis;
     }
 
     @Override
     public String toString() {
-        return "Throttler[requests: " + maximumRequestsPerPeriod + " per: " + 
timePeriodMillis + " (ms) to: "
+        return "Throttler[requests: " + maxRequestsPerPeriodExpression + " 
per: " + timePeriodMillis + " (ms) to: "
                + getProcessor() + "]";
     }
 
     public String getTraceLabel() {
-        return "throttle[" + maximumRequestsPerPeriod + " per: " + 
timePeriodMillis + "]";
+        return "throttle[" + maxRequestsPerPeriodExpression + " per: " + 
timePeriodMillis + "]";
     }
 
     // Properties
     // -----------------------------------------------------------------------
-    public long getMaximumRequestsPerPeriod() {
-        return maximumRequestsPerPeriod;
-    }
 
     /**
-     * Sets the maximum number of requests per time period
+     * Sets the maximum number of requests per time period expression
      */
-    public void setMaximumRequestsPerPeriod(long maximumRequestsPerPeriod) {
-        this.maximumRequestsPerPeriod = maximumRequestsPerPeriod;
+    public void setMaximumRequestsPerPeriodExpression(Expression 
maxRequestsPerPeriodExpression) {
+        this.maxRequestsPerPeriodExpression = maxRequestsPerPeriodExpression;
     }
 
+    public Expression getMaximumRequestsPerPeriodExpression() {
+        return maxRequestsPerPeriodExpression;
+    }
+    
     public long getTimePeriodMillis() {
         return timePeriodMillis;
     }
 
     /**
+     * Gets the current maximum request per period value.
+     */
+    public long getCurrentMaximumRequestsPerPeriod() {
+        return maximumRequestsPerPeriod;
+    }
+
+    /**
      * Sets the time period during which the maximum number of requests apply
      */
     public void setTimePeriodMillis(long timePeriodMillis) {
@@ -88,6 +98,19 @@ public class Throttler extends DelayProc
     // -----------------------------------------------------------------------
 
     protected long calculateDelay(Exchange exchange) {
+        Long longValue = maxRequestsPerPeriodExpression.evaluate(exchange, 
Long.class);
+        if (longValue != null) {
+            // log if we changed max period after initial setting
+            if (maximumRequestsPerPeriod > 0 && longValue.longValue() != 
maximumRequestsPerPeriod) {
+                log.debug("Throttler changed maximum requests per period from 
{} to {}", maximumRequestsPerPeriod, longValue);
+            }
+            maximumRequestsPerPeriod = longValue;
+        }
+
+        if (maximumRequestsPerPeriod <= 0) {
+            throw new IllegalStateException("The maximumRequestsPerPeriod must 
be a positive number, was: " + maximumRequestsPerPeriod);
+        }
+
         TimeSlot slot = nextSlot();
         if (!slot.isActive()) {
             long delay = slot.startTime - currentSystemTime();
@@ -127,7 +150,7 @@ public class Throttler extends DelayProc
         protected TimeSlot(long startTime) {
             this.startTime = startTime;
         }
-        
+
         protected void assign() {
             capacity--;
         }

Modified: 
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/ThrottlerTest.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/ThrottlerTest.java?rev=1093970&r1=1093969&r2=1093970&view=diff
==============================================================================
--- 
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/ThrottlerTest.java
 (original)
+++ 
camel/trunk/camel-core/src/test/java/org/apache/camel/processor/ThrottlerTest.java
 Sat Apr 16 11:45:55 2011
@@ -22,8 +22,11 @@ import java.util.concurrent.Executors;
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.DefaultExchange;
 import org.apache.camel.processor.Throttler.TimeSlot;
 
+import static org.apache.camel.builder.Builder.constant;
+
 /**
  * @version 
  */
@@ -71,11 +74,13 @@ public class ThrottlerTest extends Conte
     }
 
     public void testTimeSlotCalculus() throws Exception {
-        Throttler throttler = new Throttler(null, 2, 1000, null);
+        Throttler throttler = new Throttler(null, constant(3), 1000, null);
+        // calculate will assign a new slot
+        throttler.calculateDelay(new DefaultExchange(context));
         TimeSlot slot = throttler.nextSlot();
         // start a new time slot
         assertNotNull(slot);
-        // make sure the same slot is used (2 exchanges per slot)
+        // make sure the same slot is used (3 exchanges per slot)
         assertSame(slot, throttler.nextSlot());
         assertTrue(slot.isFull());
         
@@ -85,6 +90,56 @@ public class ThrottlerTest extends Conte
         assertFalse(next.isActive());
     }
 
+    public void testConfigurationWithConstantExpression() throws Exception {
+        MockEndpoint resultEndpoint = resolveMandatoryEndpoint("mock:result", 
MockEndpoint.class);
+        resultEndpoint.expectedMessageCount(messageCount);
+
+        ExecutorService executor = Executors.newFixedThreadPool(messageCount);
+
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < messageCount; i++) {
+            executor.execute(new Runnable() {
+                public void run() {
+                    template.sendBody("direct:expressionConstant", 
"<message>payload</message>");
+                }
+            });
+        }
+
+        // let's wait for the exchanges to arrive
+        resultEndpoint.assertIsSatisfied();
+
+        // now assert that they have actually been throttled
+        long minimumTime = (messageCount - 1) * INTERVAL;
+        // add a little slack
+        long delta = System.currentTimeMillis() - start + 200;
+        assertTrue("Should take at least " + minimumTime + "ms, was: " + 
delta, delta >= minimumTime);
+    }
+
+    public void testConfigurationWithHeaderExpression() throws Exception {
+        MockEndpoint resultEndpoint = resolveMandatoryEndpoint("mock:result", 
MockEndpoint.class);
+        resultEndpoint.expectedMessageCount(messageCount);
+
+        ExecutorService executor = Executors.newFixedThreadPool(messageCount);
+
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < messageCount; i++) {
+            executor.execute(new Runnable() {
+                public void run() {
+                    template.sendBodyAndHeader("direct:expressionHeader", 
"<message>payload</message>", "throttleValue", 1);
+                }
+            });
+        }
+
+        // let's wait for the exchanges to arrive
+        resultEndpoint.assertIsSatisfied();
+
+        // now assert that they have actually been throttled
+        long minimumTime = (messageCount - 1) * INTERVAL;
+        // add a little slack
+        long delta = System.currentTimeMillis() - start + 200;
+        assertTrue("Should take at least " + minimumTime + "ms, was: " + 
delta, delta >= minimumTime);
+    }
+    
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
             public void configure() {
@@ -93,6 +148,10 @@ public class ThrottlerTest extends Conte
                 // END SNIPPET: ex
                 
                 
from("direct:a").throttle(1).timePeriodMillis(INTERVAL).to("log:result", 
"mock:result");
+                
+                
from("direct:expressionConstant").throttle(constant(1)).timePeriodMillis(INTERVAL).to("log:result",
 "mock:result");
+                
+                
from("direct:expressionHeader").throttle(header("throttleValue")).timePeriodMillis(INTERVAL).to("log:result",
 "mock:result");
             }
         };
     }

Modified: 
camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/throttler.xml
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/throttler.xml?rev=1093970&r1=1093969&r2=1093970&view=diff
==============================================================================
--- 
camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/throttler.xml
 (original)
+++ 
camel/trunk/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/throttler.xml
 Sat Apr 16 11:45:55 2011
@@ -22,24 +22,44 @@
        http://camel.apache.org/schema/spring 
http://camel.apache.org/schema/spring/camel-spring.xsd
     ">
 
-  <camelContext
-    xmlns="http://camel.apache.org/schema/spring";>
-    <!-- START SNIPPET: example -->
-    <route>
-      <from uri="seda:a" />
-      <throttle maximumRequestsPerPeriod="3" timePeriodMillis="10000">
-        <to uri="mock:result" />
-      </throttle>
-    </route>
-    <!-- END SNIPPET: example -->
-
-    <route>
-      <from uri="direct:a" />
-      <throttle maximumRequestsPerPeriod="1" timePeriodMillis="500">
-        <to uri="mock:result" />
-      </throttle>
-    </route>
+    <camelContext xmlns="http://camel.apache.org/schema/spring";>
 
-  </camelContext>
+        <!-- START SNIPPET: example -->
+        <route>
+            <from uri="seda:a"/>
+            <!-- throttle 3 messages per 10 sec -->
+            <throttle timePeriodMillis="10000">
+                <constant>3</constant>
+                <to uri="mock:result"/>
+            </throttle>
+        </route>
+        <!-- END SNIPPET: example -->
+
+        <route>
+            <from uri="direct:a"/>
+            <!-- throttle 1 messages per 0.5 sec -->
+            <throttle timePeriodMillis="500">
+                <constant>1</constant>
+                <to uri="mock:result"/>
+            </throttle>
+        </route>
+
+        <route>
+            <from uri="direct:expressionConstant"/>
+            <throttle timePeriodMillis="500">
+                <constant>1</constant>
+                <to uri="mock:result"/>
+            </throttle>
+        </route>
+
+        <route>
+            <from uri="direct:expressionHeader"/>
+            <throttle timePeriodMillis="500">
+                <header>throttleValue</header>
+                <to uri="mock:result"/>
+            </throttle>
+        </route>
+
+    </camelContext>
 
 </beans>

Modified: 
camel/trunk/examples/camel-example-management/src/main/resources/META-INF/spring/camel-context.xml
URL: 
http://svn.apache.org/viewvc/camel/trunk/examples/camel-example-management/src/main/resources/META-INF/spring/camel-context.xml?rev=1093970&r1=1093969&r2=1093970&view=diff
==============================================================================
--- 
camel/trunk/examples/camel-example-management/src/main/resources/META-INF/spring/camel-context.xml
 (original)
+++ 
camel/trunk/examples/camel-example-management/src/main/resources/META-INF/spring/camel-context.xml
 Sat Apr 16 11:45:55 2011
@@ -52,7 +52,9 @@
             <from uri="file://target/inbox"/>
             <split>
                 <xpath>/stocks/stock</xpath>
-                <throttle timePeriodMillis="1000" 
maximumRequestsPerPeriod="10">
+                <!-- throttle 10 msg/sec being send the the ActiveMQ queue -->
+                <throttle>
+                    <constant>10</constant>
                     <to uri="activemq:queue:stock"/>
                 </throttle>
             </split>


Reply via email to