001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.model;
018
019import java.util.concurrent.ExecutorService;
020import java.util.concurrent.ScheduledExecutorService;
021import javax.xml.bind.annotation.XmlAccessType;
022import javax.xml.bind.annotation.XmlAccessorType;
023import javax.xml.bind.annotation.XmlAttribute;
024import javax.xml.bind.annotation.XmlRootElement;
025import javax.xml.bind.annotation.XmlTransient;
026
027import org.apache.camel.Expression;
028import org.apache.camel.Processor;
029import org.apache.camel.builder.ExpressionBuilder;
030import org.apache.camel.model.language.ExpressionDefinition;
031import org.apache.camel.processor.Throttler;
032import org.apache.camel.spi.Metadata;
033import org.apache.camel.spi.RouteContext;
034
035/**
036 * Controls the rate at which messages are passed to the next node in the route
037 *
038 * @version 
039 */
040@Metadata(label = "eip,routing")
041@XmlRootElement(name = "throttle")
042@XmlAccessorType(XmlAccessType.FIELD)
043public class ThrottleDefinition extends ExpressionNode implements ExecutorServiceAwareDefinition<ThrottleDefinition> {
044    // TODO: Camel 3.0 Should not support outputs
045
046    @XmlTransient
047    private ExecutorService executorService;
048    @XmlAttribute
049    private String executorServiceRef;
050    @XmlAttribute @Metadata(defaultValue = "1000")
051    private Long timePeriodMillis;
052    @XmlAttribute
053    private Boolean asyncDelayed;
054    @XmlAttribute @Metadata(defaultValue = "true")
055    private Boolean callerRunsWhenRejected;
056    @XmlAttribute
057    private Boolean rejectExecution;
058    
059    public ThrottleDefinition() {
060    }
061
062    public ThrottleDefinition(Expression maximumRequestsPerPeriod) {
063        super(maximumRequestsPerPeriod);
064    }
065
066    @Override
067    public String toString() {
068        return "Throttle[" + description() + " -> " + getOutputs() + "]";
069    }
070    
071    protected String description() {
072        return getExpression() + " request per " + getTimePeriodMillis() + " millis";
073    }
074
075    @Override
076    public String getLabel() {
077        return "throttle[" + description() + "]";
078    }
079
080    @Override
081    public Processor createProcessor(RouteContext routeContext) throws Exception {
082        Processor childProcessor = this.createChildProcessor(routeContext, true);
083
084        boolean async = getAsyncDelayed() != null && getAsyncDelayed();
085        boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, async);
086        ScheduledExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredScheduledExecutorService(routeContext, "Throttle", this, async);
087        
088        // should be default 1000 millis
089        long period = getTimePeriodMillis() != null ? getTimePeriodMillis() : 1000L;
090
091        // max requests per period is mandatory
092        Expression maxRequestsExpression = createMaxRequestsPerPeriodExpression(routeContext);
093        if (maxRequestsExpression == null) {
094            throw new IllegalArgumentException("MaxRequestsPerPeriod expression must be provided on " + this);
095        }
096
097        boolean reject = getRejectExecution() != null && getRejectExecution();
098        Throttler answer = new Throttler(routeContext.getCamelContext(), childProcessor, maxRequestsExpression, period, threadPool, shutdownThreadPool, reject);
099
100        answer.setAsyncDelayed(async);
101        if (getCallerRunsWhenRejected() == null) {
102            // should be true by default
103            answer.setCallerRunsWhenRejected(true);
104        } else {
105            answer.setCallerRunsWhenRejected(getCallerRunsWhenRejected());
106        }
107        return answer;
108    }
109
110    private Expression createMaxRequestsPerPeriodExpression(RouteContext routeContext) {
111        ExpressionDefinition expr = getExpression();
112        if (expr != null) {
113            return expr.createExpression(routeContext);
114        }
115        return null;
116    }
117    
118    // Fluent API
119    // -------------------------------------------------------------------------
120    /**
121     * Sets the time period during which the maximum request count is valid for
122     *
123     * @param timePeriodMillis  period in millis
124     * @return the builder
125     */
126    public ThrottleDefinition timePeriodMillis(long timePeriodMillis) {
127        setTimePeriodMillis(timePeriodMillis);
128        return this;
129    }
130    
131    /**
132     * Sets the time period during which the maximum request count per period
133     *
134     * @param maximumRequestsPerPeriod  the maximum request count number per time period
135     * @return the builder
136     */
137    public ThrottleDefinition maximumRequestsPerPeriod(long maximumRequestsPerPeriod) {
138        setExpression(ExpressionNodeHelper.toExpressionDefinition(ExpressionBuilder.constantExpression(maximumRequestsPerPeriod)));
139        return this;
140    }
141
142    /**
143     * Whether or not the caller should run the task when it was rejected by the thread pool.
144     * <p/>
145     * Is by default <tt>true</tt>
146     *
147     * @param callerRunsWhenRejected whether or not the caller should run
148     * @return the builder
149     */
150    public ThrottleDefinition callerRunsWhenRejected(boolean callerRunsWhenRejected) {
151        setCallerRunsWhenRejected(callerRunsWhenRejected);
152        return this;
153    }
154
155    /**
156     * Enables asynchronous delay which means the thread will <b>not</b> block while delaying.
157     *
158     * @return the builder
159     */
160    public ThrottleDefinition asyncDelayed() {
161        setAsyncDelayed(true);
162        return this;
163    }
164    
165    /**
166     * Whether or not throttler throws the ThrottlerRejectedExecutionException when the exchange exceeds the request limit
167     * <p/>
168     * Is by default <tt>false</tt>
169     *
170     * @param rejectExecution throw the RejectExecutionException if the exchange exceeds the request limit 
171     * @return the builder
172     */
173    public ThrottleDefinition rejectExecution(boolean rejectExecution) {
174        setRejectExecution(rejectExecution);
175        return this;
176    }
177
178    /**
179     * To use a custom thread pool (ScheduledExecutorService) by the throttler.
180     *
181     * @param executorService  the custom thread pool (must be scheduled)
182     * @return the builder
183     */
184    public ThrottleDefinition executorService(ExecutorService executorService) {
185        setExecutorService(executorService);
186        return this;
187    }
188
189    /**
190     * To use a custom thread pool (ScheduledExecutorService) by the throttler.
191     *
192     * @param executorServiceRef the reference id of the thread pool (must be scheduled)
193     * @return the builder
194     */
195    public ThrottleDefinition executorServiceRef(String executorServiceRef) {
196        setExecutorServiceRef(executorServiceRef);
197        return this;
198    }
199
200    // Properties
201    // -------------------------------------------------------------------------
202
203    /**
204     * Expression to configure the maximum number of messages to throttle per request
205     */
206    @Override
207    public void setExpression(ExpressionDefinition expression) {
208        // override to include javadoc what the expression is used for
209        super.setExpression(expression);
210    }
211
212    public Long getTimePeriodMillis() {
213        return timePeriodMillis;
214    }
215
216    public void setTimePeriodMillis(Long timePeriodMillis) {
217        this.timePeriodMillis = timePeriodMillis;
218    }
219
220    public Boolean getAsyncDelayed() {
221        return asyncDelayed;
222    }
223
224    public void setAsyncDelayed(Boolean asyncDelayed) {
225        this.asyncDelayed = asyncDelayed;
226    }
227
228    public Boolean getCallerRunsWhenRejected() {
229        return callerRunsWhenRejected;
230    }
231
232    public void setCallerRunsWhenRejected(Boolean callerRunsWhenRejected) {
233        this.callerRunsWhenRejected = callerRunsWhenRejected;
234    }
235
236    public ExecutorService getExecutorService() {
237        return executorService;
238    }
239
240    public void setExecutorService(ExecutorService executorService) {
241        this.executorService = executorService;
242    }
243
244    public String getExecutorServiceRef() {
245        return executorServiceRef;
246    }
247
248    public void setExecutorServiceRef(String executorServiceRef) {
249        this.executorServiceRef = executorServiceRef;
250    }
251    
252    public Boolean getRejectExecution() {
253        return rejectExecution;
254    }
255
256    public void setRejectExecution(Boolean rejectExecution) {
257        this.rejectExecution = rejectExecution;
258    }
259}