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 f42f4878fd80 CAMEL-26193: camel-printer - a body that does not convert
to a stream fails the exchange (#29016)
f42f4878fd80 is described below
commit f42f4878fd806b517654e625e792fcf746ee7475
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Nov 3 07:42:21 2026 +0100
CAMEL-26193: camel-printer - a body that does not convert to a stream fails
the exchange (#29016)
* CAMEL-26193: camel-printer - a body that does not convert to a stream…
… fails the exchange
The lpr producer converted the body with the non-mandatory convertTo, so a
Map, a
POJO, a number or no body gave a null stream and a print job with no data,
without
an error. It now reads the body with getMandatoryBody(InputStream.class),
which
fails with an InvalidPayloadException, and the conversion uses the
exchange, so a
text body is encoded in the charset of the exchange.
* CAMEL-26193: camel-printer - setJobName test prints to the mocked pri…
…nter by name, not the host default printer
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../org/apache/camel/catalog/components/lpr.json | 2 +-
.../org/apache/camel/component/printer/lpr.json | 2 +-
.../camel/component/printer/PrinterEndpoint.java | 2 +-
.../camel/component/printer/PrinterProducer.java | 4 +-
.../camel/component/printer/PrinterPrintTest.java | 3 +-
.../component/printer/PrinterProducerBodyTest.java | 154 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 6 +
7 files changed, 167 insertions(+), 6 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/lpr.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/lpr.json
index 6c8879855c81..42524976db0b 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/lpr.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/lpr.json
@@ -31,7 +31,7 @@
"PrinterJobName": { "index": 0, "kind": "header", "displayName": "",
"group": "producer", "label": "producer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The name of the job", "constantName":
"org.apache.camel.component.printer.PrinterEndpoint#JOB_NAME" }
},
"bodyTypes": {
- "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
"description": "The document to print, read as a stream: text, bytes, a stream
or a file, in the format of the mimeType and flavor options. A Map, a POJO, a
number or no body is not converted: the print job gets no data" },
+ "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
"description": "The document to print, read as a stream: text, bytes, a stream
or a file, in the format of the mimeType and flavor options. A Map, a POJO, a
number or no body fails with an InvalidPayloadException" },
"producerOut": { "carriers": [ "unchanged" ] }
},
"properties": {
diff --git
a/components/camel-printer/src/generated/resources/META-INF/org/apache/camel/component/printer/lpr.json
b/components/camel-printer/src/generated/resources/META-INF/org/apache/camel/component/printer/lpr.json
index 6c8879855c81..42524976db0b 100644
---
a/components/camel-printer/src/generated/resources/META-INF/org/apache/camel/component/printer/lpr.json
+++
b/components/camel-printer/src/generated/resources/META-INF/org/apache/camel/component/printer/lpr.json
@@ -31,7 +31,7 @@
"PrinterJobName": { "index": 0, "kind": "header", "displayName": "",
"group": "producer", "label": "producer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The name of the job", "constantName":
"org.apache.camel.component.printer.PrinterEndpoint#JOB_NAME" }
},
"bodyTypes": {
- "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
"description": "The document to print, read as a stream: text, bytes, a stream
or a file, in the format of the mimeType and flavor options. A Map, a POJO, a
number or no body is not converted: the print job gets no data" },
+ "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
"description": "The document to print, read as a stream: text, bytes, a stream
or a file, in the format of the mimeType and flavor options. A Map, a POJO, a
number or no body fails with an InvalidPayloadException" },
"producerOut": { "carriers": [ "unchanged" ] }
},
"properties": {
diff --git
a/components/camel-printer/src/main/java/org/apache/camel/component/printer/PrinterEndpoint.java
b/components/camel-printer/src/main/java/org/apache/camel/component/printer/PrinterEndpoint.java
index 00e47f9554f1..08784d1b1a81 100644
---
a/components/camel-printer/src/main/java/org/apache/camel/component/printer/PrinterEndpoint.java
+++
b/components/camel-printer/src/main/java/org/apache/camel/component/printer/PrinterEndpoint.java
@@ -44,7 +44,7 @@ import org.apache.camel.support.DefaultEndpoint;
@BodyTypes(producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES,
Carrier.STREAM, Carrier.FILE },
description = "The document to print, read
as a stream: text, bytes, a stream or a file,"
+ " in the format of the
mimeType and flavor options. A Map, a POJO, a number"
- + " or no body is not
converted: the print job gets no data"),
+ + " or no body fails with an
InvalidPayloadException"),
producerOut = @BodyType(Carrier.UNCHANGED))
public class PrinterEndpoint extends DefaultEndpoint implements
EndpointServiceLocation {
diff --git
a/components/camel-printer/src/main/java/org/apache/camel/component/printer/PrinterProducer.java
b/components/camel-printer/src/main/java/org/apache/camel/component/printer/PrinterProducer.java
index 1adc6fd4807b..f35ef3439f0d 100644
---
a/components/camel-printer/src/main/java/org/apache/camel/component/printer/PrinterProducer.java
+++
b/components/camel-printer/src/main/java/org/apache/camel/component/printer/PrinterProducer.java
@@ -52,8 +52,8 @@ public class PrinterProducer extends DefaultProducer {
@Override
public void process(Exchange exchange) throws Exception {
- Object body = exchange.getIn().getBody();
- InputStream is =
exchange.getContext().getTypeConverter().convertTo(InputStream.class, body);
+ // a body that is not text, bytes, a stream or a file fails, rather
than printing a job with no data
+ InputStream is = exchange.getIn().getMandatoryBody(InputStream.class);
String jobName = exchange.getIn().getHeader(PrinterEndpoint.JOB_NAME,
"Camel: lpr", String.class);
print(is, jobName);
}
diff --git
a/components/camel-printer/src/test/java/org/apache/camel/component/printer/PrinterPrintTest.java
b/components/camel-printer/src/test/java/org/apache/camel/component/printer/PrinterPrintTest.java
index 5abcc2c54173..4e2b6277c29f 100644
---
a/components/camel-printer/src/test/java/org/apache/camel/component/printer/PrinterPrintTest.java
+++
b/components/camel-printer/src/test/java/org/apache/camel/component/printer/PrinterPrintTest.java
@@ -353,7 +353,8 @@ public class PrinterPrintTest extends CamelTestSupport {
getMockEndpoint("mock:output").setExpectedMessageCount(1);
context.addRoutes(new RouteBuilder() {
public void configure() {
-
from("direct:start").to("lpr://localhost/default").to("mock:output");
+ // name the mocked printer, as the default printer of the host
could be a real one
+
from("direct:start").to("lpr://localhost/DefaultPrinter").to("mock:output");
}
});
context.start();
diff --git
a/components/camel-printer/src/test/java/org/apache/camel/component/printer/PrinterProducerBodyTest.java
b/components/camel-printer/src/test/java/org/apache/camel/component/printer/PrinterProducerBodyTest.java
new file mode 100644
index 000000000000..d63f40df0c78
--- /dev/null
+++
b/components/camel-printer/src/test/java/org/apache/camel/component/printer/PrinterProducerBodyTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.component.printer;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import javax.print.Doc;
+import javax.print.DocFlavor;
+import javax.print.DocPrintJob;
+import javax.print.PrintService;
+import javax.print.attribute.PrintRequestAttributeSet;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * The body of the lpr producer, printed on a mocked PrintService (no print
service lookup, so nothing reaches a real
+ * printer).
+ */
+public class PrinterProducerBodyTest {
+
+ private CamelContext context;
+ private PrintService printService;
+ private DocPrintJob printJob;
+ private PrinterProducer producer;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ context = new DefaultCamelContext();
+ context.start();
+
+ printService = mock(PrintService.class);
+ when(printService.getName()).thenReturn("MockedPrinter");
+
when(printService.isDocFlavorSupported(any(DocFlavor.class))).thenReturn(Boolean.TRUE);
+ printJob = mock(DocPrintJob.class);
+ when(printService.createPrintJob()).thenReturn(printJob);
+
+ PrinterEndpoint endpoint =
context.getEndpoint("lpr://localhost/MockedPrinter", PrinterEndpoint.class);
+ producer = new PrinterProducer(endpoint, endpoint.getConfig());
+ producer.setPrintService(printService);
+ producer.start();
+ }
+
+ @AfterEach
+ public void tearDown() {
+ producer.stop();
+ context.stop();
+ }
+
+ static Stream<Arguments> unsupportedBodies() {
+ return Stream.of(
+ Arguments.of(Map.of("name", "Camel")),
+ Arguments.of(new Object()),
+ Arguments.of(42),
+ Arguments.of((Object) null));
+ }
+
+ @ParameterizedTest
+ @MethodSource("unsupportedBodies")
+ public void unsupportedBodyFails(Object body) {
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody(body);
+
+ assertThrows(InvalidPayloadException.class, () ->
producer.process(exchange));
+ verify(printService, never()).createPrintJob();
+ }
+
+ @Test
+ public void textBodyIsPrinted() throws Exception {
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody("Hello Printer");
+
+ producer.process(exchange);
+
+ assertArrayEquals("Hello Printer".getBytes(StandardCharsets.UTF_8),
printedBytes());
+ }
+
+ @Test
+ public void bytesBodyIsPrinted() throws Exception {
+ byte[] data = { 1, 2, 3, 4 };
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody(data);
+
+ producer.process(exchange);
+
+ assertArrayEquals(data, printedBytes());
+ }
+
+ @Test
+ public void streamBodyIsPrinted() throws Exception {
+ byte[] data = { 5, 6, 7 };
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setBody(new ByteArrayInputStream(data));
+
+ producer.process(exchange);
+
+ assertArrayEquals(data, printedBytes());
+ }
+
+ @Test
+ public void textBodyUsesTheCharsetOfTheExchange() throws Exception {
+ Exchange exchange = new DefaultExchange(context);
+ exchange.setProperty(Exchange.CHARSET_NAME, "ISO-8859-1");
+ exchange.getIn().setBody("Grüße");
+
+ producer.process(exchange);
+
+ assertArrayEquals("Grüße".getBytes(StandardCharsets.ISO_8859_1),
printedBytes());
+ }
+
+ private byte[] printedBytes() throws Exception {
+ ArgumentCaptor<Doc> doc = ArgumentCaptor.forClass(Doc.class);
+ verify(printJob).print(doc.capture(),
any(PrintRequestAttributeSet.class));
+ try (InputStream is = doc.getValue().getStreamForBytes()) {
+ return is.readAllBytes();
+ }
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 6ed514d6f90f..3994cf3b7d42 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1108,6 +1108,12 @@ the same JVM.
An application that relied on the providers being de-registered on shutdown
must now remove them itself with
`Security.removeProvider("BC")` and `Security.removeProvider("BCPQC")`.
+=== camel-printer - a body that does not convert to a stream fails the exchange
+
+The `lpr` producer submitted a print job with no data, without an error, for a
message body that has no conversion to
+`InputStream`, such as a `Map`, a POJO or a number, and for a message without
a body. It now fails the exchange with an
+`InvalidPayloadException`. Send the document as text, bytes, a stream or a
file.
+
=== camel-debezium - the row flattened by ExtractNewRecordState is the body
With the Debezium `ExtractNewRecordState` single message transformation
(`ExtractNewDocumentState` for MongoDB), the