[ONOS-3528] Log exception for all threads that use BoundedThreadPool

Change-Id: I9c904c49998a8206ba2b5a084e03e776fa1d8237
diff --git a/utils/misc/src/main/java/org/onlab/util/BoundedThreadPool.java b/utils/misc/src/main/java/org/onlab/util/BoundedThreadPool.java
index 5ca88c2..54bcdc9 100644
--- a/utils/misc/src/main/java/org/onlab/util/BoundedThreadPool.java
+++ b/utils/misc/src/main/java/org/onlab/util/BoundedThreadPool.java
@@ -16,6 +16,8 @@
 package org.onlab.util;
 
 import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.RejectedExecutionHandler;
@@ -111,6 +113,28 @@
         updateLoad();
     }
 
+    @Override
+    protected void afterExecute(Runnable r, Throwable t) {
+        super.afterExecute(r, t);
+        if (t == null && r instanceof Future<?>) {
+            try {
+                Future<?> future = (Future<?>) r;
+                if (future.isDone()) {
+                    future.get();
+                }
+            } catch (CancellationException ce) {
+                t = ce;
+            } catch (ExecutionException ee) {
+                t = ee.getCause();
+            } catch (InterruptedException ie) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        if (t != null) {
+            log.error("Uncaught exception on " + r.getClass().getSimpleName(), t);
+        }
+    }
+
     // TODO schedule this with a fixed delay from a scheduled executor
     private final AtomicLong lastPrinted = new AtomicLong(0L);