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
Showing
3 changed files
with
216 additions
and
4 deletions
... | @@ -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 | ... | ... |
-
Please register or login to post a comment