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
commit e50d3cedfc2dfb93daadc04199718b49e24b15b8 Author: Claus Ibsen <[email protected]> AuthorDate: Sun Sep 25 13:42:07 2022 +0200 camel-jbang - get source command --- .../camel/cli/connector/LocalCliConnector.java | 13 +- .../dsl/jbang/core/commands/CamelJBangMain.java | 2 + .../core/commands/action/CamelSourceAction.java | 236 +++++++++++++++++++++ 3 files changed, 247 insertions(+), 4 deletions(-) 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 35bd4b801b9..64cb2e9f0eb 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 @@ -271,6 +271,15 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C LOG.trace("Updating output file: {}", outputFile); IOHelper.writeText(json.toJson(), outputFile); } + } else if ("source".equals(action)) { + DevConsole dc = camelContext.adapt(ExtendedCamelContext.class) + .getDevConsoleResolver().resolveDevConsole("source"); + if (dc != null) { + String filter = root.getString("filter"); + JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON, Map.of("filter", filter)); + LOG.trace("Updating output file: {}", outputFile); + IOHelper.writeText(json.toJson(), outputFile); + } } // action done so delete file @@ -285,10 +294,6 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C } } - private void changeLoggingLevel(String level) { - - } - JsonObject loadAction() { try { if (actionFile != null && actionFile.exists()) { 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 f6667184096..3bd66b67e3c 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 @@ -26,6 +26,7 @@ import org.apache.camel.dsl.jbang.core.commands.action.CamelReloadAction; import org.apache.camel.dsl.jbang.core.commands.action.CamelResetStatsAction; import org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStartAction; import org.apache.camel.dsl.jbang.core.commands.action.CamelRouteStopAction; +import org.apache.camel.dsl.jbang.core.commands.action.CamelSourceAction; import org.apache.camel.dsl.jbang.core.commands.action.CamelSourceTop; import org.apache.camel.dsl.jbang.core.commands.action.CamelThreadDump; import org.apache.camel.dsl.jbang.core.commands.action.LoggerAction; @@ -71,6 +72,7 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("processor", new CommandLine(new CamelProcessorStatus(main))) .addSubcommand("endpoint", new CommandLine(new CamelEndpointStatus(main))) .addSubcommand("service", new CommandLine(new ListService(main))) + .addSubcommand("source", new CommandLine(new CamelSourceAction(main))) .addSubcommand("vault", new CommandLine(new ListVault(main)))) .addSubcommand("top", new CommandLine(new CamelTop(main)) .addSubcommand("context", new CommandLine(new CamelContextTop(main))) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceAction.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceAction.java new file mode 100644 index 00000000000..795fb3cb454 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceAction.java @@ -0,0 +1,236 @@ +/* + * 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.action; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.support.PatternHelper; +import org.apache.camel.util.FileUtil; +import org.apache.camel.util.IOHelper; +import org.apache.camel.util.StopWatch; +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; + +import static org.apache.camel.support.LoggerHelper.stripSourceLocationLineNumber; + +@Command(name = "source", description = "Display Camel route source code") +public class CamelSourceAction extends ActionBaseCommand { + + // TODO: strip license header + + @CommandLine.Parameters(description = "Name or pid of running Camel integration", arity = "1") + String name; + + @CommandLine.Option(names = { "--filter" }, + description = "Filter source by filename (multiple names can be separated by comma)") + String filter; + + @CommandLine.Option(names = { "--sort" }, + description = "Sort source by name", defaultValue = "name") + String sort; + + private volatile long pid; + + public CamelSourceAction(CamelJBangMain main) { + super(main); + } + + @Override + public Integer call() throws Exception { + List<Row> rows = new ArrayList<>(); + + List<Long> pids = findPids(name); + if (pids.isEmpty()) { + return 0; + } else if (pids.size() > 1) { + System.out.println("Name or pid " + name + " matches " + pids.size() + + " running Camel integrations. Specify a name or PID that matches exactly one."); + return 0; + } + + this.pid = pids.get(0); + + // ensure output file is deleted before executing action + File outputFile = getOutputFile("" + pid); + FileUtil.deleteFile(outputFile); + + JsonObject root = new JsonObject(); + root.put("action", "source"); + root.put("filter", "*"); + File file = getActionFile("" + pid); + try { + IOHelper.writeText(root.toJson(), file); + } catch (Exception e) { + // ignore + } + + JsonObject jo = waitForOutputFile(outputFile); + if (jo != null) { + JsonArray arr = (JsonArray) jo.get("routes"); + for (int i = 0; i < arr.size(); i++) { + JsonObject o = (JsonObject) arr.get(i); + Row row = new Row(); + row.location = extractSourceName(o.getString("source")); + // if there are 2+ routes in the same source then we would have duplicates + if (!rows.contains(row)) { + List<JsonObject> lines = o.getCollection("code"); + for (JsonObject line : lines) { + Code code = new Code(); + code.line = line.getInteger("line"); + code.code = line.getString("code"); + row.code.add(code); + } + boolean add = true; + if (filter != null) { + String f = filter; + boolean negate = filter.startsWith("-"); + if (negate) { + f = f.substring(1); + } + // make filtering easier + if (!f.endsWith("*")) { + f += "*"; + } + boolean match = PatternHelper.matchPattern(row.location, f); + if (negate) { + match = !match; + } + if (!match) { + add = false; + } + } + if (add) { + rows.add(row); + } + } + } + } else { + System.out.println("Response from running Camel with PID " + pid + " not received within 5 seconds"); + return 1; + } + + // sort rows + rows.sort(this::sortRow); + + if (!rows.isEmpty()) { + printSource(rows); + } + + // delete output file after use + FileUtil.deleteFile(outputFile); + + return 0; + } + + 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 "name": + return o1.location.compareToIgnoreCase(o2.location) * negate; + default: + return 0; + } + } + + protected void printSource(List<Row> rows) { + for (Row row : rows) { + System.out.println(); + System.out.printf("Source: %s%n", row.location); + System.out.println("--------------------------------------------------------------------------------"); + for (int i = 0; i < row.code.size(); i++) { + Code code = row.code.get(i); + String c = Jsoner.unescape(code.code); + System.out.printf("%4d: %s%n", code.line, c); + } + System.out.println(); + } + } + + protected JsonObject waitForOutputFile(File outputFile) { + StopWatch watch = new StopWatch(); + while (watch.taken() < 5000) { + try { + // give time for response to be ready + Thread.sleep(100); + + if (outputFile.exists()) { + FileInputStream fis = new FileInputStream(outputFile); + String text = IOHelper.loadText(fis); + IOHelper.close(fis); + return (JsonObject) Jsoner.deserialize(text); + } + + } catch (Exception e) { + // ignore + } + } + return null; + } + + public static String extractSourceName(String loc) { + loc = stripSourceLocationLineNumber(loc); + if (loc != null) { + if (loc.contains(":")) { + // strip prefix + loc = loc.substring(loc.indexOf(':') + 1); + // file based such as xml and yaml + loc = FileUtil.stripPath(loc); + } + } + return loc; + } + + private static class Row { + String location; + List<Code> code = new ArrayList<>(); + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + Row row = (Row) o; + + return location.equals(row.location); + } + + @Override + public int hashCode() { + return location.hashCode(); + } + } + + private static class Code { + int line; + String code; + } + +}
