This is an automated email from the ASF dual-hosted git repository.
christophd 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 16815cdf1624 CAMEL-23817: Embed test plugin in Camel launcher
16815cdf1624 is described below
commit 16815cdf1624f6be81b1a10f6679aaa5dfd5dda4
Author: Christoph Deppisch <[email protected]>
AuthorDate: Wed Jun 24 22:21:32 2026 +0200
CAMEL-23817: Embed test plugin in Camel launcher
- Use Citrus JBang commands directly from launcher instead of spawning a
new JBang process
- Update to Citrus 5.0.0-M2 for Jackson 3 support and Camel 4.20+
---
dsl/camel-jbang/camel-jbang-plugin-test/pom.xml | 13 +-
.../dsl/jbang/core/commands/test/TestCommand.java | 3 +-
.../dsl/jbang/core/commands/test/TestPlugin.java | 175 ++++++++++++---------
.../core/commands/test/TestPluginExporter.java | 1 +
dsl/camel-jbang/camel-launcher/pom.xml | 6 +-
.../dsl/jbang/launcher/CamelLauncherMain.java | 3 +-
parent/pom.xml | 2 +-
7 files changed, 125 insertions(+), 78 deletions(-)
diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/pom.xml
b/dsl/camel-jbang/camel-jbang-plugin-test/pom.xml
index 56f87f636aa0..173091e98cd3 100644
--- a/dsl/camel-jbang/camel-jbang-plugin-test/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-plugin-test/pom.xml
@@ -49,8 +49,19 @@
<dependency>
<groupId>org.citrusframework</groupId>
- <artifactId>citrus-jbang-connector</artifactId>
+ <artifactId>citrus-jbang</artifactId>
<version>${citrus-version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-tooling-maven</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-tooling-maven</artifactId>
+ <version>${project.version}</version>
</dependency>
<!-- Test dependencies -->
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestCommand.java
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestCommand.java
index a4ad5adb6c5b..0d0e80b79c5d 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestCommand.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestCommand.java
@@ -31,6 +31,7 @@ public class TestCommand extends CamelCommand {
@Override
public Integer doCall() throws Exception {
// defaults to printing the help message
- return new CommandLine(new CommandLine.HelpCommand()).execute();
+ new CommandLine(this).execute("--help");
+ return 0;
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPlugin.java
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPlugin.java
index 275976307356..59a860268e7d 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPlugin.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPlugin.java
@@ -21,9 +21,6 @@ import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.Optional;
import org.apache.camel.RuntimeCamelException;
@@ -33,12 +30,19 @@ import
org.apache.camel.dsl.jbang.core.commands.ExportHelper;
import org.apache.camel.dsl.jbang.core.common.CamelJBangPlugin;
import org.apache.camel.dsl.jbang.core.common.Plugin;
import org.apache.camel.dsl.jbang.core.common.PluginExporter;
+import org.apache.camel.dsl.jbang.core.common.Printer;
import org.apache.camel.util.IOHelper;
import org.citrusframework.CitrusSettings;
import org.citrusframework.CitrusVersion;
-import org.citrusframework.jbang.JBangSettings;
-import org.citrusframework.jbang.JBangSupport;
-import org.citrusframework.jbang.ProcessAndOutput;
+import org.citrusframework.jbang.CitrusJBangMain;
+import org.citrusframework.jbang.commands.Agent;
+import org.citrusframework.jbang.commands.AgentRun;
+import org.citrusframework.jbang.commands.AgentStart;
+import org.citrusframework.jbang.commands.AgentStop;
+import org.citrusframework.jbang.commands.Init;
+import org.citrusframework.jbang.commands.Inspect;
+import org.citrusframework.jbang.commands.ListTests;
+import org.citrusframework.jbang.commands.Run;
import picocli.CommandLine;
@CamelJBangPlugin(name = "camel-jbang-plugin-test", firstVersion = "4.14.0")
@@ -46,10 +50,21 @@ public class TestPlugin implements Plugin {
@Override
public void customize(CommandLine commandLine, CamelJBangMain main) {
- commandLine.setExecutionStrategy(new CitrusExecutionStrategy(main))
- .addSubcommand("test", new CommandLine(new TestCommand(main))
- .setUnmatchedArgumentsAllowed(true)
- .setUnmatchedOptionsAllowedAsOptionParameters(true));
+ CitrusJBangMain citrus = new CitrusJBangMain();
+ citrus.withPrinter(new PipedPrinter(main.getOut()));
+
+ var cmd = new CommandLine(new TestCommand(main))
+ .addSubcommand("init", new CommandLine(new Init(citrus)))
+ .addSubcommand("inspect", new CommandLine(new Inspect(citrus)))
+ .addSubcommand("run", new CommandLine(new Run(citrus)))
+ .addSubcommand("ps", new CommandLine(new ListTests(citrus)),
"ls")
+ .addSubcommand("agent", new CommandLine(new Agent(citrus))
+ .addSubcommand("start", new CommandLine(new
AgentStart(citrus)))
+ .addSubcommand("run", new CommandLine(new
AgentRun(citrus)))
+ .addSubcommand("stop", new CommandLine(new
AgentStop(citrus))));
+
+ commandLine.addSubcommand("test", cmd)
+ .setExecutionStrategy(new CitrusExecutionStrategy(main));
}
@Override
@@ -58,10 +73,8 @@ public class TestPlugin implements Plugin {
}
/**
- * Command execution strategy delegates to Citrus JBang for subcommands
like init or run. Performs special command
- * preparations and makes sure to run the proper Citrus version for this
Camel release.
- *
- * @param main Camel CLI main that provides the output printer.
+ * Command execution strategy performs special command preparations and
makes sure to set and run the proper Citrus
+ * version for this Camel release.
*/
private record CitrusExecutionStrategy(CamelJBangMain main) implements
CommandLine.IExecutionStrategy {
@@ -71,38 +84,49 @@ public class TestPlugin implements Plugin {
public int execute(CommandLine.ParseResult parseResult)
throws CommandLine.ExecutionException,
CommandLine.ParameterException {
- String command;
- List<String> args = Collections.emptyList();
-
- if (parseResult.originalArgs().size() > 2) {
- command = parseResult.originalArgs().get(1);
- args = parseResult.originalArgs().subList(2,
parseResult.originalArgs().size());
- } else if (parseResult.originalArgs().size() == 2) {
- command = parseResult.originalArgs().get(1);
- } else {
- // run help command by default
- command = "--help";
+ if (!parseResult.errors().isEmpty() ||
!parseResult.unmatched().isEmpty()) {
+ // Something is wrong with the command - do not adjust anything
+ return new CommandLine.RunLast().execute(parseResult);
}
- JBangSupport citrus =
JBangSupport.jbang().app(JBangSettings.getApp())
- .withSystemProperty("citrus.jbang.version",
CitrusVersion.version())
- .withSystemProperty("citrus.camel.jbang.version", new
VersionHelper().getVersion());
+ if (isCitrusCommand(parseResult)) {
+ String command = "";
+ if (parseResult.originalArgs().size() > 2) {
+ command = parseResult.originalArgs().get(1);
+ } else if (parseResult.originalArgs().size() == 2) {
+ command = parseResult.originalArgs().get(1);
+ }
+
+ System.setProperty("citrus.jbang.version",
CitrusVersion.version());
+ System.setProperty("citrus.camel.jbang.version", new
VersionHelper().getVersion());
+
+ // Prepare commands
+ if ("init".equals(command)) {
+ prepareInitCommand();
+ }
- // Prepare commands
- if ("init".equals(command)) {
- return executeInitCommand(citrus, args);
- } else if ("run".equals(command)) {
- return executeRunCommand(citrus, args);
+ if (!isCamelLauncherRuntime()) {
+ var tccLoader =
Thread.currentThread().getContextClassLoader();
+ try {
+ // Update class loader so Citrus is able to resolve
resources
+ ClassLoader cl = TestPlugin.class.getClassLoader();
+ Thread.currentThread().setContextClassLoader(cl);
+
+ return new CommandLine.RunLast().execute(parseResult);
+ } finally {
+
Thread.currentThread().setContextClassLoader(tccLoader);
+ }
+ }
}
- return execute(citrus, command, args);
+ return new CommandLine.RunLast().execute(parseResult);
}
/**
- * Prepare and execute init command. Automatically uses test subfolder
as a working directory for creating new
- * tests. Automatically adds a jbang.properties configuration to add
required Camel Citrus dependencies.
+ * Prepare init command. Automatically uses test subfolder as a
working directory for creating new tests.
+ * Automatically adds a citrus-application.properties configuration if
not present.
*/
- private int executeInitCommand(JBangSupport citrus, List<String> args)
{
+ private void prepareInitCommand() {
Path currentDir = Paths.get(".");
Path workingDir;
// Automatically set test subfolder as a working directory
@@ -112,11 +136,11 @@ public class TestPlugin implements Plugin {
} else if (currentDir.resolve(TEST_DIR).toFile().exists()) {
// navigate to existing test subfolder
workingDir = currentDir.resolve(TEST_DIR);
- citrus.workingDir(workingDir);
+ System.setProperty(CitrusSettings.RESOURCES_WORKDIR_PROPERTY,
workingDir.toString());
} else if (currentDir.resolve(TEST_DIR).toFile().mkdirs()) {
// create test subfolder and navigate to it
workingDir = currentDir.resolve(TEST_DIR);
- citrus.workingDir(workingDir);
+ System.setProperty(CitrusSettings.RESOURCES_WORKDIR_PROPERTY,
workingDir.toString());
} else {
throw new RuntimeCamelException("Cannot create test working
directory in: " + currentDir);
}
@@ -139,50 +163,55 @@ public class TestPlugin implements Plugin {
.formatted(CitrusSettings.getApplicationPropertiesFile(),
citrusApplicationProperties));
}
}
+ }
- return execute(citrus, "init", args);
+ /**
+ * Evaluate if the current runtime is using Camel Launcher. Camel
launcher sets a System property marking the
+ * runtime nature. If the System property is not present we can assume
that the runtime is something different
+ * e.g. Camel JBang.
+ */
+ private boolean isCamelLauncherRuntime() {
+ return Boolean.parseBoolean(System.getProperty("camel.launcher",
"false"));
}
/**
- * Prepare and execute Citrus run command. Automatically navigates to
test subfolder if it is present and uses
- * this as a working directory. Runs command asynchronous streaming
logs to the main output of this Camel CLI
- * process.
+ * Evaluate command user object and check if this is a Citrus command
implementation.
*/
- private int executeRunCommand(JBangSupport citrus, List<String> args) {
- Path currentDir = Paths.get(".");
- List<String> runArgs = new ArrayList<>(args);
- // automatically navigate to test subfolder for test execution
- if (currentDir.resolve(TEST_DIR).toFile().exists()) {
- // set test subfolder as working directory
- citrus.workingDir(currentDir.resolve(TEST_DIR));
-
- // remove test folder prefix in test file path if present
- if (!args.isEmpty() && args.get(0).startsWith(TEST_DIR + "/"))
{
- runArgs = new ArrayList<>(args.subList(1, args.size()));
- runArgs.add(0, args.get(0).substring((TEST_DIR +
"/").length()));
- }
+ private boolean isCitrusCommand(CommandLine.ParseResult parseResult) {
+ CommandLine.ParseResult subcommand = parseResult;
+ while (subcommand.hasSubcommand()) {
+ subcommand = subcommand.subcommand();
}
- citrus.withOutputListener(output -> main.getOut().print(output));
- ProcessAndOutput pao = citrus.runAsync("run", runArgs);
- try {
- pao.waitFor();
- } catch (InterruptedException e) {
- main.getOut().printErr("Interrupted while running Citrus
command", e);
- }
+ Object commandObject = subcommand.commandSpec().userObject();
+ return
commandObject.getClass().getName().startsWith("org.citrusframework");
+ }
+ }
- return pao.getProcess().exitValue();
+ /**
+ * Delegates Citrus printer API methods to Camel printer implementation.
+ *
+ * @param delegate the Camel printer.
+ */
+ private record PipedPrinter(Printer delegate) implements
org.citrusframework.jbang.Printer {
+ @Override
+ public void println() {
+ delegate.println();
}
- /**
- * Uses given Citrus JBang instance to run the given command using the
given arguments.
- *
- * @return exit code of the command process.
- */
- private int execute(JBangSupport citrus, String command, List<String>
args) {
- ProcessAndOutput pao = citrus.run(command, args);
- main.getOut().print(pao.getOutput());
- return pao.getProcess().exitValue();
+ @Override
+ public void println(String line) {
+ delegate.println(line);
+ }
+
+ @Override
+ public void print(String output) {
+ delegate.print(output);
+ }
+
+ @Override
+ public void printf(String format, Object... args) {
+ delegate.printf(format, args);
}
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPluginExporter.java
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPluginExporter.java
index 2bf527e832d5..4ffb3ee4ecb1 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPluginExporter.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-test/src/main/java/org/apache/camel/dsl/jbang/core/commands/test/TestPluginExporter.java
@@ -84,6 +84,7 @@ public class TestPluginExporter implements PluginExporter {
deps.add(asDependency("citrus-yaml"));
deps.add(asDependency("citrus-xml"));
deps.add(asDependency("citrus-groovy"));
+ deps.add(asDependency("citrus-validation-text"));
deps.add(asDependency("citrus-validation-xml"));
deps.add(asDependency("citrus-validation-json"));
deps.add(asDependency("citrus-validation-yaml"));
diff --git a/dsl/camel-jbang/camel-launcher/pom.xml
b/dsl/camel-jbang/camel-launcher/pom.xml
index a5d5c08b01d5..00be949d3f02 100644
--- a/dsl/camel-jbang/camel-launcher/pom.xml
+++ b/dsl/camel-jbang/camel-launcher/pom.xml
@@ -66,12 +66,16 @@
<!-- Pre-installed plugins -->
<!-- the edit plugin is not pre-installed -->
<!-- the route-parser plugin is not pre-installed -->
- <!-- the citrus test plugin cannot be embedded -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jbang-plugin-generate</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-jbang-plugin-test</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jbang-plugin-kubernetes</artifactId>
diff --git
a/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java
b/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java
index 31f0d0017ff0..7c36cddb6b17 100644
---
a/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java
+++
b/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.jbang.launcher;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.commands.generate.GeneratePlugin;
import org.apache.camel.dsl.jbang.core.commands.kubernetes.KubernetesPlugin;
+import org.apache.camel.dsl.jbang.core.commands.test.TestPlugin;
import org.apache.camel.dsl.jbang.core.commands.tui.TuiPlugin;
import org.apache.camel.dsl.jbang.core.commands.validate.ValidatePlugin;
import picocli.CommandLine;
@@ -35,7 +36,7 @@ public class CamelLauncherMain extends CamelJBangMain {
new KubernetesPlugin().customize(commandLine, this);
new TuiPlugin().customize(commandLine, this);
new ValidatePlugin().customize(commandLine, this);
- // citrus test plugin cannot (yet) be embedded
+ new TestPlugin().customize(commandLine, this);
}
}
diff --git a/parent/pom.xml b/parent/pom.xml
index 3854a6d740c5..727ad809799d 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -102,7 +102,7 @@
<jta-api-1.2-version>1.2</jta-api-1.2-version>
<chicory-version>1.7.5</chicory-version>
<chunk-templates-version>3.6.2</chunk-templates-version>
- <citrus-version>4.10.1</citrus-version>
+ <citrus-version>5.0.0-M2</citrus-version>
<classgraph-version>4.8.184</classgraph-version>
<cloudant-version>0.10.19</cloudant-version>
<com-ibm-mq-jakarta-client-version>9.4.5.1</com-ibm-mq-jakarta-client-version>