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

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

commit 36f2b334e7583cd6293c9cf877455e869440163e
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Sat Nov 25 14:24:41 2023 +0100

    camel-jbang - Prototype for transform message command.
---
 .../camel/cli/connector/LocalCliConnector.java     |  71 ++++++
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   5 +-
 .../dsl/jbang/core/commands/TransformCommand.java  |  35 +++
 .../{Transform.java => TransformRoute.java}        |  10 +-
 .../commands/action/TransformMessageAction.java    | 270 +++++++++++++++++++++
 5 files changed, 385 insertions(+), 6 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 e85ccfd8380..110adf2b29c 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
@@ -27,6 +27,7 @@ import java.lang.management.RuntimeMXBean;
 import java.lang.management.ThreadMXBean;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -42,6 +43,7 @@ import org.apache.camel.CamelContextAware;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
+import org.apache.camel.Expression;
 import org.apache.camel.NoSuchEndpointException;
 import org.apache.camel.Processor;
 import org.apache.camel.ProducerTemplate;
@@ -52,6 +54,7 @@ import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.spi.CliConnector;
 import org.apache.camel.spi.CliConnectorFactory;
 import org.apache.camel.spi.ContextReloadStrategy;
+import org.apache.camel.spi.Language;
 import org.apache.camel.spi.ResourceReloadStrategy;
 import org.apache.camel.support.EndpointHelper;
 import org.apache.camel.support.MessageHelper;
@@ -486,6 +489,74 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
                         IOHelper.writeText(jo.toJson(), outputFile);
                     }
                 }
+            } else if ("transform".equals(action)) {
+                StopWatch watch = new StopWatch();
+                long timestamp = System.currentTimeMillis();
+                String language = root.getString("language");
+                String expression = 
Jsoner.unescape(root.getStringOrDefault("expression", ""));
+                String body = root.getString("body");
+                Collection<JsonObject> headers = root.getCollection("headers");
+                if (body != null) {
+                    InputStream is = null;
+                    Object b = body;
+                    Map<String, Object> map = null;
+                    if (body.startsWith("file:")) {
+                        File file = new File(body.substring(5));
+                        is = new FileInputStream(file);
+                        b = is;
+                    }
+                    if (headers != null) {
+                        map = new LinkedHashMap<>();
+                        for (JsonObject jo : headers) {
+                            map.put(jo.getString("key"), 
jo.getString("value"));
+                        }
+                    }
+                    final Object inputBody = b;
+                    final Map<String, Object> inputHeaders = map;
+
+                    Exchange out = 
camelContext.getCamelContextExtension().getExchangeFactory().create(false);
+                    try {
+                        Language lan = camelContext.resolveLanguage(language);
+                        Expression exp = lan.createExpression(expression);
+                        exp.init(camelContext);
+                        // create dummy exchange with
+                        out.setPattern(ExchangePattern.InOut);
+                        out.getMessage().setBody(inputBody);
+                        if (inputHeaders != null) {
+                            out.getMessage().setHeaders(inputHeaders);
+                        }
+                        String result = exp.evaluate(out, String.class);
+                        out.getMessage().setBody(result);
+                        IOHelper.close(is);
+                    } catch (Exception e) {
+                        out.setException(e);
+                    }
+                    LOG.trace("Updating output file: {}", outputFile);
+                    if (out.getException() != null) {
+                        JsonObject jo = new JsonObject();
+                        jo.put("language", language);
+                        jo.put("exchangeId", out.getExchangeId());
+                        jo.put("timestamp", timestamp);
+                        jo.put("elapsed", watch.taken());
+                        jo.put("status", "failed");
+                        // avoid double wrap
+                        jo.put("exception",
+                                
MessageHelper.dumpExceptionAsJSonObject(out.getException()).getMap("exception"));
+                        IOHelper.writeText(jo.toJson(), outputFile);
+                    } else {
+                        JsonObject jo = new JsonObject();
+                        jo.put("language", language);
+                        jo.put("exchangeId", out.getExchangeId());
+                        jo.put("timestamp", timestamp);
+                        jo.put("elapsed", watch.taken());
+                        jo.put("status", "success");
+                        // avoid double wrap
+                        jo.put("message", 
MessageHelper.dumpAsJSonObject(out.getMessage(), true, true, true, true, true,
+                                BODY_MAX_CHARS).getMap("message"));
+                        IOHelper.writeText(jo.toJson(), outputFile);
+                    }
+                    
camelContext.getCamelContextExtension().getExchangeFactory().release(out);
+                }
             }
 
             // action done so delete file
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 dd9993ad45e..836773601c4 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
@@ -37,6 +37,7 @@ import 
org.apache.camel.dsl.jbang.core.commands.action.CamelThreadDump;
 import org.apache.camel.dsl.jbang.core.commands.action.CamelTraceAction;
 import org.apache.camel.dsl.jbang.core.commands.action.LoggerAction;
 import org.apache.camel.dsl.jbang.core.commands.action.RouteControllerAction;
