SM-1847: Executor service silently swallows any Errors being thrown fron Runnable task

git-svn-id: https://svn.apache.org/repos/asf/servicemix/utils/trunk@1147181 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/servicemix/executors/impl/ExecutorImpl.java b/src/main/java/org/apache/servicemix/executors/impl/ExecutorImpl.java
index c28d428..04ee885 100644
--- a/src/main/java/org/apache/servicemix/executors/impl/ExecutorImpl.java
+++ b/src/main/java/org/apache/servicemix/executors/impl/ExecutorImpl.java
@@ -18,6 +18,8 @@
 

 import org.apache.servicemix.executors.Executor;

 import org.apache.servicemix.executors.ExecutorAwareRunnable;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

 

 import java.util.concurrent.BlockingQueue;

 import java.util.concurrent.ThreadPoolExecutor;

@@ -31,6 +33,8 @@
  */

 public class ExecutorImpl implements Executor {

 

+    private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorImpl.class);

+

     private final ThreadPoolExecutor threadPool;

 

     private ExecutorFactoryImpl executorFactory;

@@ -46,11 +50,24 @@
     public void execute(Runnable command) {

         if (config.isBypassIfSynchronous() && command instanceof ExecutorAwareRunnable) {

             if (((ExecutorAwareRunnable) command).shouldRunSynchronously()) {

-                command.run();

+                wrap(command).run();

                 return;

             }

         }

-        threadPool.execute(command);

+        threadPool.execute(wrap(command));

+    }

+

+    private Runnable wrap(final Runnable wrapped) {

+        return new Runnable() {

+            public void run() {

+                try {

+                    wrapped.run();

+                } catch (Throwable t) {

+                    LOGGER.error("Exception caught while executing submitted job", t);

+                    throw new RuntimeException("Exception caught while executing in submitted job", t);

+                }

+            }

+        };

     }

 

     public void shutdown() {

diff --git a/src/test/java/org/apache/servicemix/executors/impl/LogThrowableInExecutorTest.java b/src/test/java/org/apache/servicemix/executors/impl/LogThrowableInExecutorTest.java
new file mode 100644
index 0000000..b3f91e0
--- /dev/null
+++ b/src/test/java/org/apache/servicemix/executors/impl/LogThrowableInExecutorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.executors.impl;
+
+import junit.framework.TestCase;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.servicemix.executors.Executor;
+import org.apache.servicemix.executors.ExecutorFactory;
+import org.apache.servicemix.executors.impl.ExecutorFactoryImpl;
+import org.apache.servicemix.executors.impl.ExecutorImpl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Test case to ensure that Executor will always log throwables from the executed Runnable (SM-1847)
+ */
+public class LogThrowableInExecutorTest extends TestCase {
+
+    private static final String MESSAGE = "I'm a bad Runnable throwing errors at people";
+
+    public void testThrowableLogged() throws InterruptedException {
+        final List<LoggingEvent> events = new LinkedList<LoggingEvent>();
+
+        // unit tests use LOG4J as the backend for SLF4J so add the appender to LOG4J
+        Logger.getLogger(ExecutorImpl.class).addAppender(new AppenderSkeleton() {
+            @Override
+            protected void append(LoggingEvent event) {
+                events.add(event);
+            }
+
+            @Override
+            public boolean requiresLayout() {
+                return false;
+            }
+
+            @Override
+            public void close() {
+                //graciously do nothing
+            }
+        });
+
+        ExecutorFactory factory = new ExecutorFactoryImpl();
+        Executor executor = factory.createExecutor("test");
+        executor.execute(new Runnable() {
+            public void run() {
+                throw new Error(MESSAGE);
+            }
+        });
+
+        // let's wait to make sure the runnable is done
+        Thread.sleep(500);
+
+        assertEquals("Should have logged 1 message", 1, events.size());
+        assertTrue("Exception message should have been logged",
+                events.get(0).getThrowableStrRep()[0].contains(MESSAGE));
+
+    }
+
+}