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}