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 9af87a8bedb9 CAMEL-25973: camel-language - binary=true with a file: or 
classpath: script returns the content as a stream (#28889)
9af87a8bedb9 is described below

commit 9af87a8bedb9e04a05168f4d83345afe2b92810a
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 2 20:34:28 2026 +0100

    CAMEL-25973: camel-language - binary=true with a file: or classpath: script 
returns the content as a stream (#28889)
    
    …script returns the content as a stream
    
    With binary=true, a script with a scheme but no resource: prefix (such as
    language:constant:classpath:data.bin?binary=true) opened the resource, did 
not
    read it, and then evaluated the text of the uri. The body was the uri text 
and
    the stream was never closed. With cacheScript=true the endpoint pre-loaded 
the
    content as a String expression, so the bytes went through a text conversion.
    
    The producer now returns the opened stream as the result, as for a resource:
    script, and closes it when transform=false. The endpoint no longer pre-loads
    the script as an expression when binary=true.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/catalog/docs/language-component.adoc     | 44 ++++++++++++++++++++++
 .../src/main/docs/language-component.adoc          | 44 ++++++++++++++++++++++
 .../camel/component/language/LanguageEndpoint.java |  5 ++-
 .../camel/component/language/LanguageProducer.java | 18 ++++++---
 .../ConstantLanguageBinaryResourceTest.java        | 40 ++++++++++++++++++++
 5 files changed, 144 insertions(+), 7 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/language-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/language-component.adoc
index e7753b75dc56..fba87714eb43 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/language-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/language-component.adoc
@@ -296,3 +296,47 @@ YAML::
             uri: mock:result
 ----
 ====
+
+=== Binary content
+
+With `binary=true`, the content of the resource is not evaluated as a script, 
but is the result as a stream
+(`java.io.InputStream`). This works with and without the `resource:` prefix, 
for example to return an image:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+    .to("language:constant:classpath:images/logo.png?binary=true")
+    .to("mock:result");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:start"/>
+  <to uri="language:constant:classpath:images/logo.png?binary=true"/>
+  <to uri="mock:result"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - to:
+            uri: language:constant:classpath:images/logo.png
+            parameters:
+              binary: true
+        - to:
+            uri: mock:result
+----
+====
diff --git a/components/camel-language/src/main/docs/language-component.adoc 
b/components/camel-language/src/main/docs/language-component.adoc
index e7753b75dc56..fba87714eb43 100644
--- a/components/camel-language/src/main/docs/language-component.adoc
+++ b/components/camel-language/src/main/docs/language-component.adoc
@@ -296,3 +296,47 @@ YAML::
             uri: mock:result
 ----
 ====
+
+=== Binary content
+
+With `binary=true`, the content of the resource is not evaluated as a script, 
but is the result as a stream
+(`java.io.InputStream`). This works with and without the `resource:` prefix, 
for example to return an image:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+    .to("language:constant:classpath:images/logo.png?binary=true")
+    .to("mock:result");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:start"/>
+  <to uri="language:constant:classpath:images/logo.png?binary=true"/>
+  <to uri="mock:result"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - to:
+            uri: language:constant:classpath:images/logo.png
+            parameters:
+              binary: true
+        - to:
+            uri: mock:result
+----
+====
diff --git 
a/components/camel-language/src/main/java/org/apache/camel/component/language/LanguageEndpoint.java
 
b/components/camel-language/src/main/java/org/apache/camel/component/language/LanguageEndpoint.java
index d0b98a75153a..ca292fc0fca4 100644
--- 
a/components/camel-language/src/main/java/org/apache/camel/component/language/LanguageEndpoint.java
+++ 
b/components/camel-language/src/main/java/org/apache/camel/component/language/LanguageEndpoint.java
@@ -112,7 +112,8 @@ public class LanguageEndpoint extends ResourceEndpoint {
         if (resultTypeClass == null && resultType != null) {
             resultTypeClass = 
getCamelContext().getClassResolver().resolveMandatoryClass(resultType);
         }
-        if (cacheScript && expression == null && script != null) {
+        // binary content is not a script, so it is not pre-loaded as an 
expression
+        if (cacheScript && !binary && expression == null && script != null) {
             boolean external = script.startsWith("file:") || 
script.startsWith("http:");
             if (!external) {
                 // we can pre optimize this as the script can be loaded from 
classpath or registry etc
@@ -127,7 +128,7 @@ public class LanguageEndpoint extends ResourceEndpoint {
 
     @Override
     public Producer createProducer() throws Exception {
-        if (cacheScript && expression == null && script != null) {
+        if (cacheScript && !binary && expression == null && script != null) {
             script = resolveScript(script);
             expression = language.createExpression(script, new Object[] { 
resultTypeClass });
             expression.init(getCamelContext());
diff --git 
a/components/camel-language/src/main/java/org/apache/camel/component/language/LanguageProducer.java
 
b/components/camel-language/src/main/java/org/apache/camel/component/language/LanguageProducer.java
index fb478bbec88a..cc1f910361ac 100644
--- 
a/components/camel-language/src/main/java/org/apache/camel/component/language/LanguageProducer.java
+++ 
b/components/camel-language/src/main/java/org/apache/camel/component/language/LanguageProducer.java
@@ -88,11 +88,16 @@ public class LanguageProducer extends DefaultProducer {
                 is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(getEndpoint().getCamelContext(),
 ref);
             }
 
-            if (is != null && !getEndpoint().isBinary()) {
-                try {
-                    script = 
getEndpoint().getCamelContext().getTypeConverter().convertTo(String.class, 
exchange, is);
-                } finally {
-                    IOHelper.close(is);
+            if (is != null) {
+                if (getEndpoint().isBinary()) {
+                    // binary content is not a script, so the stream is the 
result as-is
+                    script = null;
+                } else {
+                    try {
+                        script = 
getEndpoint().getCamelContext().getTypeConverter().convertTo(String.class, 
exchange, is);
+                    } finally {
+                        IOHelper.close(is);
+                    }
                 }
             }
         }
@@ -132,6 +137,9 @@ public class LanguageProducer extends DefaultProducer {
         // set message body if transform is enabled
         if (getEndpoint().isTransform()) {
             exchange.getMessage().setBody(result);
+        } else if (result == is) {
+            // the binary content is not used, so close the stream
+            IOHelper.close(is);
         }
     }
 
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/ConstantLanguageBinaryResourceTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/ConstantLanguageBinaryResourceTest.java
index 91118a45d535..73406640c764 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/ConstantLanguageBinaryResourceTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/ConstantLanguageBinaryResourceTest.java
@@ -16,11 +16,14 @@
  */
 package org.apache.camel.language;
 
+import java.nio.file.Files;
+
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.Exchange;
 import org.apache.camel.builder.RouteBuilder;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
@@ -48,6 +51,36 @@ public class ConstantLanguageBinaryResourceTest extends 
ContextTestSupport {
         template.sendBodyAndHeader(fileUri(), data, Exchange.FILE_NAME, 
"savedlogo.jpeg");
     }
 
+    @Test
+    public void testConstantBinaryClasspathWithoutResourcePrefix() {
+        byte[] data = template.requestBody("direct:noprefix", "", 
byte[].class);
+        assertNotNull(data);
+        assertEquals(10249, data.length);
+    }
+
+    @Test
+    public void testConstantBinaryClasspathWithoutResourcePrefixCacheScript() {
+        byte[] data = template.requestBody("direct:noprefixcache", "", 
byte[].class);
+        assertNotNull(data);
+        assertEquals(10249, data.length);
+
+        // the stream is opened again for the next message
+        data = template.requestBody("direct:noprefixcache", "", byte[].class);
+        assertNotNull(data);
+        assertEquals(10249, data.length);
+    }
+
+    @Test
+    public void testConstantBinaryFileWithoutResourcePrefix() throws Exception 
{
+        // not valid UTF-8, so the content must not be read as text
+        byte[] content = new byte[] { 0, (byte) 0xFF, (byte) 0xFE, 1, 2 };
+        Files.createDirectories(testDirectory());
+        Files.write(testFile("bin.dat"), content);
+
+        byte[] data = template.requestBody("direct:file", "", byte[].class);
+        assertArrayEquals(content, data);
+    }
+
     @Override
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
@@ -56,6 +89,13 @@ public class ConstantLanguageBinaryResourceTest extends 
ContextTestSupport {
                 
from("direct:default").to("language:constant:resource:org/apache/camel/logo.jpeg?binary=true");
 
                 
from("direct:classpath").to("language:constant:resource:classpath:org/apache/camel/logo.jpeg?binary=true");
+
+                from("direct:file").to("language:constant:file:" + 
testFile("bin.dat") + "?binary=true");
+
+                
from("direct:noprefix").to("language:constant:classpath:org/apache/camel/logo.jpeg?binary=true");
+
+                from("direct:noprefixcache")
+                        
.to("language:constant:classpath:org/apache/camel/logo.jpeg?binary=true&cacheScript=true");
             }
         };
     }

Reply via email to