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