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

dlmarion pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo-classloaders.git


The following commit(s) were added to refs/heads/main by this push:
     new 0dadf6c  Remove ContextDefinition on unhandled exception in Monitor 
task (#50)
0dadf6c is described below

commit 0dadf6cc89809c0dd6035b3b3c07df0d7776a2d5
Author: Dave Marion <[email protected]>
AuthorDate: Tue Jan 20 16:59:26 2026 -0500

    Remove ContextDefinition on unhandled exception in Monitor task (#50)
    
    Closes #46
---
 .../lcc/LocalCachingContextClassLoaderFactory.java | 42 ++++++++++++++++++----
 1 file changed, 35 insertions(+), 7 deletions(-)

diff --git 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactory.java
 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactory.java
index 94fed63..2c185a6 100644
--- 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactory.java
+++ 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactory.java
@@ -99,7 +99,7 @@ public class LocalCachingContextClassLoaderFactory implements 
ContextClassLoader
   private static final Logger LOG =
       LoggerFactory.getLogger(LocalCachingContextClassLoaderFactory.class);
 
-  private final ScheduledExecutorService EXECUTOR = 
Executors.newScheduledThreadPool(0);
+  private final ScheduledExecutorService executor = 
Executors.newScheduledThreadPool(0);
 
   // stores the latest seen ContextDefinition for a remote URL location; 
String types are used here
   // for the key instead of URL because URL.hashCode could trigger network 
activity for hostname
@@ -119,13 +119,24 @@ public class LocalCachingContextClassLoaderFactory 
implements ContextClassLoader
   /**
    * Schedule a task to execute at {@code interval} seconds to update the 
LocalCachingContext if the
    * ContextDefinition has changed. The task schedules a follow-on task at the 
update interval value
-   * (if it changed).
+   * (if it changed) and the task is successful or throws a handled exception. 
When an unhandled
+   * exception is thrown, then the corresponding entry in the contextDefs map 
is cleared. The next
+   * call to {@code #getClassLoader(String)} will recreate the contextDefs map 
entry and schedule
+   * the monitor task.
    */
   private void monitorContext(final String contextLocation, long interval) {
     LOG.trace("Monitoring context definition file {} for changes at {} second 
intervals",
         contextLocation, interval);
-    EXECUTOR.schedule(() -> checkMonitoredLocation(contextLocation, interval), 
interval,
-        TimeUnit.SECONDS);
+    executor.schedule(() -> {
+      try {
+        checkMonitoredLocation(contextLocation, interval);
+      } catch (Throwable t) {
+        LOG.error("Unhandled exception occurred in context definition monitor 
thread. Removing"
+            + " context definition {}.", contextLocation, t);
+        contextDefs.remove(contextLocation);
+        throw t;
+      }
+    }, interval, TimeUnit.SECONDS);
   }
 
   @Override
@@ -268,6 +279,15 @@ public class LocalCachingContextClassLoaderFactory 
implements ContextClassLoader
       } else {
         LOG.trace("Context definition for {} has not changed", 
contextLocation);
       }
+      // reschedule this task to run if the context definition exists.
+      // Atomically lock on the context key and only reschedule if the context 
is present.
+      final long finalMonitorInterval = nextInterval;
+      contextDefs.compute(contextLocation, (k, v) -> {
+        if (v != null) {
+          monitorContext(contextLocation, finalMonitorInterval);
+        }
+        return v;
+      });
     } catch (IOException | RuntimeException e) {
       LOG.error("Error parsing updated context definition at {}. Classloader 
NOT updated!",
           contextLocation, e);
@@ -294,10 +314,18 @@ public class LocalCachingContextClassLoaderFactory 
implements ContextClassLoader
         LOG.trace("Failing to update classloader for context {} within the 
grace period",
             contextLocation, e);
       }
-    } finally {
-      monitorContext(contextLocation, nextInterval);
+      // reschedule this task to run if the context definition exists.
+      // Don't put this in finally block as we only want to reschedule
+      // on success or handled exception
+      // Atomically lock on the context key and only reschedule if the context 
is present.
+      final long finalMonitorInterval = nextInterval;
+      contextDefs.compute(contextLocation, (k, v) -> {
+        if (v != null) {
+          monitorContext(contextLocation, finalMonitorInterval);
+        }
+        return v;
+      });
     }
-
   }
 
   public static Map<String,List<String>> getReferencedFiles() {

Reply via email to