[
https://issues.apache.org/jira/browse/CAMEL-24630?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18113150#comment-18113150
]
Claus Ibsen commented on CAMEL-24630:
-------------------------------------
Fixed via [PR #26173|https://github.com/apache/camel/pull/26173], merged to
main as commit 857dc649dd0e4c4a32bd6b5caef889b5e5166ac2.
KameletComponent now only calls startRouteDefinitions() when the route does not
yet exist in the route controller (getRouteStatus(id) == null), so a
supervising controller no longer causes kamelet child routes to be materialized
a second time on reload.
_Claude Code on behalf of davsclaus_
> 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.18.1, 4.22.0
> 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
> Assignee: Omar Atie
> Priority: Minor
> Fix For: 4.23.0
>
>
> 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)