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