You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/05/17 11:02:45 UTC

[camel] 02/02: CAMEL-18118: camel-jbang - Linux terminal scripting using pipes

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 dc9ebe6dd1d1e73619dfd1ede2d7bc4e11afa16a
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue May 17 13:00:42 2022 +0200

    CAMEL-18118: camel-jbang - Linux terminal scripting using pipes
---
 .../modules/ROOT/pages/camel-jbang.adoc            | 59 ++++++++++++++++++----
 .../dsl/support/RouteBuilderLoaderSupport.java     | 55 ++++++++++++++++++++
 .../dsl/java/joor/ClassRoutesBuilderLoader.java    |  2 +-
 .../dsl/java/joor/JavaRoutesBuilderLoader.java     |  2 +-
 .../dsl/xml/jaxb/JaxbXmlRoutesBuilderLoader.java   |  8 +--
 .../camel/dsl/yaml/YamlRoutesBuilderLoader.java    |  2 +-
 .../dsl/yaml/YamlRoutesBuilderLoaderSupport.java   |  2 +-
 7 files changed, 113 insertions(+), 17 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index 932f7be9a02..6881d7fecf9 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -206,15 +206,6 @@ This is a groovy route, which you can run with (or use `*`):
 camel run simple.groovy
 ----
 
-=== Running local Kamelets
-
-You can also use Camel JBang to try local Kamelets, without the need to publish them on GitHub or package them in a jar.
-
-[source,bash]
-----
-camel run --local-kamelet-dir=/path/to/local/kamelets earthquake.yaml
-----
-
 ==== Downloading routes form GitHub gists
 
 You can also download files from gists easily as shown:
@@ -297,6 +288,56 @@ For example. you can copy this to your clipboard and then run it afterwards:
 camel run clipboard.xml
 ----
 
+=== Scripting from terminal using pipes
+
+You can also execute a Camel JBang file as a script that can be used for terminal scripting with pipes and filters.
+
+This requires to add the following line in top of the file, for example as in the `upper.yaml` file below:
+
+[source,text]
+----
+///usr/bin/env jbang --quiet camel@apache/camel pipe "$0" "$@" ; exit $?
+
+# Will upper-case the input
+- from:
+    uri: "stream:in"
+    steps:
+      - setBody:
+          simple: "${body.toUpperCase()}"
+      - to: "stream:out"
+----
+
+To be able to execute this as a script, you need to set execute file permission:
+
+[source,bash]
+----
+chmod +x upper.yaml
+----
+
+Then you can then execute this as a script:
+
+[source,bash]
+----
+echo "Hello\nWorld" | ./upper.yaml
+----
+
+Which should output:
+
+[source,text]
+----
+HELLO
+WORLD
+----
+
+=== Running local Kamelets
+
+You can also use Camel JBang to try local Kamelets, without the need to publish them on GitHub or package them in a jar.
+
+[source,bash]
+----
+camel run --local-kamelet-dir=/path/to/local/kamelets earthquake.yaml
+----
+
 === Using platform-http component
 
 When a route is started from `platform-http` then Camel JBang will automatically include a VertX HTTP server
diff --git a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
index fe67a37cfa7..05aaae5d806 100644
--- a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
+++ b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
@@ -16,6 +16,12 @@
  */
 package org.apache.camel.dsl.support;
 
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -32,6 +38,9 @@ import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.RoutesBuilderLoader;
 import org.apache.camel.spi.StartupStepRecorder;
 import org.apache.camel.support.RoutesBuilderLoaderSupport;
+import org.apache.camel.util.IOHelper;
+
+import static org.apache.camel.util.IOHelper.buffered;
 
 /**
  * Base class for {@link RoutesBuilderLoader} implementations.
@@ -115,5 +124,51 @@ public abstract class RouteBuilderLoaderSupport extends RoutesBuilderLoaderSuppo
         return builder;
     }
 
+    /**
+     * Gets the input stream to the resource
+     *
+     * @param resource  the resource
+     * @return  the input stream
+     */
+    protected InputStream resourceInputStream(Resource resource) throws IOException {
+        // load into memory as we need to skip a specific first-line if present
+        String data = loadResource(resource.getInputStream());
+        return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
+    }
+
+
+    /**
+     * Loads {@link RoutesBuilder} from {@link Resource} from the DSL implementation.
+     *
+     * @param  resource the resource to be loaded.
+     * @return          a {@link RoutesBuilder}
+     */
     protected abstract RouteBuilder doLoadRouteBuilder(Resource resource) throws Exception;
