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 e2ba787d518 Tm3 (#12231)
e2ba787d518 is described below

commit e2ba787d518f68340d13bd8a511b1037d50c4988
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Nov 28 14:50:00 2023 +0100

    Tm3 (#12231)
    
    CAMEL-20153: camel-jbang - Transform message command
---
 .../apache/camel/builder/ModelRoutesBuilder.java   |  43 ++
 .../org/apache/camel/builder/RouteBuilder.java     |  25 +-
 .../org/apache/camel/support/LoggerHelper.java     |  16 +-
 .../modules/ROOT/pages/camel-jbang.adoc            | 139 ++++
 .../camel/cli/connector/LocalCliConnector.java     | 751 +++++++++++++--------
 .../apache/camel/cli/connector/LoggerHelper.java   |  37 +
 .../apache/camel/dsl/jbang/core/commands/Run.java  |   3 +-
 .../commands/action/TransformMessageAction.java    |  28 +-
 8 files changed, 731 insertions(+), 311 deletions(-)

diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/ModelRoutesBuilder.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/ModelRoutesBuilder.java
new file mode 100644
index 00000000000..2270afe5eda
--- /dev/null
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/ModelRoutesBuilder.java
@@ -0,0 +1,43 @@
+/*
+ * 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.builder;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.model.rest.RestsDefinition;
+
+/**
+ * A {@link org.apache.camel.RoutesBuilder} that provides access to the model 
definitions.
+ */
+public interface ModelRoutesBuilder {
+
+    /**
+     * Prepares the model definitions of this builder.
+     */
+    void prepareModel(CamelContext context) throws Exception;
+
+    /**
+     * Gets the routes
+     */
+    RoutesDefinition getRoutes();
+
+    /**
+     * Gets the rest-dsl
+     */
+    RestsDefinition getRests();
+
+}
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
index d3a032bc011..0be85d0471c 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
@@ -68,7 +68,7 @@ import org.slf4j.LoggerFactory;
  * A <a href="http://camel.apache.org/dsl.html";>Java DSL</a> which is used to 
build {@link Route} instances in a
  * {@link CamelContext} for smart routing.
  */
-public abstract class RouteBuilder extends BuilderSupport implements 
RoutesBuilder, Ordered, ResourceAware {
+public abstract class RouteBuilder extends BuilderSupport implements 
RoutesBuilder, ModelRoutesBuilder, Ordered, ResourceAware {
     protected Logger log = LoggerFactory.getLogger(getClass());
 
     private Resource resource;
@@ -601,7 +601,7 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
     }
 
     @Override
-    public void addRoutesToCamelContext(CamelContext context) throws Exception 
{
+    public void prepareModel(CamelContext context) throws Exception {
         // must configure routes before rests
         configureRoutes(context);
         configureRests(context);
@@ -617,6 +617,13 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         for (RouteDefinition route : routeCollection.getRoutes()) {
             routeCollection.prepareRoute(route);
         }
+    }
+
+    @Override
+    public void addRoutesToCamelContext(CamelContext context) throws Exception 
{
+        prepareModel(context);
+
+        // this will add the routes to camel
         populateRoutes();
 
         if (this instanceof OnCamelContextEvent) {
@@ -885,6 +892,16 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         }
     }
 
+    @Override
+    public RoutesDefinition getRoutes() {
+        return getRouteCollection();
+    }
+
+    @Override
+    public RestsDefinition getRests() {
+        return getRestCollection();
+    }
+
     public List<RegistryBeanDefinition> getBeans() {
         return beans;
     }
@@ -905,10 +922,6 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         return this.routeCollection;
     }
 
-    public void setRouteCollection(RoutesDefinition routeCollection) {
-        this.routeCollection = routeCollection;
-    }
-
     public RouteTemplatesDefinition getRouteTemplateCollection() {
         return routeTemplateCollection;
     }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/LoggerHelper.java 
b/core/camel-support/src/main/java/org/apache/camel/support/LoggerHelper.java
index 28cca1bf379..ef512e8bbcd 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/LoggerHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/LoggerHelper.java
@@ -103,15 +103,17 @@ public final class LoggerHelper {
         int cnt = StringHelper.countChar(location, ':');
         if (cnt > 1) {
             int pos = location.lastIndexOf(':');
-            String num = location.substring(pos);
-            try {
-                return Integer.valueOf(num);
-            } catch (Exception e) {
-                return null;
+            // in case pos is end of line
+            if (pos < location.length() - 1) {
+                String num = location.substring(pos + 1);
+                try {
+                    return Integer.valueOf(num);
+                } catch (Exception e) {
+                    return null;
+                }
             }
-        } else {
-            return null;
         }
+        return null;
     }
 
 }
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index a4b7e769ddb..b402046aea3 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -2244,6 +2244,145 @@ And the output:
 Then you can continue to update the `transform.json` until you have the 
desired result. And if you make a mistake
 then you see an error (in red) with stacktrace that hopefully can help you out 
how to fix this.
 
+=== Transforming directly from Camel DSL source
+
+In the example above we externalize the transformation into a template file 
named `transform.json`.
+However, its also possible to transform directly in the Camel DSL route.
+
+In the following we cover the json-transform example from: 
https://github.com/apache/camel-kamelets-examples/tree/main/jbang/json-transform
+
+In this example a random beer is fetched via the beer-source kamelet. The beer 
data is as follows:
+
+[source,json]
+----
+ {
+   "id": 2104,
+   "uid": "cefb36e1-e42f-4083-8f97-fc4ff85e56fb",
+   "brand": "Pabst Blue Ribbon",
+   "name": "St. Bernardus Abt 12",
+   "style": "Belgian Strong Ale",
+   "hop": "Amarillo",
+   "yeast": "1388 - Belgian Strong Ale",
+   "malts": "Caramel",
+   "ibu": "43 IBU",
+   "alcohol": "3.8%",
+   "blg": "16.0°Blg"
+ }
+----
+
+We save this beer into a file named `sample.json` as we want to use this as 
source for message transformation.
+
+Now suppose we want to transform this to a smaller JSon document with only a 
few elements, then we can do this
+directly in the route:
+
+[source,yaml]
+----
+- route:
+    nodePrefixId: route-c38
+    id: route-66b0
+    from:
+      uri: kamelet:beer-source
+      id: from-3996
+      steps:
+        - setBody:
+            expression:
+              simple:
+                expression: >-
+                  TODO
+                id: simple-b320
+            id: setBody-fa01
+        - log:
+            message: ${body}
+            id: log-0c79
+----
+
+As we can see the route has a `setBody` where we want to transform the 
message, which we can do with the simple
+language combined with JQ (or JSonPath). At first, we just write TODO to get 
started:
+
+[source,bash]
+----
+camel transform message --body=file:sample.json --source=beer-jq.yaml  --watch 
--pretty
+----
+
+And the output from the transformation is:
+
+[source,text]
+----
+ Exchange  (DefaultExchange)  InOut   D9F909701338607-0000000000000004
+ Message   (DefaultMessage)
+ Body      (String) (bytes: 4)
+ TODO
+----
+
+We can now do live updates in the Camel route, and when we save the file, the 
transform command will output the changes in the terminal.
+So we modify the DSL to grab the details we want, such as:
+
+[source,yaml]
+----
+    expression: >-
+      {
+        "kind": "${jq(.brand)}",
+        "beer": "${jq(.name)}"
+      }
+    id: simple-b320
+----
+
+Notice how we use inlined JQ expressions to grab the desired data from the 
sample, and when saving the file, we have
+the result shown in the terminal:
+
+[source,text]
+----
+ Exchange  (DefaultExchange)  InOut   D9F909701338607-000000000000003E
+ Message   (DefaultMessage)
+ Body      (String) (bytes: 78)
+ {
+   "kind": "Pabst Blue Ribbon",
+   "beer": "St. Bernardus Abt 12"
+ }
+----
+
+And when we are done with the transformation we can stop by `ctrl + c` to exit 
the command.
+
+When transforming messages directly from the DSL (using the `--source` 
option), then by default
+Camel will pick the last expression in the route. If you only have one 
expression like in this example,
+then that is easy. But when you have many, then you need to tell which one to 
use.
+
+You can do this either by referring to the line number in the source code of 
the EIP / expression, or
+by its id (if specified).
+
+So suppose the example had many expressions, and the one we want to use is on 
line 11, then we can do as follows:
+
+[source,bash]
+----
+camel transform message --body=file:sample.json --source=beer-jq.yaml:11  
--watch --pretty
+----
+
+Notice how we specify the line number in the source file name, by prefixing 
with `:line-number`.
+The line number does not have to be 100% accurate, as long the number is 
within range of the EIP, until
+the next EIP in the route. In the example this means you can pick number in 
range 8-17.
+
+If we want to use the id of the expression/EIP, we can do the same as shown 
below:
+
+[source,bash]
+----
+camel transform message --body=file:sample.json 
--source=beer-jq.yaml:setBody-fa01  --watch --pretty
+----
+
+You can also transform the message from JSon to XML as shown (or any other 
kind with fixed structure):
+
+[source,yaml]
+----
+- setBody:
+    expression:
+      simple:
+        expression: >-
+          <beer id="${jq(.id)}">
+            <name>${jq(.name)}</name>
+            <kind>${jq(.brand)}</kind>
+          </beer>
+        id: simple-b320
+    id: setBody-fa01
+----
 
 == Listing what Camel components is available
 
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 641569e55b9..0ddbc80b69d 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
@@ -18,6 +18,7 @@ package org.apache.camel.cli.connector;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.lang.management.ClassLoadingMXBean;
 import java.lang.management.GarbageCollectorMXBean;
@@ -25,6 +26,8 @@ import java.lang.management.ManagementFactory;
 import java.lang.management.MemoryMXBean;
 import java.lang.management.RuntimeMXBean;
 import java.lang.management.ThreadMXBean;
+import java.nio.file.Files;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -48,14 +51,24 @@ import org.apache.camel.NoSuchEndpointException;
 import org.apache.camel.Processor;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.Route;
+import org.apache.camel.RoutesBuilder;
 import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.builder.ModelRoutesBuilder;
 import org.apache.camel.console.DevConsole;
 import org.apache.camel.console.DevConsoleRegistry;
+import org.apache.camel.model.HasExpressionType;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.model.ProcessorDefinitionHelper;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.language.ExpressionDefinition;
 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.Resource;
+import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.spi.ResourceReloadStrategy;
+import org.apache.camel.spi.RoutesLoader;
 import org.apache.camel.support.EndpointHelper;
 import org.apache.camel.support.MessageHelper;
 import org.apache.camel.support.PatternHelper;
@@ -98,6 +111,8 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
     private File traceFile;
     private File debugFile;
     private long traceFilePos; // keep track of trace offset
+    private byte[] lastSource;
+    private ExpressionDefinition lastSourceExpression;
 
     public LocalCliConnector(CliConnectorFactory cliConnectorFactory) {
         this.cliConnectorFactory = cliConnectorFactory;
@@ -216,327 +231,293 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
 
             String action = root.getString("action");
             if ("route".equals(action)) {
-                // id is a pattern
-                String[] patterns = root.getString("id").split(",");
-                // find matching IDs
-                List<String> ids = camelContext.getRoutes()
-                        .stream().map(Route::getRouteId)
-                        .filter(routeId -> {
-                            for (String p : patterns) {
-                                if (PatternHelper.matchPattern(routeId, p)) {
-                                    return true;
-                                }
-                            }
-                            return false;
-                        })
-                        .toList();
-                for (String id : ids) {
-                    try {
-                        String command = root.getString("command");
-                        if ("start".equals(command)) {
-                            if ("*".equals(id)) {
-                                
camelContext.getRouteController().startAllRoutes();
-                            } else {
-                                
camelContext.getRouteController().startRoute(id);
-                            }
-                        } else if ("stop".equals(command)) {
-                            if ("*".equals(id)) {
-                                
camelContext.getRouteController().stopAllRoutes();
-                            } else {
-                                
camelContext.getRouteController().stopRoute(id);
-                            }
-                        } else if ("suspend".equals(command)) {
-                            camelContext.getRouteController().suspendRoute(id);
-                        } else if ("resume".equals(command)) {
-                            camelContext.getRouteController().resumeRoute(id);
-                        }
-                    } catch (Exception e) {
-                        // ignore
-                    }
-                }
+                doActionRouteTask(root);
             } else if ("logger".equals(action)) {
-                try {
-                    String command = root.getString("command");
-                    if ("set-logging-level".equals(command)) {
-                        String logger = root.getString("logger-name");
-                        String level = root.getString("logging-level");
-                        LoggerHelper.changeLoggingLevel(logger, level);
-                    }
-                } catch (Exception e) {
-                    // ignore
-                }
+                doActionLoggerTask(root);
             } else if ("gc".equals(action)) {
                 System.gc();
             } else if ("reload".equals(action)) {
-                ContextReloadStrategy cr = 
camelContext.hasService(ContextReloadStrategy.class);
-                if (cr != null) {
-                    cr.onReload("Camel CLI");
-                } else {
-                    ResourceReloadStrategy rr = 
camelContext.hasService(ResourceReloadStrategy.class);
-                    if (rr != null) {
-                        rr.onReload("Camel CLI");
-                    }
-                }
+                doActionReloadTask();
             } else if ("debug".equals(action)) {
-                DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
-                        .resolveById("debug");
-                if (dc != null) {
-                    String cmd = root.getStringOrDefault("command", "");
-                    String bp = root.getStringOrDefault("breakpoint", "");
-                    String history = root.getStringOrDefault("history", 
"false");
-                    JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON,
-                            Map.of("command", cmd, "breakpoint", bp, 
"history", history));
-                    LOG.trace("Updating output file: {}", outputFile);
-                    IOHelper.writeText(json.toJson(), outputFile);
-                }
+                doActionDebugTask(root);
             } else if ("reset-stats".equals(action)) {
-                ManagedCamelContext mcc = 
camelContext.getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
-                if (mcc != null) {
-                    mcc.getManagedCamelContext().reset(true);
-                }
+                doActionResetStatsTask();
             } else if ("thread-dump".equals(action)) {
-                DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
-                        .resolveById("thread");
-                if (dc != null) {
-                    JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON, Map.of("stackTrace", "true"));
-                    LOG.trace("Updating output file: {}", outputFile);
-                    IOHelper.writeText(json.toJson(), outputFile);
-                }
+                doActionThreadDumpTask();
             } else if ("top-processors".equals(action)) {
-                DevConsole dc
-                        = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class).resolveById("top");
-                if (dc != null) {
-                    JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON, Map.of(Exchange.HTTP_PATH, "/*"));
-                    LOG.trace("Updating output file: {}", outputFile);
-                    IOHelper.writeText(json.toJson(), outputFile);
-                }
+                doActionTopProcessorsTask();
             } else if ("source".equals(action)) {
-                DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
-                        .resolveById("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);
-                }
+                doActionSourceTask(root);
             } else if ("route-dump".equals(action)) {
-                DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
-                        .resolveById("route-dump");
-                if (dc != null) {
-                    String filter = root.getString("filter");
-                    String format = root.getString("format");
-                    String uriAsParameters = root.getString("uriAsParameters");
-                    JsonObject json
-                            = (JsonObject) dc.call(DevConsole.MediaType.JSON,
-                                    Map.of("filter", filter, "format", format, 
"uriAsParameters", uriAsParameters));
-                    LOG.trace("Updating output file: {}", outputFile);
-                    IOHelper.writeText(json.toJson(), outputFile);
-                }
+                doActionRouteDumpTask(root);
             } else if ("route-controller".equals(action)) {
-                DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
-                        .resolveById("route-controller");
-                if (dc != null) {
-                    String stacktrace = root.getString("stacktrace");
-                    JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON, Map.of("stacktrace", stacktrace));
-                    LOG.trace("Updating output file: {}", outputFile);
-                    IOHelper.writeText(json.toJson(), outputFile);
-                }
+                doActionRouteControllerTask(root);
             } else if ("startup-recorder".equals(action)) {
-                DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
-                        .resolveById("startup-recorder");
-                if (dc != null) {
-                    JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON);
-                    LOG.trace("Updating output file: {}", outputFile);
-                    IOHelper.writeText(json.toJson(), outputFile);
-                }
+                doActionStartupRecorder();
             } else if ("stub".equals(action)) {
-                String filter = root.getString("filter");
-                String limit = root.getString("limit");
-                String browse = root.getString("browse");
-
-                DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
-                        .resolveById("stub");
-                if (dc != null) {
-                    JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON,
-                            Map.of("filter", filter, "limit", limit, "browse", 
browse));
-                    LOG.trace("Updating output file: {}", outputFile);
-                    IOHelper.writeText(json.toJson(), outputFile);
-                }
+                doActionStubTask(root);
             } else if ("send".equals(action)) {
-                StopWatch watch = new StopWatch();
-                long timestamp = System.currentTimeMillis();
-                String endpoint = root.getString("endpoint");
-                String body = root.getString("body");
-                String exchangePattern = root.getString("exchangePattern");
-                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 HashMap<>();
-                        for (JsonObject jo : headers) {
-                            map.put(jo.getString("key"), 
jo.getString("value"));
-                        }
+                doActionSendTask(root);
+            } else if ("transform".equals(action)) {
+                doActionTransformTask(root);
+            }
+
+            // action done so delete file
+            FileUtil.deleteFile(actionFile);
+
+        } catch (Exception e) {
+            // ignore
+            // TODO: reduce to debug
+            LOG.warn("Error executing action file: {} due to: {}. This 
exception is ignored.", actionFile, e.getMessage(),
+                    e);
+        }
+    }
+
+    private void doActionTransformTask(JsonObject root) throws Exception {
+        StopWatch watch = new StopWatch();
+        long timestamp = System.currentTimeMillis();
+        String source = root.getString("source");
+        String language = root.getString("language");
+        String template = Jsoner.unescape(root.getStringOrDefault("template", 
""));
+        if (template.startsWith("file:")) {
+            template = "resource:" + template;
+        }
+        String body = Jsoner.unescape(root.getString("body"));
+        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 = IOHelper.loadText(is);
+        }
+        Collection<JsonObject> headers = root.getCollection("headers");
+        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 {
+            if (source != null) {
+                Integer sourceLine = 
LoggerHelper.extractSourceLocationLineNumber(source);
+                String sourceId = LoggerHelper.extractSourceLocationId(source);
+                source = LoggerHelper.stripSourceLocationLineNumber(source);
+                LOG.debug("Source: {} line: {} id: {}", source, sourceLine, 
sourceId);
+
+                boolean update = true;
+                File f = new File(source);
+                if (f.isFile() && f.exists()) {
+                    byte[] data = Files.readAllBytes(f.toPath());
+                    if (Arrays.equals(lastSource, data)) {
+                        LOG.debug("Source file: {} is not updated since last", 
source);
+                        update = false;
                     }
-                    final Object inputBody = b;
-                    final Map<String, Object> inputHeaders = map;
-                    Exchange out;
-                    Endpoint target = null;
-                    if (endpoint == null) {
-                        List<Route> routes = camelContext.getRoutes();
-                        if (!routes.isEmpty()) {
-                            // grab endpoint from 1st route
-                            target = routes.get(0).getEndpoint();
-                        }
+                    lastSource = data;
+                }
+                if (update) {
+                    if (sourceLine != null) {
+                        LOG.info("Transforming from source: {}:{}", source, 
sourceLine);
+                    } else if (sourceId != null) {
+                        LOG.info("Transforming from source: {}:{}", source, 
sourceId);
                     } else {
-                        // is the endpoint a pattern or route id
-                        boolean scheme = endpoint.contains(":");
-                        boolean pattern = endpoint.endsWith("*");
-                        if (!scheme || pattern) {
-                            if (!scheme) {
-                                endpoint = endpoint + "*";
-                            }
-                            for (Route route : camelContext.getRoutes()) {
-                                Endpoint e = route.getEndpoint();
-                                if (EndpointHelper.matchEndpoint(camelContext, 
e.getEndpointUri(), endpoint)) {
-                                    target = e;
-                                    break;
-                                }
-                            }
-                            if (target == null) {
-                                // okay it may refer to a route id
-                                for (Route route : camelContext.getRoutes()) {
-                                    String id = route.getRouteId();
-                                    Endpoint e = route.getEndpoint();
-                                    if 
(EndpointHelper.matchEndpoint(camelContext, id, endpoint)) {
-                                        target = e;
-                                        break;
-                                    }
-                                }
-                            }
-                        } else {
-                            target = camelContext.getEndpoint(endpoint);
-                        }
+                        LOG.info("Transforming from source: {}", source);
                     }
 
-                    if (target != null) {
-                        out = producer.send(target, new Processor() {
-                            @Override
-                            public void process(Exchange exchange) throws 
Exception {
-                                exchange.getMessage().setBody(inputBody);
-                                if (inputHeaders != null) {
-                                    
exchange.getMessage().setHeaders(inputHeaders);
+                    // load route definition
+                    if (!source.startsWith("file:")) {
+                        source = "file:" + source;
+                    }
+                    // load the source via routes loader, and find the 
builders, which we can use to get to the model
+                    Resource res = 
camelContext.getCamelContextExtension().getContextPlugin(ResourceLoader.class)
+                            .resolveResource(source);
+                    RoutesLoader loader = 
camelContext.getCamelContextExtension().getContextPlugin(RoutesLoader.class);
+                    Collection<RoutesBuilder> builders = 
loader.findRoutesBuilders(res);
+                    for (RoutesBuilder builder : builders) {
+                        // use the model as we just want to find the EIP with 
the inlined expression
+                        ModelRoutesBuilder mrb = (ModelRoutesBuilder) builder;
+                        // must prepare model before we can access them
+                        mrb.prepareModel(camelContext);
+                        // find the EIP with the inlined expression to use
+                        ExpressionDefinition found = null;
+                        for (RouteDefinition rd : mrb.getRoutes().getRoutes()) 
{
+                            Collection<ProcessorDefinition> defs
+                                    = 
ProcessorDefinitionHelper.filterTypeInOutputs(rd.getOutputs(),
+                                            ProcessorDefinition.class);
+                            for (ProcessorDefinition p : defs) {
+                                if (p instanceof HasExpressionType et) {
+                                    ExpressionDefinition def = 
et.getExpressionType();
+                                    if (def != null) {
+                                        if (sourceLine != null) {
+                                            if (p.getLineNumber() == -1 || 
p.getLineNumber() <= sourceLine) {
+                                                found = def;
+                                            }
+                                        } else if (sourceId != null) {
+                                            if (sourceId.equals(p.getId()) || 
sourceId.equals(def.getId())) {
+                                                found = def;
+                                            }
+                                        } else {
+                                            found = def;
+                                        }
+                                    }
                                 }
-                                exchange.setPattern(
-                                        "InOut".equals(exchangePattern) ? 
ExchangePattern.InOut : ExchangePattern.InOnly);
                             }
-                        });
-                        IOHelper.close(is);
-                        LOG.trace("Updating output file: {}", outputFile);
-                        if (out.getException() != null) {
-                            JsonObject jo = new JsonObject();
-                            jo.put("endpoint", target.getEndpointUri());
-                            jo.put("exchangeId", out.getExchangeId());
-                            jo.put("exchangePattern", exchangePattern);
-                            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 if ("InOut".equals(exchangePattern)) {
-                            JsonObject jo = new JsonObject();
-                            jo.put("endpoint", target.getEndpointUri());
-                            jo.put("exchangeId", out.getExchangeId());
-                            jo.put("exchangePattern", exchangePattern);
-                            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);
-                        } else {
-                            JsonObject jo = new JsonObject();
-                            jo.put("endpoint", target.getEndpointUri());
-                            jo.put("exchangeId", out.getExchangeId());
-                            jo.put("exchangePattern", exchangePattern);
-                            jo.put("timestamp", timestamp);
-                            jo.put("elapsed", watch.taken());
-                            jo.put("status", "success");
-                            IOHelper.writeText(jo.toJson(), outputFile);
+                            if (found != null) {
+                                lastSourceExpression = found;
+                            }
                         }
-                    } else {
-                        // there is no valid endpoint
-                        JsonObject jo = new JsonObject();
-                        jo.put("endpoint", root.getString("endpoint"));
-                        jo.put("exchangeId", "");
-                        jo.put("exchangePattern", exchangePattern);
-                        jo.put("timestamp", timestamp);
-                        jo.put("elapsed", watch.taken());
-                        jo.put("status", "failed");
-                        // avoid double wrap
-                        jo.put("exception",
-                                MessageHelper.dumpExceptionAsJSonObject(new 
NoSuchEndpointException(root.getString("endpoint")))
-                                        .getMap("exception"));
-                        IOHelper.writeText(jo.toJson(), outputFile);
                     }
                 }
-            } else if ("transform".equals(action)) {
-                StopWatch watch = new StopWatch();
-                long timestamp = System.currentTimeMillis();
-                String language = root.getString("language");
-                String template = Jsoner.unescape(root.getString("template"));
-                if (template.startsWith("file:")) {
-                    template = "resource:" + template;
-                }
-                String body = Jsoner.unescape(root.getString("body"));
-                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 = IOHelper.loadText(is);
-                }
-                Collection<JsonObject> headers = root.getCollection("headers");
-                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(template);
-                    exp.init(camelContext);
+                if (lastSourceExpression != null) {
                     // 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);
+                    String result = lastSourceExpression.evaluate(out, 
String.class);
                     out.getMessage().setBody(result);
-                    IOHelper.close(is);
-                } catch (Exception e) {
-                    out.setException(e);
                 }
+            } else {
+                // transform via language
+                Language lan = camelContext.resolveLanguage(language);
+                Expression exp = lan.createExpression(template);
+                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();
+            if (language != null) {
+                jo.put("language", language);
+            }
+            if (source != null) {
+                jo.put("source", source);
+            }
+            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();
+            if (language != null) {
+                jo.put("language", language);
+            }
+            if (source != null) {
+                jo.put("source", source);
+            }
+            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);
+    }
+
+    private void doActionSendTask(JsonObject root) throws Exception {
+        StopWatch watch = new StopWatch();
+        long timestamp = System.currentTimeMillis();
+        String endpoint = root.getString("endpoint");
+        String body = root.getString("body");
+        String exchangePattern = root.getString("exchangePattern");
+        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 HashMap<>();
+                for (JsonObject jo : headers) {
+                    map.put(jo.getString("key"), jo.getString("value"));
+                }
+            }
+            final Object inputBody = b;
+            final Map<String, Object> inputHeaders = map;
+            Exchange out;
+            Endpoint target = null;
+            if (endpoint == null) {
+                List<Route> routes = camelContext.getRoutes();
+                if (!routes.isEmpty()) {
+                    // grab endpoint from 1st route
+                    target = routes.get(0).getEndpoint();
+                }
+            } else {
+                // is the endpoint a pattern or route id
+                boolean scheme = endpoint.contains(":");
+                boolean pattern = endpoint.endsWith("*");
+                if (!scheme || pattern) {
+                    if (!scheme) {
+                        endpoint = endpoint + "*";
+                    }
+                    for (Route route : camelContext.getRoutes()) {
+                        Endpoint e = route.getEndpoint();
+                        if (EndpointHelper.matchEndpoint(camelContext, 
e.getEndpointUri(), endpoint)) {
+                            target = e;
+                            break;
+                        }
+                    }
+                    if (target == null) {
+                        // okay it may refer to a route id
+                        for (Route route : camelContext.getRoutes()) {
+                            String id = route.getRouteId();
+                            Endpoint e = route.getEndpoint();
+                            if (EndpointHelper.matchEndpoint(camelContext, id, 
endpoint)) {
+                                target = e;
+                                break;
+                            }
+                        }
+                    }
+                } else {
+                    target = camelContext.getEndpoint(endpoint);
+                }
+            }
+
+            if (target != null) {
+                out = producer.send(target, new Processor() {
+                    @Override
+                    public void process(Exchange exchange) throws Exception {
+                        exchange.getMessage().setBody(inputBody);
+                        if (inputHeaders != null) {
+                            exchange.getMessage().setHeaders(inputHeaders);
+                        }
+                        exchange.setPattern(
+                                "InOut".equals(exchangePattern) ? 
ExchangePattern.InOut : ExchangePattern.InOnly);
+                    }
+                });
+                IOHelper.close(is);
                 LOG.trace("Updating output file: {}", outputFile);
                 if (out.getException() != null) {
                     JsonObject jo = new JsonObject();
-                    jo.put("language", language);
+                    jo.put("endpoint", target.getEndpointUri());
                     jo.put("exchangeId", out.getExchangeId());
+                    jo.put("exchangePattern", exchangePattern);
                     jo.put("timestamp", timestamp);
                     jo.put("elapsed", watch.taken());
                     jo.put("status", "failed");
@@ -544,10 +525,11 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
                     jo.put("exception",
                             
MessageHelper.dumpExceptionAsJSonObject(out.getException()).getMap("exception"));
                     IOHelper.writeText(jo.toJson(), outputFile);
-                } else {
+                } else if ("InOut".equals(exchangePattern)) {
                     JsonObject jo = new JsonObject();
-                    jo.put("language", language);
+                    jo.put("endpoint", target.getEndpointUri());
                     jo.put("exchangeId", out.getExchangeId());
+                    jo.put("exchangePattern", exchangePattern);
                     jo.put("timestamp", timestamp);
                     jo.put("elapsed", watch.taken());
                     jo.put("status", "success");
@@ -555,17 +537,200 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
                     jo.put("message", 
MessageHelper.dumpAsJSonObject(out.getMessage(), true, true, true, true, true,
                             BODY_MAX_CHARS).getMap("message"));
                     IOHelper.writeText(jo.toJson(), outputFile);
+                } else {
+                    JsonObject jo = new JsonObject();
+                    jo.put("endpoint", target.getEndpointUri());
+                    jo.put("exchangeId", out.getExchangeId());
+                    jo.put("exchangePattern", exchangePattern);
+                    jo.put("timestamp", timestamp);
+                    jo.put("elapsed", watch.taken());
+                    jo.put("status", "success");
+                    IOHelper.writeText(jo.toJson(), outputFile);
                 }
-                
camelContext.getCamelContextExtension().getExchangeFactory().release(out);
+            } else {
+                // there is no valid endpoint
+                JsonObject jo = new JsonObject();
+                jo.put("endpoint", root.getString("endpoint"));
+                jo.put("exchangeId", "");
+                jo.put("exchangePattern", exchangePattern);
+                jo.put("timestamp", timestamp);
+                jo.put("elapsed", watch.taken());
+                jo.put("status", "failed");
+                // avoid double wrap
+                jo.put("exception",
+                        MessageHelper.dumpExceptionAsJSonObject(new 
NoSuchEndpointException(root.getString("endpoint")))
+                                .getMap("exception"));
+                IOHelper.writeText(jo.toJson(), outputFile);
             }
+        }
+    }
 
-            // action done so delete file
-            FileUtil.deleteFile(actionFile);
+    private void doActionStubTask(JsonObject root) throws Exception {
+        String filter = root.getString("filter");
+        String limit = root.getString("limit");
+        String browse = root.getString("browse");
 
+        DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("stub");
+        if (dc != null) {
+            JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON,
+                    Map.of("filter", filter, "limit", limit, "browse", 
browse));
+            LOG.trace("Updating output file: {}", outputFile);
+            IOHelper.writeText(json.toJson(), outputFile);
+        }
+    }
+
+    private void doActionStartupRecorder() throws Exception {
+        DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("startup-recorder");
+        if (dc != null) {
+            JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON);
+            LOG.trace("Updating output file: {}", outputFile);
+            IOHelper.writeText(json.toJson(), outputFile);
+        }
+    }
+
+    private void doActionRouteControllerTask(JsonObject root) throws Exception 
{
+        DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("route-controller");
+        if (dc != null) {
+            String stacktrace = root.getString("stacktrace");
+            JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON, 
Map.of("stacktrace", stacktrace));
+            LOG.trace("Updating output file: {}", outputFile);
+            IOHelper.writeText(json.toJson(), outputFile);
+        }
+    }
+
+    private void doActionRouteDumpTask(JsonObject root) throws Exception {
+        DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("route-dump");
+        if (dc != null) {
+            String filter = root.getString("filter");
+            String format = root.getString("format");
+            String uriAsParameters = root.getString("uriAsParameters");
+            JsonObject json
+                    = (JsonObject) dc.call(DevConsole.MediaType.JSON,
+                            Map.of("filter", filter, "format", format, 
"uriAsParameters", uriAsParameters));
+            LOG.trace("Updating output file: {}", outputFile);
+            IOHelper.writeText(json.toJson(), outputFile);
+        }
+    }
+
+    private void doActionSourceTask(JsonObject root) throws Exception {
+        DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("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);
+        }
+    }
+
+    private void doActionTopProcessorsTask() throws IOException {
+        DevConsole dc
+                = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class).resolveById("top");
+        if (dc != null) {
+            JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON, 
Map.of(Exchange.HTTP_PATH, "/*"));
+            LOG.trace("Updating output file: {}", outputFile);
+            IOHelper.writeText(json.toJson(), outputFile);
+        }
+    }
+
+    private void doActionThreadDumpTask() throws IOException {
+        DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("thread");
+        if (dc != null) {
+            JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON, 
Map.of("stackTrace", "true"));
+            LOG.trace("Updating output file: {}", outputFile);
+            IOHelper.writeText(json.toJson(), outputFile);
+        }
+    }
+
+    private void doActionResetStatsTask() throws Exception {
+        ManagedCamelContext mcc = 
camelContext.getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
+        if (mcc != null) {
+            mcc.getManagedCamelContext().reset(true);
+        }
+    }
+
+    private void doActionDebugTask(JsonObject root) throws Exception {
+        DevConsole dc = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("debug");
+        if (dc != null) {
+            String cmd = root.getStringOrDefault("command", "");
+            String bp = root.getStringOrDefault("breakpoint", "");
+            String history = root.getStringOrDefault("history", "false");
+            JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON,
+                    Map.of("command", cmd, "breakpoint", bp, "history", 
history));
+            LOG.trace("Updating output file: {}", outputFile);
+            IOHelper.writeText(json.toJson(), outputFile);
+        }
+    }
+
+    private void doActionReloadTask() {
+        ContextReloadStrategy cr = 
camelContext.hasService(ContextReloadStrategy.class);
+        if (cr != null) {
+            cr.onReload("Camel CLI");
+        } else {
+            ResourceReloadStrategy rr = 
camelContext.hasService(ResourceReloadStrategy.class);
+            if (rr != null) {
+                rr.onReload("Camel CLI");
+            }
+        }
+    }
+
+    private void doActionLoggerTask(JsonObject root) {
+        try {
+            String command = root.getString("command");
+            if ("set-logging-level".equals(command)) {
+                String logger = root.getString("logger-name");
+                String level = root.getString("logging-level");
+                LoggerHelper.changeLoggingLevel(logger, level);
+            }
         } catch (Exception e) {
             // ignore
-            LOG.debug("Error executing action file: {} due to: {}. This 
exception is ignored.", actionFile, e.getMessage(),
-                    e);
+        }
+    }
+
+    private void doActionRouteTask(JsonObject root) {
+        // id is a pattern
+        String[] patterns = root.getString("id").split(",");
+        // find matching IDs
+        List<String> ids = camelContext.getRoutes()
+                .stream().map(Route::getRouteId)
+                .filter(routeId -> {
+                    for (String p : patterns) {
+                        if (PatternHelper.matchPattern(routeId, p)) {
+                            return true;
+                        }
+                    }
+                    return false;
+                })
+                .toList();
+        for (String id : ids) {
+            try {
+                String command = root.getString("command");
+                if ("start".equals(command)) {
+                    if ("*".equals(id)) {
+                        camelContext.getRouteController().startAllRoutes();
+                    } else {
+                        camelContext.getRouteController().startRoute(id);
+                    }
+                } else if ("stop".equals(command)) {
+                    if ("*".equals(id)) {
+                        camelContext.getRouteController().stopAllRoutes();
+                    } else {
+                        camelContext.getRouteController().stopRoute(id);
+                    }
+                } else if ("suspend".equals(command)) {
+                    camelContext.getRouteController().suspendRoute(id);
+                } else if ("resume".equals(command)) {
+                    camelContext.getRouteController().resumeRoute(id);
+                }
+            } catch (Exception e) {
+                // ignore
+            }
         }
     }
 
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LoggerHelper.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LoggerHelper.java
index 82e899238e3..aa01bae8b9b 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LoggerHelper.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LoggerHelper.java
@@ -23,6 +23,8 @@ import javax.management.Attribute;
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
 
