davsclaus commented on code in PR #27847:
URL: https://github.com/apache/camel/pull/27847#discussion_r4275256820
##########
core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java:
##########
@@ -193,6 +230,114 @@ protected void doStart() throws Exception {
super.doStart();
}
+ private static boolean matches(AntPathMatcher matcher, String part, String
name, String path, String relative) {
+ return name.equals(part) || matcher.match(part, path, false)
+ || relative != null && matcher.match(part, relative, false);
+ }
+
+ /**
+ * A folder that an exclude of the pattern matches as a whole, such as
<tt>inbox</tt> for <tt>!inbox/**</tt>: the
+ * folder is left out of the scan, so the files the routes read and write
in it are not walked on every scan.
+ */
+ @Override
+ protected boolean isExcludedDirectory(Path dir) {
+ if (excludes.isEmpty() || folder == null) {
+ return false;
+ }
+ String relative = relativePath(dir.toFile());
+ if (relative == null || relative.isEmpty()) {
+ return false;
+ }
+ AntPathMatcher matcher = new AntPathMatcher();
+ for (String part : excludes) {
Review Comment:
Done in 3d9dc19eaf34: `isExcludedDirectory` uses `AntPathMatcher.INSTANCE`.
_Claude Code on behalf of @davsclaus._
##########
core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java:
##########
@@ -193,6 +230,114 @@ protected void doStart() throws Exception {
super.doStart();
}
+ private static boolean matches(AntPathMatcher matcher, String part, String
name, String path, String relative) {
+ return name.equals(part) || matcher.match(part, path, false)
+ || relative != null && matcher.match(part, relative, false);
+ }
+
+ /**
+ * A folder that an exclude of the pattern matches as a whole, such as
<tt>inbox</tt> for <tt>!inbox/**</tt>: the
+ * folder is left out of the scan, so the files the routes read and write
in it are not walked on every scan.
+ */
+ @Override
+ protected boolean isExcludedDirectory(Path dir) {
+ if (excludes.isEmpty() || folder == null) {
+ return false;
+ }
+ String relative = relativePath(dir.toFile());
+ if (relative == null || relative.isEmpty()) {
+ return false;
+ }
+ AntPathMatcher matcher = new AntPathMatcher();
+ for (String part : excludes) {
+ // inbox and inbox/** match the folder itself; a file pattern such
as *.json does not leave a folder out
+ if (part.equals(relative) || matcher.match(part, relative + "/_",
false)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** The path of the file relative to the watched folder, with /
separators, or null when it is not under it. */
+ private String relativePath(File file) {
+ String base = new File(getFolder()).getAbsolutePath();
+ String path = file.getAbsolutePath();
+ if (!path.startsWith(base)) {
+ return null;
+ }
+ String relative = path.substring(base.length()).replace('\\', '/');
+ while (relative.startsWith("/")) {
+ relative = relative.substring(1);
+ }
+ return relative;
+ }
+
+ /**
+ * Routes that write files into a folder under the watched one reload
themselves: the reload restarts the routes,
+ * they write again, and that is a change again (a file copied into an
inbox, moved to done or archive). When the
+ * last {@link #LOOP_RELOADS} reloads within {@link #LOOP_MILLIS} were
each for files under the same folder only,
+ * and none of those files is a source of a route, a properties file or a
Java or Groovy source, a WARN names the
+ * folder and how to leave it out of reloading, once per folder
(CAMEL-25670).
+ *
+ * @return whether the warning was logged now
+ */
+ boolean checkReloadLoop(List<File> changed, long now) {
+ String data = dataFolder(changed);
+ if (data == null) {
+ dataReloads.clear();
+ return false;
+ }
+ dataReloads.addLast(new DataReload(now, data));
+ while (dataReloads.size() > LOOP_RELOADS) {
+ dataReloads.removeFirst();
+ }
+ long took = now - dataReloads.getFirst().time();
+ if (dataReloads.size() == LOOP_RELOADS && took <= LOOP_MILLIS
+ && dataReloads.stream().allMatch(r -> r.folder().equals(data))
&& loopWarned.add(data)) {
+ LOG.warn("Routes reloaded {} times in {} seconds, each time
because files under {}/ changed, which are not"
+ + " sources of the routes. If that folder holds data the
routes read or write, leave it out of"
+ + " reloading: camel run --data-dir={}, or !{}/** in the
reload pattern"
+ + " (camel.main.routesReloadPattern).",
+ LOOP_RELOADS, Math.max(1, Math.round(took / 1000.0)),
data, data, data);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * The folder right under the watched one that holds all the changed
files, when none of them is a source (a route
+ * was loaded from it, or it is a properties, Java or Groovy file), else
null.
+ */
+ private String dataFolder(List<File> changed) {
+ Set<String> sources = new HashSet<>();
+ for (Route route : getCamelContext().getRoutes()) {
+ Resource rs = route.getSourceResource();
+ if (rs != null && rs.getLocation() != null) {
+ sources.add(FileUtil.stripPath(rs.getLocation()));
+ }
+ }
+ String data = null;
+ for (File file : changed) {
+ String name = file.getName();
+ if (name.endsWith(".properties") || name.endsWith(".java") ||
name.endsWith(".groovy")
+ || sources.contains(name)) {
+ return null;
Review Comment:
Good catch, fixed in 3d9dc19eaf34. The route sources are now compared by
their full normalized path (from the `file:` location of each route's source
resource), so `inbox/route.yaml` stays data when a route is loaded from
`route.yaml`.
_Claude Code on behalf of @davsclaus._
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]