This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch gs in repository https://gitbox.apache.org/repos/asf/camel.git
commit 5d0d1e6d214f234dc3e256daf222a1c4b24dfdd3 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Jul 14 16:52:43 2025 +0200 CAMEL-22214: camel-groovy - Allow to pre-load groovy source files for shared functions and DTOs --- .../language/groovy/DefaultGroovyScriptCompiler.java | 13 ++++++++++++- .../camel/processor/groovy/DefaultGroovyCompilerTest.java | 2 +- .../camel/processor/groovy/GroovyCompilerRouteTest.java | 4 +++- .../processor/groovy/MainGroovyCompilerRouteTest.java | 4 +--- .../resources/{myscript => camel-groovy}/Cheese.groovy | 0 .../test/resources/{myscript => camel-groovy}/Dude.groovy | 0 .../org/apache/camel/impl/engine/SimpleCamelContext.java | 7 ++----- .../META-INF/camel-main-configuration-metadata.json | 2 +- core/camel-main/src/main/docs/main.adoc | 2 +- .../apache/camel/main/DefaultConfigurationConfigurer.java | 10 ++++++---- .../apache/camel/main/DefaultConfigurationProperties.java | 6 +++++- .../camel/dsl/jbang/core/commands/ExportBaseCommand.java | 4 ++++ .../org/apache/camel/dsl/jbang/core/commands/Run.java | 15 +++++++++++++++ .../src/main/java/org/apache/camel/main/KameletMain.java | 6 ++++++ 14 files changed, 57 insertions(+), 18 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 64602f4a5b2..eea750eb45b 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 @@ -31,8 +31,10 @@ import org.apache.camel.spi.Resource; import org.apache.camel.spi.annotations.JdkService; import org.apache.camel.support.PluginHelper; import org.apache.camel.support.service.ServiceSupport; +import org.apache.camel.util.FileUtil; import org.apache.camel.util.IOHelper; import org.apache.camel.util.StopWatch; +import org.apache.camel.util.StringHelper; import org.codehaus.groovy.control.CompilerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -135,8 +137,17 @@ public class DefaultGroovyScriptCompiler extends ServiceSupport loc = resource.getLocation(); } if (loc != null) { - cps.add(loc); 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); } } 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 31350946601..62057ad0dcb 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 @@ -30,7 +30,7 @@ public class DefaultGroovyCompilerTest extends CamelTestSupport { public void testCompiler() throws Exception { DefaultGroovyScriptCompiler compiler = new DefaultGroovyScriptCompiler(); compiler.setCamelContext(context); - compiler.setScriptPattern("myscript/*.groovy"); + compiler.setScriptPattern("camel-groovy/*"); compiler.start(); Class<?> clazz = compiler.loadClass("Dude"); diff --git a/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/GroovyCompilerRouteTest.java b/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/GroovyCompilerRouteTest.java index f29a827801a..62c4cc512b4 100644 --- a/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/GroovyCompilerRouteTest.java +++ b/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/GroovyCompilerRouteTest.java @@ -32,7 +32,9 @@ public class GroovyCompilerRouteTest extends CamelTestSupport { DefaultGroovyScriptCompiler compiler = new DefaultGroovyScriptCompiler(); compiler.setCamelContext(context); - compiler.setScriptPattern("file:src/test/resources/myscript/*.groovy"); + // test with file instead of default + // compiler.setScriptPattern("classpath:camel-groovy/*"); + compiler.setScriptPattern("file:src/test/resources/camel-groovy/*"); context.addService(compiler); return context; diff --git a/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/MainGroovyCompilerRouteTest.java b/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/MainGroovyCompilerRouteTest.java index d9319504930..19126fa6614 100644 --- a/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/MainGroovyCompilerRouteTest.java +++ b/components/camel-groovy/src/test/java/org/apache/camel/processor/groovy/MainGroovyCompilerRouteTest.java @@ -31,7 +31,6 @@ public class MainGroovyCompilerRouteTest { public void testCompilerRoute() throws Exception { Main main = new Main(); main.configure().addRoutesBuilder(createRouteBuilder()); - main.configure().withGroovyScriptPattern("myscript/*.groovy"); main.start(); CamelContext context = main.getCamelContext(); @@ -45,12 +44,11 @@ public class MainGroovyCompilerRouteTest { DefaultGroovyScriptCompiler compiler = context.hasService(DefaultGroovyScriptCompiler.class); Assertions.assertNotNull(compiler); - Assertions.assertEquals("myscript/*.groovy", compiler.getScriptPattern()); + Assertions.assertEquals("classpath:camel-groovy/*", compiler.getScriptPattern()); Assertions.assertEquals(2, compiler.getClassesSize()); Assertions.assertTrue(compiler.getCompileTime() > 0, "Should take time to compile, was: " + compiler.getCompileTime()); main.stop(); - } protected RoutesBuilder createRouteBuilder() throws Exception { diff --git a/components/camel-groovy/src/test/resources/myscript/Cheese.groovy b/components/camel-groovy/src/test/resources/camel-groovy/Cheese.groovy similarity index 100% rename from components/camel-groovy/src/test/resources/myscript/Cheese.groovy rename to components/camel-groovy/src/test/resources/camel-groovy/Cheese.groovy diff --git a/components/camel-groovy/src/test/resources/myscript/Dude.groovy b/components/camel-groovy/src/test/resources/camel-groovy/Dude.groovy similarity index 100% rename from components/camel-groovy/src/test/resources/myscript/Dude.groovy rename to components/camel-groovy/src/test/resources/camel-groovy/Dude.groovy diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java index 45fc96b0158..941ce38253e 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java @@ -481,11 +481,8 @@ public class SimpleCamelContext extends AbstractCamelContext { getCamelContextExtension().getBootstrapFactoryFinder(), GroovyScriptCompiler.FACTORY, GroovyScriptCompiler.class); - if (result.isPresent()) { - return result.get(); - } else { - throw new IllegalArgumentException("Cannot find GroovyScriptCompiler on classpath. Add camel-groovy to classpath."); - } + // camel-groovy is optional + return result.orElse(null); } private CliConnectorFactory createCliConnectorFactory() { diff --git a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json index c857def438b..6d6f1b846e3 100644 --- a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json +++ b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json @@ -72,7 +72,7 @@ { "name": "camel.main.extraShutdownTimeout", "description": "Extra timeout in seconds to graceful shutdown Camel. When Camel is shutting down then Camel first shutdown all the routes (shutdownTimeout). Then additional services is shutdown (extraShutdownTimeout).", "sourceType": "org.apache.camel.main.MainConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 15 }, { "name": "camel.main.fileConfigurations", "description": "Directory to load additional configuration files that contains configuration values that takes precedence over any other configuration. This can be used to refer to files that may have secret configuration that has been mounted on the file system for containers. You can specify a pattern to load from sub directories and a name pattern such as \/var\/app\/secret\/.properties, multiple directories can be separated by comma.", " [...] { "name": "camel.main.globalOptions", "description": "Sets global options that can be referenced in the camel context Important: This has nothing to do with property placeholders, and is just a plain set of key\/value pairs which are used to configure global options on CamelContext, such as a maximum debug logging length etc.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "object", "javaType": "java.util.Map" }, - { "name": "camel.main.groovyScriptPattern", "description": "Directories to scan for groovy source to be pre-compiled. For example: scripts\/.groovy will scan inside the classpath folder scripts for all groovy source files. By default, sources are scanned from the classpath, but you can prefix with file: to use file system. The directories are using Ant-path style pattern, and multiple directories can be specified separated by comma.", "sourceType": "org.apache.camel.main.DefaultConfi [...] + { "name": "camel.main.groovyScriptPattern", "description": "Directories to scan for groovy source to be pre-compiled. For example: scripts\/.groovy will scan inside the classpath folder scripts for all groovy source files. By default, sources are scanned from the classpath, but you can prefix with file: to use file system. The directories are using Ant-path style pattern, and multiple directories can be specified separated by comma. This requires having camel-groovy JAR on the classp [...] { "name": "camel.main.inflightRepositoryBrowseEnabled", "description": "Sets whether the inflight repository should allow browsing each inflight exchange. This is by default disabled as there is a very slight performance overhead when enabled.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, { "name": "camel.main.javaRoutesExcludePattern", "description": "Used for exclusive filtering RouteBuilder classes which are collected from the registry or via classpath scanning. The exclusive filtering takes precedence over inclusive filtering. The pattern is using Ant-path style pattern. Multiple patterns can be specified separated by comma. For example to exclude all classes starting with Bar use: **\/Bar* To exclude all routes form a specific package use: com\/mycomp [...] { "name": "camel.main.javaRoutesIncludePattern", "description": "Used for inclusive filtering RouteBuilder classes which are collected from the registry or via classpath scanning. The exclusive filtering takes precedence over inclusive filtering. The pattern is using Ant-path style pattern. Multiple patterns can be specified separated by comma. Multiple patterns can be specified separated by comma. For example to include all classes starting with Foo use: **\/Foo To include a [...] diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc index dcbbafc5283..5853f280995 100644 --- a/core/camel-main/src/main/docs/main.adoc +++ b/core/camel-main/src/main/docs/main.adoc @@ -69,7 +69,7 @@ The camel.main supports 123 options, which are listed below. | *camel.main.extraShutdown{zwsp}Timeout* | Extra timeout in seconds to graceful shutdown Camel. When Camel is shutting down then Camel first shutdown all the routes (shutdownTimeout). Then additional services is shutdown (extraShutdownTimeout). | 15 | int | *camel.main.fileConfigurations* | Directory to load additional configuration files that contains configuration values that takes precedence over any other configuration. This can be used to refer to files that may have secret configuration that has been mounted on the file system for containers. You can specify a pattern to load from sub directories and a name pattern such as /var/app/secret/.properties, multiple directories can be separated by comma. | | String | *camel.main.globalOptions* | Sets global options that can be referenced in the camel context Important: This has nothing to do with property placeholders, and is just a plain set of key/value pairs which are used to configure global options on CamelContext, such as a maximum debug logging length etc. | | Map -| *camel.main.groovyScriptPattern* | Directories to scan for groovy source to be pre-compiled. For example: scripts/.groovy will scan inside the classpath folder scripts for all groovy source files. By default, sources are scanned from the classpath, but you can prefix with file: to use file system. The directories are using Ant-path style pattern, and multiple directories can be specified separated by comma. | | String +| *camel.main.groovyScriptPattern* | Directories to scan for groovy source to be pre-compiled. For example: scripts/.groovy will scan inside the classpath folder scripts for all groovy source files. By default, sources are scanned from the classpath, but you can prefix with file: to use file system. The directories are using Ant-path style pattern, and multiple directories can be specified separated by comma. This requires having camel-groovy JAR on the classpath. | classpath:camel-groov [...] | *camel.main.inflightRepository{zwsp}BrowseEnabled* | Sets whether the inflight repository should allow browsing each inflight exchange. This is by default disabled as there is a very slight performance overhead when enabled. | false | boolean | *camel.main.javaRoutesExclude{zwsp}Pattern* | Used for exclusive filtering RouteBuilder classes which are collected from the registry or via classpath scanning. The exclusive filtering takes precedence over inclusive filtering. The pattern is using Ant-path style pattern. Multiple patterns can be specified separated by comma. For example to exclude all classes starting with Bar use: **/Bar* To exclude all routes form a specific package use: com/mycompany/bar/* To exclud [...] | *camel.main.javaRoutesInclude{zwsp}Pattern* | Used for inclusive filtering RouteBuilder classes which are collected from the registry or via classpath scanning. The exclusive filtering takes precedence over inclusive filtering. The pattern is using Ant-path style pattern. Multiple patterns can be specified separated by comma. Multiple patterns can be specified separated by comma. For example to include all classes starting with Foo use: **/Foo To include all routes form a speci [...] diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java index 01c95520170..7ee6a68df70 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java @@ -354,10 +354,12 @@ public final class DefaultConfigurationConfigurer { } if (config.getGroovyScriptPattern() != null) { GroovyScriptCompiler gsc = camelContext.getCamelContextExtension().getContextPlugin(GroovyScriptCompiler.class); - gsc.setScriptPattern(config.getGroovyScriptPattern()); - camelContext.addService(gsc); - // force start compiler eager so Camel routes can load these pre-compiled classes - ServiceHelper.startService(gsc); + if (gsc != null) { + gsc.setScriptPattern(config.getGroovyScriptPattern()); + camelContext.addService(gsc); + // force start compiler eager so Camel routes can load these pre-compiled classes + ServiceHelper.startService(gsc); + } } if (config.getRouteFilterIncludePattern() != null || config.getRouteFilterExcludePattern() != null) { diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java index f09fca6bb2c..288fb9deafb 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java @@ -129,7 +129,7 @@ public abstract class DefaultConfigurationProperties<T> { @Metadata(defaultValue = "true") private boolean routesReloadRemoveAllRoutes = true; private boolean routesReloadRestartDuration; - private String groovyScriptPattern; + private String groovyScriptPattern = "classpath:camel-groovy/*"; @Metadata(defaultValue = "default", enums = "default,prototype,pooled") private String exchangeFactory = "default"; private int exchangeFactoryCapacity = 100; @@ -1406,6 +1406,8 @@ public abstract class DefaultConfigurationProperties<T> { * By default, sources are scanned from the classpath, but you can prefix with file: to use file system. * * The directories are using Ant-path style pattern, and multiple directories can be specified separated by comma. + * + * This requires having camel-groovy JAR on the classpath. */ public void setGroovyScriptPattern(String groovyScriptPattern) { this.groovyScriptPattern = groovyScriptPattern; @@ -2792,6 +2794,8 @@ public abstract class DefaultConfigurationProperties<T> { * By default, sources are scanned from the classpath, but you can prefix with file: to use file system. * * The directories are using Ant-path style pattern, and multiple directories can be specified separated by comma. + * + * This requires having camel-groovy JAR on the classpath. */ public T withGroovyScriptPattern(String groovyScriptPattern) { this.groovyScriptPattern = groovyScriptPattern; diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java index 955ca465adb..e28970ce739 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportBaseCommand.java @@ -76,6 +76,7 @@ public abstract class ExportBaseCommand extends CamelCommand { "camel.component.kamelet.location", "camel.jbang.classpathFiles", "camel.jbang.localKameletDir", + "camel.jbang.groovyFiles", "camel.jbang.scriptFiles", "camel.jbang.tlsFiles", "camel.jbang.jkubeFiles", @@ -616,6 +617,7 @@ public abstract class ExportBaseCommand extends CamelCommand { boolean camel = !kamelet && "camel.main.routesIncludePattern".equals(k); boolean jkube = "camel.jbang.jkubeFiles".equals(k); boolean script = "camel.jbang.scriptFiles".equals(k); + boolean groovy = "camel.jbang.groovyFiles".equals(k); boolean tls = "camel.jbang.tlsFiles".equals(k); boolean web = ext != null && List.of("css", "html", "ico", "jpeg", "jpg", "js", "png").contains(ext); Path targetDir; @@ -627,6 +629,8 @@ public abstract class ExportBaseCommand extends CamelCommand { targetDir = srcKameletsResourcesDir; } else if (script) { targetDir = srcJavaDirRoot.getParent().resolve("scripts"); + } else if (groovy) { + targetDir = srcResourcesDir.resolve("camel-groovy"); } else if (tls) { targetDir = srcJavaDirRoot.getParent().resolve("tls"); } else if (web) { 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 38bcdba303f..4e15ab82eb7 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 @@ -716,6 +716,7 @@ public class Run extends CamelCommand { StringJoiner sjReload = new StringJoiner(","); StringJoiner sjClasspathFiles = new StringJoiner(","); StringJoiner sjScriptFiles = new StringJoiner(","); + StringJoiner sjGroovyFiles = new StringJoiner(","); StringJoiner sjTlsFiles = new StringJoiner(","); StringJoiner sjKamelets = new StringJoiner(","); StringJoiner sjJKubeFiles = new StringJoiner(","); @@ -755,6 +756,10 @@ public class Run extends CamelCommand { // script files sjScriptFiles.add(file); continue; + } else if (isGroovyFile(file)) { + // groovy files + sjGroovyFiles.add("file:" + file); + continue; } else if (isTlsFile(file)) { // tls files sjTlsFiles.add(file); @@ -869,6 +874,12 @@ public class Run extends CamelCommand { } else { writeSetting(main, profileProperties, "camel.jbang.scriptFiles", () -> null); } + if (sjGroovyFiles.length() > 0) { + main.addInitialProperty("camel.jbang.groovyFiles", sjGroovyFiles.toString()); + writeSettings("camel.jbang.groovyFiles", sjGroovyFiles.toString()); + } else { + writeSetting(main, profileProperties, "camel.jbang.groovyFiles", () -> null); + } if (sjTlsFiles.length() > 0) { main.addInitialProperty("camel.jbang.tlsFiles", sjTlsFiles.toString()); writeSettings("camel.jbang.tlsFiles", sjTlsFiles.toString()); @@ -1946,6 +1957,10 @@ public class Run extends CamelCommand { return false; } + private boolean isGroovyFile(String name) { + return name.endsWith(".groovy"); + } + private boolean isScriptFile(String name) { return name.endsWith(".sh"); } diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java index 272f554893d..cfc5b7225bb 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java @@ -440,6 +440,12 @@ public class KameletMain extends MainCommandLineSupport { pc.setPropertiesFunctionResolver(new DependencyDownloaderPropertiesFunctionResolver(answer, false)); } + // groovy scripts + String groovyFiles = getInitialProperties().getProperty(getInstanceType() + ".groovyFiles"); + if (groovyFiles != null) { + configure().withGroovyScriptPattern(groovyFiles); + } + boolean prompt = "true".equals(getInitialProperties().get(getInstanceType() + ".prompt")); if (prompt) { answer.getPropertiesComponent().addPropertiesSource(new PromptPropertyPlaceholderSource());
