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");
}
};
}