This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-spring-boot-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
The following commit(s) were added to refs/heads/camel-spring-boot-4.22.x by
this push:
new aee14c11de2 CAMEL-24499: camel-spring-boot - route detail view
bypasses the start-exception filter (#1908)
aee14c11de2 is described below
commit aee14c11de25dd41634ad7b3c077186dcc1e79cc
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Aug 28 13:08:43 2026 +0200
CAMEL-24499: camel-spring-boot - route detail view bypasses the
start-exception filter (#1908)
RouteEndpointInfo annotates its properties map with
@JsonIgnoreProperties({"route.start.exception"}), but
RouteDetailsEndpointInfo
re-declared the same field without the annotation plus a getProperties()
override, so the actuator detail operation serialized the property the base
view
filters. That property holds the Throwable itself, so Jackson emitted the
whole
object graph - nested causes, every stack frame with class, file and line,
classloader names and the JDK version.
Both the annotation and the shadowing field were introduced together under
CAMEL-20993, so the filter was intended and the subclass field simply
escaped
it. The shadow and its getter are removed and the annotated base-class
property
is used instead. getProperties() stays available on the subclass by
inheritance, so this is source and binary compatible.
The regression test uses Awaitility to wait until the supervising route
controller has actually recorded route.start.exception before building
either
view, so the assertions cannot pass vacuously.
Closes #1898
---
.../boot/actuate/endpoint/CamelRoutesEndpoint.java | 12 +--
.../CamelRoutesEndpointStartExceptionTest.java | 110 +++++++++++++++++++++
2 files changed, 112 insertions(+), 10 deletions(-)
diff --git
a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java
index d7904766f1e..a0a4a4e941e 100644
---
a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java
+++
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpoint.java
@@ -276,16 +276,12 @@ public class CamelRoutesEndpoint {
@JsonProperty("details")
private RouteDetails routeDetails;
- private final Map<String, Object> properties;
public RouteDetailsEndpointInfo(final CamelContext camelContext, final
Route route) {
super(route);
- if (route.getProperties() != null) {
- this.properties = new HashMap<>(route.getProperties());
- } else {
- this.properties = Collections.emptyMap();
- }
+ // route properties are carried by the base class, whose field
holds the
+ // @JsonIgnoreProperties filter - do not shadow it here, or the
filter is bypassed
if (camelContext.getManagementStrategy().getManagementAgent() !=
null) {
ManagedCamelContext mcc =
camelContext.getCamelContextExtension()
.getContextPlugin(ManagedCamelContext.class);
@@ -293,10 +289,6 @@ public class CamelRoutesEndpoint {
}
}
- public Map<String, Object> getProperties() {
- return properties;
- }
-
@JsonInclude(JsonInclude.Include.NON_EMPTY)
static class RouteDetails {
diff --git
a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointStartExceptionTest.java
b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointStartExceptionTest.java
new file mode 100644
index 00000000000..f6eff86ee6b
--- /dev/null
+++
b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/actuate/endpoint/CamelRoutesEndpointStartExceptionTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.spring.boot.actuate.endpoint;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Route;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The {@code route.start.exception} route property is filtered out of the
actuator route views. The base
+ * {@link CamelRoutesEndpoint.RouteEndpointInfo} carries the {@code
@JsonIgnoreProperties} filter; the detail view must
+ * not bypass it.
+ */
+@EnableAutoConfiguration
+@SpringBootTest(classes = { CamelAutoConfiguration.class,
CamelRoutesEndpointAutoConfiguration.class,
+ ActuatorTestControlledRoutes.class }, properties = {
+ "management.endpoints.web.exposure.include=*",
+ "camel.routecontroller.enabled=true",
+ "camel.routecontroller.initial-delay=100",
+ "camel.routecontroller.back-off-delay=100",
+ "camel.routecontroller.back-off-max-attempts=3",
+ "camel.main.routes-exclude-pattern=*",
+ "camel.main.routes-collector-enabled=true" })
+public class CamelRoutesEndpointStartExceptionTest {
+
+ private static final String FAILING_ROUTE_ID = "controlled-bar";
+ private static final String START_EXCEPTION = "route.start.exception";
+
+ @Autowired
+ CamelRoutesEndpoint endpoint;
+
+ @Autowired
+ CamelContext camelContext;
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ private Route failingRoute() {
+ return camelContext.getRouteController().getControlledRoutes().stream()
+ .filter(route -> FAILING_ROUTE_ID.equals(route.getId()))
+ .findAny()
+ .orElse(null);
+ }
+
+ /**
+ * The supervising route controller starts routes asynchronously, so the
start failure is not recorded when the
+ * context finishes refreshing. Without waiting for it the assertions
below would pass vacuously - there would be
+ * no property to leak in the first place.
+ */
+ @BeforeEach
+ public void waitUntilTheStartFailureIsRecorded() {
+ await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> {
+ Route route = failingRoute();
+ assertNotNull(route, "the supervised route should be known to the
route controller");
+ assertInstanceOf(Throwable.class,
route.getProperties().get(START_EXCEPTION),
+ "the route start failure should be recorded before the
endpoint views are checked");
+ });
+ }
+
+ @Test
+ public void infoViewDoesNotExposeStartException() throws Exception {
+ Object info = endpoint.doReadAction(FAILING_ROUTE_ID,
CamelRoutesEndpoint.ReadAction.INFO);
+ assertNotNull(info);
+
+ String json = mapper.writeValueAsString(info);
+ assertFalse(json.contains(START_EXCEPTION),
+ START_EXCEPTION + " must not be serialized in the info view,
but was: " + json);
+ assertTrue(json.contains("customId"),
+ "the remaining route properties should still be serialized,
but were not: " + json);
+ }
+
+ @Test
+ public void detailViewDoesNotExposeStartException() throws Exception {
+ Object details = endpoint.doReadAction(FAILING_ROUTE_ID,
CamelRoutesEndpoint.ReadAction.DETAIL);
+ assertNotNull(details);
+
+ String json = mapper.writeValueAsString(details);
+ assertFalse(json.contains(START_EXCEPTION),
+ START_EXCEPTION + " must not be serialized in the detail view,
but was: " + json);
+ assertTrue(json.contains("customId"),
+ "the remaining route properties should still be serialized,
but were not: " + json);
+ }
+}