+
+    private static String loadResource(InputStream in) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        InputStreamReader isr = new InputStreamReader(in);
+        boolean first = true;
+        try {
+            BufferedReader reader = IOHelper.buffered(isr);
+            while (true) {
+                String line = reader.readLine();
+                if (line != null) {
+                    // we need to skip first line if it starts with a special script marker for camel-jbang in pipe mode
+                    if (first && line.startsWith("///usr/bin/env jbang --quiet camel@apache/camel pipe")) {
+                        line = ""; // use an empty line so line numbers still matches
+                    }
+                    builder.append(line);
+                    builder.append("\n");
+                    first = false;
+                } else {
+                    break;
+                }
+            }
+            return builder.toString();
+        } finally {
+            IOHelper.close(isr, in);
+        }
+    }
+
 }
diff --git a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/ClassRoutesBuilderLoader.java b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/ClassRoutesBuilderLoader.java
index b216f220d3d..c7eb34402f1 100644
--- a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/ClassRoutesBuilderLoader.java
+++ b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/ClassRoutesBuilderLoader.java
@@ -57,7 +57,7 @@ public class ClassRoutesBuilderLoader extends ExtendedRouteBuilderLoaderSupport
         Map<String, byte[]> byteCodes = new LinkedHashMap<>();
         for (Resource res : resources) {
             String className = asClassName(res);
-            InputStream is = res.getInputStream();
+            InputStream is = resourceInputStream(res);
             if (is != null) {
                 byte[] code = is.readAllBytes();
                 byteCodes.put(className, code);
diff --git a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
index c157262cd00..238721e66eb 100644
--- a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
+++ b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
@@ -82,7 +82,7 @@ public class JavaRoutesBuilderLoader extends ExtendedRouteBuilderLoaderSupport {
 
         Map<String, Resource> nameToResource = new HashMap<>();
         for (Resource resource : resources) {
-            try (InputStream is = resource.getInputStream()) {
+            try (InputStream is = resourceInputStream(resource)) {
                 if (is == null) {
                     throw new FileNotFoundException(resource.getLocation());
                 }
diff --git a/dsl/camel-xml-jaxb-dsl/src/main/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlRoutesBuilderLoader.java b/dsl/camel-xml-jaxb-dsl/src/main/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlRoutesBuilderLoader.java
index f20a0ac144f..48e3453d84d 100644
--- a/dsl/camel-xml-jaxb-dsl/src/main/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlRoutesBuilderLoader.java
+++ b/dsl/camel-xml-jaxb-dsl/src/main/java/org/apache/camel/dsl/xml/jaxb/JaxbXmlRoutesBuilderLoader.java
@@ -50,28 +50,28 @@ public class JaxbXmlRoutesBuilderLoader extends RouteBuilderLoaderSupport {
             @Override
             public void configure() throws Exception {
                 // we use configure to load the routes
-                try (InputStream is = resource.getInputStream()) {
+                try (InputStream is = resourceInputStream(resource)) {
                     RouteTemplatesDefinition templates = loadRouteTemplatesDefinition(getCamelContext(), is);
                     if (templates != null) {
                         setRouteTemplateCollection(templates);
                     }
                 }
 
-                try (InputStream is = resource.getInputStream()) {
+                try (InputStream is = resourceInputStream(resource)) {
                     TemplatedRoutesDefinition templates = loadTemplatedRoutesDefinition(getCamelContext(), is);
                     if (templates != null) {
                         setTemplatedRouteCollection(templates);
                     }
                 }
 
-                try (InputStream is = resource.getInputStream()) {
+                try (InputStream is = resourceInputStream(resource)) {
                     RestsDefinition rests = loadRestsDefinition(getCamelContext(), is);
                     if (rests != null) {
                         setRestCollection(rests);
                     }
                 }
 
-                try (InputStream is = resource.getInputStream()) {
+                try (InputStream is = resourceInputStream(resource)) {
                     RoutesDefinition routes = loadRoutesDefinition(getCamelContext(), is);
                     if (routes != null) {
                         CamelContextAware.trySetCamelContext(getRouteCollection(), getCamelContext());
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
index f63b5eb4d71..a6f8cea52ef 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
@@ -712,7 +712,7 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
             throw new FileNotFoundException("Resource not found: " + resource.getLocation());
         }
 
-        try (InputStream is = resource.getInputStream()) {
+        try (InputStream is = resourceInputStream(resource)) {
             LoadSettings local = LoadSettings.builder().setLabel(resource.getLocation()).build();
             final YamlDeserializationContext ctx = newYamlDeserializationContext(local, resource);
             final StreamReader reader = new StreamReader(local, new YamlUnicodeReader(is));
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
index de84468b15f..f841970fa22 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
@@ -76,7 +76,7 @@ public abstract class YamlRoutesBuilderLoaderSupport extends RouteBuilderLoaderS
             throw new FileNotFoundException("Resource not found: " + resource.getLocation());
         }
 
-        try (InputStream is = resource.getInputStream()) {
+        try (InputStream is = resourceInputStream(resource)) {
             // need a local settings because we want the label to be the resource we parse so the parser
             // can show parsing errors referring to actual resource file being parsed.
             LoadSettings local = LoadSettings.builder().setLabel(resource.getLocation()).build();