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 b276c6e5f05 CAMEL-20164: camel-jbang - Add camel get consumer command b276c6e5f05 is described below commit b276c6e5f05f9e7b615cbcaaa03d0b6df5a19129 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Wed Nov 29 16:00:28 2023 +0100 CAMEL-20164: camel-jbang - Add camel get consumer command --- .../camel/impl/console/ConsumerDevConsole.java | 4 +- .../camel/cli/connector/LocalCliConnector.java | 7 + .../dsl/JettyComponentBuilderFactory.java | 69 +++++++ .../dsl/jbang/core/commands/CamelJBangMain.java | 2 + .../jbang/core/commands/process/ListConsumer.java | 215 +++++++++++++++++++++ 5 files changed, 295 insertions(+), 2 deletions(-) diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java index 8d94953b527..869d5e017d6 100644 --- a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java @@ -58,7 +58,7 @@ public class ConsumerDevConsole extends AbstractDevConsole { sb.append("\n"); } sb.append(String.format("\n Id: %s", id)); - sb.append(String.format("\n From: %s", mc.getEndpointUri())); + sb.append(String.format("\n Uri: %s", mc.getEndpointUri())); sb.append(String.format("\n State: %s", mc.getState())); sb.append(String.format("\n Class: %s", mc.getServiceType())); sb.append(String.format("\n Inflight: %d", inflight)); @@ -144,7 +144,7 @@ public class ConsumerDevConsole extends AbstractDevConsole { } jo.put("id", id); - jo.put("from", mc.getEndpointUri()); + jo.put("uri", mc.getEndpointUri()); jo.put("state", mc.getState()); jo.put("class", mc.getServiceType()); jo.put("inflight", inflight); 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 0ddbc80b69d..36141d2d70b 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 @@ -889,6 +889,13 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C String data = json.toJson() + System.lineSeparator(); IOHelper.writeText(data, debugFile); } + DevConsole dc14 = dcr.resolveById("consumer"); + if (dc14 != null) { + JsonObject json = (JsonObject) dc14.call(DevConsole.MediaType.JSON); + if (json != null && !json.isEmpty()) { + root.put("consumers", json); + } + } } // various details JsonObject services = collectServices(); diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/JettyComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/JettyComponentBuilderFactory.java index b32428fc999..a9f447a4cd9 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/JettyComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/JettyComponentBuilderFactory.java @@ -275,6 +275,71 @@ public interface JettyComponentBuilderFactory { doSetProperty("useXForwardedForHeader", useXForwardedForHeader); return this; } + /** + * The size threshold after which files will be written to disk for + * multipart/form-data requests. By default the files are not written to + * disk. + * + * The option is a: <code>int</code> type. + * + * Default: 0 + * Group: consumer (advanced) + * + * @param fileSizeThreshold the value to set + * @return the dsl builder + */ + default JettyComponentBuilder fileSizeThreshold(int fileSizeThreshold) { + doSetProperty("fileSizeThreshold", fileSizeThreshold); + return this; + } + /** + * The directory location where files will be store for + * multipart/form-data requests. By default the files are written in the + * system temporary folder. + * + * The option is a: <code>java.lang.String</code> type. + * + * Group: consumer (advanced) + * + * @param filesLocation the value to set + * @return the dsl builder + */ + default JettyComponentBuilder filesLocation( + java.lang.String filesLocation) { + doSetProperty("filesLocation", filesLocation); + return this; + } + /** + * The maximum size allowed for uploaded files. -1 means no limit. + * + * The option is a: <code>long</code> type. + * + * Default: -1 + * Group: consumer (advanced) + * + * @param maxFileSize the value to set + * @return the dsl builder + */ + default JettyComponentBuilder maxFileSize(long maxFileSize) { + doSetProperty("maxFileSize", maxFileSize); + return this; + } + /** + * The maximum size allowed for multipart/form-data requests. -1 means + * no limit. + * + * The option is a: <code>long</code> type. + * + * Default: -1 + * Group: consumer (advanced) + * + * @param maxRequestSize the value to set + * @return the dsl builder + */ + default JettyComponentBuilder maxRequestSize(long maxRequestSize) { + doSetProperty("maxRequestSize", maxRequestSize); + return this; + } /** * To use a custom thread pool for the server. This option should only * be used in special circumstances. @@ -621,6 +686,10 @@ public interface JettyComponentBuilderFactory { case "sendServerVersion": ((JettyHttpComponent11) component).setSendServerVersion((boolean) value); return true; case "useContinuation": ((JettyHttpComponent11) component).setUseContinuation((boolean) value); return true; case "useXForwardedForHeader": ((JettyHttpComponent11) component).setUseXForwardedForHeader((boolean) value); return true; + case "fileSizeThreshold": ((JettyHttpComponent11) component).setFileSizeThreshold((int) value); return true; + case "filesLocation": ((JettyHttpComponent11) component).setFilesLocation((java.lang.String) value); return true; + case "maxFileSize": ((JettyHttpComponent11) component).setMaxFileSize((long) value); return true; + case "maxRequestSize": ((JettyHttpComponent11) component).setMaxRequestSize((long) value); return true; case "threadPool": ((JettyHttpComponent11) component).setThreadPool((org.eclipse.jetty.util.thread.ThreadPool) value); return true; case "allowJavaSerializedObject": ((JettyHttpComponent11) component).setAllowJavaSerializedObject((boolean) value); return true; case "autowiredEnabled": ((JettyHttpComponent11) component).setAutowiredEnabled((boolean) value); return true; 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 836773601c4..c0806c1baa7 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 @@ -63,6 +63,7 @@ import org.apache.camel.dsl.jbang.core.commands.process.Hawtio; import org.apache.camel.dsl.jbang.core.commands.process.Jolokia; import org.apache.camel.dsl.jbang.core.commands.process.ListBlocked; import org.apache.camel.dsl.jbang.core.commands.process.ListCircuitBreaker; +import org.apache.camel.dsl.jbang.core.commands.process.ListConsumer; import org.apache.camel.dsl.jbang.core.commands.process.ListEndpoint; import org.apache.camel.dsl.jbang.core.commands.process.ListEvent; import org.apache.camel.dsl.jbang.core.commands.process.ListHealth; @@ -103,6 +104,7 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("processor", new CommandLine(new CamelProcessorStatus(main))) .addSubcommand("count", new CommandLine(new CamelCount(main))) .addSubcommand("health", new CommandLine(new ListHealth(main))) + .addSubcommand("consumer", new CommandLine(new ListConsumer(main))) .addSubcommand("endpoint", new CommandLine(new ListEndpoint(main))) .addSubcommand("event", new CommandLine(new ListEvent(main))) .addSubcommand("inflight", new CommandLine(new ListInflight(main))) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListConsumer.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListConsumer.java new file mode 100644 index 00000000000..422526f1dee --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListConsumer.java @@ -0,0 +1,215 @@ +/* + * 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 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.support.PatternHelper; +import org.apache.camel.util.TimeUtils; +import org.apache.camel.util.json.JsonArray; +import org.apache.camel.util.json.JsonObject; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +@Command(name = "consumer", description = "Get status of Camel consumers", sortOptions = false) +public class ListConsumer 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 = { "--limit" }, + description = "Filter consumers by limiting to the given number of rows") + int limit; + + @CommandLine.Option(names = { "--filter" }, + description = "Filter consumers by URI") + String filter; + + @CommandLine.Option(names = { "--short-uri" }, + description = "List endpoint URI without query parameters (short)") + boolean shortUri; + + @CommandLine.Option(names = { "--wide-uri" }, + description = "List endpoint URI in full details") + boolean wideUri; + + public ListConsumer(CamelJBangMain main) { + super(main); + } + + @Override + public Integer doProcessWatchCall() throws Exception { + List<Row> rows = new ArrayList<>(); + + // make it easier to filter + if (filter != null && !filter.endsWith("*")) { + filter += "*"; + } + + 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"); + JsonObject jo = (JsonObject) root.get("consumers"); + if (context != null && jo != null) { + JsonArray array = (JsonArray) jo.get("consumers"); + for (int i = 0; i < array.size(); i++) { + JsonObject o = (JsonObject) array.get(i); + Row row = new Row(); + row.name = context.getString("name"); + if ("CamelJBang".equals(row.name)) { + row.name = ProcessHelper.extractName(root, ph); + } + row.pid = Long.toString(ph.pid()); + row.id = o.getString("id"); + row.uri = o.getString("uri"); + row.state = o.getString("state"); + row.className = o.getString("class"); + row.inflight = o.getInteger("inflight"); + row.polling = o.getBoolean("polling"); + row.totalCounter = o.getLong("totalCounter"); + row.uptime = extractSince(ph); + row.age = TimeUtils.printSince(row.uptime); + boolean add = true; + if (filter != null) { + String f = filter; + boolean negate = filter.startsWith("-"); + if (negate) { + f = f.substring(1); + } + boolean match = PatternHelper.matchPattern(row.uri, f); + if (negate) { + match = !match; + } + if (!match) { + add = false; + } + } + if (limit > 0 && rows.size() >= limit) { + add = false; + } + if (add) { + rows.add(row); + } + } + } + } + }); + + // sort rows + rows.sort(this::sortRow); + + if (!rows.isEmpty()) { + printTable(rows); + } + + return 0; + } + + protected void printTable(List<Row> rows) { + System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, 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("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.age), + new Column().header("ID").with(r -> r.id), + new Column().header("STATE").with(this::getState), + new Column().header("INFLIGHT").with(r -> "" + r.inflight), + new Column().header("POLLS").with(this::getTotal), + new Column().header("URI").visible(!wideUri).dataAlign(HorizontalAlign.LEFT) + .maxWidth(90, OverflowBehaviour.ELLIPSIS_RIGHT) + .with(this::getUri), + new Column().header("URI").visible(wideUri).dataAlign(HorizontalAlign.LEFT) + .maxWidth(140, OverflowBehaviour.NEWLINE) + .with(this::getUri)))); + } + + private String getUri(Row r) { + String u = r.uri; + if (shortUri) { + int pos = u.indexOf('?'); + if (pos > 0) { + u = u.substring(0, pos); + } + } + return u; + } + + private String getState(Row r) { + if (r.polling != null && r.polling) { + return "Polling"; + } + return r.state; + } + + private String getTotal(Row r) { + if (r.totalCounter != null) { + return String.valueOf(r.totalCounter); + } + return ""; + } + + 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.uptime, o2.uptime) * negate; + default: + return 0; + } + } + + static class Row { + String pid; + String name; + long uptime; + String age; + String id; + String uri; + String state; + String className; + int inflight; + Boolean polling; + Long totalCounter; + } + +}