You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by jb...@apache.org on 2021/03/23 13:45:07 UTC

[activemq] branch activemq-5.15.x updated: Stop failed timer task from breaking timers.

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch activemq-5.15.x
in repository https://gitbox.apache.org/repos/asf/activemq.git


The following commit(s) were added to refs/heads/activemq-5.15.x by this push:
     new 401101f  Stop failed timer task from breaking timers.
401101f is described below

commit 401101f9404dfe079b1ff0fbdd1a15510ef47bc6
Author: Matt Daley <m6...@gmail.com>
AuthorDate: Fri Nov 22 23:14:19 2019 +0000

    Stop failed timer task from breaking timers.
    
    (cherry picked from commit b379879dbfa357930f431bd955c42c78d241834d)
---
 .../apache/activemq/thread/SchedulerTimerTask.java | 25 +++++++++++++++++++++-
 .../org/apache/activemq/thread/SchedulerTest.java  | 17 +++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/activemq-client/src/main/java/org/apache/activemq/thread/SchedulerTimerTask.java b/activemq-client/src/main/java/org/apache/activemq/thread/SchedulerTimerTask.java
index 95cbdb1..55bb323 100644
--- a/activemq-client/src/main/java/org/apache/activemq/thread/SchedulerTimerTask.java
+++ b/activemq-client/src/main/java/org/apache/activemq/thread/SchedulerTimerTask.java
@@ -16,6 +16,9 @@
  */
 package org.apache.activemq.thread;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.util.TimerTask;
 
 /**
@@ -23,6 +26,8 @@ import java.util.TimerTask;
  *
  */
 public class SchedulerTimerTask extends TimerTask {
+    private static final Logger LOGGER = LoggerFactory.getLogger(SchedulerTimerTask.class);
+
     private final Runnable task;
 
     public SchedulerTimerTask(Runnable task) {
@@ -30,6 +35,24 @@ public class SchedulerTimerTask extends TimerTask {
     }
 
     public void run() {
-        this.task.run();                         
+        try {
+            this.task.run();
+        } catch (Error error) {
+            // Very bad error. Can't swallow this but can log it.
+            LOGGER.error("Scheduled task error", error);
+            throw error;
+        } catch (Exception exception) {
+            // It is a known issue of java.util.Timer that if a TimerTask.run() method throws an exception, the
+            // Timer's thread exits as if Timer.cancel() had been called. This makes the Timer completely unusable from
+            // that point on - whenever the Timer triggers there is an 'IllegalStateException: Timer already cancelled'
+            // and the task does not get executed.
+            //
+            // In practice, this makes the ActiveMQ client unable to refresh connections to brokers. Generally, tasks
+            // are well coded to not throw exceptions but if they do, problems ensue...
+            //
+            // The answer, here, is to log the exception and then carry on without throwing further. This gives the
+            // added benefit that, having seen the exception thrown, one can then go and fix the offending task!
+            LOGGER.error("Scheduled task exception", exception);
+        }
     }
 }
\ No newline at end of file
diff --git a/activemq-client/src/test/java/org/apache/activemq/thread/SchedulerTest.java b/activemq-client/src/test/java/org/apache/activemq/thread/SchedulerTest.java
index d63c831..e0743b3 100644
--- a/activemq-client/src/test/java/org/apache/activemq/thread/SchedulerTest.java
+++ b/activemq-client/src/test/java/org/apache/activemq/thread/SchedulerTest.java
@@ -69,16 +69,33 @@ public class SchedulerTest {
         assertFalse(latch.await(1000, TimeUnit.MILLISECONDS));
     }
 
+    @Test
+    public void testExecutePeriodicallyTaskExceptionDoesNotBreakTimer() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(10);
+        scheduler.executePeriodically(new CountDownRunnable(latch, 5L), 10);
+        assertTrue(latch.await(5000, TimeUnit.MILLISECONDS));
+    }
+
     private static class CountDownRunnable implements Runnable {
         final CountDownLatch latch;
+        final Long throwAtCount;
 
         CountDownRunnable(final CountDownLatch latch) {
+            this(latch, null);
+        }
+
+        CountDownRunnable(final CountDownLatch latch, Long throwAtCount) {
             this.latch = latch;
+            this.throwAtCount = throwAtCount;
         }
 
         @Override
         public void run() {
             latch.countDown();
+
+            if (throwAtCount != null && latch.getCount() == throwAtCount) {
+                throw new RuntimeException("You never want this to happen in a real task!!");
+            }
         }
     }