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 af32e288b81c CAMEL-25831: camel-core - dev mode: a reload skips the
file of a bean that was deleted, which failed every reload (#28119)
af32e288b81c is described below
commit af32e288b81cdcb933a4cccfbd2188b78121c559
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Oct 19 19:37:18 2026 +0200
CAMEL-25831: camel-core - dev mode: a reload skips the file of a bean that
was deleted, which failed every reload (#28119)
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../RouteWatcherReloadDeletedBeanSourceTest.java | 111 +++++++++++++++++++++
.../camel/support/RouteWatcherReloadStrategy.java | 7 +-
2 files changed, 115 insertions(+), 3 deletions(-)
diff --git
a/core/camel-core/src/test/java/org/apache/camel/support/RouteWatcherReloadDeletedBeanSourceTest.java
b/core/camel-core/src/test/java/org/apache/camel/support/RouteWatcherReloadDeletedBeanSourceTest.java
new file mode 100644
index 000000000000..b32aa199bd0f
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/support/RouteWatcherReloadDeletedBeanSourceTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.support;
+
+import java.io.FileNotFoundException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.impl.engine.DefaultRoutesLoader;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.RoutesLoader;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * CAMEL-25831: a reload with a Java source loads the files that declare beans
again. The file of a bean that was
+ * deleted (such as a Java class moved to another folder) failed that reload,
and every reload after it.
+ */
+public class RouteWatcherReloadDeletedBeanSourceTest extends
ContextTestSupport {
+
+ @TempDir
+ Path dir;
+
+ private RecordingRoutesLoader loader;
+ private RouteWatcherReloadStrategy strategy;
+
+ @Override
+ @BeforeEach
+ public void setUp() throws Exception {
+ super.setUp();
+ loader = new RecordingRoutesLoader();
+ loader.setCamelContext(context);
+
context.getCamelContextExtension().addContextPlugin(RoutesLoader.class, loader);
+ strategy = new RouteWatcherReloadStrategy();
+ strategy.setCamelContext(context);
+ }
+
+ @Test
+ public void theFileOfABeanThatIsGoneIsNotLoadedAgain() throws Exception {
+ Resource changed = file("Changed.java", "class Changed {}");
+ Resource bean = file("Greeter.java", "class Greeter {}");
+ Resource gone = file("Gone.java", null);
+ strategy.setBeanResources(() -> List.of(bean, gone));
+
+ strategy.onRouteReload(List.of(changed), false);
+ // and the next reload too
+ strategy.onRouteReload(List.of(changed), false);
+
+ assertEquals(List.of(changed.getLocation(), bean.getLocation(),
changed.getLocation(), bean.getLocation()),
+ loader.loaded);
+ }
+
+ @Test
+ public void aReloadResourceThatIsGoneIsNotLoadedAgain() throws Exception {
+ Resource changed = file("Changed.java", "class Changed {}");
+ Resource extra = file("extra.properties", "a = 1");
+ Resource gone = file("gone.properties", null);
+
context.getRegistry().bind(RouteWatcherReloadStrategy.RELOAD_RESOURCES,
List.of(extra, gone));
+
+ strategy.onRouteReload(List.of(changed), false);
+
+ assertEquals(List.of(changed.getLocation(), extra.getLocation()),
loader.loaded);
+ }
+
+ // a file with the content, or a file that is gone when the content is null
+ private Resource file(String name, String content) throws Exception {
+ Path path = dir.resolve(name);
+ if (content != null) {
+ Files.writeString(path, content);
+ }
+ return ResourceHelper.resolveResource(context, "file:" + path);
+ }
+
+ // loads no routes: it records the files, and fails on a file that is
gone, as the loaders do
+ private static class RecordingRoutesLoader extends DefaultRoutesLoader {
+ private final List<String> loaded = new ArrayList<>();
+
+ @Override
+ public Set<String> updateRoutes(Collection<Resource> resources) throws
Exception {
+ for (Resource resource : resources) {
+ if (!resource.exists()) {
+ throw new FileNotFoundException(resource.getLocation() + "
does not exist");
+ }
+ loaded.add(resource.getLocation());
+ }
+ return Set.of();
+ }
+ }
+}
diff --git
a/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
b/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
index bbeb6de930f3..bb9d767da998 100644
---
a/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
+++
b/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
@@ -567,18 +567,19 @@ public class RouteWatcherReloadStrategy extends
FileWatcherResourceReloadStrateg
=
getCamelContext().getRegistry().lookupByNameAndType(RELOAD_RESOURCES,
Collection.class);
if (extras != null) {
for (Resource extra : extras) {
- if (!sources.contains(extra)) {
+ // a file that is gone would fail the reload, and every
reload after it (CAMEL-25831)
+ if (!sources.contains(extra) && !isDeletedFile(extra)) {
sources.add(extra);
}
}
}
// the Java sources are compiled again, so all beans are created
again: a bean of a changed class, and the
- // beans that call it
+ // beans that call it (not those whose file is gone, such as a
Java class that was deleted, CAMEL-25831)
if (beanResources != null &&
sources.stream().anyMatch(RouteWatcherReloadStrategy::isJavaSource)) {
Collection<Resource> beans = beanResources.get();
if (beans != null) {
for (Resource bean : beans) {
- if (bean != null && !equalResourceLocation(sources,
bean)) {
+ if (bean != null && !isDeletedFile(bean) &&
!equalResourceLocation(sources, bean)) {
sources.add(bean);
}
}