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 2023/12/24 12:04:06 UTC

(camel) 01/08: CAMEL-14028: Allow DataFormats to unmarshal known data formats without first converting to bytes

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 <cl...@gmail.com>
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) {