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 6c9a375e4173 CAMEL-23707: Fix row-mutation bug in ListVault with 
multiple vault types
6c9a375e4173 is described below

commit 6c9a375e4173cd8d4fa9aeb2ec49ee382e8a18f9
Author: Adriano Machado <[email protected]>
AuthorDate: Sun Jun 7 15:46:16 2026 -0400

    CAMEL-23707: Fix row-mutation bug in ListVault with multiple vault types
    
    ListVault.doProcessWatchCall() mutated a single shared Row object across
    all vault-type blocks. When a vault had exactly one secret, the row.copy()
    guard (i > 0) never fired, so subsequent vault blocks overwrote the vault
    label of already-added rows. Fix by saving a baseRow before vault
    processing and resetting row = baseRow.copy() at the start of each vault
    block. Adds ListVaultTest with regression coverage for multi-vault
    single-secret scenarios in both table and JSON output modes.
    
    Closes #23822
---
 dsl/camel-jbang/camel-jbang-core/pom.xml           |   6 +
 .../dsl/jbang/core/commands/process/ListVault.java |   7 +
 .../jbang/core/commands/process/ListVaultTest.java | 300 +++++++++++++++++++++
 .../process/ProcessCommandTestSupport.java         | 157 +++++++++++
 4 files changed, 470 insertions(+)

diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml 
b/dsl/camel-jbang/camel-jbang-core/pom.xml
index d4f09fb9a376..d60c2c6d4efc 100644
--- a/dsl/camel-jbang/camel-jbang-core/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-core/pom.xml
@@ -186,6 +186,12 @@
             <version>${wiremock-version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <version>${mockito-version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVault.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVault.java
index ecbdfd00b27e..d4cc990c8970 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVault.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVault.java
@@ -87,10 +87,12 @@ public class ListVault extends ProcessWatchCommand {
                         if ("CamelJBang".equals(row.name)) {
                             row.name = ProcessHelper.extractName(root, ph);
                         }
+                        final Row baseRow = row.copy();
                         JsonObject vaults = (JsonObject) root.get("vaults");
                         if (vaults != null) {
                             JsonObject aws = (JsonObject) 
vaults.get("aws-secrets");
                             if (aws != null) {
+                                row = baseRow.copy();
                                 row.vault = "AWS";
                                 row.region = aws.getString("region");
                                 row.lastCheck = 
aws.getLongOrDefault("lastCheckTimestamp", 0);
@@ -109,6 +111,7 @@ public class ListVault extends ProcessWatchCommand {
                             }
                             JsonObject gcp = (JsonObject) 
vaults.get("gcp-secrets");
                             if (gcp != null) {
+                                row = baseRow.copy();
                                 row.vault = "GCP";
                                 row.lastCheck = 
gcp.getLongOrDefault("lastCheckTimestamp", 0);
                                 row.lastReload = 
gcp.getLongOrDefault("lastReloadTimestamp", 0);
@@ -126,6 +129,7 @@ public class ListVault extends ProcessWatchCommand {
                             }
                             JsonObject azure = (JsonObject) 
vaults.get("azure-secrets");
                             if (azure != null) {
+                                row = baseRow.copy();
                                 row.vault = "Azure";
                                 row.lastCheck = 
azure.getLongOrDefault("lastCheckTimestamp", 0);
                                 row.lastReload = 
azure.getLongOrDefault("lastReloadTimestamp", 0);
@@ -144,6 +148,7 @@ public class ListVault extends ProcessWatchCommand {
 
                             JsonObject kubernetes = (JsonObject) 
vaults.get("kubernetes-secrets");
                             if (kubernetes != null) {
+                                row = baseRow.copy();
                                 row.vault = "Kubernetes";
                                 row.lastCheck = 
kubernetes.getLongOrDefault("startCheckTimestamp", 0);
                                 row.lastReload = 
kubernetes.getLongOrDefault("lastReloadTimestamp", 0);
@@ -162,6 +167,7 @@ public class ListVault extends ProcessWatchCommand {
 
                             JsonObject hashicorp = (JsonObject) 
vaults.get("hashicorp-secrets");
                             if (hashicorp != null) {
+                                row = baseRow.copy();
                                 row.vault = "Hashicorp";
                                 row.lastCheck = 
hashicorp.getLongOrDefault("startCheckTimestamp", 0);
                                 row.lastReload = 
hashicorp.getLongOrDefault("lastReloadTimestamp", 0);
@@ -170,6 +176,7 @@ public class ListVault extends ProcessWatchCommand {
 
                             JsonObject cmKubernetes = (JsonObject) 
vaults.get("kubernetes-configmaps");
                             if (cmKubernetes != null) {
+                                row = baseRow.copy();
                                 row.vault = "Kubernetes-cm";
                                 row.lastCheck = 
cmKubernetes.getLongOrDefault("startCheckTimestamp", 0);
                                 row.lastReload = 
cmKubernetes.getLongOrDefault("lastReloadTimestamp", 0);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ListVaultTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ListVaultTest.java
new file mode 100644
index 000000000000..b26eac12ef5d
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ListVaultTest.java
@@ -0,0 +1,300 @@
+/*
+ * 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.dsl.jbang.core.commands.process;
+
+import java.util.stream.Stream;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mockStatic;
+
+@ExtendWith(MockitoExtension.class)
+class ListVaultTest extends ProcessCommandTestSupport {
+
+    @Test
+    void testEmptyOutputWhenNoVaults() throws Exception {
+        JsonObject root = new JsonObject();
+        root.put("context", contextObj());
+        writeStatusFile(TEST_PID, root);
+
+        ListVault command = new ListVault(new 
CamelJBangMain().withPrinter(printer));
+        command.sort = "pid";
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            ProcessHandle ph = mockProcessHandle(TEST_PID);
+            ProcessHandle currentHandle = mockCurrentHandle();
+            mocked.when(ProcessHandle::current).thenReturn(currentHandle);
+            mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> 
Stream.of(ph));
+
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            assertEquals("", printer.getOutput().trim());
+        }
+    }
+
+    @Test
+    void testShowsAwsVault() throws Exception {
+        JsonObject root = new JsonObject();
+        root.put("context", contextObj());
+        root.put("vaults", vaults("aws-secrets", vault("us-east-1", 
"aws-secret")));
+        writeStatusFile(TEST_PID, root);
+
+        ListVault command = new ListVault(new 
CamelJBangMain().withPrinter(printer));
+        command.sort = "pid";
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            ProcessHandle ph = mockProcessHandle(TEST_PID);
+            ProcessHandle currentHandle = mockCurrentHandle();
+            mocked.when(ProcessHandle::current).thenReturn(currentHandle);
+            mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> 
Stream.of(ph));
+
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            String output = printer.getOutput();
+            assertTrue(output.contains("AWS"), "Should show AWS vault");
+            assertTrue(output.contains("aws-secret"), "Should show AWS secret 
name");
+        }
+    }
+
+    @Test
+    void testShowsHashicorpVault() throws Exception {
+        JsonObject root = new JsonObject();
+        root.put("context", contextObj());
+        root.put("vaults", vaults("hashicorp-secrets", hashicorpVault()));
+        writeStatusFile(TEST_PID, root);
+
+        ListVault command = new ListVault(new 
CamelJBangMain().withPrinter(printer));
+        command.sort = "pid";
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            ProcessHandle ph = mockProcessHandle(TEST_PID);
+            ProcessHandle currentHandle = mockCurrentHandle();
+            mocked.when(ProcessHandle::current).thenReturn(currentHandle);
+            mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> 
Stream.of(ph));
+
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            assertTrue(printer.getOutput().contains("Hashicorp"), "Should show 
Hashicorp vault");
+        }
+    }
+
+    @Test
+    void testShowsKubernetesConfigMapVault() throws Exception {
+        JsonObject root = new JsonObject();
+        root.put("context", contextObj());
+        root.put("vaults", vaults("kubernetes-configmaps", 
configMapVault("app-config")));
+        writeStatusFile(TEST_PID, root);
+
+        ListVault command = new ListVault(new 
CamelJBangMain().withPrinter(printer));
+        command.sort = "pid";
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            ProcessHandle ph = mockProcessHandle(TEST_PID);
+            ProcessHandle currentHandle = mockCurrentHandle();
+            mocked.when(ProcessHandle::current).thenReturn(currentHandle);
+            mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> 
Stream.of(ph));
+
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            String output = printer.getOutput();
+            assertTrue(output.contains("Kubernetes-cm"), "Should show 
Kubernetes configmap vault");
+            assertTrue(output.contains("app-config"), "Should show configmap 
name");
+        }
+    }
+
+    @Test
+    void testMultipleVaultTypesEachWithOneSingleSecret() throws Exception {
+        JsonObject root = new JsonObject();
+        root.put("context", contextObj());
+        root.put("vaults", vaults(
+                "aws-secrets", vault("us-east-1", "aws-secret"),
+                "gcp-secrets", vault(null, "gcp-secret"),
+                "azure-secrets", vault(null, "azure-secret")));
+        writeStatusFile(TEST_PID, root);
+
+        ListVault command = new ListVault(new 
CamelJBangMain().withPrinter(printer));
+        command.sort = "pid";
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            ProcessHandle ph = mockProcessHandle(TEST_PID);
+            ProcessHandle currentHandle = mockCurrentHandle();
+            mocked.when(ProcessHandle::current).thenReturn(currentHandle);
+            mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> 
Stream.of(ph));
+
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            String output = printer.getOutput();
+            // All three vault types must appear; the bug causes only the last 
one (Azure) to show
+            assertTrue(output.contains("AWS"), "AWS row should retain its 
vault type");
+            assertTrue(output.contains("GCP"), "GCP row should retain its 
vault type");
+            assertTrue(output.contains("Azure"), "Azure row should retain its 
vault type");
+        }
+    }
+
+    @Test
+    void testSingleVaultWithMultipleSecrets() throws Exception {
+        JsonObject root = new JsonObject();
+        root.put("context", contextObj());
+        root.put("vaults", vaults("aws-secrets", vault("eu-west-1", 
"secret-a", "secret-b", "secret-c")));
+        writeStatusFile(TEST_PID, root);
+
+        ListVault command = new ListVault(new 
CamelJBangMain().withPrinter(printer));
+        command.sort = "pid";
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            ProcessHandle ph = mockProcessHandle(TEST_PID);
+            ProcessHandle currentHandle = mockCurrentHandle();
+            mocked.when(ProcessHandle::current).thenReturn(currentHandle);
+            mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> 
Stream.of(ph));
+
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            String output = printer.getOutput();
+            assertTrue(output.contains("secret-a"), "First secret should 
appear");
+            assertTrue(output.contains("secret-b"), "Second secret should 
appear (requires row.copy())");
+            assertTrue(output.contains("secret-c"), "Third secret should 
appear (requires row.copy())");
+            // All three rows must carry the same vault type
+            long awsCount = output.lines().filter(l -> 
l.contains("AWS")).count();
+            assertEquals(3, awsCount, "Each secret must be associated with AWS 
vault");
+        }
+    }
+
+    @Test
+    void testJsonOutput() throws Exception {
+        JsonObject root = new JsonObject();
+        root.put("context", contextObj());
+        root.put("vaults", vaults("aws-secrets", vault("us-east-1", 
"aws-secret")));
+        writeStatusFile(TEST_PID, root);
+
+        ListVault command = new ListVault(new 
CamelJBangMain().withPrinter(printer));
+        command.sort = "pid";
+        command.jsonOutput = true;
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            ProcessHandle ph = mockProcessHandle(TEST_PID);
+            ProcessHandle currentHandle = mockCurrentHandle();
+            mocked.when(ProcessHandle::current).thenReturn(currentHandle);
+            mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> 
Stream.of(ph));
+
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            String output = printer.getOutput();
+            assertTrue(output.startsWith("["), "JSON output should be array");
+            assertTrue(output.contains("\"vault\":\"AWS\""));
+            assertTrue(output.contains("aws-secret"));
+        }
+    }
+
+    @Test
+    void testMultipleVaultTypesEachWithOneSingleSecretJsonOutput() throws 
Exception {
+        JsonObject root = new JsonObject();
+        root.put("context", contextObj());
+        root.put("vaults", vaults(
+                "aws-secrets", vault("us-east-1", "aws-secret"),
+                "gcp-secrets", vault(null, "gcp-secret"),
+                "azure-secrets", vault(null, "azure-secret")));
+        writeStatusFile(TEST_PID, root);
+
+        ListVault command = new ListVault(new 
CamelJBangMain().withPrinter(printer));
+        command.sort = "pid";
+        command.jsonOutput = true;
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            ProcessHandle ph = mockProcessHandle(TEST_PID);
+            ProcessHandle currentHandle = mockCurrentHandle();
+            mocked.when(ProcessHandle::current).thenReturn(currentHandle);
+            mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> 
Stream.of(ph));
+
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            String output = printer.getOutput();
+            assertTrue(output.startsWith("["), "JSON output should be array");
+            // Each vault type must appear exactly once with its own label
+            assertTrue(output.contains("\"vault\":\"AWS\""), "AWS entry must 
retain its vault label");
+            assertTrue(output.contains("\"vault\":\"GCP\""), "GCP entry must 
retain its vault label");
+            assertTrue(output.contains("\"vault\":\"Azure\""), "Azure entry 
must retain its vault label");
+        }
+    }
+
+    private static JsonObject contextObj() {
+        JsonObject ctx = new JsonObject();
+        ctx.put("name", "myApp");
+        return ctx;
+    }
+
+    private static JsonObject vaults(Object... keyValues) {
+        JsonObject vaults = new JsonObject();
+        for (int i = 0; i < keyValues.length; i += 2) {
+            vaults.put((String) keyValues[i], keyValues[i + 1]);
+        }
+        return vaults;
+    }
+
+    private static JsonObject vault(String region, String... secrets) {
+        long now = System.currentTimeMillis();
+        JsonObject vault = new JsonObject();
+        if (region != null) {
+            vault.put("region", region);
+        }
+        vault.put("lastCheckTimestamp", now - 1000);
+        vault.put("lastReloadTimestamp", now - 500);
+        vault.put("startCheckTimestamp", now - 1000);
+        vault.put("secrets", secretArray(secrets));
+        return vault;
+    }
+
+    private static JsonObject hashicorpVault() {
+        long now = System.currentTimeMillis();
+        JsonObject vault = new JsonObject();
+        vault.put("startCheckTimestamp", now - 1000);
+        vault.put("lastReloadTimestamp", now - 500);
+        return vault;
+    }
+
+    private static JsonObject configMapVault(String... configMaps) {
+        JsonObject vault = vault(null);
+        vault.put("configmap", secretArray(configMaps));
+        return vault;
+    }
+
+    private static JsonArray secretArray(String... names) {
+        JsonArray arr = new JsonArray();
+        for (String name : names) {
+            JsonObject secret = new JsonObject();
+            secret.put("name", name);
+            secret.put("timestamp", System.currentTimeMillis() - 2000);
+            arr.add(secret);
+        }
+        return arr;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessCommandTestSupport.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessCommandTestSupport.java
new file mode 100644
index 000000000000..0eca566564b3
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessCommandTestSupport.java
@@ -0,0 +1,157 @@
+/*
+ * 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.dsl.jbang.core.commands.process;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.Comparator;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelCommandBaseTestSupport;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Base class for process command tests. Handles file system setup, status 
file writing, and ProcessHandle mock creation
+ * for use with Mockito's mockStatic.
+ */
+abstract class ProcessCommandTestSupport extends CamelCommandBaseTestSupport {
+
+    static final long TEST_PID = 12345L;
+    static final long CURRENT_PID = 99999L;
+
+    @BeforeEach
+    @Override
+    public void setup() throws Exception {
+        super.setup();
+        CommandLineHelper.useHomeDir("target/test-process");
+        Files.createDirectories(CommandLineHelper.getCamelDir());
+    }
+
+    @AfterEach
+    public void cleanup() throws Exception {
+        Path camelDir = CommandLineHelper.getCamelDir();
+        if (Files.exists(camelDir)) {
+            try (Stream<Path> walk = Files.walk(camelDir)) {
+                walk.sorted(Comparator.reverseOrder()).forEach(p -> {
+                    try {
+                        Files.deleteIfExists(p);
+                    } catch (IOException e) {
+                        // best effort cleanup
+                    }
+                });
+            }
+        }
+    }
+
+    protected static void writeStatusFile(long pid, JsonObject root) throws 
Exception {
+        Path f = CommandLineHelper.getCamelDir().resolve(pid + "-status.json");
+        Files.writeString(f, root.toJson());
+    }
+
+    /**
+     * Creates a mock ProcessHandle for the test process. Info is 
pre-configured with empty commandLine and a fixed
+     * start instant so extractName falls through to context.name.
+     */
+    protected static ProcessHandle mockProcessHandle(long pid) {
+        ProcessHandle ph = mock(ProcessHandle.class);
+        ProcessHandle.Info info = mock(ProcessHandle.Info.class);
+        when(ph.pid()).thenReturn(pid);
+        when(ph.info()).thenReturn(info);
+        when(info.commandLine()).thenReturn(Optional.empty());
+        // lenient: only called when table rows are populated (not for 
empty-result tests)
+        
lenient().when(info.startInstant()).thenReturn(Optional.of(Instant.now().minusSeconds(60)));
+        return ph;
+    }
+
+    /**
+     * Creates a mock for ProcessHandle.current() — a distinct process so the 
"skip current" filter in findPids does not
+     * exclude the test handle.
+     */
+    protected static ProcessHandle mockCurrentHandle() {
+        ProcessHandle current = mock(ProcessHandle.class);
+        when(current.pid()).thenReturn(CURRENT_PID);
+        return current;
+    }
+
+    /**
+     * Builds a minimal context-level status JSON. Phase 5 = Running in 
CamelCommandHelper.extractState. Includes an
+     * empty routes array so CamelContextStatus does not throw NPE.
+     */
+    protected static JsonObject buildContextStatus(String contextName, int 
phase) {
+        JsonObject stats = new JsonObject();
+        stats.put("exchangesTotal", "0");
+        stats.put("exchangesFailed", "0");
+        stats.put("exchangesInflight", "0");
+
+        JsonObject context = new JsonObject();
+        context.put("name", contextName);
+        context.put("phase", phase);
+        context.put("statistics", stats);
+
+        JsonObject hc = new JsonObject();
+        hc.put("ready", phase == 5);
+
+        JsonObject root = new JsonObject();
+        root.put("context", context);
+        root.put("healthChecks", hc);
+        root.put("routes", new JsonArray());
+        return root;
+    }
+
+    /**
+     * Builds a route-level status JSON with one route entry and basic 
statistics.
+     */
+    protected static JsonObject buildRouteStatus(String routeId, String state) 
{
+        JsonObject routeStats = new JsonObject();
+        routeStats.put("exchangesTotal", "0");
+        routeStats.put("exchangesFailed", "0");
+        routeStats.put("exchangesInflight", "0");
+        routeStats.put("meanProcessingTime", "-1");
+        routeStats.put("maxProcessingTime", "0");
+        routeStats.put("minProcessingTime", "0");
+
+        JsonObject route = new JsonObject();
+        route.put("routeId", routeId);
+        route.put("from", "timer:tick");
+        route.put("state", state);
+        route.put("uptime", "1m");
+        route.put("statistics", routeStats);
+
+        JsonArray routes = new JsonArray();
+        routes.add(route);
+
+        JsonObject context = new JsonObject();
+        context.put("name", "myApp");
+        context.put("phase", 5);
+
+        JsonObject root = new JsonObject();
+        root.put("context", context);
+        root.put("routes", routes);
+        return root;
+    }
+}

Reply via email to