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


The following commit(s) were added to refs/heads/main by this push:
     new b1d584332d8 CAMEL-22214: camel-groovy - Reloading of compiled sources
b1d584332d8 is described below

commit b1d584332d8a4be97d1f84301c26909fb6e8c396
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Jul 16 14:32:51 2025 +0200

    CAMEL-22214: camel-groovy - Reloading of compiled sources
---
 .../groovy/DefaultGroovyScriptCompiler.java        | 95 ++++++++++------------
 .../camel/language/groovy/GroovyDevConsole.java    |  2 +
 .../language/groovy/GroovyScriptClassLoader.java   |  7 ++
 .../groovy/DefaultGroovyCompilerTest.java          |  6 +-
 .../camel/support/RouteWatcherReloadStrategy.java  | 14 ++++
 .../apache/camel/dsl/jbang/core/commands/Run.java  |  4 +
 6 files changed, 76 insertions(+), 52 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 f16d9df0a34..6b2a936c185 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
@@ -17,6 +17,7 @@
 package org.apache.camel.language.groovy;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import groovy.lang.GroovyShell;
@@ -103,19 +104,9 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
         return workDir;
     }
 
-    /**
-     * Loads the given class
-     *
-     * @param  name                   the FQN class name
-     * @return                        the loaded class
-     * @throws ClassNotFoundException is thrown if class is not found
-     */
-    public Class<?> loadClass(String name) throws ClassNotFoundException {
-        if (classLoader != null) {
-            return classLoader.findClass(name);
-        } else {
-            throw new ClassNotFoundException(name);
-        }
+    @ManagedAttribute(description = "Whether re-compiling is enabled")
+    public boolean isRecompileEnabled() {
+        return reload;
     }
 
     @Override
@@ -149,49 +140,51 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
 
         if (scriptPattern != null) {
             LOG.info("Pre-compiling Groovy sources from: {}", scriptPattern);
-            doCompile();
+            doCompile(scanForGroovySources(scriptPattern));
         }
     }
 