+import org.apache.camel.util.StringHelper;
+
 /**
  * Helper for logger action.
  *
@@ -70,4 +72,39 @@ public final class LoggerHelper {
         }
     }
 
+    public static String stripSourceLocationLineNumber(String location) {
+        int cnt = StringHelper.countChar(location, ':');
+        if (cnt >= 1) {
+            int pos = location.lastIndexOf(':');
+            return location.substring(0, pos);
+        } else {
+            return location;
+        }
+    }
+
+    public static Integer extractSourceLocationLineNumber(String location) {
+        int cnt = StringHelper.countChar(location, ':');
+        if (cnt >= 1) {
+            int pos = location.lastIndexOf(':');
+            String num = location.substring(pos + 1);
+            try {
+                return Integer.valueOf(num);
+            } catch (Exception var5) {
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    public static String extractSourceLocationId(String location) {
+        int cnt = StringHelper.countChar(location, ':');
+        if (cnt >= 1) {
+            int pos = location.lastIndexOf(':');
+            return location.substring(pos + 1);
+        } else {
+            return null;
+        }
+    }
+
 }
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 7876bd6ee2e..0afec4ba0ae 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
@@ -123,7 +123,7 @@ public class Run extends CamelCommand {
                 arity = "0..9", paramLabel = "<files>", parameterConsumer = 
FilesConsumer.class)
     Path[] filePaths; // Defined only for file path completion; the field 
never used
 
-    List<String> files = new ArrayList<>();
+    public List<String> files = new ArrayList<>();
 
     @Option(names = { "--source-dir" },
             description = "Source directory for dynamically loading Camel 
file(s) to run. When using this, then files cannot be specified at the same 
time.")
@@ -313,6 +313,7 @@ public class Run extends CamelCommand {
         this.background = true;
         this.camelVersion = camelVersion;
         this.empty = true;
+        this.ignoreLoadingError = true;
         this.name = "transform";
         return run();
     }
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
index b958329d31e..fd07c1c96d4 100644
--- 
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
@@ -54,15 +54,18 @@ public class TransformMessageAction extends 
ActionWatchCommand {
                         description = "Message header (key=value)")
     List<String> headers;
 
+    @CommandLine.Option(names = {
+            "--source" },
+                        description = "Instead of using external template file 
then refer to an existing Camel route source with inlined Camel language 
expression in a route. (use :line-number or :id to refer to the exact location 
of the EIP to use)")
+    private String source;
+
     @CommandLine.Option(names = {
             "--language" },
-                        required = true,
                         description = "The language to use for message 
transformation")
     private String language;
 
     @CommandLine.Option(names = {
             "--template" },
-                        required = true,
                         description = "The template to use for message 
transformation (prefix with file: to refer to loading message body from file)")
     private String template;
 
@@ -108,6 +111,16 @@ public class TransformMessageAction extends 
ActionWatchCommand {
 
     @Override
     public Integer doCall() throws Exception {
+        // either source or language/template is required
+        if (source == null && template == null && language == null) {
+            System.err.println("Either source or template and language must be 
configured");
+            return -1;
+        }
+        if (source == null && (template == null || language == null)) {
+            System.err.println("Both template and language must be 
configured");
+            return -1;
+        }
+
         Integer exit;
         try {
             // start a new empty camel in the background
@@ -147,8 +160,15 @@ public class TransformMessageAction extends 
ActionWatchCommand {
 
         JsonObject root = new JsonObject();
         root.put("action", "transform");
-        root.put("language", language);
-        root.put("template", Jsoner.escape(template));
+        if (source != null) {
+            root.put("source", source);
+        }
+        if (language != null) {
+            root.put("language", language);
+        }
+        if (template != null) {
+            root.put("template", Jsoner.escape(template));
+        }
         root.put("body", Jsoner.escape(body));
         if (headers != null) {
             JsonArray arr = new JsonArray();

Reply via email to