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 33ac7a87ba9 camel-jbang - Run background should wait (#16418)
33ac7a87ba9 is described below
commit 33ac7a87ba9a9889fec130c67bbaa302de252fb3
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Dec 2 13:15:05 2024 +0100
camel-jbang - Run background should wait (#16418)
* CAMEL-21463: Camel-jbang while running the process in background unable
to access log if the process start fails with errors
---
.../modules/ROOT/pages/camel-jbang.adoc | 7 +
.../dsl/jbang/core/commands/CamelCommand.java | 4 +
.../apache/camel/dsl/jbang/core/commands/Run.java | 167 +++++++++++++++------
3 files changed, 128 insertions(+), 50 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index 741af075c3e..2337bb64633 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -1775,6 +1775,8 @@ The `run` command allows running Camel in the background
with the `--background`
Therefore, to see and understand what happens then the management commands
cane be used, such as `camel ps`, `camel get`, and `camel log`.
+NOTE: Only Camel Main is supported to run in background
+
[source,bash]
----
$ camel run chuck.yaml --background
@@ -1802,6 +1804,11 @@ $ camel stop chuck
Shutting down Camel integration (pid: 80093)
----
+When running in background, then Camel JBang (**4.10 onwards**) will now
automatic wait for the integration
+to startup before returning from the CLI command. This ensures that if there
are any startup
+errors such as compilation errors or DSL errors etc. then these are captured
and printed in the shell.
+You can use the option `--background-wait=false` to turn this off.
+
==== Starting and Stopping routes
The `camel cmd` is intended for executing miscellaneous commands in the
running Camel integrations.
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
index de8247f53f4..7667dbefc4b 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
@@ -117,6 +117,10 @@ public abstract class CamelCommand implements
Callable<Integer> {
return new File(CommandLineHelper.getCamelDir(), pid + "-debug.json");
}
+ public File getRunBackgroundLogFile(String uuid) {
+ return new File(CommandLineHelper.getCamelDir(), uuid + "-run.log");
+ }
+
protected Printer printer() {
var out = getMain().getOut();
CommandHelper.SetPrinter(out);
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 95a5f19d4d1..d14a6e35056 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
@@ -22,6 +22,7 @@ import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -59,7 +60,10 @@ import org.apache.camel.util.CamelCaseOrderedProperties;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StopWatch;
import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
import org.apache.camel.xml.io.util.XmlStreamDetector;
import org.apache.camel.xml.io.util.XmlStreamInfo;
import picocli.CommandLine;
@@ -68,6 +72,7 @@ import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import static
org.apache.camel.dsl.jbang.core.common.CamelCommandHelper.CAMEL_INSTANCE_TYPE;
+import static
org.apache.camel.dsl.jbang.core.common.CamelCommandHelper.extractState;
import static
org.apache.camel.dsl.jbang.core.common.GistHelper.asGistSingleUrl;
import static org.apache.camel.dsl.jbang.core.common.GistHelper.fetchGistUrls;
import static
org.apache.camel.dsl.jbang.core.common.GitHubHelper.asGithubSingleUrl;
@@ -129,6 +134,10 @@ public class Run extends CamelCommand {
@Option(names = { "--background" }, defaultValue = "false", description =
"Run in the background")
public boolean background;
+ @Option(names = { "--background-wait" }, defaultValue = "true",
+ description = "To wait for run in background to startup
successfully, before returning")
+ public boolean backgroundWait = true;
+
@Option(names = { "--empty" }, defaultValue = "false", description = "Run
an empty Camel without loading source files")
public boolean empty;
@@ -896,6 +905,11 @@ public class Run extends CamelCommand {
}
protected int runQuarkus() throws Exception {
+ if (background) {
+ printer().println("Run Camel Quarkus with --background is not
supported");
+ return 1;
+ }
+
// create temp run dir
File runDir = new File(RUN_PLATFORM_DIR,
Long.toString(System.currentTimeMillis()));
if (!this.background) {
@@ -934,14 +948,14 @@ public class Run extends CamelCommand {
eq.ignoreLoadingError = this.ignoreLoadingError;
eq.lazyBean = this.lazyBean;
+ printer().println("Running using Quarkus v" + eq.quarkusVersion + "
(preparing and downloading files)");
+
// run export
int exit = eq.export();
if (exit != 0 || this.exportRun) {
return exit;
}
- System.out.println("Running using Quarkus v" + eq.quarkusVersion + "
(preparing and downloading files)");
-
// run quarkus via maven
String mvnw = "/mvnw";
if (FileUtil.isWindows()) {
@@ -950,24 +964,19 @@ public class Run extends CamelCommand {
ProcessBuilder pb = new ProcessBuilder();
pb.command(runDir + mvnw, "--quiet", "--file", runDir.toString(),
"package", "quarkus:" + (dev ? "dev" : "run"));
- if (background) {
- Process p = pb.start();
- this.spawnPid = p.pid();
- if (!exportRun && !transformRun && !transformMessageRun) {
- printer().println("Running Camel Quarkus integration: " + name
+ " (version: " + eq.quarkusVersion
- + ") in background");
- }
- return 0;
- } else {
- pb.inheritIO(); // run in foreground (with IO so logs are visible)
- Process p = pb.start();
- this.spawnPid = p.pid();
- // wait for that process to exit as we run in foreground
- return p.waitFor();
- }
+ pb.inheritIO(); // run in foreground (with IO so logs are visible)
+ Process p = pb.start();
+ this.spawnPid = p.pid();
+ // wait for that process to exit as we run in foreground
+ return p.waitFor();
}
protected int runSpringBoot() throws Exception {
+ if (background) {
+ printer().println("Run Camel Spring Boot with --background is not
supported");
+ return 1;
+ }
+
// create temp run dir
File runDir = new File(RUN_PLATFORM_DIR,
Long.toString(System.currentTimeMillis()));
if (!this.background) {
@@ -1010,14 +1019,14 @@ public class Run extends CamelCommand {
eq.ignoreLoadingError = this.ignoreLoadingError;
eq.lazyBean = this.lazyBean;
+ printer().println("Running using Spring Boot v" + eq.springBootVersion
+ " (preparing and downloading files)");
+
// run export
int exit = eq.export();
if (exit != 0 || exportRun) {
return exit;
}
- System.out.println("Running using Spring Boot v" +
eq.springBootVersion + " (preparing and downloading files)");
-
// prepare spring-boot for logging to file
InputStream is =
Run.class.getClassLoader().getResourceAsStream("spring-boot-logback.xml");
eq.safeCopy(is, new File(eq.exportDir +
"/src/main/resources/logback.xml"));
@@ -1030,21 +1039,11 @@ public class Run extends CamelCommand {
}
pb.command(runDir + mvnw, "--quiet", "--file", runDir.toString(),
"spring-boot:run");
- if (background) {
- Process p = pb.start();
- this.spawnPid = p.pid();
- if (!exportRun && !transformRun && !transformMessageRun) {
- printer().println("Running Camel Spring Boot integration: " +
name + " (version: " + camelVersion
- + ") in background");
- }
- return 0;
- } else {
- pb.inheritIO(); // run in foreground (with IO so logs are visible)
- Process p = pb.start();
- this.spawnPid = p.pid();
- // wait for that process to exit as we run in foreground
- return p.waitFor();
- }
+ pb.inheritIO(); // run in foreground (with IO so logs are visible)
+ Process p = pb.start();
+ this.spawnPid = p.pid();
+ // wait for that process to exit as we run in foreground
+ return p.waitFor();
}
private boolean acceptPropertiesFile(String file) {
@@ -1150,6 +1149,7 @@ public class Run extends CamelCommand {
openapi = answer.getProperty("camel.jbang.open-api", openapi);
download =
"true".equals(answer.getProperty("camel.jbang.download", download ? "true" :
"false"));
background =
"true".equals(answer.getProperty("camel.jbang.background", background ? "true"
: "false"));
+ backgroundWait =
"true".equals(answer.getProperty("camel.jbang.backgroundWait", backgroundWait ?
"true" : "false"));
jvmDebugPort =
parseJvmDebugPort(answer.getProperty("camel.jbang.jvmDebug",
Integer.toString(jvmDebugPort)));
camelVersion = answer.getProperty("camel.jbang.camel-version",
camelVersion);
kameletsVersion =
answer.getProperty("camel.jbang.kameletsVersion", kameletsVersion);
@@ -1201,6 +1201,9 @@ public class Run extends CamelCommand {
if (background) {
cmds.remove("--background=true");
cmds.remove("--background");
+ cmds.remove("--background-wait");
+ cmds.remove("--background-wait=false");
+ cmds.remove("--background-wait=true");
}
if (camelVersion != null) {
cmds.remove("--camel-version=" + camelVersion);
@@ -1236,13 +1239,7 @@ public class Run extends CamelCommand {
pb.command(jbangArgs);
if (background) {
- Process p = pb.start();
- this.spawnPid = p.pid();
- if (!exportRun && !transformRun && !transformMessageRun) {
- printer().println("Running Camel integration: " + name + "
(version: " + camelVersion
- + ") in background with PID: " + p.pid());
- }
- return 0;
+ return runBackgroundProcess(pb, "Camel Main");
} else {
pb.inheritIO(); // run in foreground (with IO so logs are visible)
Process p = pb.start();
@@ -1266,17 +1263,74 @@ public class Run extends CamelCommand {
cmds.remove("--background=true");
cmds.remove("--background");
+ cmds.remove("--background-wait=false");
+ cmds.remove("--background-wait=true");
+ cmds.remove("--background-wait");
addCamelCommand(cmds);
ProcessBuilder pb = new ProcessBuilder();
pb.command(cmds);
+
+ return runBackgroundProcess(pb, "Camel Main");
+ }
+
+ protected int runBackgroundProcess(ProcessBuilder pb, String kind) throws
Exception {
+ File log = null;
+ if (backgroundWait) {
+ // store background output in a log file to capture any error on
startup
+ log = getRunBackgroundLogFile("" + new Random().nextLong());
+ log.deleteOnExit();
+ pb.redirectErrorStream(true);
+ pb.redirectOutput(log);
+ }
+
Process p = pb.start();
this.spawnPid = p.pid();
if (!exportRun && !transformRun && !transformMessageRun) {
- printer().println("Running Camel integration: " + name + " in
background with PID: " + p.pid());
+ printer().println(
+ "Running " + kind + ": " + name + " in background with
PID: " + p.pid()
+ + (backgroundWait ? " (waiting to startup)" :
""));
+ }
+
+ int ec = 0;
+ if (log != null) {
+ StopWatch watch = new StopWatch();
+ int state = 0; // state 5 is running
+ while (p.isAlive() && watch.taken() < 20000 && state < 5) {
+ JsonObject root = loadStatus(p.pid());
+ if (root != null) {
+ JsonObject context = (JsonObject) root.get("context");
+ if (context != null) {
+ state = context.getInteger("phase");
+ }
+ }
+ if (state < 5) {
+ try {
+ Thread.sleep(500);
+ } catch (Exception e) {
+ // we want to exit
+ break;
+ }
+ }
+ }
+ if (!p.isAlive()) {
+ ec = p.exitValue();
+ if (ec != 0) {
+ printer().println(kind + ": " + name + " startup failure");
+ printer().println("");
+ String text = IOHelper.loadText(new FileInputStream(log));
+ printer().print(text);
+ }
+ } else {
+ printer().println(kind + ": " + name + " (state: " +
extractState(state) + ")");
+ }
}
- return 0;
+ if (log != null) {
+ log.delete();
+ }
+
+ return ec;
}
protected int runDebug(KameletMain main) throws Exception {
@@ -1335,6 +1389,9 @@ public class Run extends CamelCommand {
if (background) {
cmds.remove("--background=true");
cmds.remove("--background");
+ cmds.remove("--background-wait=true");
+ cmds.remove("--background-wait=false");
+ cmds.remove("--background-wait");
}
if (repositories != null) {
if (!VersionHelper.isGE(v, "3.18.1")) {
@@ -1354,13 +1411,7 @@ public class Run extends CamelCommand {
ProcessBuilder pb = new ProcessBuilder();
pb.command(jbangArgs);
if (background) {
- Process p = pb.start();
- this.spawnPid = p.pid();
- if (!exportRun && !transformRun && !transformMessageRun) {
- printer().println("Running Camel integration: " + name + "
(version: " + camelVersion
- + ") in background with PID: " + p.pid());
- }
- return 0;
+ return runBackgroundProcess(pb, "Camel Main");
} else {
pb.inheritIO(); // run in foreground (with IO so logs are visible)
Process p = pb.start();
@@ -1873,4 +1924,20 @@ public class Run extends CamelCommand {
cmds.add(0, "camel");
}
}
+
+ private JsonObject loadStatus(long pid) {
+ try {
+ File f = getStatusFile(Long.toString(pid));
+ if (f != null) {
+ FileInputStream fis = new FileInputStream(f);
+ String text = IOHelper.loadText(fis);
+ IOHelper.close(fis);
+ return (JsonObject) Jsoner.deserialize(text);
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ return null;
+ }
+
}