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 ad1effe57cdc019a017348e17ae969d76b448d8e Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Aug 22 13:24:45 2022 +0200 CAMEL-18406: camel-jbang - Status command --- .../camel/impl/console/ContextDevConsole.java | 5 +- .../dsl/jbang/core/commands/CamelCommand.java | 11 ++- .../dsl/jbang/core/commands/CamelJBangMain.java | 2 + .../apache/camel/dsl/jbang/core/commands/Run.java | 33 ++++++- .../jbang/core/commands/process/CamelStatus.java | 102 +++++++++++++++++++++ 5 files changed, 144 insertions(+), 9 deletions(-) diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java index 4519ae69d57..5ae52f460fc 100644 --- a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java @@ -34,8 +34,8 @@ public class ContextDevConsole extends AbstractDevConsole { protected String doCallText(Map<String, Object> options) { StringBuilder sb = new StringBuilder(); - sb.append(String.format("Apache Camel %s (%s) uptime %s", getCamelContext().getVersion(), getCamelContext().getName(), - getCamelContext().getUptime())); + sb.append(String.format("Apache Camel %s %s (%s) uptime %s", getCamelContext().getVersion(), + getCamelContext().getStatus().statusLowerCase(), getCamelContext().getName(), getCamelContext().getUptime())); sb.append("\n"); ManagedCamelContext mcc = getCamelContext().getExtension(ManagedCamelContext.class); @@ -57,6 +57,7 @@ public class ContextDevConsole extends AbstractDevConsole { JsonObject root = new JsonObject(); root.put("name", getCamelContext().getName()); root.put("version", getCamelContext().getVersion()); + root.put("state", getCamelContext().getStatus()); root.put("uptime", getCamelContext().getUptime()); ManagedCamelContext mcc = getCamelContext().getExtension(ManagedCamelContext.class); diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java index caa1c6f2ee9..d89a4446a24 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java @@ -16,15 +16,15 @@ */ package org.apache.camel.dsl.jbang.core.commands; +import java.io.File; import java.util.concurrent.Callable; import picocli.CommandLine; public abstract class CamelCommand implements Callable<Integer> { - public static final String PID_DIR = "${sys:user.home}/.camel"; - private final CamelJBangMain main; + private File camelDir; //CHECKSTYLE:OFF @CommandLine.Option(names = { "-h", "--help" }, usageHelp = true, description = "Display the help and sub-commands") @@ -39,4 +39,11 @@ public abstract class CamelCommand implements Callable<Integer> { return main; } + public File getStatusFile(String pid) { + if (camelDir == null) { + camelDir = new File(System.getProperty("user.home"), ".camel"); + } + return new File(camelDir, pid + "-status.json"); + } + } 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 9d404805843..8dfe2bf9373 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 @@ -20,6 +20,7 @@ import java.util.concurrent.Callable; import org.apache.camel.catalog.CamelCatalog; import org.apache.camel.catalog.DefaultCamelCatalog; +import org.apache.camel.dsl.jbang.core.commands.process.CamelStatus; 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.ListProcess; @@ -37,6 +38,7 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("run", new CommandLine(new Run(main))) .addSubcommand("ps", new CommandLine(new ListProcess(main))) .addSubcommand("stop", new CommandLine(new StopProcess(main))) + .addSubcommand("status", new CommandLine(new CamelStatus(main))) .addSubcommand("init", new CommandLine(new Init(main))) .addSubcommand("bind", new CommandLine(new Bind(main))) .addSubcommand("jolokia", new CommandLine(new Jolokia(main))) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java index 618db4f344b..3359aa06810 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java @@ -48,6 +48,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.apicurio.datamodels.Library; import io.apicurio.datamodels.openapi.models.OasDocument; import org.apache.camel.CamelContext; +import org.apache.camel.ExtendedCamelContext; +import org.apache.camel.console.DevConsole; import org.apache.camel.dsl.jbang.core.common.RuntimeUtil; import org.apache.camel.generator.openapi.RestDslGenerator; import org.apache.camel.impl.lw.LightweightCamelContext; @@ -59,6 +61,7 @@ import org.apache.camel.util.FileUtil; import org.apache.camel.util.IOHelper; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.StringHelper; +import org.apache.camel.util.json.JsonObject; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import picocli.CommandLine; @@ -90,6 +93,7 @@ class Run extends CamelCommand { private CamelContext context; private File lockFile; + private File statusFile; private ScheduledExecutorService executor; private boolean silentRun; private boolean pipeRun; @@ -241,6 +245,9 @@ class Run extends CamelCommand { if (lockFile != null) { FileUtil.deleteFile(lockFile); } + if (statusFile != null) { + FileUtil.deleteFile(statusFile); + } return 0; } @@ -400,13 +407,30 @@ class Run extends CamelCommand { writeSetting(main, profileProperties, "camel.jbang.jfr-profile", jfrProfile != null ? jfrProfile : null); if (fileLock) { - lockFile = createLockFile(); + lockFile = createLockFile(getPid()); + if (lockFile != null) { + statusFile = createLockFile(lockFile.getName() + "-status.json"); + } // to trigger shutdown on file lock deletion executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleWithFixedDelay(() -> { // if the lock file is deleted then stop if (!lockFile.exists()) { context.stop(); + return; + } + // update status file with details from the context console + try { + DevConsole dc = main.getCamelContext().adapt(ExtendedCamelContext.class) + .getDevConsoleResolver().resolveDevConsole("context"); + if (dc != null) { + JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON); + if (json != null) { + IOHelper.writeText(json.toJson(), statusFile); + } + } + } catch (Throwable e) { + // ignore } }, 1000, 1000, TimeUnit.MILLISECONDS); } @@ -687,14 +711,13 @@ class Run extends CamelCommand { } } - public File createLockFile() throws IOException { + public File createLockFile(String name) { File answer = null; - String pid = getPid(); - if (pid != null) { + if (name != null) { File dir = new File(System.getProperty("user.home"), ".camel"); try { dir.mkdirs(); - answer = new File(dir, pid); + answer = new File(dir, name); if (!answer.exists()) { answer.createNewFile(); } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelStatus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelStatus.java new file mode 100644 index 00000000000..f8678efdf0e --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelStatus.java @@ -0,0 +1,102 @@ +/* + * 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.File; +import java.io.FileInputStream; +import java.math.BigDecimal; +import java.util.Locale; +import java.util.Map; + +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.util.IOHelper; +import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.TimeUtils; +import org.apache.camel.util.json.JsonObject; +import org.apache.camel.util.json.Jsoner; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +@Command(name = "status", description = "List status of the running Camel applications") +public class CamelStatus extends ProcessBaseCommand { + + @CommandLine.Option(names = { "--sort" }, + description = "Sort by pid, name or age", defaultValue = "pid") + String sort; + + public CamelStatus(CamelJBangMain main) { + super(main); + } + + @Override + public Integer call() throws Exception { + ProcessHandle.allProcesses() + .sorted((o1, o2) -> { + switch (sort) { + case "pid": + return Long.compare(o1.pid(), o2.pid()); + case "name": + return extractName(o1).compareTo(extractName(o2)); + case "age": + // we want newest in top + return Long.compare(extractSince(o1), extractSince(o2)) * -1; + default: + return 0; + } + }) + .forEach(ph -> { + String name = extractName(ph); + if (ObjectHelper.isNotEmpty(name)) { + String ago = TimeUtils.printSince(extractSince(ph)); + JsonObject status = loadStatus(ph.pid()); + if (status != null) { + String state = status.getString("state").toLowerCase(Locale.ROOT); + Map<String, ?> stats = status.getMap("statistics"); + if (stats != null) { + BigDecimal total = (BigDecimal) stats.get("exchangesTotal"); + BigDecimal inflight = (BigDecimal) stats.get("exchangesInflight"); + BigDecimal failed = (BigDecimal) stats.get("exchangesFailed"); + System.out.printf("%s camel run %s %s (age: %s, total: %s, inflight: %s, failed: %s)%n", + ph.pid(), name, state, ago, total, inflight, failed); + } else { + System.out.printf("%s camel run %s %s (age: %s)%n", + ph.pid(), name, state, ago); + } + } else { + System.out.println(ph.pid() + " camel run " + name + " (age: " + ago + ")"); + } + } + }); + return 0; + } + + private JsonObject loadStatus(long pid) { + try { + File f = getStatusFile("" + pid); + if (f != null) { + FileInputStream fis = new FileInputStream(f); + String text = IOHelper.loadText(fis); + IOHelper.close(fis); + return (JsonObject) Jsoner.deserialize(text); + } + } catch (Throwable e) { + // ignore + } + return null; + } + +}