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.impl; 018 019import java.util.HashMap; 020import java.util.Map; 021import java.util.concurrent.ScheduledExecutorService; 022import java.util.concurrent.TimeUnit; 023 024import org.apache.camel.CamelContext; 025import org.apache.camel.Component; 026import org.apache.camel.LoggingLevel; 027import org.apache.camel.PollingConsumer; 028import org.apache.camel.ResolveEndpointFailedException; 029import org.apache.camel.spi.PollingConsumerPollStrategy; 030import org.apache.camel.spi.ScheduledPollConsumerScheduler; 031import org.apache.camel.spi.UriParam; 032import org.apache.camel.util.CamelContextHelper; 033import org.apache.camel.util.EndpointHelper; 034import org.apache.camel.util.IntrospectionSupport; 035 036/** 037 * A base class for {@link org.apache.camel.Endpoint} which creates a {@link ScheduledPollConsumer} 038 * 039 * @version 040 */ 041public abstract class ScheduledPollEndpoint extends DefaultEndpoint { 042 043 private static final String SPRING_SCHEDULER = "org.apache.camel.spring.pollingconsumer.SpringScheduledPollConsumerScheduler"; 044 private static final String QUARTZ_2_SCHEDULER = "org.apache.camel.pollconsumer.quartz2.QuartzScheduledPollConsumerScheduler"; 045 046 // if adding more options then align with org.apache.camel.impl.ScheduledPollConsumer 047 @UriParam(optionalPrefix = "consumer.", defaultValue = "true", label = "consumer,scheduler", 048 description = "Whether the scheduler should be auto started.") 049 private boolean startScheduler = true; 050 @UriParam(optionalPrefix = "consumer.", defaultValue = "1000", label = "consumer,scheduler", 051 description = "Milliseconds before the first poll starts." 052 + " You can also specify time values using units, such as 60s (60 seconds), 5m30s (5 minutes and 30 seconds), and 1h (1 hour).") 053 private long initialDelay = 1000; 054 @UriParam(optionalPrefix = "consumer.", defaultValue = "500", label = "consumer,scheduler", 055 description = "Milliseconds before the next poll." 056 + " You can also specify time values using units, such as 60s (60 seconds), 5m30s (5 minutes and 30 seconds), and 1h (1 hour).") 057 private long delay = 500; 058 @UriParam(optionalPrefix = "consumer.", defaultValue = "MILLISECONDS", label = "consumer,scheduler", 059 description = "Time unit for initialDelay and delay options.") 060 private TimeUnit timeUnit = TimeUnit.MILLISECONDS; 061 @UriParam(optionalPrefix = "consumer.", defaultValue = "true", label = "consumer,scheduler", 062 description = "Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in JDK for details.") 063 private boolean useFixedDelay = true; 064 @UriParam(optionalPrefix = "consumer.", label = "consumer,advanced", 065 description = "A pluggable org.apache.camel.PollingConsumerPollingStrategy allowing you to provide your custom implementation" 066 + " to control error handling usually occurred during the poll operation before an Exchange have been created and being routed in Camel.") 067 private PollingConsumerPollStrategy pollStrategy = new DefaultPollingConsumerPollStrategy(); 068 @UriParam(optionalPrefix = "consumer.", defaultValue = "TRACE", label = "consumer,scheduler", 069 description = "The consumer logs a start/complete log line when it polls. This option allows you to configure the logging level for that.") 070 private LoggingLevel runLoggingLevel = LoggingLevel.TRACE; 071 @UriParam(optionalPrefix = "consumer.", label = "consumer", 072 description = "If the polling consumer did not poll any files, you can enable this option to send an empty message (no body) instead.") 073 private boolean sendEmptyMessageWhenIdle; 074 @UriParam(optionalPrefix = "consumer.", label = "consumer,scheduler", 075 description = "If greedy is enabled, then the ScheduledPollConsumer will run immediately again, if the previous run polled 1 or more messages.") 076 private boolean greedy; 077 @UriParam(optionalPrefix = "consumer.", enums = "none,spring,quartz2", 078 defaultValue = "none", label = "consumer,scheduler", description = "To use a cron scheduler from either camel-spring or camel-quartz2 component") 079 private ScheduledPollConsumerScheduler scheduler; 080 private String schedulerName = "none"; // used when configuring scheduler using a string value 081 @UriParam(prefix = "scheduler.", multiValue = true, label = "consumer,scheduler", 082 description = "To configure additional properties when using a custom scheduler or any of the Quartz2, Spring based scheduler.") 083 private Map<String, Object> schedulerProperties; 084 @UriParam(optionalPrefix = "consumer.", label = "consumer,scheduler", 085 description = "Allows for configuring a custom/shared thread pool to use for the consumer. By default each consumer has its own single threaded thread pool.") 086 private ScheduledExecutorService scheduledExecutorService; 087 @UriParam(optionalPrefix = "consumer.", label = "consumer,scheduler", 088 description = "To let the scheduled polling consumer backoff if there has been a number of subsequent idles/errors in a row." 089 + " The multiplier is then the number of polls that will be skipped before the next actual attempt is happening again." 090 + " When this option is in use then backoffIdleThreshold and/or backoffErrorThreshold must also be configured.") 091 private int backoffMultiplier; 092 @UriParam(optionalPrefix = "consumer.", label = "consumer,scheduler", 093 description = "The number of subsequent idle polls that should happen before the backoffMultipler should kick-in.") 094 private int backoffIdleThreshold; 095 @UriParam(optionalPrefix = "consumer.", label = "consumer,scheduler", 096 description = "The number of subsequent error polls (failed due some error) that should happen before the backoffMultipler should kick-in.") 097 private int backoffErrorThreshold; 098 099 protected ScheduledPollEndpoint(String endpointUri, Component component) { 100 super(endpointUri, component); 101 } 102 103 @Deprecated 104 protected ScheduledPollEndpoint(String endpointUri, CamelContext context) { 105 super(endpointUri, context); 106 } 107 108 @Deprecated 109 protected ScheduledPollEndpoint(String endpointUri) { 110 super(endpointUri); 111 } 112 113 protected ScheduledPollEndpoint() { 114 } 115 116 public void configureProperties(Map<String, Object> options) { 117 super.configureProperties(options); 118 configureScheduledPollConsumerProperties(options, getConsumerProperties()); 119 } 120 121 protected void configureScheduledPollConsumerProperties(Map<String, Object> options, Map<String, Object> consumerProperties) { 122 // special for scheduled poll consumers as we want to allow end users to configure its options 123 // from the URI parameters without the consumer. prefix 124 Map<String, Object> schedulerProperties = IntrospectionSupport.extractProperties(options, "scheduler."); 125 if (schedulerProperties != null && !schedulerProperties.isEmpty()) { 126 setSchedulerProperties(schedulerProperties); 127 } 128 129 if (scheduler == null && schedulerName != null) { 130 if ("none".equals(schedulerName)) { 131 // no cron scheduler in use 132 scheduler = null; 133 } else if ("spring".equals(schedulerName)) { 134 // special for scheduler if its "spring" or "quartz2" 135 try { 136 Class<? extends ScheduledPollConsumerScheduler> clazz = getCamelContext().getClassResolver().resolveMandatoryClass(SPRING_SCHEDULER, ScheduledPollConsumerScheduler.class); 137 setScheduler(getCamelContext().getInjector().newInstance(clazz)); 138 } catch (ClassNotFoundException e) { 139 throw new IllegalArgumentException("Cannot load " + SPRING_SCHEDULER + " from classpath. Make sure camel-spring.jar is on the classpath.", e); 140 } 141 } else if ("quartz2".equals(schedulerName)) { 142 // special for scheduler if its "spring" or "quartz2" 143 try { 144 Class<? extends ScheduledPollConsumerScheduler> clazz = getCamelContext().getClassResolver().resolveMandatoryClass(QUARTZ_2_SCHEDULER, ScheduledPollConsumerScheduler.class); 145 setScheduler(getCamelContext().getInjector().newInstance(clazz)); 146 } catch (ClassNotFoundException e) { 147 throw new IllegalArgumentException("Cannot load " + QUARTZ_2_SCHEDULER + " from classpath. Make sure camel-quartz2.jar is on the classpath.", e); 148 } 149 } else { 150 // must refer to a custom scheduler by the given name 151 setScheduler(CamelContextHelper.mandatoryLookup(getCamelContext(), schedulerName, ScheduledPollConsumerScheduler.class)); 152 } 153 } 154 } 155 156 @Override 157 protected void configurePollingConsumer(PollingConsumer consumer) throws Exception { 158 Map<String, Object> copy = new HashMap<String, Object>(getConsumerProperties()); 159 Map<String, Object> throwaway = new HashMap<String, Object>(); 160 161 // filter out unwanted options which is intended for the scheduled poll consumer 162 // as these options are not supported on the polling consumer 163 configureScheduledPollConsumerProperties(copy, throwaway); 164 165 // set reference properties first as they use # syntax that fools the regular properties setter 166 EndpointHelper.setReferenceProperties(getCamelContext(), consumer, copy); 167 EndpointHelper.setProperties(getCamelContext(), consumer, copy); 168 169 if (!isLenientProperties() && copy.size() > 0) { 170 throw new ResolveEndpointFailedException(this.getEndpointUri(), "There are " + copy.size() 171 + " parameters that couldn't be set on the endpoint polling consumer." 172 + " Check the uri if the parameters are spelt correctly and that they are properties of the endpoint." 173 + " Unknown consumer parameters=[" + copy + "]"); 174 } 175 } 176 177 protected void initConsumerProperties() { 178 // must setup consumer properties before we are ready to start 179 Map<String, Object> options = getConsumerProperties(); 180 if (!options.containsKey("startScheduler")) { 181 options.put("startScheduler", isStartScheduler()); 182 } 183 if (!options.containsKey("initialDelay")) { 184 options.put("initialDelay", getInitialDelay()); 185 } 186 if (!options.containsKey("delay")) { 187 options.put("delay", getDelay()); 188 } 189 if (!options.containsKey("timeUnit")) { 190 options.put("timeUnit", getTimeUnit()); 191 } 192 if (!options.containsKey("useFixedDelay")) { 193 options.put("useFixedDelay", isUseFixedDelay()); 194 } 195 if (!options.containsKey("pollStrategy")) { 196 options.put("pollStrategy", getPollStrategy()); 197 } 198 if (!options.containsKey("runLoggingLevel")) { 199 options.put("runLoggingLevel", getRunLoggingLevel()); 200 } 201 if (!options.containsKey("sendEmptyMessageWhenIdle")) { 202 options.put("sendEmptyMessageWhenIdle", isSendEmptyMessageWhenIdle()); 203 } 204 if (!options.containsKey("greedy")) { 205 options.put("greedy", isGreedy()); 206 } 207 if (!options.containsKey("scheduler")) { 208 options.put("scheduler", getScheduler()); 209 } 210 if (!options.containsKey("schedulerProperties")) { 211 options.put("schedulerProperties", getSchedulerProperties()); 212 } 213 if (!options.containsKey("scheduledExecutorService")) { 214 options.put("scheduledExecutorService", getScheduledExecutorService()); 215 } 216 if (!options.containsKey("backoffMultiplier")) { 217 options.put("backoffMultiplier", getBackoffMultiplier()); 218 } 219 if (!options.containsKey("backoffIdleThreshold")) { 220 options.put("backoffIdleThreshold", getBackoffIdleThreshold()); 221 } 222 if (!options.containsKey("backoffErrorThreshold")) { 223 options.put("backoffErrorThreshold", getBackoffErrorThreshold()); 224 } 225 } 226 227 @Override 228 protected void doStart() throws Exception { 229 initConsumerProperties(); 230 super.doStart(); 231 } 232 233 @Override 234 protected void doStop() throws Exception { 235 super.doStop(); 236 // noop 237 } 238 239 public boolean isStartScheduler() { 240 return startScheduler; 241 } 242 243 /** 244 * Whether the scheduler should be auto started. 245 */ 246 public void setStartScheduler(boolean startScheduler) { 247 this.startScheduler = startScheduler; 248 } 249 250 public long getInitialDelay() { 251 return initialDelay; 252 } 253 254 /** 255 * Milliseconds before the first poll starts. 256 * <p/> 257 * The default value is 1000. 258 * You can also specify time values using units, such as 60s (60 seconds), 5m30s (5 minutes and 30 seconds), and 1h (1 hour). 259 * @see <a href="http://camel.apache.org/how-do-i-specify-time-period-in-a-human-friendly-syntax.html">human friendly syntax</a> 260 */ 261 public void setInitialDelay(long initialDelay) { 262 this.initialDelay = initialDelay; 263 } 264 265 public long getDelay() { 266 return delay; 267 } 268 269 /** 270 * Milliseconds before the next poll. 271 * <p/> 272 * The default value is 500. 273 * You can also specify time values using units, such as 60s (60 seconds), 5m30s (5 minutes and 30 seconds), and 1h (1 hour). 274 * @see <a href="http://camel.apache.org/how-do-i-specify-time-period-in-a-human-friendly-syntax.html">human friendly syntax</a> 275 */ 276 public void setDelay(long delay) { 277 this.delay = delay; 278 } 279 280 public TimeUnit getTimeUnit() { 281 return timeUnit; 282 } 283 284 /** 285 * Time unit for initialDelay and delay options. 286 */ 287 public void setTimeUnit(TimeUnit timeUnit) { 288 this.timeUnit = timeUnit; 289 } 290 291 public boolean isUseFixedDelay() { 292 return useFixedDelay; 293 } 294 295 /** 296 * Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in JDK for details. 297 */ 298 public void setUseFixedDelay(boolean useFixedDelay) { 299 this.useFixedDelay = useFixedDelay; 300 } 301 302 public PollingConsumerPollStrategy getPollStrategy() { 303 return pollStrategy; 304 } 305 306 /** 307 * A pluggable org.apache.camel.PollingConsumerPollingStrategy allowing you to provide your custom implementation 308 * to control error handling usually occurred during the poll operation before an Exchange have been created 309 * and being routed in Camel. In other words the error occurred while the polling was gathering information, 310 * for instance access to a file network failed so Camel cannot access it to scan for files. 311 * The default implementation will log the caused exception at WARN level and ignore it. 312 */ 313 public void setPollStrategy(PollingConsumerPollStrategy pollStrategy) { 314 this.pollStrategy = pollStrategy; 315 // we are allowed to change poll strategy 316 } 317 318 public LoggingLevel getRunLoggingLevel() { 319 return runLoggingLevel; 320 } 321 322 /** 323 * The consumer logs a start/complete log line when it polls. This option allows you to configure the logging level for that. 324 */ 325 public void setRunLoggingLevel(LoggingLevel runLoggingLevel) { 326 this.runLoggingLevel = runLoggingLevel; 327 } 328 329 public boolean isSendEmptyMessageWhenIdle() { 330 return sendEmptyMessageWhenIdle; 331 } 332 333 /** 334 * If the polling consumer did not poll any files, you can enable this option to send an empty message (no body) instead. 335 */ 336 public void setSendEmptyMessageWhenIdle(boolean sendEmptyMessageWhenIdle) { 337 this.sendEmptyMessageWhenIdle = sendEmptyMessageWhenIdle; 338 } 339 340 public boolean isGreedy() { 341 return greedy; 342 } 343 344 /** 345 * If greedy is enabled, then the ScheduledPollConsumer will run immediately again, if the previous run polled 1 or more messages. 346 */ 347 public void setGreedy(boolean greedy) { 348 this.greedy = greedy; 349 } 350 351 public ScheduledPollConsumerScheduler getScheduler() { 352 return scheduler; 353 } 354 355 /** 356 * Allow to plugin a custom org.apache.camel.spi.ScheduledPollConsumerScheduler to use as the scheduler for 357 * firing when the polling consumer runs. The default implementation uses the ScheduledExecutorService and 358 * there is a Quartz2, and Spring based which supports CRON expressions. 359 * 360 * Notice: If using a custom scheduler then the options for initialDelay, useFixedDelay, timeUnit, 361 * and scheduledExecutorService may not be in use. Use the text quartz2 to refer to use the Quartz2 scheduler; 362 * and use the text spring to use the Spring based; and use the text #myScheduler to refer to a custom scheduler 363 * by its id in the Registry. See Quartz2 page for an example. 364 */ 365 public void setScheduler(ScheduledPollConsumerScheduler scheduler) { 366 this.scheduler = scheduler; 367 } 368 369 /** 370 * Allow to plugin a custom org.apache.camel.spi.ScheduledPollConsumerScheduler to use as the scheduler for 371 * firing when the polling consumer runs. This option is used for referring to one of the built-in schedulers 372 * either <tt>spring</tt>, or <tt>quartz2</tt>. Using <tt>none</tt> refers to no scheduler to be used. 373 */ 374 public void setScheduler(String schedulerName) { 375 this.schedulerName = schedulerName; 376 } 377 378 public Map<String, Object> getSchedulerProperties() { 379 return schedulerProperties; 380 } 381 382 /** 383 * To configure additional properties when using a custom scheduler or any of the Quartz2, Spring based scheduler. 384 */ 385 public void setSchedulerProperties(Map<String, Object> schedulerProperties) { 386 this.schedulerProperties = schedulerProperties; 387 } 388 389 public ScheduledExecutorService getScheduledExecutorService() { 390 return scheduledExecutorService; 391 } 392 393 /** 394 * Allows for configuring a custom/shared thread pool to use for the consumer. 395 * By default each consumer has its own single threaded thread pool. 396 * This option allows you to share a thread pool among multiple consumers. 397 */ 398 public void setScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) { 399 this.scheduledExecutorService = scheduledExecutorService; 400 } 401 402 public int getBackoffMultiplier() { 403 return backoffMultiplier; 404 } 405 406 /** 407 * To let the scheduled polling consumer backoff if there has been a number of subsequent idles/errors in a row. 408 * The multiplier is then the number of polls that will be skipped before the next actual attempt is happening again. 409 * When this option is in use then backoffIdleThreshold and/or backoffErrorThreshold must also be configured. 410 */ 411 public void setBackoffMultiplier(int backoffMultiplier) { 412 this.backoffMultiplier = backoffMultiplier; 413 } 414 415 public int getBackoffIdleThreshold() { 416 return backoffIdleThreshold; 417 } 418 419 /** 420 * The number of subsequent idle polls that should happen before the backoffMultipler should kick-in. 421 */ 422 public void setBackoffIdleThreshold(int backoffIdleThreshold) { 423 this.backoffIdleThreshold = backoffIdleThreshold; 424 } 425 426 public int getBackoffErrorThreshold() { 427 return backoffErrorThreshold; 428 } 429 430 /** 431 * The number of subsequent error polls (failed due some error) that should happen before the backoffMultipler should kick-in. 432 */ 433 public void setBackoffErrorThreshold(int backoffErrorThreshold) { 434 this.backoffErrorThreshold = backoffErrorThreshold; 435 } 436 437}