-    protected void doCompile() throws Exception {
-        if (scriptPattern != null) {
-            StopWatch watch = new StopWatch();
+    protected Collection<Resource> scanForGroovySources(String scriptPattern) 
throws Exception {
+        List<Resource> list = new ArrayList<>();
 
-            // scan for groovy source files to include
-            List<String> cps = new ArrayList<>();
-            List<String> codes = new ArrayList<>();
-            PackageScanResourceResolver resolver = 
PluginHelper.getPackageScanResourceResolver(camelContext);
-            for (String pattern : scriptPattern.split(",")) {
-                for (Resource resource : resolver.findResources(pattern)) {
-                    if (resource.exists()) {
-                        String loc = null;
-                        if ("classpath".equals(resource.getScheme())) {
-                            loc = resource.getLocation();
-                        } else if ("file".equals(resource.getScheme())) {
-                            loc = resource.getLocation();
-                        }
-                        if (loc != null) {
-                            String code = 
IOHelper.loadText(resource.getInputStream());
-                            loc = StringHelper.after(loc, ":", loc);
-
-                            String cp = FileUtil.onlyPath(loc);
-                            if (cp == null) {
-                                cp = ".";
-                            }
-                            if (!cps.contains(cp)) {
-                                cps.add(cp);
-                            }
-
-                            codes.add(code);
-                        }
+        PackageScanResourceResolver resolver = 
PluginHelper.getPackageScanResourceResolver(camelContext);
+        for (String pattern : scriptPattern.split(",")) {
+            for (Resource resource : resolver.findResources(pattern)) {
+                if (resource.exists()) {
+                    if ("classpath".equals(resource.getScheme()) || 
"file".equals(resource.getScheme())) {
+                        list.add(resource);
                     }
                 }
             }
+        }
+
+        return list;
+    }
+
+    protected void doCompile(Collection<Resource> resources) throws Exception {
+        List<String> cps = new ArrayList<>();
+        List<String> codes = new ArrayList<>();
+        for (Resource resource : resources) {
+            String loc = resource.getLocation();
+            if (loc != null) {
+                String code = IOHelper.loadText(resource.getInputStream());
+                loc = StringHelper.after(loc, ":", loc);
+                String cp = FileUtil.onlyPath(loc);
+                if (cp == null) {
+                    cp = ".";
+                }
+                if (!cps.contains(cp)) {
+                    cps.add(cp);
+                }
+                codes.add(code);
+            }
             if (codes.isEmpty()) {
                 // nothing to compile
                 return;
             }
 
+            StopWatch watch = new StopWatch();
+
             // use work dir for writing compiled classes
             CompileStrategy cs = 
camelContext.getCamelContextExtension().getContextPlugin(CompileStrategy.class);
             if (cs != null && cs.getWorkDir() != null) {
@@ -218,13 +211,16 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
             // parse code into classes and add to classloader
             for (String code : codes) {
                 if (LOG.isTraceEnabled()) {
-                    LOG.trace("Pre-compiling Groovy source:\n{}", code);
+                    LOG.trace("Compiling Groovy source:\n{}", code);
                 }
                 counter++;
                 Class<?> clazz = shell.getClassLoader().parseClass(code);
                 if (clazz != null) {
-                    LOG.debug("Pre-compiled Groovy class: {}", 
clazz.getName());
-                    classLoader.addClass(clazz.getName(), clazz);
+                    String name = clazz.getName();
+                    LOG.debug("Compiled Groovy class: {}", name);
+                    // remove before adding in case it's recompiled
+                    classLoader.removeClass(name);
+                    classLoader.addClass(name, clazz);
                 }
             }
             taken += watch.taken();
@@ -236,11 +232,10 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
         super.doStop();
 
         if (counter > 0) {
-            LOG.debug("Pre-compiled {} Groovy sources in {} millis", counter, 
taken);
+            LOG.debug("Compiled {} Groovy sources in {} millis", counter, 
taken);
         }
 
         IOHelper.close(classLoader);
-
     }
 
     @Override
@@ -265,7 +260,7 @@ public class DefaultGroovyScriptCompiler extends 
ServiceSupport
                         classLoader.clear();
                     }
                     LOG.info("Re-compiling Groovy sources from: {}", 
scriptPattern);
-                    doCompile();
+                    doCompile(scanForGroovySources(scriptPattern));
                 }
             }
         }
diff --git 
a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyDevConsole.java
 
b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyDevConsole.java
index d761f9c4a4b..ff7d56dfc2d 100644
--- 
a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyDevConsole.java
+++ 
b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyDevConsole.java
@@ -39,6 +39,7 @@ public class GroovyDevConsole extends AbstractDevConsole {
             sb.append(String.format("\n    Compiled Size: %s", 
compiler.getCompileSize()));
             sb.append(String.format("\n    Compiled Classes: %s", 
compiler.getClassesSize()));
             sb.append(String.format("\n    Compiled Time: %s (ms)", 
compiler.getCompileTime()));
+            sb.append(String.format("\n    Re-compile Enabled: %b", 
compiler.isRecompileEnabled()));
             if (compiler.getWorkDir() != null) {
                 sb.append(String.format("\n    Work Directory: %s", 
compiler.getWorkDir()));
             }
@@ -58,6 +59,7 @@ public class GroovyDevConsole extends AbstractDevConsole {
             jo.put("compiledSize", compiler.getCompileSize());
             jo.put("compiledClasses", compiler.getClassesSize());
             jo.put("compiledTime", compiler.getCompileTime());
+            jo.put("recompileEnabled", compiler.isRecompileEnabled());
             if (compiler.getWorkDir() != null) {
                 jo.put("workDir", compiler.getWorkDir());
             }
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 c6764a60d67..5c503ad3679 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
@@ -65,6 +65,13 @@ public class GroovyScriptClassLoader extends ClassLoader 
implements Closeable {
         throw new ClassNotFoundException(name);
     }
 
+    public void removeClass(String name) {
+        Class<?> clazz = classes.get(name);
+        if (clazz != null) {
+            InvokerHelper.removeClass(clazz);
+        }
+    }
+
     @Override
     public void close() throws IOException {
         clear();
diff --git 
a/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/DefaultGroovyCompilerTest.java
 
b/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/DefaultGroovyCompilerTest.java
index 62057ad0dcb..50506f39e93 100644
--- 
a/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/DefaultGroovyCompilerTest.java
+++ 
b/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/DefaultGroovyCompilerTest.java
@@ -33,12 +33,14 @@ public class DefaultGroovyCompilerTest extends 
CamelTestSupport {
         compiler.setScriptPattern("camel-groovy/*");
         compiler.start();
 
-        Class<?> clazz = compiler.loadClass("Dude");
+        Class<?> clazz = 
context.getClassResolver().resolveMandatoryClass("Dude");
         Object dude = ObjectHelper.newInstance(clazz);
+        Assertions.assertNotNull(dude);
+
         Method m = clazz.getMethod("order", int.class);
         Object o = ObjectHelper.newInstance(clazz);
-        Object out = m.invoke(o, 5);
 
+        Object out = m.invoke(o, 5);
         Assertions.assertEquals("I want to order 5 gauda", out);
     }
 
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
index 5c6804dd72e..db9ddabaf0e 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
@@ -32,6 +32,7 @@ import org.apache.camel.Route;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.ServiceStatus;
 import org.apache.camel.StartupSummaryLevel;
+import org.apache.camel.spi.GroovyScriptCompiler;
 import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.PropertiesReload;
 import org.apache.camel.spi.PropertiesSource;
@@ -150,6 +151,8 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
             setResourceReload((name, resource) -> {
                 if (name.endsWith(".properties")) {
                     onPropertiesReload(resource, true);
+                } else if (name.endsWith(".groovy")) {
+                    onGroovyReload(resource, true);
                 } else {
                     onRouteReload(List.of(resource), false);
                 }
@@ -227,6 +230,17 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
         return null;
     }
 
+    protected void onGroovyReload(Resource resource, boolean reloadRoutes) 
throws Exception {
+        GroovyScriptCompiler compiler
+                = 
getCamelContext().getCamelContextExtension().getContextPlugin(GroovyScriptCompiler.class);
+        if (compiler != null) {
+            // trigger all routes to be reloaded (which will also trigger 
reloading this resource)
+            if (reloadRoutes) {
+                onRouteReload(null, false);
+            }
+        }
+    }
+
     @SuppressWarnings("unchecked")
     protected void onRouteReload(Collection<Resource> resources, boolean 
removeEverything) {
         // remember all existing resources
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index 4e15ab82eb7..aad5d65ba2c 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -759,6 +759,10 @@ public class Run extends CamelCommand {
             } else if (isGroovyFile(file)) {
                 // groovy files
                 sjGroovyFiles.add("file:" + file);
+                if (dev) {
+                    // groovy files can also be reloaded
+                    sjReload.add(file);
+                }
                 continue;
             } else if (isTlsFile(file)) {
                 // tls files

Reply via email to