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);
                         }
                     }

Reply via email to