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 561ddc65ddfc CAMEL-26930: camel-jbang - dev mode watches the folder
that has all the files, and reloads application.properties (#29946)
561ddc65ddfc is described below
commit 561ddc65ddfc7e1a0d05b3f516412a01996f7030
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 14 19:30:49 2026 +0100
CAMEL-26930: camel-jbang - dev mode watches the folder that has all the
files, and reloads application.properties (#29946)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../src/test/resources/golden/expected/dev.run.txt | 4 +-
.../camel/dsl/jbang/run/CamelMainRunner.java | 22 +++----
.../apache/camel/dsl/jbang/run/RunPipeline.java | 77 ++++++++++++++++------
.../dsl/jbang/run/RunPipelineReloadFolderTest.java | 77 ++++++++++++++++++++++
4 files changed, 144 insertions(+), 36 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/expected/dev.run.txt
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/expected/dev.run.txt
index 2065f48c2f4a..ead8946b3481 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/expected/dev.run.txt
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/expected/dev.run.txt
@@ -44,10 +44,10 @@ camel.main.durationMaxAction=stop
camel.main.modeline=true
camel.main.name=hello
camel.main.routesIncludePattern=file:src/test/resources/golden/yaml/hello.camel.yaml
-camel.main.routesReloadDirectory=src/test/resources/golden/yaml
+camel.main.routesReloadDirectory=src/test/resources/golden
camel.main.routesReloadDirectoryRecursive=true
camel.main.routesReloadEnabled=true
-camel.main.routesReloadPattern=src/test/resources/golden/yaml/hello.camel.yaml,src/test/resources/golden/groovy/Greeting.groovy
+camel.main.routesReloadPattern=yaml/hello.camel.yaml,groovy/Greeting.groovy
camel.main.shutdownTimeout=5
# override properties
diff --git
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/CamelMainRunner.java
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/CamelMainRunner.java
index 0e2fbcdbdb31..03616347461d 100644
---
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/CamelMainRunner.java
+++
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/CamelMainRunner.java
@@ -22,7 +22,6 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
-import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
@@ -32,7 +31,6 @@ import org.apache.camel.dsl.jbang.run.common.MavenCommand;
import org.apache.camel.dsl.jbang.run.common.Printer;
import org.apache.camel.dsl.jbang.run.common.RuntimeType;
import org.apache.camel.dsl.jbang.run.export.ExportRequest;
-import org.apache.camel.util.FileUtil;
import org.apache.maven.model.Model;
import static
org.apache.camel.dsl.jbang.run.MavenRunSupport.CAMEL_MAIN_RUN_LOG_CONFIG;
@@ -266,26 +264,22 @@ final class CamelMainRunner {
List<String> args = new ArrayList<>();
String reloadDir;
String pattern;
+ boolean recursive;
if (request.sourceDir != null) {
reloadDir = request.sourceDir;
pattern = RunPipeline.sourceDirReloadPattern(request.dataDir);
+ recursive = true;
} else {
- // use the directory of the first file that is in another folder
than the current dir
- reloadDir = ".";
- StringJoiner sj = new StringJoiner(",");
- for (String f : files) {
- String path = FileUtil.onlyPath(f);
- if (path != null && !path.equals(".camel-jbang") &&
".".equals(reloadDir)) {
- reloadDir = path;
- }
- sj.add(FileUtil.stripPath(f));
- }
- pattern = sj.toString();
+ // the folder that has all the files, not the one of the first
file in another folder (CAMEL-26930)
+ RunPipeline.ReloadFolder reload = RunPipeline.reloadFolder(files);
+ reloadDir = reload.directory();
+ pattern = reload.pattern();
+ recursive = reload.recursive();
}
args.add("-Dcamel.main.routesReloadEnabled=true");
args.add("-Dcamel.main.routesReloadDirectory=" +
Paths.get(reloadDir).toAbsolutePath().normalize());
args.add("-Dcamel.main.routesReloadPattern=" + pattern);
- args.add("-Dcamel.main.routesReloadDirectoryRecursive=" +
(request.sourceDir != null ? "true" : "false"));
+ args.add("-Dcamel.main.routesReloadDirectoryRecursive=" + recursive);
args.add("-Dcamel.main.routesReloadRemoveAllRoutes=true");
return args;
}
diff --git
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunPipeline.java
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunPipeline.java
index f77c29953d1b..08d35933ecbd 100644
---
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunPipeline.java
+++
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunPipeline.java
@@ -1168,20 +1168,10 @@ public class RunPipeline {
main.addInitialProperty("camel.main.routesReloadDirectoryRecursive", "true");
} else {
dataDirWithoutSourceDir.run();
- String pattern = sjReload.toString();
- String reloadDir = ".";
- // use current dir, however if we run a file that are in
another folder, then we should track that folder instead
- for (String r : sjReload.toString().split(",")) {
- String path = FileUtil.onlyPath(r);
- if (path != null && !path.equals(".camel-jbang")) {
- reloadDir = path;
- break;
- }
- }
- main.addInitialProperty("camel.main.routesReloadDirectory",
reloadDir);
- main.addInitialProperty("camel.main.routesReloadPattern",
pattern);
-
main.addInitialProperty("camel.main.routesReloadDirectoryRecursive",
- isReloadRecursive(pattern) ? "true" : "false");
+ ReloadFolder reload =
reloadFolder(List.of(sjReload.toString().split(",")));
+ main.addInitialProperty("camel.main.routesReloadDirectory",
reload.directory());
+ main.addInitialProperty("camel.main.routesReloadPattern",
reload.pattern());
+
main.addInitialProperty("camel.main.routesReloadDirectoryRecursive",
Boolean.toString(reload.recursive()));
}
if (request.maxMessages <= 0 && request.maxSeconds <= 0 &&
request.maxIdleSeconds <= 0) {
// do not shutdown the JVM but stop routes when max duration
is triggered, so a reload can start them
@@ -1191,14 +1181,61 @@ public class RunPipeline {
}
}
- private static boolean isReloadRecursive(String reload) {
- for (String part : reload.split(",")) {
- String dir = FileUtil.onlyPath(part);
- if (dir != null) {
- return true;
+ /**
+ * The folder dev mode watches for the files given on the command line,
and the pattern of those files.
+ *
+ * @param directory the folder, relative to the current directory unless a
file was given by its absolute path
+ * @param pattern the files, relative to the folder
+ * @param recursive whether a file is in a sub folder of the folder
+ */
+ public record ReloadFolder(String directory, String pattern, boolean
recursive) {
+ }
+
+ /**
+ * The folder to watch for the files given on the command line: the
deepest folder that has all of them, with the
+ * files relative to it. The folder of the first file in another folder
was watched, so a change of a file in the
+ * current directory, such as application.properties, was not seen, and
./application.properties did not match the
+ * pattern of the files (CAMEL-26930). The files in .camel-jbang, which
the watch leaves out, do not widen it.
+ */
+ public static ReloadFolder reloadFolder(Collection<String> files) {
+ Path cwd = Paths.get("").toAbsolutePath();
+ Path internal = cwd.resolve(".camel-jbang");
+ List<Path> paths = new ArrayList<>();
+ boolean absolute = false;
+ Path dir = null;
+ for (String file : files) {
+ if (file == null || file.isBlank()) {
+ continue;
}
+ Path path = Paths.get(file.trim());
+ absolute |= path.isAbsolute();
+ path = cwd.resolve(path).normalize();
+ paths.add(path);
+ Path parent = path.getParent() != null ? path.getParent() : cwd;
+ if (!parent.startsWith(internal)) {
+ dir = dir == null ? parent : commonFolder(dir, parent);
+ }
+ }
+ if (dir == null) {
+ dir = cwd;
+ }
+ StringJoiner pattern = new StringJoiner(",");
+ boolean recursive = false;
+ for (Path path : paths) {
+ String relative = dir.relativize(path).toString().replace('\\',
'/');
+ recursive |= relative.contains("/");
+ pattern.add(relative);
+ }
+ String directory = absolute ? dir.toString() :
cwd.relativize(dir).toString();
+ return new ReloadFolder(directory.isEmpty() ? "." : directory,
pattern.toString(), recursive);
+ }
+
+ private static Path commonFolder(Path a, Path b) {
+ Path answer = a;
+ while (answer != null && !b.startsWith(answer)) {
+ answer = answer.getParent();
}
- return false;
+ return answer != null ? answer : a.getRoot();
}
/**
diff --git
a/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunPipelineReloadFolderTest.java
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunPipelineReloadFolderTest.java
new file mode 100644
index 000000000000..9b0a7c942c49
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunPipelineReloadFolderTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.run;
+
+import java.nio.file.Path;
+import java.util.List;
+
+import org.apache.camel.dsl.jbang.run.RunPipeline.ReloadFolder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * The folder dev mode watches for the files given on the command line, and
their pattern (CAMEL-26930).
+ */
+class RunPipelineReloadFolderTest {
+
+ @TempDir
+ Path dir;
+
+ @Test
+ void filesInTheCurrentDirectory() {
+ assertThat(RunPipeline.reloadFolder(List.of("a.camel.yaml",
"b.camel.yaml")))
+ .isEqualTo(new ReloadFolder(".", "a.camel.yaml,b.camel.yaml",
false));
+ }
+
+ @Test
+ void theAutoDetectedPropertiesMatch() {
+ // camel run adds ./application.properties, which the pattern did not
match
+ assertThat(RunPipeline.reloadFolder(List.of("a.camel.yaml",
"./application.properties")))
+ .isEqualTo(new ReloadFolder(".",
"a.camel.yaml,application.properties", false));
+ }
+
+ @Test
+ void filesInTwoFoldersWatchTheFolderThatHasBoth() {
+ // the folder of other/b.camel.yaml was watched, so a change of
a.camel.yaml was not seen
+ assertThat(RunPipeline.reloadFolder(List.of("a.camel.yaml",
"other/b.camel.yaml", "./application.properties")))
+ .isEqualTo(new ReloadFolder(".",
"a.camel.yaml,other/b.camel.yaml,application.properties", true));
+ assertThat(RunPipeline.reloadFolder(List.of("../x/a.camel.yaml",
"../y/b.camel.yaml")))
+ .isEqualTo(new ReloadFolder("..",
"x/a.camel.yaml,y/b.camel.yaml", true));
+ }
+
+ @Test
+ void filesInOneOtherFolderWatchThatFolder() {
+ assertThat(RunPipeline.reloadFolder(List.of("routes/a.camel.yaml",
"routes/b.camel.yaml")))
+ .isEqualTo(new ReloadFolder("routes",
"a.camel.yaml,b.camel.yaml", false));
+ }
+
+ @Test
+ void theFilesOfTheCliDoNotWidenTheFolder() {
+ assertThat(RunPipeline.reloadFolder(List.of("routes/a.camel.yaml",
".camel-jbang/generated.yaml")).directory())
+ .isEqualTo("routes");
+ }
+
+ @Test
+ void anAbsolutePathGivesAnAbsoluteFolder() {
+ String a = dir.resolve("x/a.camel.yaml").toString();
+ String b = dir.resolve("y/b.camel.yaml").toString();
+ assertThat(RunPipeline.reloadFolder(List.of(a, b)))
+ .isEqualTo(new ReloadFolder(dir.toString(),
"x/a.camel.yaml,y/b.camel.yaml", true));
+ }
+}