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}