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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit d6e8143260c0fa0c6d8fb50d296096be5bddc7db
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Jul 16 09:13:48 2025 +0200

    CAMEL-22214: camel-groovy - Reloading of compiled sources
---
 .../groovy/DefaultGroovyScriptCompiler.java        | 74 ++++++++++++++++++++--
 .../camel/language/groovy/GroovyLanguage.java      | 10 ++-
 .../language/groovy/GroovyScriptClassLoader.java   |  4 ++
 3 files changed, 80 insertions(+), 8 deletions(-)

diff --git 
a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/DefaultGroovyScriptCompiler.java
 
b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/DefaultGroovyScriptCompiler.java
index 96a6d64ccff..76710b4d93e 100644
--- 
a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/DefaultGroovyScriptCompiler.java
+++ 
b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/DefaultGroovyScriptCompiler.java
@@ -22,15 +22,19 @@ import java.util.List;
 import groovy.lang.GroovyShell;
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
+import org.apache.camel.Ordered;
 import org.apache.camel.StaticService;
 import org.apache.camel.api.management.ManagedAttribute;
 import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.spi.CamelEvent;
 import org.apache.camel.spi.CompileStrategy;
+import org.apache.camel.spi.EventNotifier;
 import org.apache.camel.spi.GroovyScriptCompiler;
 import org.apache.camel.spi.PackageScanResourceResolver;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.annotations.JdkService;
 import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.SimpleEventNotifierSupport;
 import org.apache.camel.support.service.ServiceSupport;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.IOHelper;
@@ -40,6 +44,8 @@ import org.codehaus.groovy.control.CompilerConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.camel.language.groovy.GroovyLanguage.RELOAD_ORDER;
+
 @JdkService(GroovyScriptCompiler.FACTORY)
 @ManagedResource(description = "Managed GroovyScriptCompiler")
 public class DefaultGroovyScriptCompiler extends ServiceSupport
@@ -47,11 +53,14 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
 
     private static final Logger LOG = 
LoggerFactory.getLogger(DefaultGroovyScriptCompiler.class);
 
-    private long taken;
     private GroovyScriptClassLoader classLoader;
     private CamelContext camelContext;
+    private EventNotifier notifier;
     private String scriptPattern;
     private String workDir;
+    private long taken;
+    private int counter;
+    private boolean reload;
 
     @Override
     public CamelContext getCamelContext() {
@@ -73,6 +82,11 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
         return classLoader.size();
     }
 
+    @ManagedAttribute(description = "Number of times Groovy compiler has 
executed")
+    public int getCompileSize() {
+        return counter;
+    }
+
     @ManagedAttribute(description = "Directories to scan for Groovy source to 
be pre-compiled")
     @Override
     public String getScriptPattern() {
@@ -114,6 +128,17 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
             if (classLoader == null) {
                 classLoader = new GroovyScriptClassLoader();
                 context.getClassResolver().addClassLoader(classLoader);
+                // make classloader available for groovy language
+                
context.getCamelContextExtension().addContextPlugin(GroovyScriptClassLoader.class,
 classLoader);
+            }
+
+            String profile = context.getCamelContextExtension().getProfile();
+            if ("dev".equals(profile)) {
+                reload = true;
+                if (notifier == null) {
+                    notifier = new ReloadNotifier();
+                    
getCamelContext().getManagementStrategy().addEventNotifier(notifier);
+                }
             }
         }
     }
@@ -123,12 +148,14 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
         super.doStart();
 
         if (scriptPattern != null) {
-            StopWatch watch = new StopWatch();
-
             LOG.info("Pre-compiling Groovy sources from: {}", scriptPattern);
+            doCompile();
+        }
+    }
 
-            // make classloader available for groovy language
-            
camelContext.getCamelContextExtension().addContextPlugin(GroovyScriptClassLoader.class,
 classLoader);
+    protected void doCompile() throws Exception {
+        if (scriptPattern != null) {
+            StopWatch watch = new StopWatch();
 
             // scan for groovy source files to include
             List<String> cps = new ArrayList<>();
@@ -160,6 +187,10 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
                     }
                 }
             }
+            if (codes.isEmpty()) {
+                // nothing to compile
+                return;
+            }
 
             // use work dir for writing compiled classes
             CompileStrategy cs = 
