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); }
