Jian Li
Committed by Gerrit Code Review

A new ScheduledExecutorService that captures and logs exception

The default ScheduledExecutorService does not provide the
capability to capture and log the exception during executing
scheduleAtFixedRate and scheduleWithFixedDelay methods. This
makes it difficult to debug the program when the scheudled
tasks are failed for some reasons.
A new ScheduledExecutorService allows the developers to capture
and log any exceptions if the tasks are failed during execution.

Change-Id: I549ba0f479b9e302f0e668482873b3032dfea147
...@@ -39,15 +39,16 @@ import org.projectfloodlight.openflow.protocol.OFPortStatus; ...@@ -39,15 +39,16 @@ import org.projectfloodlight.openflow.protocol.OFPortStatus;
39 import org.slf4j.Logger; 39 import org.slf4j.Logger;
40 40
41 import java.util.HashMap; 41 import java.util.HashMap;
42 -import java.util.concurrent.Executors;
43 import java.util.concurrent.ScheduledExecutorService; 42 import java.util.concurrent.ScheduledExecutorService;
44 import java.util.concurrent.ScheduledFuture; 43 import java.util.concurrent.ScheduledFuture;
45 import java.util.concurrent.TimeUnit; 44 import java.util.concurrent.TimeUnit;
46 45
46 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
47 +import static org.onlab.util.Tools.groupedThreads;
48 +import static org.onlab.util.Tools.loggableScheduledExecutor;
47 import static org.onosproject.net.DeviceId.deviceId; 49 import static org.onosproject.net.DeviceId.deviceId;
48 import static org.onosproject.openflow.controller.Dpid.uri; 50 import static org.onosproject.openflow.controller.Dpid.uri;
49 import static org.slf4j.LoggerFactory.getLogger; 51 import static org.slf4j.LoggerFactory.getLogger;
50 -import static org.onlab.util.Tools.groupedThreads;
51 52
52 /** 53 /**
53 * Provider which uses an OpenFlow controller to collect control message. 54 * Provider which uses an OpenFlow controller to collect control message.
...@@ -105,8 +106,9 @@ public class OpenFlowControlMessageProvider extends AbstractProvider ...@@ -105,8 +106,9 @@ public class OpenFlowControlMessageProvider extends AbstractProvider
105 // listens all OpenFlow outgoing message events 106 // listens all OpenFlow outgoing message events
106 controller.getSwitches().forEach(sw -> sw.addEventListener(outMsgListener)); 107 controller.getSwitches().forEach(sw -> sw.addEventListener(outMsgListener));
107 108
108 - executor = Executors.newSingleThreadScheduledExecutor( 109 + executor = loggableScheduledExecutor(
109 - groupedThreads("onos/provider", "aggregator")); 110 + newSingleThreadScheduledExecutor(groupedThreads("onos/provider",
111 + "aggregator")));
110 112
111 connectInitialDevices(); 113 connectInitialDevices();
112 log.info("Started"); 114 log.info("Started");
......
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
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 +
17 +package org.onlab.util;
18 +
19 +import org.slf4j.Logger;
20 +
21 +import java.util.Collection;
22 +import java.util.List;
23 +import java.util.concurrent.Callable;
24 +import java.util.concurrent.ExecutionException;
25 +import java.util.concurrent.Future;
26 +import java.util.concurrent.ScheduledExecutorService;
27 +import java.util.concurrent.ScheduledFuture;
28 +import java.util.concurrent.TimeUnit;
29 +import java.util.concurrent.TimeoutException;
30 +
31 +import static org.slf4j.LoggerFactory.getLogger;
32 +
33 +/**
34 + * A new scheduled executor service that does not eat exception.
35 + */
36 +class LogScheduledExecutorService implements ScheduledExecutorService {
37 +
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 + */
48 + LogScheduledExecutorService(ScheduledExecutorService executor) {
49 + 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 + *
64 + * @param executor new scheduled executor service
65 + */
66 + void setBackingExecutor(ScheduledExecutorService executor) {
67 + ScheduledExecutorService oldExecutor = this.executor;
68 + this.executor = executor;
69 + oldExecutor.shutdown();
70 + }
71 +
72 + @Override
73 + public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
74 + return executor.schedule(wrap(command), delay, unit);
75 + }
76 +
77 + @Override
78 + public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
79 + return executor.schedule(() -> {
80 + V v = null;
81 + try {
82 + v = callable.call();
83 + } catch (Exception e) {
84 + log.error("Uncaught exception on " + callable.getClass(), e);
85 + }
86 + return v;
87 + }, delay, unit);
88 + }
89 +
90 + @Override
91 + public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
92 + long period, TimeUnit unit) {
93 + return executor.scheduleAtFixedRate(wrap(command), initialDelay, period, unit);
94 + }
95 +
96 + @Override
97 + public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
98 + long delay, TimeUnit unit) {
99 + return executor.scheduleWithFixedDelay(wrap(command), initialDelay, delay, unit);
100 + }
101 +
102 + @Override
103 + public void shutdown() {
104 + throw new UnsupportedOperationException(NOT_ALLOWED);
105 + }
106 +
107 + @Override
108 + public List<Runnable> shutdownNow() {
109 + throw new UnsupportedOperationException(NOT_ALLOWED);
110 + }
111 +
112 + @Override
113 + public boolean isShutdown() {
114 + return executor.isShutdown();
115 + }
116 +
117 + @Override
118 + public boolean isTerminated() {
119 + return executor.isTerminated();
120 + }
121 +
122 + @Override
123 + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
124 + return executor.awaitTermination(timeout, unit);
125 + }
126 +
127 + @Override
128 + public <T> Future<T> submit(Callable<T> task) {
129 + return executor.submit(task);
130 + }
131 +
132 + @Override
133 + public <T> Future<T> submit(Runnable task, T result) {
134 + return executor.submit(task, result);
135 + }
136 +
137 + @Override
138 + public Future<?> submit(Runnable task) {
139 + return executor.submit(task);
140 + }
141 +
142 + @Override
143 + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
144 + throws InterruptedException {
145 + return executor.invokeAll(tasks);
146 + }
147 +
148 + @Override
149 + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
150 + long timeout, TimeUnit unit)
151 + throws InterruptedException {
152 + return executor.invokeAll(tasks, timeout, unit);
153 + }
154 +
155 + @Override
156 + public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
157 + throws InterruptedException, ExecutionException {
158 + return executor.invokeAny(tasks);
159 + }
160 +
161 + @Override
162 + public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
163 + long timeout, TimeUnit unit) throws InterruptedException,
164 + ExecutionException, TimeoutException {
165 + return executor.invokeAny(tasks, timeout, unit);
166 + }
167 +
168 + @Override
169 + public void execute(Runnable command) {
170 + executor.execute(command);
171 + }
172 +
173 + private Runnable wrap(Runnable command) {
174 + return new LoggableRunnable(command);
175 + }
176 +
177 + /**
178 + * A runnable class that allows to capture and log the exceptions.
179 + */
180 + private class LoggableRunnable implements Runnable {
181 + private Runnable runnable;
182 +
183 + public LoggableRunnable(Runnable runnable) {
184 + super();
185 + this.runnable = runnable;
186 + }
187 +
188 + @Override
189 + public void run() {
190 + try {
191 + runnable.run();
192 + } catch (Exception e) {
193 + log.error("Uncaught exception on " + runnable.getClass().getSimpleName(), e);
194 + throw new RuntimeException(e);
195 + }
196 + }
197 + }
198 +}
...@@ -45,6 +45,7 @@ import java.util.Set; ...@@ -45,6 +45,7 @@ import java.util.Set;
45 import java.util.concurrent.CompletableFuture; 45 import java.util.concurrent.CompletableFuture;
46 import java.util.concurrent.ExecutionException; 46 import java.util.concurrent.ExecutionException;
47 import java.util.concurrent.Future; 47 import java.util.concurrent.Future;
48 +import java.util.concurrent.ScheduledExecutorService;
48 import java.util.concurrent.ThreadFactory; 49 import java.util.concurrent.ThreadFactory;
49 import java.util.concurrent.TimeUnit; 50 import java.util.concurrent.TimeUnit;
50 import java.util.concurrent.TimeoutException; 51 import java.util.concurrent.TimeoutException;
...@@ -129,6 +130,17 @@ public abstract class Tools { ...@@ -129,6 +130,17 @@ public abstract class Tools {
129 } 130 }
130 131
131 /** 132 /**
133 + * Returns a loggable scheduled executor service that allows to capture and
134 + * log any exceptions if the scheduled tasks are failed during execution.
135 + *
136 + * @param executor scheduled executor service
137 + * @return loggable scheduled executor service
138 + */
139 + public static ScheduledExecutorService loggableScheduledExecutor(ScheduledExecutorService executor) {
140 + return new LogScheduledExecutorService(executor);
141 + }
142 +
143 + /**
132 * Returns a thread factory that produces threads with MIN_PRIORITY. 144 * Returns a thread factory that produces threads with MIN_PRIORITY.
133 * 145 *
134 * @param factory backing ThreadFactory 146 * @param factory backing ThreadFactory
......