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.RouteContext;
033
034/**
035 * Represents an XML <throttle/> element
036 *
037 * @version 
038 */
039@XmlRootElement(name = "throttle")
040@XmlAccessorType(XmlAccessType.FIELD)
041public class ThrottleDefinition extends ExpressionNode implements ExecutorServiceAwareDefinition<ThrottleDefinition> {
042    // TODO: Camel 3.0 Should not support outputs
043
044    @XmlTransient
045    private ExecutorService executorService;
046    @XmlAttribute
047    private String executorServiceRef;
048    @XmlAttribute
049    private Long timePeriodMillis;
050    @XmlAttribute
051    private Boolean asyncDelayed;
052    @XmlAttribute
053    private Boolean callerRunsWhenRejected;
054    @XmlAttribute
055    private Boolean rejectExecution;
056    
057    public ThrottleDefinition() {
058    }
059
060    public ThrottleDefinition(Expression maximumRequestsPerPeriod) {
061        super(maximumRequestsPerPeriod);
062    }
063
064    @Override
065    public String toString() {
066        return "Throttle[" + description() + " -> " + getOutputs() + "]";
067    }
068    
069    protected String description() {
070        return getExpression() + " request per " + getTimePeriodMillis() + " millis";
071    }
072
073    @Override
074    public String getShortName() {
075        return "throttle";
076    }
077
078    @Override
079    public String getLabel() {
080        return "throttle[" + description() + "]";
081    }
082
083    @Override
084    public Processor createProcessor(RouteContext routeContext) throws Exception {
085        Processor childProcessor = this.createChildProcessor(routeContext, true);
086
087        boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, isAsyncDelayed());
088        ScheduledExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredScheduledExecutorService(routeContext, "Throttle", this, isAsyncDelayed());
089        
090        // should be default 1000 millis
091        long period = getTimePeriodMillis() != null ? getTimePeriodMillis() : 1000L;
092
093        // max requests per period is mandatory
094        Expression maxRequestsExpression = createMaxRequestsPerPeriodExpression(routeContext);
095        if (maxRequestsExpression == null) {
096            throw new IllegalArgumentException("MaxRequestsPerPeriod expression must be provided on " + this);
097        }
098
099        Throttler answer = new Throttler(routeContext.getCamelContext(), childProcessor, maxRequestsExpression, period, threadPool, shutdownThreadPool, isRejectExecution());
100
101        if (getAsyncDelayed() != null) {
102            answer.setAsyncDelayed(getAsyncDelayed());
103        }
104        
105        if (getCallerRunsWhenRejected() == null) {
106            // should be true by default
107            answer.setCallerRunsWhenRejected(true);
108        } else {
109            answer.setCallerRunsWhenRejected(getCallerRunsWhenRejected());
110        }
111        return answer;
112    }
113
114    private Expression createMaxRequestsPerPeriodExpression(RouteContext routeContext) {
115        ExpressionDefinition expr = getExpression();
116        if (expr != null) {
117            return expr.createExpression(routeContext);
118        }
119        return null;
120    }
121    
122    // Fluent API
123    // -------------------------------------------------------------------------
124    /**
125     * Sets the time period during which the maximum request count is valid for
126     *
127     * @param timePeriodMillis  period in millis
128     * @return the builder
129     */
130    public ThrottleDefinition timePeriodMillis(long timePeriodMillis) {
131        setTimePeriodMillis(timePeriodMillis);
132        return this;
133    }
134    
135    /**
136     * Sets the time period during which the maximum request count per period
137     *
138     * @param maximumRequestsPerPeriod  the maximum request count number per time period
139     * @return the builder
140     */
141    public ThrottleDefinition maximumRequestsPerPeriod(Long maximumRequestsPerPeriod) {
142        setExpression(ExpressionNodeHelper.toExpressionDefinition(ExpressionBuilder.constantExpression(maximumRequestsPerPeriod)));
143        return this;
144    }
145
146    /**
147     * Whether or not the caller should run the task when it was rejected by the thread pool.
148     * <p/>
149     * Is by default <tt>true</tt>
150     *
151     * @param callerRunsWhenRejected whether or not the caller should run
152     * @return the builder
153     */
154    public ThrottleDefinition callerRunsWhenRejected(boolean callerRunsWhenRejected) {
155        setCallerRunsWhenRejected(callerRunsWhenRejected);
156        return this;
157    }
158
159    /**
160     * Enables asynchronous delay which means the thread will <b>no</b> block while delaying.
161     *
162     * @return the builder
163     */
164    public ThrottleDefinition asyncDelayed() {
165        setAsyncDelayed(true);
166        return this;
167    }
168    
169    /**
170     * Whether or not throttler throws the ThrottlerRejectedExecutionException when the exchange exceeds the request limit
171     * <p/>
172     * Is by default <tt>false</tt>
173     *
174     * @param rejectExecution throw the RejectExecutionException if the exchange exceeds the request limit 
175     * @return the builder
176     */
177    public ThrottleDefinition rejectExecution(boolean rejectExecution) {
178        setRejectExecution(rejectExecution);
179        return this;
180    }
181
182    /**
183     * Sets the ExecutorService which could be used by throttle definition
184     *
185     * @param executorService  
186     * @return the builder
187     */
188    public ThrottleDefinition executorService(ExecutorService executorService) {
189        setExecutorService(executorService);
190        return this;
191    }
192
193    /**
194     * Sets the ExecutorService which could be used by throttle definition
195     *
196     * @param executorServiceRef the reference id of the Executor Service  
197     * @return the builder
198     */
199    public ThrottleDefinition executorServiceRef(String executorServiceRef) {
200        setExecutorServiceRef(executorServiceRef);
201        return this;
202    }
203    
204    
205
206    // Properties
207    // -------------------------------------------------------------------------
208
209    public Long getTimePeriodMillis() {
210        return timePeriodMillis;
211    }
212
213    public void setTimePeriodMillis(Long timePeriodMillis) {
214        this.timePeriodMillis = timePeriodMillis;
215    }
216
217    public Boolean getAsyncDelayed() {
218        return asyncDelayed;
219    }
220
221    public void setAsyncDelayed(Boolean asyncDelayed) {
222        this.asyncDelayed = asyncDelayed;
223    }
224
225    public boolean isAsyncDelayed() {
226        return asyncDelayed != null && asyncDelayed;
227    }
228
229    public Boolean getCallerRunsWhenRejected() {
230        return callerRunsWhenRejected;
231    }
232
233    public void setCallerRunsWhenRejected(Boolean callerRunsWhenRejected) {
234        this.callerRunsWhenRejected = callerRunsWhenRejected;
235    }
236
237    public ExecutorService getExecutorService() {
238        return executorService;
239    }
240
241    public void setExecutorService(ExecutorService executorService) {
242        this.executorService = executorService;
243    }
244
245    public String getExecutorServiceRef() {
246        return executorServiceRef;
247    }
248
249    public void setExecutorServiceRef(String executorServiceRef) {
250        this.executorServiceRef = executorServiceRef;
251    }
252    
253    public boolean isRejectExecution() {
254        return rejectExecution != null ? rejectExecution : false;
255    }
256
257    public void setRejectExecution(Boolean rejectExecution) {
258        this.rejectExecution = rejectExecution;
259    }
260}