This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch unmarshal in repository https://gitbox.apache.org/repos/asf/camel.git
commit 698066615ab2e01f9b3aa98693d763f3d9f6d7d4 Author: Claus Ibsen <[email protected]> AuthorDate: Sun Dec 24 11:49:03 2023 +0100 CAMEL-14028: Allow DataFormats to unmarshal known data formats without first converting to bytes --- .../camel/converter/jaxb/JaxbDataFormat.java | 34 ++++++++++++++++------ .../main/java/org/apache/camel/spi/DataFormat.java | 28 ++++++++++++++++++ .../org/apache/camel/model/SplitDefinition.java | 7 ++--- .../transformer/DataFormatTransformer.java | 4 +-- .../rest/RestProducerBindingProcessorTest.java | 4 +-- .../camel/processor/SplitterSingleMapTest.java | 6 ++-- .../camel/processor/converter/ConvertBodyTest.java | 4 --- .../processor/converter/ConvertHeaderTest.java | 4 --- .../support/processor/UnmarshalProcessor.java | 4 +-- 9 files changed, 64 insertions(+), 31 deletions(-) diff --git a/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java b/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java index dcb0e2c0db6..7bb3db0c02f 100644 --- a/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java +++ b/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java @@ -21,7 +21,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; +import java.io.Reader; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; @@ -53,12 +53,12 @@ import org.apache.camel.CamelContextAware; import org.apache.camel.Exchange; import org.apache.camel.ExchangePropertyKey; import org.apache.camel.InvalidPayloadException; +import org.apache.camel.NoTypeConversionAvailableException; import org.apache.camel.TypeConverter; import org.apache.camel.spi.DataFormat; import org.apache.camel.spi.DataFormatContentTypeHeader; import org.apache.camel.spi.DataFormatName; import org.apache.camel.spi.annotations.Dataformat; -import org.apache.camel.support.ExchangeHelper; import org.apache.camel.support.ResourceHelper; import org.apache.camel.support.service.ServiceSupport; import org.apache.camel.util.IOHelper; @@ -271,16 +271,26 @@ public class JaxbDataFormat extends ServiceSupport } @Override - public Object unmarshal(Exchange exchange, InputStream stream) throws IOException { + public Object unmarshal(Exchange exchange, InputStream stream) throws Exception { + throw new UnsupportedOperationException("Not in use"); + } + + @Override + public Object unmarshal(Exchange exchange, Object body) throws Exception { try { Object answer; - final XMLStreamReader xmlReader; + XMLStreamReader xmlReader; if (needFiltering(exchange)) { xmlReader - = typeConverter.convertTo(XMLStreamReader.class, exchange, createNonXmlFilterReader(exchange, stream)); + = typeConverter.convertTo(XMLStreamReader.class, exchange, createNonXmlFilterReader(exchange, body)); } else { - xmlReader = typeConverter.convertTo(XMLStreamReader.class, exchange, stream); + xmlReader = typeConverter.tryConvertTo(XMLStreamReader.class, exchange, body); + if (xmlReader == null) { + // fallback to input stream + InputStream is = getCamelContext().getTypeConverter().mandatoryConvertTo(InputStream.class, exchange, body); + xmlReader = typeConverter.convertTo(XMLStreamReader.class, exchange, is); + } } String partClassFromHeader = exchange.getIn().getHeader(JaxbConstants.JAXB_PART_CLASS, String.class); if (partClass != null || partClassFromHeader != null) { @@ -306,9 +316,15 @@ public class JaxbDataFormat extends ServiceSupport } } - private NonXmlFilterReader createNonXmlFilterReader(Exchange exchange, InputStream stream) - throws UnsupportedEncodingException { - return new NonXmlFilterReader(new InputStreamReader(stream, ExchangeHelper.getCharsetName(exchange))); + private NonXmlFilterReader createNonXmlFilterReader(Exchange exchange, Object body) + throws NoTypeConversionAvailableException { + Reader reader = getCamelContext().getTypeConverter().tryConvertTo(Reader.class, exchange, body); + if (reader == null) { + // fallback to input stream + InputStream is = getCamelContext().getTypeConverter().mandatoryConvertTo(InputStream.class, exchange, body); + reader = new InputStreamReader(is); + } + return new NonXmlFilterReader(reader); } protected boolean needFiltering(Exchange exchange) { diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/DataFormat.java b/core/camel-api/src/main/java/org/apache/camel/spi/DataFormat.java index 78ddc9ab059..c461b8ad97e 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/DataFormat.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/DataFormat.java @@ -52,6 +52,34 @@ public interface DataFormat extends Service { * @param stream the input stream with the object to be unmarshalled * @return the unmarshalled object * @throws Exception can be thrown + * @see #unmarshal(Exchange, Object) */ Object unmarshal(Exchange exchange, InputStream stream) throws Exception; + + /** + * Unmarshals the given body into an object. + * <p/> + * <b>Notice:</b> The result is set as body on the exchange OUT message. It is possible to mutate the OUT message + * provided in the given exchange parameter. For instance adding headers to the OUT message will be preserved. + * <p/> + * It's also legal to return the <b>same</b> passed <tt>exchange</tt> as is but also a {@link Message} object as + * well which will be used as the OUT message of <tt>exchange</tt>. + * <p/> + * This method can be used when a dataformat is optimized to handle any kind of message body as-is. For example + * camel-jaxb has been optimized to do this. The regular {@link #unmarshal(Exchange, InputStream)} method requires + * Camel to convert the message body into an {@link InputStream} prior to calling the unmarshal method. This can be + * avoided if the data-format implementation can be optimized to handle this by itself, such as camel-jaxb that can + * handle message body as a String payload out of the box. When a data format implementation is using this method, + * then the {@link #unmarshal(Exchange, InputStream)} must also be implemented but should be empty, as Camel will + * not invoke this method. + * + * @param exchange the current exchange + * @param body the input object to be unmarshalled + * @return the unmarshalled object + * @throws Exception can be thrown + */ + default Object unmarshal(Exchange exchange, Object body) throws Exception { + InputStream is = exchange.getContext().getTypeConverter().mandatoryConvertTo(InputStream.class, exchange, body); + return unmarshal(exchange, is); + } } diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/SplitDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/SplitDefinition.java index de0b40653d2..a323920c93f 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/SplitDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/SplitDefinition.java @@ -115,10 +115,9 @@ public class SplitDefinition extends OutputExpressionNode implements ExecutorSer // ------------------------------------------------------------------------- /** - * Delimiter used in splitting messages. - * Can be turned off using the value <tt>false</tt>. - * To force not splitting then the delimiter can be set to <tt>single</tt> to use the value as a single list, - * this can be needed in some special situations. + * Delimiter used in splitting messages. Can be turned off using the value <tt>false</tt>. To force not splitting + * then the delimiter can be set to <tt>single</tt> to use the value as a single list, this can be needed in some + * special situations. * <p/> * The default value is comma. * diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/transformer/DataFormatTransformer.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/transformer/DataFormatTransformer.java index 7e197d7c942..c58d156a59f 100644 --- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/transformer/DataFormatTransformer.java +++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/transformer/DataFormatTransformer.java @@ -16,8 +16,6 @@ */ package org.apache.camel.processor.transformer; -import java.io.InputStream; - import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.Message; @@ -64,7 +62,7 @@ public class DataFormatTransformer extends Transformer { // Unmarshaling into Java Object if ((DataType.isAnyType(to) || to.isJavaType()) && (from.equals(getFrom()) || from.getScheme().equals(getName()))) { LOG.debug("Unmarshaling with: {}", dataFormat); - Object answer = dataFormat.unmarshal(exchange, message.getBody(InputStream.class)); + Object answer = dataFormat.unmarshal(exchange, message.getBody()); if (!DataType.isAnyType(to) && to.getName() != null) { Class<?> toClass = context.getClassResolver().resolveClass(to.getName()); if (!toClass.isAssignableFrom(answer.getClass())) { diff --git a/core/camel-core/src/test/java/org/apache/camel/component/rest/RestProducerBindingProcessorTest.java b/core/camel-core/src/test/java/org/apache/camel/component/rest/RestProducerBindingProcessorTest.java index 4b6400bb25d..e6d345d5409 100644 --- a/core/camel-core/src/test/java/org/apache/camel/component/rest/RestProducerBindingProcessorTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/component/rest/RestProducerBindingProcessorTest.java @@ -70,7 +70,7 @@ public class RestProducerBindingProcessorTest { exchange.setIn(input); final ResponsePojo response = new ResponsePojo(); - when(outJsonDataFormat.unmarshal(same(exchange), any(InputStream.class))).thenReturn(response); + when(outJsonDataFormat.unmarshal(same(exchange), any(Object.class))).thenReturn(response); final ArgumentCaptor<AsyncCallback> bindingCallback = ArgumentCaptor.forClass(AsyncCallback.class); @@ -105,7 +105,7 @@ public class RestProducerBindingProcessorTest { exchange.setIn(input); final ResponsePojo response = new ResponsePojo(); - when(outXmlDataFormat.unmarshal(same(exchange), any(InputStream.class))).thenReturn(response); + when(outXmlDataFormat.unmarshal(same(exchange), any(Object.class))).thenReturn(response); final ArgumentCaptor<AsyncCallback> bindingCallback = ArgumentCaptor.forClass(AsyncCallback.class); diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/SplitterSingleMapTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/SplitterSingleMapTest.java index ff12d7c2b55..a47b7434e84 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/SplitterSingleMapTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/SplitterSingleMapTest.java @@ -16,14 +16,14 @@ */ package org.apache.camel.processor; +import java.util.LinkedHashMap; +import java.util.Map; + import org.apache.camel.ContextTestSupport; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.junit.jupiter.api.Test; -import java.util.LinkedHashMap; -import java.util.Map; - public class SplitterSingleMapTest extends ContextTestSupport { @Test diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertBodyTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertBodyTest.java index c7af7ffbd0c..af5f8745510 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertBodyTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertBodyTest.java @@ -33,10 +33,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; -import java.io.ByteArrayInputStream; -import java.nio.charset.UnsupportedCharsetException; -import java.util.Date; - import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertHeaderTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertHeaderTest.java index 799547701e3..c3a5be38fcc 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertHeaderTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertHeaderTest.java @@ -31,10 +31,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; -import java.io.ByteArrayInputStream; -import java.nio.charset.UnsupportedCharsetException; -import java.util.Date; - import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; diff --git a/core/camel-support/src/main/java/org/apache/camel/support/processor/UnmarshalProcessor.java b/core/camel-support/src/main/java/org/apache/camel/support/processor/UnmarshalProcessor.java index 8b710a64bb0..25e2acba8ac 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/processor/UnmarshalProcessor.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/processor/UnmarshalProcessor.java @@ -67,13 +67,13 @@ public class UnmarshalProcessor extends AsyncProcessorSupport implements Traceab // The body is null, and it is an allowed value so let's skip the unmarshalling out = exchange.getOut(); } else { - stream = in.getMandatoryBody(InputStream.class); + Object body = in.getBody(); // lets set up the out message before we invoke the dataFormat so that it can mutate it if necessary out = exchange.getOut(); out.copyFrom(in); - result = dataFormat.unmarshal(exchange, stream); + result = dataFormat.unmarshal(exchange, body); } if (result instanceof Exchange) { if (result != exchange) {