+import org.apache.camel.dsl.jbang.core.commands.action.TransformMessageAction;
 import org.apache.camel.dsl.jbang.core.commands.catalog.CatalogCommand;
 import org.apache.camel.dsl.jbang.core.commands.catalog.CatalogComponent;
 import org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDataFormat;
@@ -93,7 +94,9 @@ public class CamelJBangMain implements Callable<Integer> {
                 .addSubcommand("ps", new CommandLine(new ListProcess(main)))
                 .addSubcommand("stop", new CommandLine(new StopProcess(main)))
                 .addSubcommand("trace", new CommandLine(new 
CamelTraceAction(main)))
-                .addSubcommand("transform", new CommandLine(new 
Transform(main)))
+                .addSubcommand("transform", new CommandLine(new 
TransformCommand(main))
+                        .addSubcommand("route", new CommandLine(new 
TransformRoute(main)))
+                        .addSubcommand("message", new CommandLine(new 
TransformMessageAction(main))))
                 .addSubcommand("get", new CommandLine(new CamelStatus(main))
                         .addSubcommand("context", new CommandLine(new 
CamelContextStatus(main)))
                         .addSubcommand("route", new CommandLine(new 
CamelRouteStatus(main)))
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformCommand.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformCommand.java
new file mode 100644
index 00000000000..fb4f910af19
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "transform",
+                     description = "Transform message or Camel routes (use 
transform --help to see sub commands)")
+public class TransformCommand extends CamelCommand {
+
+    public TransformCommand(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doCall() throws Exception {
+        new CommandLine(this).execute("--help");
+        return 0;
+    }
+
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Transform.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformRoute.java
similarity index 95%
rename from 
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Transform.java
rename to 
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformRoute.java
index 6c5e47acab9..1b648000fd6 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Transform.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformRoute.java
@@ -29,8 +29,8 @@ import org.apache.camel.util.StopWatch;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
 
-@Command(name = "transform", description = "Transform Camel routes to XML or 
YAML format", sortOptions = false)
-public class Transform extends CamelCommand {
+@Command(name = "route", description = "Transform Camel routes to XML or YAML 
format", sortOptions = false)
+public class TransformRoute extends CamelCommand {
 
     @CommandLine.Parameters(description = "The Camel file(s) to run. If no 
files specified then application.properties is used as source for which files 
to run.",
                             arity = "0..9", paramLabel = "<files>", 
parameterConsumer = FilesConsumer.class)
@@ -60,7 +60,7 @@ public class Transform extends CamelCommand {
                         description = "Whether to ignore route loading and 
compilation errors (use this with care!)")
     boolean ignoreLoadingError;
 
-    public Transform(CamelJBangMain main) {
+    public TransformRoute(CamelJBangMain main) {
         super(main);
     }
 
@@ -130,9 +130,9 @@ public class Transform extends CamelCommand {
         return null;
     }
 
-    static class FilesConsumer extends ParameterConsumer<Transform> {
+    static class FilesConsumer extends ParameterConsumer<TransformRoute> {
         @Override
-        protected void doConsumeParameters(Stack<String> args, Transform cmd) {
+        protected void doConsumeParameters(Stack<String> args, TransformRoute 
cmd) {
             String arg = args.pop();
             cmd.files.add(arg);
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/TransformMessageAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/TransformMessageAction.java
new file mode 100644
index 00000000000..aa8d2e34637
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/TransformMessageAction.java
@@ -0,0 +1,270 @@
+/*
+ * 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.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.StringHelper;
+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 org.fusesource.jansi.Ansi;
+import org.fusesource.jansi.AnsiConsole;
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "message",
+                     description = "Transform message from one format to 
another via an existing running Camel integration",
+                     sortOptions = false)
+public class TransformMessageAction extends ActionBaseCommand {
+
+    @CommandLine.Parameters(description = "Name or pid of running Camel 
integration", arity = "0..1")
+    String name = "*";
+
+    @CommandLine.Option(names = { "--body" }, required = true,
+                        description = "Message body to send (prefix with file: 
to refer to loading message body from file)")
+    String body;
+
+    @CommandLine.Option(names = { "--header" },
+                        description = "Message header (key=value)")
+    List<String> headers;
+
+    @CommandLine.Option(names = {
+            "--language" },
+                        required = true,
+                        description = "The language to use for message 
transformation")
+    private String language;
+
+    @CommandLine.Option(names = {
+            "--expression" },
+                        required = true,
+                        description = "The expression (template) to use for 
message transformation")
+    private String expression;
+
+    @CommandLine.Option(names = {
+            "--output" },
+                        description = "File to store output. If none provide 
then output is printed to console.")
+    private String output;
+
+    @CommandLine.Option(names = { "--show-exchange-properties" }, defaultValue 
= "false",
+                        description = "Show exchange properties from the 
output message")
+    boolean showExchangeProperties;
+
+    @CommandLine.Option(names = { "--show-headers" }, defaultValue = "true",
+                        description = "Show message headers from the output 
message")
+    boolean showHeaders = true;
+
+    @CommandLine.Option(names = { "--show-body" }, defaultValue = "true",
+                        description = "Show message body from the output 
message")
+    boolean showBody = true;
+
+    @CommandLine.Option(names = { "--show-exception" }, defaultValue = "true",
+                        description = "Show exception and stacktrace for 
failed transformation")
+    boolean showException = true;
+
+    @CommandLine.Option(names = { "--timeout" }, defaultValue = "20000",
+                        description = "Timeout in millis waiting for message 
to be transformed")
+    long timeout = 20000;
+
+    @CommandLine.Option(names = { "--logging-color" }, defaultValue = "true", 
description = "Use colored logging")
+    boolean loggingColor = true;
+
+    @CommandLine.Option(names = { "--pretty" },
+                        description = "Pretty print message body when using 
JSon or XML format")
+    boolean pretty;
+
+    private volatile long pid;
+
+    private MessageTableHelper tableHelper;
+
+    public TransformMessageAction(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doCall() throws Exception {
+        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(Long.toString(pid));
+        FileUtil.deleteFile(outputFile);
+
+        JsonObject root = new JsonObject();
+        root.put("action", "transform");
+        root.put("language", language);
+        String exp = expression;
+        if (expression.startsWith("file:")) {
+            File f = new File(expression.substring(5));
+            if (!f.isFile() && !f.exists()) {
+                System.err.println("File " + f.getName() + " does not exists");
+                return -1;
+            }
+            exp = IOHelper.loadText(new FileInputStream(f));
+        }
+        root.put("expression", Jsoner.escape(exp));
+        root.put("body", body);
+        if (headers != null) {
+            JsonArray arr = new JsonArray();
+            for (String h : headers) {
+                JsonObject jo = new JsonObject();
+                if (!h.contains("=")) {
+                    System.out.println("Header must be in key=value format, 
was: " + h);
+                    return 0;
+                }
+                jo.put("key", StringHelper.before(h, "="));
+                jo.put("value", StringHelper.after(h, "="));
+                arr.add(jo);
+            }
+            root.put("headers", arr);
+        }
+        File f = getActionFile(Long.toString(pid));
+        try {
+            IOHelper.writeText(root.toJson(), f);
+        } catch (Exception e) {
+            // ignore
+        }
+
+        JsonObject jo = waitForOutputFile(outputFile);
+        if (jo != null) {
+            printStatusLine(jo);
+            String exchangeId = jo.getString("exchangeId");
+            JsonObject message = jo.getMap("message");
+            JsonObject cause = jo.getMap("exception");
+            if (message != null || cause != null) {
+                if (output != null) {
+                    File target = new File(output);
+                    String json = jo.toJson();
+                    if (pretty) {
+                        json = Jsoner.prettyPrint(json, 2);
+                    }
+                    IOHelper.writeText(json, target);
+                }
+                if (!showExchangeProperties && message != null) {
+                    message.remove("exchangeProperties");
+                }
+                if (!showHeaders && message != null) {
+                    message.remove("headers");
+                }
+                if (!showBody && message != null) {
+                    message.remove("body");
+                }
+                if (!showException && cause != null) {
+                    cause = null;
+                }
+                if (output == null) {
+                    tableHelper = new MessageTableHelper();
+                    tableHelper.setPretty(pretty);
+                    tableHelper.setLoggingColor(loggingColor);
+                    
tableHelper.setShowExchangeProperties(showExchangeProperties);
+                    String table = tableHelper.getDataAsTable(exchangeId, 
"InOut", null, message, cause);
+                    System.out.println(table);
+                }
+            }
+        }
+
+        // delete output file after use
+        FileUtil.deleteFile(outputFile);
+
+        return 0;
+    }
+
+    private void printStatusLine(JsonObject jo) {
+        // timstamp
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+        String ts = sdf.format(new Date(jo.getLong("timestamp")));
+        if (loggingColor) {
+            
AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(ts).reset());
+        } else {
+            System.out.print(ts);
+        }
+        // pid
+        System.out.print("  ");
+        String p = String.format("%5.5s", this.pid);
+        if (loggingColor) {
+            AnsiConsole.out().print(Ansi.ansi().fgMagenta().a(p).reset());
+            
AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a("
 --- ").reset());
+        } else {
+            System.out.print(p);
+            System.out.print(" --- ");
+        }
+        // status
+        System.out.print(getStatus(jo));
+        // elapsed
+        String e = TimeUtils.printDuration(jo.getLong("elapsed"), true);
+        if (loggingColor) {
+            AnsiConsole.out().print(Ansi.ansi().fgBrightDefault().a(" (" + e + 
")").reset());
+        } else {
+            System.out.print("(" + e + ")");
+        }
+        System.out.println();
+    }
+
+    private String getStatus(JsonObject r) {
+        boolean failed = "failed".equals(r.getString("status"));
+        String status;
+        if (failed) {
+            status = "Failed (exception)";
+        } else if (output != null) {
+            status = "Output saved to file (success)";
+        } else {
+            status = "Message transformed (success)";
+        }
+        if (loggingColor) {
+            return Ansi.ansi().fg(failed ? Ansi.Color.RED : 
Ansi.Color.GREEN).a(status).reset().toString();
+        } else {
+            return status;
+        }
+    }
+
+    protected JsonObject waitForOutputFile(File outputFile) {
+        StopWatch watch = new StopWatch();
+        while (watch.taken() < timeout) {
+            try {
+                // give time for response to be ready
+                Thread.sleep(20);
+
+                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;
+    }
+}

Reply via email to