blob: 1bdd6c58c1574e02b632d950fac1f61e095105a7 [file] [log] [blame]
Jian Li1b4cb332016-03-02 16:32:51 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Jian Li1b4cb332016-03-02 16:32:51 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onlab.util;
18
19import org.slf4j.Logger;
20
21import java.util.Collection;
22import java.util.List;
23import java.util.concurrent.Callable;
24import java.util.concurrent.ExecutionException;
25import java.util.concurrent.Future;
26import java.util.concurrent.ScheduledExecutorService;
27import java.util.concurrent.ScheduledFuture;
28import java.util.concurrent.TimeUnit;
29import java.util.concurrent.TimeoutException;
30
31import static org.slf4j.LoggerFactory.getLogger;
32
33/**
34 * A new scheduled executor service that does not eat exception.
35 */
Jian Lia1d7f272016-03-28 17:21:47 -070036public class SharedScheduledExecutorService implements ScheduledExecutorService {
Jian Li1b4cb332016-03-02 16:32:51 -080037
38 private static final String NOT_ALLOWED = "Shutdown of scheduled executor is not allowed";
39 private final Logger log = getLogger(getClass());
40
41 private ScheduledExecutorService executor;
42
43 /**
44 * Creates a wrapper for the given scheduled executor service.
45 *
46 * @param executor executor service to wrap
47 */
Jian Li66f15262016-03-03 11:18:40 -080048 SharedScheduledExecutorService(ScheduledExecutorService executor) {
Jian Li1b4cb332016-03-02 16:32:51 -080049 this.executor = executor;
50 }
51
52 /**
53 * Returns the backing scheduled executor service.
54 *
55 * @return backing executor service
56 */
57 ScheduledExecutorService backingExecutor() {
58 return executor;
59 }
60
61 /**
62 * Swaps the backing executor with a new one and shuts down the old one.
63 *
Jian Lia1d7f272016-03-28 17:21:47 -070064 * @param executorService new scheduled executor service
Jian Li1b4cb332016-03-02 16:32:51 -080065 */
Jian Lia1d7f272016-03-28 17:21:47 -070066 void setBackingExecutor(ScheduledExecutorService executorService) {
Jian Li1b4cb332016-03-02 16:32:51 -080067 ScheduledExecutorService oldExecutor = this.executor;
Jian Lia1d7f272016-03-28 17:21:47 -070068 this.executor = executorService;
Jian Li1b4cb332016-03-02 16:32:51 -080069 oldExecutor.shutdown();
70 }
71
Jian Lia1d7f272016-03-28 17:21:47 -070072 /**
73 * Creates and executes a one-shot action that becomes enabled
74 * after the given delay.
75 *
76 * @param command the task to execute
77 * @param delay the time from now to delay execution
78 * @param unit the time unit of the delay parameter
79 * @param repeatFlag the flag to denote whether to restart a failed task
80 * @return a ScheduledFuture representing pending completion of
81 * the task and whose {@code get()} method will return
82 * {@code null} upon completion
83 */
84 public ScheduledFuture<?> schedule(Runnable command, long delay,
85 TimeUnit unit, boolean repeatFlag) {
86 return executor.schedule(wrap(command, repeatFlag), delay, unit);
87 }
88
Jian Li1b4cb332016-03-02 16:32:51 -080089 @Override
90 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
Jian Lia1d7f272016-03-28 17:21:47 -070091 return schedule(command, delay, unit, false);
Jian Li1b4cb332016-03-02 16:32:51 -080092 }
93
94 @Override
95 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
96 return executor.schedule(() -> {
97 V v = null;
98 try {
99 v = callable.call();
100 } catch (Exception e) {
101 log.error("Uncaught exception on " + callable.getClass(), e);
102 }
103 return v;
104 }, delay, unit);
105 }
106
Jian Lia1d7f272016-03-28 17:21:47 -0700107 /**
108 * Creates and executes a periodic action that becomes enabled first
109 * after the given initial delay, and subsequently with the given
110 * period; that is executions will commence after
111 * {@code initialDelay} then {@code initialDelay+period}, then
112 * {@code initialDelay + 2 * period}, and so on.
113 * Depends on the repeat flag that the user set, the failed tasks can be
114 * either restarted or terminated. If the repeat flag is set to to true,
115 * ant execution of the task encounters an exception, subsequent executions
116 * are permitted, otherwise, subsequent executions are suppressed.
117 * If any execution of this task takes longer than its period, then
118 * subsequent executions may start late, but will not concurrently execute.
119 *
120 * @param command the task to execute
121 * @param initialDelay the time to delay first execution
122 * @param period the period between successive executions
123 * @param unit the time unit of the initialDelay and period parameters
124 * @param repeatFlag the flag to denote whether to restart a failed task
125 * @return a ScheduledFuture representing pending completion of
126 * the task, and whose {@code get()} method will throw an
127 * exception upon cancellation
128 */
129 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
130 long initialDelay,
131 long period,
132 TimeUnit unit,
133 boolean repeatFlag) {
134 return executor.scheduleAtFixedRate(wrap(command, repeatFlag),
135 initialDelay, period, unit);
136 }
137
Jian Li1b4cb332016-03-02 16:32:51 -0800138 @Override
139 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
140 long period, TimeUnit unit) {
Jian Lia1d7f272016-03-28 17:21:47 -0700141 return scheduleAtFixedRate(command, initialDelay, period, unit, false);
142 }
143
144 /**
145 * Creates and executes a periodic action that becomes enabled first
146 * after the given initial delay, and subsequently with the
147 * given delay between the termination of one execution and the
148 * commencement of the next.
149 * Depends on the repeat flag that the user set, the failed tasks can be
150 * either restarted or terminated. If the repeat flag is set to to true,
151 * ant execution of the task encounters an exception, subsequent executions
152 * are permitted, otherwise, subsequent executions are suppressed.
153 *
154 * @param command the task to execute
155 * @param initialDelay the time to delay first execution
156 * @param delay the delay between the termination of one
157 * execution and the commencement of the next
158 * @param unit the time unit of the initialDelay and delay parameters
159 * @param repeatFlag the flag to denote whether to restart a failed task
160 * @return a ScheduledFuture representing pending completion of
161 * the task, and whose {@code get()} method will throw an
162 * exception upon cancellation
163 */
164 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
165 long initialDelay,
166 long delay,
167 TimeUnit unit,
168 boolean repeatFlag) {
169 return executor.scheduleWithFixedDelay(wrap(command, repeatFlag),
170 initialDelay, delay, unit);
Jian Li1b4cb332016-03-02 16:32:51 -0800171 }
172
173 @Override
174 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
175 long delay, TimeUnit unit) {
Jian Lia1d7f272016-03-28 17:21:47 -0700176 return scheduleWithFixedDelay(command, initialDelay, delay, unit, false);
Jian Li1b4cb332016-03-02 16:32:51 -0800177 }
178
179 @Override
180 public void shutdown() {
181 throw new UnsupportedOperationException(NOT_ALLOWED);
182 }
183
184 @Override
185 public List<Runnable> shutdownNow() {
186 throw new UnsupportedOperationException(NOT_ALLOWED);
187 }
188
189 @Override
190 public boolean isShutdown() {
191 return executor.isShutdown();
192 }
193
194 @Override
195 public boolean isTerminated() {
196 return executor.isTerminated();
197 }
198
199 @Override
200 public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
201 return executor.awaitTermination(timeout, unit);
202 }
203
204 @Override
205 public <T> Future<T> submit(Callable<T> task) {
206 return executor.submit(task);
207 }
208
209 @Override
210 public <T> Future<T> submit(Runnable task, T result) {
211 return executor.submit(task, result);
212 }
213
214 @Override
215 public Future<?> submit(Runnable task) {
216 return executor.submit(task);
217 }
218
219 @Override
220 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
221 throws InterruptedException {
222 return executor.invokeAll(tasks);
223 }
224
225 @Override
226 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
227 long timeout, TimeUnit unit)
228 throws InterruptedException {
229 return executor.invokeAll(tasks, timeout, unit);
230 }
231
232 @Override
233 public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
234 throws InterruptedException, ExecutionException {
235 return executor.invokeAny(tasks);
236 }
237
238 @Override
239 public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
240 long timeout, TimeUnit unit) throws InterruptedException,
241 ExecutionException, TimeoutException {
242 return executor.invokeAny(tasks, timeout, unit);
243 }
244
245 @Override
246 public void execute(Runnable command) {
247 executor.execute(command);
248 }
249
Jian Lia1d7f272016-03-28 17:21:47 -0700250 private Runnable wrap(Runnable command, boolean repeatFlag) {
251 return new LoggableRunnable(command, repeatFlag);
Jian Li1b4cb332016-03-02 16:32:51 -0800252 }
253
254 /**
255 * A runnable class that allows to capture and log the exceptions.
256 */
257 private class LoggableRunnable implements Runnable {
258 private Runnable runnable;
Jian Lia1d7f272016-03-28 17:21:47 -0700259 private boolean repeatFlag;
Jian Li1b4cb332016-03-02 16:32:51 -0800260
Jian Lia1d7f272016-03-28 17:21:47 -0700261 public LoggableRunnable(Runnable runnable, boolean repeatFlag) {
Jian Li1b4cb332016-03-02 16:32:51 -0800262 super();
263 this.runnable = runnable;
Jian Lia1d7f272016-03-28 17:21:47 -0700264 this.repeatFlag = repeatFlag;
Jian Li1b4cb332016-03-02 16:32:51 -0800265 }
266
267 @Override
268 public void run() {
Jian Lia1d7f272016-03-28 17:21:47 -0700269 if (Thread.currentThread().isInterrupted()) {
270 log.info("Task interrupted, quitting");
271 return;
272 }
273
Jian Li1b4cb332016-03-02 16:32:51 -0800274 try {
275 runnable.run();
276 } catch (Exception e) {
277 log.error("Uncaught exception on " + runnable.getClass().getSimpleName(), e);
Jian Lia1d7f272016-03-28 17:21:47 -0700278
279 // if repeat flag set as false, we simply throw an exception to
280 // terminate this task
281 if (!repeatFlag) {
Ray Milkey6a51cb92018-03-06 09:03:03 -0800282 throw new IllegalStateException(e);
Jian Lia1d7f272016-03-28 17:21:47 -0700283 }
Jian Li1b4cb332016-03-02 16:32:51 -0800284 }
285 }
286 }
287}