001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.scheduling.config; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.Date; 022import java.util.HashMap; 023import java.util.LinkedHashSet; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027import java.util.concurrent.Executors; 028import java.util.concurrent.ScheduledExecutorService; 029 030import org.springframework.beans.factory.DisposableBean; 031import org.springframework.beans.factory.InitializingBean; 032import org.springframework.scheduling.TaskScheduler; 033import org.springframework.scheduling.Trigger; 034import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; 035import org.springframework.scheduling.support.CronTrigger; 036import org.springframework.util.Assert; 037import org.springframework.util.CollectionUtils; 038 039/** 040 * Helper bean for registering tasks with a {@link TaskScheduler}, typically using cron 041 * expressions. 042 * 043 * <p>As of Spring 3.1, {@code ScheduledTaskRegistrar} has a more prominent user-facing 044 * role when used in conjunction with the @{@link 045 * org.springframework.scheduling.annotation.EnableAsync EnableAsync} annotation and its 046 * {@link org.springframework.scheduling.annotation.SchedulingConfigurer 047 * SchedulingConfigurer} callback interface. 048 * 049 * @author Juergen Hoeller 050 * @author Chris Beams 051 * @author Tobias Montagna-Hay 052 * @since 3.0 053 * @see org.springframework.scheduling.annotation.EnableAsync 054 * @see org.springframework.scheduling.annotation.SchedulingConfigurer 055 */ 056public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean { 057 058 private TaskScheduler taskScheduler; 059 060 private ScheduledExecutorService localExecutor; 061 062 private List<TriggerTask> triggerTasks; 063 064 private List<CronTask> cronTasks; 065 066 private List<IntervalTask> fixedRateTasks; 067 068 private List<IntervalTask> fixedDelayTasks; 069 070 private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap<Task, ScheduledTask>(16); 071 072 private final Set<ScheduledTask> scheduledTasks = new LinkedHashSet<ScheduledTask>(16); 073 074 075 /** 076 * Set the {@link TaskScheduler} to register scheduled tasks with. 077 */ 078 public void setTaskScheduler(TaskScheduler taskScheduler) { 079 Assert.notNull(taskScheduler, "TaskScheduler must not be null"); 080 this.taskScheduler = taskScheduler; 081 } 082 083 /** 084 * Set the {@link TaskScheduler} to register scheduled tasks with, or a 085 * {@link java.util.concurrent.ScheduledExecutorService} to be wrapped as a 086 * {@code TaskScheduler}. 087 */ 088 public void setScheduler(Object scheduler) { 089 Assert.notNull(scheduler, "Scheduler object must not be null"); 090 if (scheduler instanceof TaskScheduler) { 091 this.taskScheduler = (TaskScheduler) scheduler; 092 } 093 else if (scheduler instanceof ScheduledExecutorService) { 094 this.taskScheduler = new ConcurrentTaskScheduler(((ScheduledExecutorService) scheduler)); 095 } 096 else { 097 throw new IllegalArgumentException("Unsupported scheduler type: " + scheduler.getClass()); 098 } 099 } 100 101 /** 102 * Return the {@link TaskScheduler} instance for this registrar (may be {@code null}). 103 */ 104 public TaskScheduler getScheduler() { 105 return this.taskScheduler; 106 } 107 108 109 /** 110 * Specify triggered tasks as a Map of Runnables (the tasks) and Trigger objects 111 * (typically custom implementations of the {@link Trigger} interface). 112 */ 113 public void setTriggerTasks(Map<Runnable, Trigger> triggerTasks) { 114 this.triggerTasks = new ArrayList<TriggerTask>(); 115 for (Map.Entry<Runnable, Trigger> task : triggerTasks.entrySet()) { 116 addTriggerTask(new TriggerTask(task.getKey(), task.getValue())); 117 } 118 } 119 120 /** 121 * Specify triggered tasks as a list of {@link TriggerTask} objects. Primarily used 122 * by {@code <task:*>} namespace parsing. 123 * @since 3.2 124 * @see ScheduledTasksBeanDefinitionParser 125 */ 126 public void setTriggerTasksList(List<TriggerTask> triggerTasks) { 127 this.triggerTasks = triggerTasks; 128 } 129 130 /** 131 * Get the trigger tasks as an unmodifiable list of {@link TriggerTask} objects. 132 * @return the list of tasks (never {@code null}) 133 * @since 4.2 134 */ 135 public List<TriggerTask> getTriggerTaskList() { 136 return (this.triggerTasks != null? Collections.unmodifiableList(this.triggerTasks) : 137 Collections.<TriggerTask>emptyList()); 138 } 139 140 /** 141 * Specify triggered tasks as a Map of Runnables (the tasks) and cron expressions. 142 * @see CronTrigger 143 */ 144 public void setCronTasks(Map<Runnable, String> cronTasks) { 145 this.cronTasks = new ArrayList<CronTask>(); 146 for (Map.Entry<Runnable, String> task : cronTasks.entrySet()) { 147 addCronTask(task.getKey(), task.getValue()); 148 } 149 } 150 151 /** 152 * Specify triggered tasks as a list of {@link CronTask} objects. Primarily used by 153 * {@code <task:*>} namespace parsing. 154 * @since 3.2 155 * @see ScheduledTasksBeanDefinitionParser 156 */ 157 public void setCronTasksList(List<CronTask> cronTasks) { 158 this.cronTasks = cronTasks; 159 } 160 161 /** 162 * Get the cron tasks as an unmodifiable list of {@link CronTask} objects. 163 * @return the list of tasks (never {@code null}) 164 * @since 4.2 165 */ 166 public List<CronTask> getCronTaskList() { 167 return (this.cronTasks != null ? Collections.unmodifiableList(this.cronTasks) : 168 Collections.<CronTask>emptyList()); 169 } 170 171 /** 172 * Specify triggered tasks as a Map of Runnables (the tasks) and fixed-rate values. 173 * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) 174 */ 175 public void setFixedRateTasks(Map<Runnable, Long> fixedRateTasks) { 176 this.fixedRateTasks = new ArrayList<IntervalTask>(); 177 for (Map.Entry<Runnable, Long> task : fixedRateTasks.entrySet()) { 178 addFixedRateTask(task.getKey(), task.getValue()); 179 } 180 } 181 182 /** 183 * Specify fixed-rate tasks as a list of {@link IntervalTask} objects. Primarily used 184 * by {@code <task:*>} namespace parsing. 185 * @since 3.2 186 * @see ScheduledTasksBeanDefinitionParser 187 */ 188 public void setFixedRateTasksList(List<IntervalTask> fixedRateTasks) { 189 this.fixedRateTasks = fixedRateTasks; 190 } 191 192 /** 193 * Get the fixed-rate tasks as an unmodifiable list of {@link IntervalTask} objects. 194 * @return the list of tasks (never {@code null}) 195 * @since 4.2 196 */ 197 public List<IntervalTask> getFixedRateTaskList() { 198 return (this.fixedRateTasks != null ? Collections.unmodifiableList(this.fixedRateTasks) : 199 Collections.<IntervalTask>emptyList()); 200 } 201 202 /** 203 * Specify triggered tasks as a Map of Runnables (the tasks) and fixed-delay values. 204 * @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) 205 */ 206 public void setFixedDelayTasks(Map<Runnable, Long> fixedDelayTasks) { 207 this.fixedDelayTasks = new ArrayList<IntervalTask>(); 208 for (Map.Entry<Runnable, Long> task : fixedDelayTasks.entrySet()) { 209 addFixedDelayTask(task.getKey(), task.getValue()); 210 } 211 } 212 213 /** 214 * Specify fixed-delay tasks as a list of {@link IntervalTask} objects. Primarily used 215 * by {@code <task:*>} namespace parsing. 216 * @since 3.2 217 * @see ScheduledTasksBeanDefinitionParser 218 */ 219 public void setFixedDelayTasksList(List<IntervalTask> fixedDelayTasks) { 220 this.fixedDelayTasks = fixedDelayTasks; 221 } 222 223 /** 224 * Get the fixed-delay tasks as an unmodifiable list of {@link IntervalTask} objects. 225 * @return the list of tasks (never {@code null}) 226 * @since 4.2 227 */ 228 public List<IntervalTask> getFixedDelayTaskList() { 229 return (this.fixedDelayTasks != null ? Collections.unmodifiableList(this.fixedDelayTasks) : 230 Collections.<IntervalTask>emptyList()); 231 } 232 233 234 /** 235 * Add a Runnable task to be triggered per the given {@link Trigger}. 236 * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) 237 */ 238 public void addTriggerTask(Runnable task, Trigger trigger) { 239 addTriggerTask(new TriggerTask(task, trigger)); 240 } 241 242 /** 243 * Add a {@code TriggerTask}. 244 * @since 3.2 245 * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) 246 */ 247 public void addTriggerTask(TriggerTask task) { 248 if (this.triggerTasks == null) { 249 this.triggerTasks = new ArrayList<TriggerTask>(); 250 } 251 this.triggerTasks.add(task); 252 } 253 254 /** 255 * Add a Runnable task to be triggered per the given cron expression 256 */ 257 public void addCronTask(Runnable task, String expression) { 258 addCronTask(new CronTask(task, expression)); 259 } 260 261 /** 262 * Add a {@link CronTask}. 263 * @since 3.2 264 */ 265 public void addCronTask(CronTask task) { 266 if (this.cronTasks == null) { 267 this.cronTasks = new ArrayList<CronTask>(); 268 } 269 this.cronTasks.add(task); 270 } 271 272 /** 273 * Add a {@code Runnable} task to be triggered at the given fixed-rate interval. 274 * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) 275 */ 276 public void addFixedRateTask(Runnable task, long interval) { 277 addFixedRateTask(new IntervalTask(task, interval, 0)); 278 } 279 280 /** 281 * Add a fixed-rate {@link IntervalTask}. 282 * @since 3.2 283 * @see TaskScheduler#scheduleAtFixedRate(Runnable, long) 284 */ 285 public void addFixedRateTask(IntervalTask task) { 286 if (this.fixedRateTasks == null) { 287 this.fixedRateTasks = new ArrayList<IntervalTask>(); 288 } 289 this.fixedRateTasks.add(task); 290 } 291 292 /** 293 * Add a Runnable task to be triggered with the given fixed delay. 294 * @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) 295 */ 296 public void addFixedDelayTask(Runnable task, long delay) { 297 addFixedDelayTask(new IntervalTask(task, delay, 0)); 298 } 299 300 /** 301 * Add a fixed-delay {@link IntervalTask}. 302 * @since 3.2 303 * @see TaskScheduler#scheduleWithFixedDelay(Runnable, long) 304 */ 305 public void addFixedDelayTask(IntervalTask task) { 306 if (this.fixedDelayTasks == null) { 307 this.fixedDelayTasks = new ArrayList<IntervalTask>(); 308 } 309 this.fixedDelayTasks.add(task); 310 } 311 312 313 /** 314 * Return whether this {@code ScheduledTaskRegistrar} has any tasks registered. 315 * @since 3.2 316 */ 317 public boolean hasTasks() { 318 return (!CollectionUtils.isEmpty(this.triggerTasks) || 319 !CollectionUtils.isEmpty(this.cronTasks) || 320 !CollectionUtils.isEmpty(this.fixedRateTasks) || 321 !CollectionUtils.isEmpty(this.fixedDelayTasks)); 322 } 323 324 325 /** 326 * Calls {@link #scheduleTasks()} at bean construction time. 327 */ 328 @Override 329 public void afterPropertiesSet() { 330 scheduleTasks(); 331 } 332 333 /** 334 * Schedule all registered tasks against the underlying {@linkplain 335 * #setTaskScheduler(TaskScheduler) task scheduler}. 336 */ 337 protected void scheduleTasks() { 338 if (this.taskScheduler == null) { 339 this.localExecutor = Executors.newSingleThreadScheduledExecutor(); 340 this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor); 341 } 342 if (this.triggerTasks != null) { 343 for (TriggerTask task : this.triggerTasks) { 344 addScheduledTask(scheduleTriggerTask(task)); 345 } 346 } 347 if (this.cronTasks != null) { 348 for (CronTask task : this.cronTasks) { 349 addScheduledTask(scheduleCronTask(task)); 350 } 351 } 352 if (this.fixedRateTasks != null) { 353 for (IntervalTask task : this.fixedRateTasks) { 354 addScheduledTask(scheduleFixedRateTask(task)); 355 } 356 } 357 if (this.fixedDelayTasks != null) { 358 for (IntervalTask task : this.fixedDelayTasks) { 359 addScheduledTask(scheduleFixedDelayTask(task)); 360 } 361 } 362 } 363 364 private void addScheduledTask(ScheduledTask task) { 365 if (task != null) { 366 this.scheduledTasks.add(task); 367 } 368 } 369 370 371 /** 372 * Schedule the specified trigger task, either right away if possible 373 * or on initialization of the scheduler. 374 * @return a handle to the scheduled task, allowing to cancel it 375 * @since 4.3 376 */ 377 public ScheduledTask scheduleTriggerTask(TriggerTask task) { 378 ScheduledTask scheduledTask = this.unresolvedTasks.remove(task); 379 boolean newTask = false; 380 if (scheduledTask == null) { 381 scheduledTask = new ScheduledTask(); 382 newTask = true; 383 } 384 if (this.taskScheduler != null) { 385 scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger()); 386 } 387 else { 388 addTriggerTask(task); 389 this.unresolvedTasks.put(task, scheduledTask); 390 } 391 return (newTask ? scheduledTask : null); 392 } 393 394 /** 395 * Schedule the specified cron task, either right away if possible 396 * or on initialization of the scheduler. 397 * @return a handle to the scheduled task, allowing to cancel it 398 * (or {@code null} if processing a previously registered task) 399 * @since 4.3 400 */ 401 public ScheduledTask scheduleCronTask(CronTask task) { 402 ScheduledTask scheduledTask = this.unresolvedTasks.remove(task); 403 boolean newTask = false; 404 if (scheduledTask == null) { 405 scheduledTask = new ScheduledTask(); 406 newTask = true; 407 } 408 if (this.taskScheduler != null) { 409 scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger()); 410 } 411 else { 412 addCronTask(task); 413 this.unresolvedTasks.put(task, scheduledTask); 414 } 415 return (newTask ? scheduledTask : null); 416 } 417 418 /** 419 * Schedule the specified fixed-rate task, either right away if possible 420 * or on initialization of the scheduler. 421 * @return a handle to the scheduled task, allowing to cancel it 422 * (or {@code null} if processing a previously registered task) 423 * @since 4.3 424 */ 425 public ScheduledTask scheduleFixedRateTask(IntervalTask task) { 426 ScheduledTask scheduledTask = this.unresolvedTasks.remove(task); 427 boolean newTask = false; 428 if (scheduledTask == null) { 429 scheduledTask = new ScheduledTask(); 430 newTask = true; 431 } 432 if (this.taskScheduler != null) { 433 if (task.getInitialDelay() > 0) { 434 Date startTime = new Date(System.currentTimeMillis() + task.getInitialDelay()); 435 scheduledTask.future = 436 this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), startTime, task.getInterval()); 437 } 438 else { 439 scheduledTask.future = 440 this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), task.getInterval()); 441 } 442 } 443 else { 444 addFixedRateTask(task); 445 this.unresolvedTasks.put(task, scheduledTask); 446 } 447 return (newTask ? scheduledTask : null); 448 } 449 450 /** 451 * Schedule the specified fixed-delay task, either right away if possible 452 * or on initialization of the scheduler. 453 * @return a handle to the scheduled task, allowing to cancel it 454 * (or {@code null} if processing a previously registered task) 455 * @since 4.3 456 */ 457 public ScheduledTask scheduleFixedDelayTask(IntervalTask task) { 458 ScheduledTask scheduledTask = this.unresolvedTasks.remove(task); 459 boolean newTask = false; 460 if (scheduledTask == null) { 461 scheduledTask = new ScheduledTask(); 462 newTask = true; 463 } 464 if (this.taskScheduler != null) { 465 if (task.getInitialDelay() > 0) { 466 Date startTime = new Date(System.currentTimeMillis() + task.getInitialDelay()); 467 scheduledTask.future = 468 this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), startTime, task.getInterval()); 469 } 470 else { 471 scheduledTask.future = 472 this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), task.getInterval()); 473 } 474 } 475 else { 476 addFixedDelayTask(task); 477 this.unresolvedTasks.put(task, scheduledTask); 478 } 479 return (newTask ? scheduledTask : null); 480 } 481 482 483 @Override 484 public void destroy() { 485 for (ScheduledTask task : this.scheduledTasks) { 486 task.cancel(); 487 } 488 if (this.localExecutor != null) { 489 this.localExecutor.shutdownNow(); 490 } 491 } 492 493}