This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch allow-null in repository https://gitbox.apache.org/repos/asf/camel.git
commit 57dd08322daf8ffd17006c926e293f152aaff996 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Thu Apr 10 18:08:19 2025 +0200 CAMEL-21949: camel-core - Type converter with allowNull should be a valid response for ConvertBodyTo --- .../org/apache/camel/spi/BulkTypeConverters.java | 19 ++- .../converter/CamelBaseBulkConverterLoader.java | 6 +- .../impl/converter/CoreTypeConverterRegistry.java | 19 ++- .../converter/ConvertBodyAllowNullTest.java | 150 +++++++++++++++++++++ .../camel/processor/converter/ConvertBodyTest.java | 6 +- .../processor/converter/ConvertHeaderTest.java | 5 +- .../processor/converter/ConvertVariableTest.java | 5 +- .../stream/StreamCacheBulkConverterLoader.java | 6 +- .../jaxp/CamelXmlJaxpBulkConverterLoader.java | 6 +- .../resources/velocity/bulk-converter-loader.vm | 6 +- 10 files changed, 194 insertions(+), 34 deletions(-) diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BulkTypeConverters.java b/core/camel-api/src/main/java/org/apache/camel/spi/BulkTypeConverters.java index 3c7f96d9af6..797e66dd7a2 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/BulkTypeConverters.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/BulkTypeConverters.java @@ -55,6 +55,11 @@ public interface BulkTypeConverters extends Ordered, TypeConverter { */ <T> T convertTo(Class<?> from, Class<T> to, Exchange exchange, Object value) throws TypeConversionException; + default Object allowNullConvertTo(Class<?> from, Class<?> to, Exchange exchange, Object value) + throws TypeConversionException { + return convertTo(from, to, exchange, value); + } + /** * Tries to convert the value to the specified type, returning <tt>null</tt> if not possible to convert. * <p/> @@ -67,7 +72,11 @@ public interface BulkTypeConverters extends Ordered, TypeConverter { */ default <T> T tryConvertTo(Class<?> from, Class<T> to, Exchange exchange, Object value) throws TypeConversionException { try { - convertTo(from, to, exchange, value); + Object t = allowNullConvertTo(from, to, exchange, value); + if (t == Void.class) { + return null; + } + return (T) t; } catch (Exception e) { // ignore } @@ -89,11 +98,13 @@ public interface BulkTypeConverters extends Ordered, TypeConverter { */ default <T> T mandatoryConvertTo(Class<?> from, Class<T> to, Exchange exchange, Object value) throws TypeConversionException, NoTypeConversionAvailableException { - T t = convertTo(from, to, exchange, value); - if (t == null) { + Object t = allowNullConvertTo(from, to, exchange, value); + if (t == Void.class) { + return null; + } else if (t == null) { throw new NoTypeConversionAvailableException(value, to); } else { - return t; + return (T) t; } } diff --git a/core/camel-base/src/generated/java/org/apache/camel/converter/CamelBaseBulkConverterLoader.java b/core/camel-base/src/generated/java/org/apache/camel/converter/CamelBaseBulkConverterLoader.java index ad8da21668a..d393ccfaec0 100644 --- a/core/camel-base/src/generated/java/org/apache/camel/converter/CamelBaseBulkConverterLoader.java +++ b/core/camel-base/src/generated/java/org/apache/camel/converter/CamelBaseBulkConverterLoader.java @@ -54,11 +54,7 @@ public final class CamelBaseBulkConverterLoader implements TypeConverterLoader, public <T> T convertTo(Class<?> from, Class<T> to, Exchange exchange, Object value) throws TypeConversionException { try { Object obj = doConvertTo(from, to, exchange, value); - if (obj == Void.class) { - return null; - } else { - return (T) obj; - } + return (T) obj; } catch (TypeConversionException e) { throw e; } catch (Exception e) { diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java b/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java index f37de3bf176..e6cc5048980 100644 --- a/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java +++ b/core/camel-base/src/main/java/org/apache/camel/impl/converter/CoreTypeConverterRegistry.java @@ -154,6 +154,9 @@ public abstract class CoreTypeConverterRegistry extends ServiceSupport implement if (value != null) { T ret = fastConvertTo(type, exchange, value); + if (ret == Void.class) { + return null; + } if (ret != null) { return ret; } @@ -161,7 +164,11 @@ public abstract class CoreTypeConverterRegistry extends ServiceSupport implement // NOTE: we cannot optimize any more if value is String as it may be time pattern and other patterns } - return (T) doConvertToAndStat(type, exchange, value, false); + Object answer = doConvertToAndStat(type, exchange, value, false); + if (answer == Void.class) { + answer = null; + } + return (T) answer; } private static Boolean customParseBoolean(String str) { @@ -191,6 +198,9 @@ public abstract class CoreTypeConverterRegistry extends ServiceSupport implement } Object answer = doConvertToAndStat(type, exchange, value, false); + if (answer == Void.class) { + return null; + } if (answer == null) { // Could not find suitable conversion throw new NoTypeConversionAvailableException(value, type); @@ -249,7 +259,11 @@ public abstract class CoreTypeConverterRegistry extends ServiceSupport implement // NOTE: we cannot optimize any more if value is String as it may be time pattern and other patterns } - return (T) doConvertToAndStat(type, exchange, value, true); + Object answer = doConvertToAndStat(type, exchange, value, true); + if (answer == Void.class) { + return null; + } + return (T) answer; } private static <T> void requireNonNullBoolean(Class<T> type, Object value, Object answer) { @@ -290,7 +304,6 @@ public abstract class CoreTypeConverterRegistry extends ServiceSupport implement if (!tryConvert) { statistics.incrementHit(); } - return answer; } } diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertBodyAllowNullTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertBodyAllowNullTest.java new file mode 100644 index 00000000000..4336d94de3c --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertBodyAllowNullTest.java @@ -0,0 +1,150 @@ +/* + * 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.processor.converter; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ConvertBodyAllowNullTest extends ContextTestSupport { + + // TODO: custom type converter with allow null + + @Test + public void testConvertAllowNull() throws Exception { + Object val = context.getTypeConverter().convertTo(Integer.class, Float.NaN); + Assertions.assertNull(val); + val = context.getTypeConverter().mandatoryConvertTo(Integer.class, Float.NaN); + Assertions.assertNull(val); + val = context.getTypeConverter().tryConvertTo(Integer.class, Float.NaN); + Assertions.assertNull(val); + + val = context.getTypeConverter().convertTo(Integer.class, 123); + Assertions.assertEquals(123, val); + val = context.getTypeConverter().mandatoryConvertTo(Integer.class, 123); + Assertions.assertEquals(123, val); + val = context.getTypeConverter().tryConvertTo(Integer.class, 123); + Assertions.assertEquals(123, val); + } + + @Test + public void testConvertAllowNullWithExchange() throws Exception { + Exchange exchange = context.getEndpoint("mock:result").createExchange(); + + Object val = context.getTypeConverter().convertTo(Integer.class, exchange, Float.NaN); + Assertions.assertNull(val); + val = context.getTypeConverter().mandatoryConvertTo(Integer.class, exchange, Float.NaN); + Assertions.assertNull(val); + val = context.getTypeConverter().tryConvertTo(Integer.class, exchange, Float.NaN); + Assertions.assertNull(val); + + val = context.getTypeConverter().convertTo(Integer.class, exchange, 123); + Assertions.assertEquals(123, val); + val = context.getTypeConverter().mandatoryConvertTo(Integer.class, exchange, 123); + Assertions.assertEquals(123, val); + val = context.getTypeConverter().tryConvertTo(Integer.class, exchange, 123); + Assertions.assertEquals(123, val); + } + + @Test + public void testConvertToAllowNullOptional() throws Exception { + MockEndpoint result = getMockEndpoint("mock:result"); + result.expectedMessageCount(1); + result.message(0).body().isNull(); + + template.sendBody("direct:optional", Float.NaN); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testConvertToAllowNull() throws Exception { + MockEndpoint result = getMockEndpoint("mock:result"); + result.expectedMessageCount(1); + result.message(0).body().isNull(); + + template.sendBody("direct:mandatory", Float.NaN); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testHeaderConvertToAllowNullOptional() throws Exception { + MockEndpoint result = getMockEndpoint("mock:result"); + result.expectedMessageCount(1); + result.message(0).header("foo").isNull(); + + template.sendBodyAndHeader("direct:header-optional", "Hello World", "foo", Float.NaN); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testHeaderConvertToAllowNull() throws Exception { + MockEndpoint result = getMockEndpoint("mock:result"); + result.expectedMessageCount(1); + result.message(0).header("foo").isNull(); + + template.sendBodyAndHeader("direct:header-mandatory", "Hello World", "foo", Float.NaN); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testVarConvertToAllowNullOptional() throws Exception { + MockEndpoint result = getMockEndpoint("mock:result"); + result.expectedMessageCount(1); + result.message(0).variable("foo").isNull(); + + fluentTemplate.withVariable("foo", Float.NaN).withBody("Hello World").to("direct:var-optional").send(); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testVarConvertToAllowNull() throws Exception { + MockEndpoint result = getMockEndpoint("mock:result"); + result.expectedMessageCount(1); + result.message(0).variable("foo").isNull(); + + fluentTemplate.withVariable("foo", Float.NaN).withBody("Hello World").to("direct:var-mandatory").send(); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + context.setStreamCaching(false); + + from("direct:optional").convertBodyTo(Integer.class, false).to("mock:result"); + from("direct:mandatory").convertBodyTo(Integer.class).to("mock:result"); + + from("direct:header-optional").convertHeaderTo("foo", Integer.class, false).to("mock:result"); + from("direct:header-mandatory").convertHeaderTo("foo", Integer.class).to("mock:result"); + + from("direct:var-optional").convertVariableTo("foo", Integer.class, false).to("mock:result"); + from("direct:var-mandatory").convertVariableTo("foo", Integer.class).to("mock:result"); + } + }; + } + +} 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 56a15e1d1bc..5a7329ec87b 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 @@ -24,8 +24,8 @@ import java.util.Date; import org.apache.camel.ContextTestSupport; import org.apache.camel.Exchange; import org.apache.camel.InvalidPayloadException; -import org.apache.camel.NoTypeConversionAvailableException; import org.apache.camel.RuntimeCamelException; +import org.apache.camel.TypeConversionException; import org.apache.camel.builder.ExchangeBuilder; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; @@ -107,10 +107,10 @@ public class ConvertBodyTest extends ContextTestSupport { public void testConvertToIntegerNotMandatory() throws Exception { // mandatory should fail try { - template.sendBody("direct:start", Double.NaN); + template.sendBody("direct:start", "xxx"); fail(); } catch (Exception e) { - assertIsInstanceOf(NoTypeConversionAvailableException.class, e.getCause().getCause()); + assertIsInstanceOf(TypeConversionException.class, e.getCause().getCause()); } // optional should cause null body 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 638bb35d151..db0851cf28b 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 @@ -25,6 +25,7 @@ import org.apache.camel.ContextTestSupport; import org.apache.camel.Exchange; import org.apache.camel.NoTypeConversionAvailableException; import org.apache.camel.RuntimeCamelException; +import org.apache.camel.TypeConversionException; import org.apache.camel.builder.ExchangeBuilder; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; @@ -118,10 +119,10 @@ public class ConvertHeaderTest extends ContextTestSupport { public void testConvertToIntegerNotMandatory() throws Exception { // mandatory should fail try { - template.sendBodyAndHeader("direct:start", null, "foo", Double.NaN); + template.sendBodyAndHeader("direct:start", null, "foo", "xxx"); fail(); } catch (Exception e) { - assertIsInstanceOf(NoTypeConversionAvailableException.class, e.getCause()); + assertIsInstanceOf(TypeConversionException.class, e.getCause()); } // optional should cause null body diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertVariableTest.java index a367acd14cc..62c7113114b 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertVariableTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/converter/ConvertVariableTest.java @@ -26,6 +26,7 @@ import org.apache.camel.Exchange; import org.apache.camel.FluentProducerTemplate; import org.apache.camel.NoSuchVariableException; import org.apache.camel.NoTypeConversionAvailableException; +import org.apache.camel.TypeConversionException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.junit.jupiter.api.BeforeEach; @@ -126,9 +127,9 @@ public class ConvertVariableTest extends ContextTestSupport { @Test public void testConvertToIntegerNotMandatory() throws Exception { // mandatory should fail - Exchange out = fluent.to("direct:start").withVariable("foo", Double.NaN).send(); + Exchange out = fluent.to("direct:start").withVariable("foo", "xxx").send(); assertTrue(out.isFailed()); - assertIsInstanceOf(NoTypeConversionAvailableException.class, out.getException()); + assertIsInstanceOf(TypeConversionException.class, out.getException()); // optional should cause null body getMockEndpoint("mock:result").expectedMessageCount(1); diff --git a/core/camel-support/src/generated/java/org/apache/camel/converter/stream/StreamCacheBulkConverterLoader.java b/core/camel-support/src/generated/java/org/apache/camel/converter/stream/StreamCacheBulkConverterLoader.java index dc652f04eeb..c3f1b386b8e 100644 --- a/core/camel-support/src/generated/java/org/apache/camel/converter/stream/StreamCacheBulkConverterLoader.java +++ b/core/camel-support/src/generated/java/org/apache/camel/converter/stream/StreamCacheBulkConverterLoader.java @@ -54,11 +54,7 @@ public final class StreamCacheBulkConverterLoader implements TypeConverterLoader public <T> T convertTo(Class<?> from, Class<T> to, Exchange exchange, Object value) throws TypeConversionException { try { Object obj = doConvertTo(from, to, exchange, value); - if (obj == Void.class) { - return null; - } else { - return (T) obj; - } + return (T) obj; } catch (TypeConversionException e) { throw e; } catch (Exception e) { diff --git a/core/camel-xml-jaxp/src/generated/java/org/apache/camel/converter/jaxp/CamelXmlJaxpBulkConverterLoader.java b/core/camel-xml-jaxp/src/generated/java/org/apache/camel/converter/jaxp/CamelXmlJaxpBulkConverterLoader.java index 982d5f58cb6..89845e2de23 100644 --- a/core/camel-xml-jaxp/src/generated/java/org/apache/camel/converter/jaxp/CamelXmlJaxpBulkConverterLoader.java +++ b/core/camel-xml-jaxp/src/generated/java/org/apache/camel/converter/jaxp/CamelXmlJaxpBulkConverterLoader.java @@ -54,11 +54,7 @@ public final class CamelXmlJaxpBulkConverterLoader implements TypeConverterLoade public <T> T convertTo(Class<?> from, Class<T> to, Exchange exchange, Object value) throws TypeConversionException { try { Object obj = doConvertTo(from, to, exchange, value); - if (obj == Void.class) { - return null; - } else { - return (T) obj; - } + return (T) obj; } catch (TypeConversionException e) { throw e; } catch (Exception e) { diff --git a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/bulk-converter-loader.vm b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/bulk-converter-loader.vm index f9b12c1781c..8c4f18190ec 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/bulk-converter-loader.vm +++ b/tooling/maven/camel-package-maven-plugin/src/main/resources/velocity/bulk-converter-loader.vm @@ -77,11 +77,7 @@ public final class ${className} implements TypeConverterLoader, BulkTypeConverte public <T> T convertTo(Class<?> from, Class<T> to, Exchange exchange, Object value) throws TypeConversionException { try { Object obj = doConvertTo(from, to, exchange, value); - if (obj == Void.class) { - return null; - } else { - return (T) obj; - } + return (T) obj; } catch (TypeConversionException e) { throw e; } catch (Exception e) {