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