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;
+ }
+}