You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/05/27 10:21:14 UTC

(camel) branch CAMEL-23624 updated (2b1c4a99f0df -> e5e12e2734c9)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a change to branch CAMEL-23624
in repository https://gitbox.apache.org/repos/asf/camel.git


    omit 2b1c4a99f0df CAMEL-23624: Add camel get error CLI command
    omit 6212f67ee1c6 CAMEL-23533: ErrorRegistry - capture rich exchange snapshots following BacklogTracer pattern
     add 7dc2fc19522e chore(deps): Bump aws-java-sdk2-version from 2.44.12 to 2.44.13 (#23549)
     add 6851a94075b8 CAMEL-23621: filter tool argument headers against declared parameters
     add e9c4541a91ce CAMEL-23621: remove backwards-compatibility bypass and fix raw JsonNode headers
     add 7e65891f77ae CAMEL-23621: document tool argument header filtering in upgrade guide
     add 410108aa9071 Add information on commons-dbcp dependency removal on jooq component
     add 6ec11413eed3 chore(deps): Bump quarkus-version from 3.35.4 to 3.36.0
     add 107a047a2f7c chore(camel-test-infra-kafka): upgrade kafka.container.image to 4.3.0
     add 675e783ed7ae CAMEL-23533: ErrorRegistry - capture rich exchange snapshots
     new e5e12e2734c9 CAMEL-23624: Add camel get error CLI command

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (2b1c4a99f0df)
            \
             N -- N -- N   refs/heads/CAMEL-23624 (e5e12e2734c9)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../agent/LangChain4jAgentProducer.java            | 31 ++++++++++++++++++++-
 .../tools/LangChain4jToolsProducer.java            | 13 +++++++++
 .../langchain4j/tools/LangChain4jToolTest.java     | 29 ++++++++++++++++++++
 .../springai/tools/SpringAiToolsEndpoint.java      | 14 +++++++++-
 .../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc    | 32 ++++++++++++++++++++++
 parent/pom.xml                                     |  4 +--
 .../test/infra/kafka/services/container.properties |  2 +-
 7 files changed, 120 insertions(+), 5 deletions(-)


(camel) 01/01: CAMEL-23624: Add camel get error CLI command

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch CAMEL-23624
in repository https://gitbox.apache.org/repos/asf/camel.git

commit e5e12e2734c97e23f830d85a5baa80d5eab67320
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed May 27 12:18:15 2026 +0200

    CAMEL-23624: Add camel get error CLI command
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../camel/impl/console/ErrorRegistryConsole.java   |  81 ++++-
 .../camel/cli/connector/LocalCliConnector.java     |   8 +
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   1 +
 .../dsl/jbang/core/commands/process/ListError.java | 344 +++++++++++++++++++++
 4 files changed, 418 insertions(+), 16 deletions(-)

diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/ErrorRegistryConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/ErrorRegistryConsole.java
index 6b5477fb033f..9faba207c66b 100644
--- a/core/camel-console/src/main/java/org/apache/camel/impl/console/ErrorRegistryConsole.java
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/ErrorRegistryConsole.java
@@ -16,13 +16,16 @@
  */
 package org.apache.camel.impl.console;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.camel.spi.BacklogErrorEventMessage;
 import org.apache.camel.spi.ErrorRegistry;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.TimeUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
@@ -44,14 +47,27 @@ public class ErrorRegistryConsole extends AbstractDevConsole {
      */
     public static final String STACK_TRACE = "stackTrace";
 
+    /**
+     * Filter by exception type (case-insensitive substring match)
+     */
+    public static final String EXCEPTION = "exception";
+
+    /**
+     * Filter by time window as duration string (e.g. "60s", "5m", "1h"). Only entries within this window are included.
+     */
+    public static final String AGO = "ago";
+
+    /**
+     * Filter by handled status ("true" or "false")
+     */
+    public static final String HANDLED = "handled";
+
     public ErrorRegistryConsole() {
         super("camel", "errors", "Error Registry", "Display captured routing errors");
     }
 
     @Override
     protected String doCallText(Map<String, Object> options) {
-        String routeId = (String) options.get(ROUTE_ID);
-        int max = parseLimit(options);
         boolean includeStackTrace = "true".equals(options.get(STACK_TRACE));
 
         StringBuilder sb = new StringBuilder();
@@ -60,12 +76,7 @@ public class ErrorRegistryConsole extends AbstractDevConsole {
         sb.append(String.format("%n    Enabled: %s", registry.isEnabled()));
         sb.append(String.format("%n    Size: %s", registry.size()));
 
-        Collection<BacklogErrorEventMessage> entries;
-        if (routeId != null) {
-            entries = registry.forRoute(routeId).browse(max);
-        } else {
-            entries = registry.browse(max);
-        }
+        List<BacklogErrorEventMessage> entries = fetchAndFilter(registry, options);
 
         for (BacklogErrorEventMessage entry : entries) {
             sb.append(String.format("%n    %s (route: %s, node: %s, endpoint: %s, handled: %s)",
@@ -94,8 +105,6 @@ public class ErrorRegistryConsole extends AbstractDevConsole {
 
     @Override
     protected JsonObject doCallJson(Map<String, Object> options) {
-        String routeId = (String) options.get(ROUTE_ID);
-        int max = parseLimit(options);
         boolean includeStackTrace = "true".equals(options.get(STACK_TRACE));
 
         JsonObject root = new JsonObject();
@@ -106,12 +115,7 @@ public class ErrorRegistryConsole extends AbstractDevConsole {
         root.put("maximumEntries", registry.getMaximumEntries());
         root.put("timeToLive", registry.getTimeToLive().toString());
 
-        Collection<BacklogErrorEventMessage> entries;
-        if (routeId != null) {
-            entries = registry.forRoute(routeId).browse(max);
-        } else {
-            entries = registry.browse(max);
-        }
+        List<BacklogErrorEventMessage> entries = fetchAndFilter(registry, options);
 
         final JsonArray list = new JsonArray();
         for (BacklogErrorEventMessage entry : entries) {
@@ -130,6 +134,51 @@ public class ErrorRegistryConsole extends AbstractDevConsole {
         return root;
     }
 
+    private static List<BacklogErrorEventMessage> fetchAndFilter(ErrorRegistry registry, Map<String, Object> options) {
+        String routeId = (String) options.get(ROUTE_ID);
+        String exceptionFilter = (String) options.get(EXCEPTION);
+        String agoFilter = (String) options.get(AGO);
+        String handledFilter = (String) options.get(HANDLED);
+        int max = parseLimit(options);
+
+        // fetch all entries (route-scoped if requested), apply filters, then limit
+        Collection<BacklogErrorEventMessage> all;
+        if (routeId != null) {
+            all = registry.forRoute(routeId).browse();
+        } else {
+            all = registry.browse();
+        }
+
+        long agoCutoff = -1;
+        if (agoFilter != null) {
+            try {
+                long millis = TimeUtils.toMilliSeconds(agoFilter);
+                agoCutoff = System.currentTimeMillis() - millis;
+            } catch (Exception e) {
+                // ignore invalid ago value
+            }
+        }
+
+        List<BacklogErrorEventMessage> result = new ArrayList<>();
+        for (BacklogErrorEventMessage entry : all) {
+            if (agoCutoff > 0 && entry.getTimestamp() < agoCutoff) {
+                continue;
+            }
+            if (exceptionFilter != null
+                    && !entry.getExceptionType().toLowerCase().contains(exceptionFilter.toLowerCase())) {
+                continue;
+            }
+            if (handledFilter != null && !String.valueOf(entry.isHandled()).equals(handledFilter)) {
+                continue;
+            }
+            result.add(entry);
+            if (max > 0 && max < Integer.MAX_VALUE && result.size() >= max) {
+                break;
+            }
+        }
+        return result;
+    }
+
     private static int parseLimit(Map<String, Object> options) {
         String limit = (String) options.get(LIMIT);
         if (limit == null) {
diff --git a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index f32121763448..a4ca0c6ccaf9 100644
--- a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -1384,6 +1384,14 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
                         root.put("groovy", json);
                     }
                 }
+                DevConsole dc26 = dcr.resolveById("errors");
+                if (dc26 != null) {
+                    JsonObject json = (JsonObject) dc26.call(DevConsole.MediaType.JSON,
+                            Map.of("stackTrace", "true"));
+                    if (json != null && !json.isEmpty()) {
+                        root.put("errors", json);
+                    }
+                }
             }
             // various details
             JsonObject mem = collectMemory();
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index 52366e048c5d..9d0996730950 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -148,6 +148,7 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("context", new CommandLine(new CamelContextStatus(this)))
                         .addSubcommand("count", new CommandLine(new CamelCount(this)))
                         .addSubcommand("endpoint", new CommandLine(new ListEndpoint(this)))
+                        .addSubcommand("error", new CommandLine(new ListError(this)))
                         .addSubcommand("event", new CommandLine(new ListEvent(this)))
                         .addSubcommand("groovy", new CommandLine(new ListGroovy(this)))
                         .addSubcommand("group", new CommandLine(new CamelRouteGroupStatus(this)))
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListError.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListError.java
new file mode 100644
index 000000000000..96237a6276f8
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListError.java
@@ -0,0 +1,344 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
+import com.github.freva.asciitable.OverflowBehaviour;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.PidNameAgeCompletionCandidates;
+import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
+import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+@Command(name = "error",
+         description = "Get captured routing errors of Camel integrations", sortOptions = false, showDefaultValues = true)
+public class ListError extends ProcessWatchCommand {
+
+    @CommandLine.Parameters(description = "Name or pid of running Camel integration", arity = "0..1")
+    String name = "*";
+
+    @CommandLine.Option(names = { "--sort" }, completionCandidates = PidNameAgeCompletionCandidates.class,
+                        description = "Sort by pid, name or age", defaultValue = "pid")
+    String sort;
+
+    @CommandLine.Option(names = { "--route" },
+                        description = "Filter by route ID")
+    String route;
+
+    @CommandLine.Option(names = { "--exception" },
+                        description = "Filter by exception type (substring match)")
+    String exception;
+
+    @CommandLine.Option(names = { "--ago" },
+                        description = "Filter by time window, e.g. 60s, 5m, 1h")
+    String ago;
+
+    @CommandLine.Option(names = { "--handled" },
+                        description = "Filter by handled status (true or false)")
+    String handled;
+
+    @CommandLine.Option(names = { "--limit" },
+                        description = "Maximum number of entries to display")
+    int limit;
+
+    @CommandLine.Option(names = { "--show" },
+                        description = "Comma-separated detail sections to show: body, headers, properties, variables, history, stackTrace")
+    String show;
+
+    public ListError(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doProcessWatchCall() throws Exception {
+        List<Row> rows = new ArrayList<>();
+
+        Set<String> showSet = show != null
+                ? Arrays.stream(show.split(",")).map(String::trim).collect(Collectors.toSet())
+                : Set.of();
+
+        List<Long> pids = findPids(name);
+        ProcessHandle.allProcesses()
+                .filter(ph -> pids.contains(ph.pid()))
+                .forEach(ph -> {
+                    JsonObject root = loadStatus(ph.pid());
+                    if (root != null) {
+                        JsonObject context = (JsonObject) root.get("context");
+                        if (context == null) {
+                            return;
+                        }
+                        String pName = context.getString("name");
+                        if ("CamelJBang".equals(pName)) {
+                            pName = ProcessHelper.extractName(root, ph);
+                        }
+                        String pid = Long.toString(ph.pid());
+
+                        JsonObject errors = (JsonObject) root.get("errors");
+                        if (errors != null) {
+                            JsonArray arr = (JsonArray) errors.get("errors");
+                            if (arr != null) {
+                                for (Object o : arr) {
+                                    JsonObject jo = (JsonObject) o;
+                                    Row row = new Row();
+                                    row.pid = pid;
+                                    row.name = pName;
+                                    row.routeId = jo.getString("routeId");
+                                    row.nodeId = jo.getString("nodeId");
+                                    row.exchangeId = jo.getString("exchangeId");
+                                    row.handled = jo.getBoolean("handled") != null && jo.getBoolean("handled");
+                                    Long ts = jo.getLong("timestamp");
+                                    if (ts != null) {
+                                        row.timestamp = ts;
+                                    }
+                                    row.location = jo.getString("location");
+
+                                    // extract exception info
+                                    JsonObject ex = (JsonObject) jo.get("exception");
+                                    if (ex != null) {
+                                        row.exceptionType = ex.getString("type");
+                                        row.exceptionMessage = ex.getString("message");
+                                        row.stackTrace = extractStackTrace(ex);
+                                    }
+
+                                    // extract message data
+                                    JsonObject msg = (JsonObject) jo.get("message");
+                                    if (msg != null) {
+                                        row.body = msg.get("body") != null ? msg.get("body").toString() : null;
+                                        row.headers = msg.get("headers");
+                                    }
+
+                                    // exchange properties and variables
+                                    row.properties = jo.get("exchangeProperties");
+                                    row.variables = jo.get("exchangeVariables");
+
+                                    // message history
+                                    Object mhObj = jo.get("messageHistory");
+                                    if (mhObj instanceof JsonArray mhArr) {
+                                        row.messageHistory = new String[mhArr.size()];
+                                        for (int i = 0; i < mhArr.size(); i++) {
+                                            row.messageHistory[i] = mhArr.get(i).toString();
+                                        }
+                                    }
+
+                                    // apply client-side filters
+                                    if (matchesFilters(row)) {
+                                        rows.add(row);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                });
+
+        // sort rows
+        rows.sort(this::sortRow);
+
+        // apply limit
+        List<Row> display = rows;
+        if (limit > 0 && rows.size() > limit) {
+            display = rows.subList(0, limit);
+        }
+
+        if (!display.isEmpty()) {
+            if (jsonOutput) {
+                printer().println(Jsoner.serialize(display.stream().map(r -> {
+                    JsonObject jo = new JsonObject();
+                    jo.put("pid", r.pid);
+                    jo.put("name", r.name);
+                    jo.put("age", getAge(r));
+                    jo.put("route", r.routeId);
+                    jo.put("nodeId", r.nodeId);
+                    jo.put("exchangeId", r.exchangeId);
+                    jo.put("handled", r.handled);
+                    jo.put("exception", r.exceptionType);
+                    jo.put("message", r.exceptionMessage);
+                    return jo;
+                }).collect(Collectors.toList())));
+            } else {
+                printer().println(AsciiTable.getTable(AsciiTable.NO_BORDERS, display, Arrays.asList(
+                        new Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> r.pid),
+                        new Column().header("NAME").dataAlign(HorizontalAlign.LEFT)
+                                .maxWidth(30, OverflowBehaviour.ELLIPSIS_RIGHT)
+                                .with(r -> r.name),
+                        new Column().header("AGO").dataAlign(HorizontalAlign.RIGHT)
+                                .with(this::getAge),
+                        new Column().header("ROUTE").dataAlign(HorizontalAlign.LEFT)
+                                .maxWidth(25, OverflowBehaviour.ELLIPSIS_RIGHT)
+                                .with(r -> r.routeId),
+                        new Column().header("NODE").dataAlign(HorizontalAlign.LEFT)
+                                .maxWidth(25, OverflowBehaviour.ELLIPSIS_RIGHT)
+                                .with(r -> r.nodeId),
+                        new Column().header("HANDLED").dataAlign(HorizontalAlign.CENTER)
+                                .with(r -> r.handled ? "true" : "false"),
+                        new Column().header("EXCEPTION").dataAlign(HorizontalAlign.LEFT)
+                                .maxWidth(40, OverflowBehaviour.ELLIPSIS_RIGHT)
+                                .with(r -> shortExceptionType(r.exceptionType)),
+                        new Column().header("MESSAGE").dataAlign(HorizontalAlign.LEFT)
+                                .maxWidth(60, OverflowBehaviour.ELLIPSIS_RIGHT)
+                                .with(r -> r.exceptionMessage))));
+
+                // show detail sections
+                if (!showSet.isEmpty()) {
+                    for (Row r : display) {
+                        printer().println();
+                        printer().printf("    Exchange: %s (route: %s, node: %s)%n",
+                                r.exchangeId, r.routeId, r.nodeId);
+                        if (showSet.contains("body") && r.body != null) {
+                            printer().printf("    Body:%n      %s%n", r.body);
+                        }
+                        if (showSet.contains("headers") && r.headers != null) {
+                            printer().printf("    Headers: %s%n", r.headers);
+                        }
+                        if (showSet.contains("properties") && r.properties != null) {
+                            printer().printf("    Properties: %s%n", r.properties);
+                        }
+                        if (showSet.contains("variables") && r.variables != null) {
+                            printer().printf("    Variables: %s%n", r.variables);
+                        }
+                        if (showSet.contains("history") && r.messageHistory != null) {
+                            printer().printf("    Message History:%n");
+                            for (String step : r.messageHistory) {
+                                printer().printf("      %s%n", step);
+                            }
+                        }
+                        if (showSet.contains("stackTrace") && r.stackTrace != null) {
+                            printer().printf("    Stack Trace:%n");
+                            for (String line : r.stackTrace) {
+                                printer().printf("      %s%n", line);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return 0;
+    }
+
+    private boolean matchesFilters(Row row) {
+        if (route != null && !route.equals(row.routeId)) {
+            return false;
+        }
+        if (exception != null && (row.exceptionType == null
+                || !row.exceptionType.toLowerCase().contains(exception.toLowerCase()))) {
+            return false;
+        }
+        if (handled != null && !handled.equals(String.valueOf(row.handled))) {
+            return false;
+        }
+        if (ago != null) {
+            try {
+                long millis = TimeUtils.toMilliSeconds(ago);
+                long cutoff = System.currentTimeMillis() - millis;
+                if (row.timestamp < cutoff) {
+                    return false;
+                }
+            } catch (Exception e) {
+                // ignore invalid ago value
+            }
+        }
+        return true;
+    }
+
+    private String getAge(Row r) {
+        if (r.timestamp > 0) {
+            return TimeUtils.printSince(r.timestamp);
+        }
+        return "";
+    }
+
+    private static String shortExceptionType(String type) {
+        if (type == null) {
+            return "";
+        }
+        int dot = type.lastIndexOf('.');
+        if (dot > 0) {
+            return type.substring(dot + 1);
+        }
+        return type;
+    }
+
+    private static String[] extractStackTrace(JsonObject ex) {
+        Object st = ex.get("stackTrace");
+        if (st instanceof JsonArray arr) {
+            String[] result = new String[arr.size()];
+            for (int i = 0; i < arr.size(); i++) {
+                result[i] = arr.get(i).toString();
+            }
+            return result;
+        }
+        return null;
+    }
+
+    protected int sortRow(Row o1, Row o2) {
+        String s = sort;
+        int negate = 1;
+        if (s.startsWith("-")) {
+            s = s.substring(1);
+            negate = -1;
+        }
+        switch (s) {
+            case "pid":
+                return Long.compare(Long.parseLong(o1.pid), Long.parseLong(o2.pid)) * negate;
+            case "name":
+                return o1.name.compareToIgnoreCase(o2.name) * negate;
+            case "age":
+                return Long.compare(o1.timestamp, o2.timestamp) * negate;
+            default:
+                return 0;
+        }
+    }
+
+    private static class Row implements Cloneable {
+        String pid;
+        String name;
+        long timestamp;
+        String routeId;
+        String nodeId;
+        String exchangeId;
+        boolean handled;
+        String location;
+        String exceptionType;
+        String exceptionMessage;
+        String[] stackTrace;
+        String body;
+        Object headers;
+        Object properties;
+        Object variables;
+        String[] messageHistory;
+
+        Row copy() {
+            try {
+                return (Row) clone();
+            } catch (CloneNotSupportedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}