Serdar Gökay created CAMEL-24630:
------------------------------------

             Summary: camel-kamelet: supervised route reload creates duplicate 
internal routes and causes context console NPE
                 Key: CAMEL-24630
                 URL: https://issues.apache.org/jira/browse/CAMEL-24630
             Project: Camel
          Issue Type: Bug
          Components: camel-kamelet
    Affects Versions: 4.22.0, 4.18.1
         Environment: Apache Camel 4.18.1 and 4.22.0; Java 21.0.10 (Eclipse 
Temurin), Linux aarch64. DefaultSupervisingRouteController enabled. Reproduced 
with standalone native Camel APIs and separately with Camel JBang management 
reload/status endpoints. No JDK 25 or virtual-thread configuration is required.
            Reporter: Serdar Gökay


h3. What happens

Reloading a Kamelet route with DefaultSupervisingRouteController enabled 
creates duplicate internal route entries. A subsequent reload leaves an entry 
for which getRouteStatus(routeId) returns null. 
ManagedCamelContext.getStartedRoutes() then throws NullPointerException; the 
Camel JBang context dev console returns HTTP 500.

Reproduced with unmodified Apache Camel 4.18.1 and 4.22.0 on Java 21. The 
standalone reproducer below uses only native Camel dependencies and APIs; no 
application framework, Kubernetes or external service is required.

h3. Expected behavior

The example contains one parent route and one Kamelet-created route. After each 
reload, the context should still contain two unique routes and started-route 
introspection should succeed.

h3. Observed behavior

* Initial startup: 2 routes, both Started.
* After the first reload: 3 entries; the same Kamelet route ID appears twice.
* After the second reload: 4 entries; an old Kamelet route ID has null status 
and getStartedRoutes() throws NPE.
* In the JBang runtime, timer messages continue while /q/dev/context repeatedly 
returns HTTP 500. This was not a process crash.
* A control run without the supervising controller did not reproduce the 
duplicate entries.

h3. Reproduction

Save the two files below as pom.xml and SupervisedKameletReload.java in an 
empty directory. Run from that directory with Java 21 and Maven:

{code:bash}
mvn -q -Dcamel.version=4.22.0 
org.apache.maven.plugins:maven-dependency-plugin:3.8.1:build-classpath 
-Dmdep.outputFile=classpath.txt
java -Dorg.slf4j.simpleLogger.defaultLogLevel=warn -cp "$(cat classpath.txt)" 
SupervisedKameletReload.java
{code}

Repeat the Maven command with -Dcamel.version=4.18.1 and run the Java command 
again to reproduce on 4.18.1.

The reproducer creates a local route template consumed through kamelet:, 
enables DefaultSupervisingRouteController with an initial delay of 1000 ms, 
then repeats the native stop/remove, route-template cleanup, endpoint-registry 
cleanup and route loading sequence. It prints route IDs and statuses before 
querying getStartedRoutes(). The same route-count progression and NPE were 
observed on both versions.

The same symptom was also reproduced using native Camel JBang management 
requests: /q/dev/reload?reload=true&wait=true followed by /q/dev/context with 
Accept: application/json.

h3. Example output (4.22.0)

{code}
After 0 reload(s), routes=2
probe-parent status=Started
probe-source-1 status=Started
Started routes=2
After 1 reload(s), routes=3
probe-source-3 status=Started
probe-source-3 status=Started
probe-parent status=Started
Started routes=3
After 2 reload(s), routes=4
probe-source-3 status=null
probe-source-5 status=Started
probe-source-5 status=Started
probe-parent status=Started
Exception in thread "main" java.lang.NullPointerException: Cannot invoke 
"org.apache.camel.ServiceStatus.isStarted()" because the return value of 
"org.apache.camel.spi.RouteController.getRouteStatus(String)" is null
    at 
org.apache.camel.management.mbean.ManagedCamelContext.getStartedRoutes(ManagedCamelContext.java:314)
    at SupervisedKameletReload.main(SupervisedKameletReload.java:32)
{code}

In 4.18.1, the corresponding ManagedCamelContext frame is line 312.

h3. pom.xml

{code:xml}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
    <modelVersion>4.0.0</modelVersion>
    <groupId>example</groupId>
    <artifactId>camel-supervised-kamelet-reproducer</artifactId>
    <version>1</version>
    <properties><camel.version>4.18.1</camel.version></properties>
    <dependencies>
        
<dependency><groupId>org.apache.camel</groupId><artifactId>camel-kamelet</artifactId><version>${camel.version}</version></dependency>
        
<dependency><groupId>org.apache.camel</groupId><artifactId>camel-management</artifactId><version>${camel.version}</version></dependency>
        
<dependency><groupId>org.apache.camel</groupId><artifactId>camel-timer</artifactId><version>${camel.version}</version></dependency>
        
<dependency><groupId>org.apache.camel</groupId><artifactId>camel-log</artifactId><version>${camel.version}</version></dependency>
        
<dependency><groupId>org.apache.camel</groupId><artifactId>camel-core-languages</artifactId><version>${camel.version}</version></dependency>
        
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>2.0.17</version></dependency>
    </dependencies>
</project>
{code}

h3. SupervisedKameletReload.java

{code:java}
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.engine.DefaultSupervisingRouteController;
import org.apache.camel.management.mbean.ManagedCamelContext;

public class SupervisedKameletReload {
    static RouteBuilder routes() {
        return new RouteBuilder() {
            public void configure() {
                routeTemplate("probe-source")
                    .from("timer:probe?repeatCount=1&delay=10")
                    .setBody(constant("hello")).to("kamelet:sink");
                from("kamelet:probe-source").routeId("probe-parent")
                    .process(exchange -> {});
            }
        };
    }

    public static void main(String[] args) throws Exception {
        try (var context = new DefaultCamelContext()) {
            var controller = new DefaultSupervisingRouteController();
            controller.setInitialDelay(1000);
            context.setRouteController(controller);
            context.addRoutes(routes());
            context.start();
            for (int iteration = 0; iteration <= 2; iteration++) {
                Thread.sleep(1500);
                System.out.println("After " + iteration + " reload(s), routes=" 
+ context.getRoutesSize());
                for (var route : context.getRoutes()) {
                    System.out.println(route.getId() + " status=" + 
controller.getRouteStatus(route.getId()));
                }
                System.out.println("Started routes=" + new 
ManagedCamelContext(context).getStartedRoutes());
                if (iteration == 2) break;
                controller.removeAllRoutes();
                context.removeRouteTemplates("*");
                context.getEndpointRegistry().clear();
                context.addRoutes(routes());
            }
        }
    }
}
{code}




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to