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.commonj; 018 019import java.util.Date; 020import java.util.concurrent.Delayed; 021import java.util.concurrent.FutureTask; 022import java.util.concurrent.ScheduledFuture; 023import java.util.concurrent.TimeUnit; 024 025import commonj.timers.Timer; 026import commonj.timers.TimerListener; 027 028import org.springframework.scheduling.TaskScheduler; 029import org.springframework.scheduling.Trigger; 030import org.springframework.scheduling.support.SimpleTriggerContext; 031import org.springframework.scheduling.support.TaskUtils; 032import org.springframework.util.ErrorHandler; 033 034/** 035 * Implementation of Spring's {@link TaskScheduler} interface, wrapping 036 * a CommonJ {@link commonj.timers.TimerManager}. 037 * 038 * @author Juergen Hoeller 039 * @author Mark Fisher 040 * @since 3.0 041 */ 042public class TimerManagerTaskScheduler extends TimerManagerAccessor implements TaskScheduler { 043 044 private volatile ErrorHandler errorHandler; 045 046 047 /** 048 * Provide an {@link ErrorHandler} strategy. 049 */ 050 public void setErrorHandler(ErrorHandler errorHandler) { 051 this.errorHandler = errorHandler; 052 } 053 054 055 @Override 056 public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) { 057 return new ReschedulingTimerListener(errorHandlingTask(task, true), trigger).schedule(); 058 } 059 060 @Override 061 public ScheduledFuture<?> schedule(Runnable task, Date startTime) { 062 TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, false)); 063 Timer timer = getTimerManager().schedule(futureTask, startTime); 064 futureTask.setTimer(timer); 065 return futureTask; 066 } 067 068 @Override 069 public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) { 070 TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true)); 071 Timer timer = getTimerManager().scheduleAtFixedRate(futureTask, startTime, period); 072 futureTask.setTimer(timer); 073 return futureTask; 074 } 075 076 @Override 077 public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) { 078 TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true)); 079 Timer timer = getTimerManager().scheduleAtFixedRate(futureTask, 0, period); 080 futureTask.setTimer(timer); 081 return futureTask; 082 } 083 084 @Override 085 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { 086 TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true)); 087 Timer timer = getTimerManager().schedule(futureTask, startTime, delay); 088 futureTask.setTimer(timer); 089 return futureTask; 090 } 091 092 @Override 093 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) { 094 TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true)); 095 Timer timer = getTimerManager().schedule(futureTask, 0, delay); 096 futureTask.setTimer(timer); 097 return futureTask; 098 } 099 100 private Runnable errorHandlingTask(Runnable delegate, boolean isRepeatingTask) { 101 return TaskUtils.decorateTaskWithErrorHandler(delegate, this.errorHandler, isRepeatingTask); 102 } 103 104 105 /** 106 * ScheduledFuture adapter that wraps a CommonJ Timer. 107 */ 108 private static class TimerScheduledFuture extends FutureTask<Object> implements TimerListener, ScheduledFuture<Object> { 109 110 protected transient Timer timer; 111 112 protected transient boolean cancelled = false; 113 114 public TimerScheduledFuture(Runnable runnable) { 115 super(runnable, null); 116 } 117 118 public void setTimer(Timer timer) { 119 this.timer = timer; 120 } 121 122 @Override 123 public void timerExpired(Timer timer) { 124 runAndReset(); 125 } 126 127 @Override 128 public boolean cancel(boolean mayInterruptIfRunning) { 129 boolean result = super.cancel(mayInterruptIfRunning); 130 this.timer.cancel(); 131 this.cancelled = true; 132 return result; 133 } 134 135 @Override 136 public long getDelay(TimeUnit unit) { 137 return unit.convert(this.timer.getScheduledExecutionTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); 138 } 139 140 @Override 141 public int compareTo(Delayed other) { 142 if (this == other) { 143 return 0; 144 } 145 long diff = getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS); 146 return (diff == 0 ? 0 : ((diff < 0)? -1 : 1)); 147 } 148 } 149 150 151 /** 152 * ScheduledFuture adapter for trigger-based rescheduling. 153 */ 154 private class ReschedulingTimerListener extends TimerScheduledFuture { 155 156 private final Trigger trigger; 157 158 private final SimpleTriggerContext triggerContext = new SimpleTriggerContext(); 159 160 private volatile Date scheduledExecutionTime; 161 162 public ReschedulingTimerListener(Runnable runnable, Trigger trigger) { 163 super(runnable); 164 this.trigger = trigger; 165 } 166 167 public ScheduledFuture<?> schedule() { 168 this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext); 169 if (this.scheduledExecutionTime == null) { 170 return null; 171 } 172 setTimer(getTimerManager().schedule(this, this.scheduledExecutionTime)); 173 return this; 174 } 175 176 @Override 177 public void timerExpired(Timer timer) { 178 Date actualExecutionTime = new Date(); 179 super.timerExpired(timer); 180 Date completionTime = new Date(); 181 this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime); 182 if (!this.cancelled) { 183 schedule(); 184 } 185 } 186 } 187 188}