You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2025/07/16 08:38:19 UTC
(camel) 01/03: CAMEL-22214: camel-groovy - Reloading of compiled sources
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 <cl...@gmail.com>
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);
}