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)