camelContext.getCamelContextExtension().getContextPlugin(CompileStrategy.class);
@@ -170,6 +201,9 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
             // setup compiler via groovy shell
             CompilerConfiguration cc = new CompilerConfiguration();
             cc.setClasspathList(cps);
+            if (reload) {
+                cc.setRecompileGroovySource(true);
+            }
             if (workDir != null) {
                 LOG.debug("Writing compiled Groovy classes to directory: {}", 
workDir);
                 cc.setTargetDirectory(workDir);
@@ -186,6 +220,7 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
                 if (LOG.isTraceEnabled()) {
                     LOG.trace("Pre-compiling Groovy source:\n{}", code);
                 }
+                counter++;
                 Class<?> clazz = shell.getClassLoader().parseClass(code);
                 if (clazz != null) {
                     LOG.debug("Pre-compiled Groovy class: {}", 
clazz.getName());
@@ -200,10 +235,35 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
     protected void doStop() throws Exception {
         super.doStop();
 
-        if (classLoader.size() > 0) {
-            LOG.debug("Pre-compiled {} Groovy sources in {} millis", 
classLoader.size(), taken);
+        if (counter > 0) {
+            LOG.debug("Pre-compiled {} Groovy sources in {} millis", counter, 
taken);
         }
 
         IOHelper.close(classLoader);
     }
+
+    private final class ReloadNotifier extends SimpleEventNotifierSupport 
implements Ordered {
+
+        @Override
+        public void notify(CamelEvent event) throws Exception {
+            // if context or route is reloading then clear classloader to 
ensure old scripts are removed from memory.
+            if (event instanceof CamelEvent.CamelContextReloadingEvent || 
event instanceof CamelEvent.RouteReloadedEvent) {
+                if (classLoader != null) {
+                    classLoader.clear();
+                    // trigger re-compilation
+                    if (scriptPattern != null) {
+                        LOG.info("Re-compiling Groovy sources from: {}", 
scriptPattern);
+                        doCompile();
+                    }
+                }
+            }
+        }
+
+        @Override
+        public int getOrder() {
+            // ensure this is triggered before groovy language reloader (we 
want first)
+            return RELOAD_ORDER - 100;
+        }
+    }
+
 }
diff --git 
a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyLanguage.java
 
b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyLanguage.java
index 9160d612f79..189aaa4aa08 100644
--- 
a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyLanguage.java
+++ 
b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyLanguage.java
@@ -23,6 +23,7 @@ import groovy.lang.Binding;
 import groovy.lang.GroovyShell;
 import groovy.lang.Script;
 import org.apache.camel.Exchange;
+import org.apache.camel.Ordered;
 import org.apache.camel.Service;
 import org.apache.camel.spi.CamelEvent;
 import org.apache.camel.spi.EventNotifier;
@@ -43,6 +44,8 @@ public class GroovyLanguage extends TypedLanguageSupport 
implements ScriptingLan
 
     private static final Logger LOG = 
LoggerFactory.getLogger(GroovyLanguage.class);
 
+    static final int RELOAD_ORDER = 100;
+
     /**
      * In case the expression is referring to an external resource, it 
indicates whether it is still needed to load the
      * resource.
@@ -89,7 +92,7 @@ public class GroovyLanguage extends TypedLanguageSupport 
implements ScriptingLan
         }
     }
 
-    private final class ReloadNotifier extends SimpleEventNotifierSupport {
+    private final class ReloadNotifier extends SimpleEventNotifierSupport 
implements Ordered {
 
         @Override
         public void notify(CamelEvent event) throws Exception {
@@ -99,6 +102,11 @@ public class GroovyLanguage extends TypedLanguageSupport 
implements ScriptingLan
                 scriptCache.clear();
             }
         }
+
+        @Override
+        public int getOrder() {
+            return RELOAD_ORDER;
+        }
     }
 
     private static final class GroovyClassService implements Service {
diff --git 
a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyScriptClassLoader.java
 
b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyScriptClassLoader.java
index 97709f5042c..c6764a60d67 100644
--- 
a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyScriptClassLoader.java
+++ 
b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyScriptClassLoader.java
@@ -67,6 +67,10 @@ public class GroovyScriptClassLoader extends ClassLoader 
implements Closeable {
 
     @Override
     public void close() throws IOException {
+        clear();
+    }
+
+    void clear() {
         for (Class<?> clazz : classes.values()) {
             InvokerHelper.removeClass(clazz);
         }

Reply via email to