This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch typecoerce in repository https://gitbox.apache.org/repos/asf/camel.git
commit 96ac8518d69216304e466fcf4e92e0e0e0d00614 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Fri Jan 19 08:05:15 2024 +0100 CAMEL-20346: Comparison with type converters and stream caching should ensure the stream cache is reset so its re-readable afterwards. The contains function should also support file and stream cached input types --- .../camel/processor/StreamCachingChoiceTest.java | 68 +++++++++++++++++++++ .../StreamCachingChoiceWithVariableTest.java | 69 ++++++++++++++++++++++ .../camel/processor/ThrottlerMethodCallTest.java | 2 +- .../org/apache/camel/util/ObjectHelperTest.java | 50 ++++++++++++++++ .../test/resources/org/apache/camel/util/foo.txt | 1 + .../test/resources/org/apache/camel/util/quote.txt | 2 + .../org/apache/camel/support/ObjectHelper.java | 57 ++++++++++++++---- 7 files changed, 237 insertions(+), 12 deletions(-) diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/StreamCachingChoiceTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/StreamCachingChoiceTest.java new file mode 100644 index 00000000000..a3ff48268dd --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/processor/StreamCachingChoiceTest.java @@ -0,0 +1,68 @@ +/* + * 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; + +import java.io.File; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.converter.stream.FileInputStreamCache; +import org.junit.jupiter.api.Test; + +public class StreamCachingChoiceTest extends ContextTestSupport { + + private static final String TEST_FILE = "src/test/resources/org/apache/camel/converter/stream/test.xml"; + + @Test + public void testStreamCaching() throws Exception { + getMockEndpoint("mock:paris").expectedMessageCount(0); + getMockEndpoint("mock:madrid").expectedMessageCount(0); + getMockEndpoint("mock:london").expectedMessageCount(1); + getMockEndpoint("mock:other").expectedMessageCount(1); + + File file = new File(TEST_FILE); + FileInputStreamCache cache = new FileInputStreamCache(file); + template.sendBody("direct:start", cache); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:start") + .choice() + .when().simple("${body} contains 'Paris'") + .to("mock:paris") + .when().simple("${body} contains 'London'") + .to("mock:london") + .otherwise() + .to("mock:other") + .end() + .choice() + .when().simple("${body} contains 'Paris'") + .to("mock:paris") + .when().simple("${body} contains 'Madrid'") + .to("mock:madrid") + .otherwise() + .to("mock:other") + .end(); + } + }; + } +} diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/StreamCachingChoiceWithVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/StreamCachingChoiceWithVariableTest.java new file mode 100644 index 00000000000..a24c1e6271c --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/processor/StreamCachingChoiceWithVariableTest.java @@ -0,0 +1,69 @@ +/* + * 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; + +import java.io.File; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.converter.stream.FileInputStreamCache; +import org.junit.jupiter.api.Test; + +public class StreamCachingChoiceWithVariableTest extends ContextTestSupport { + + private static final String TEST_FILE = "src/test/resources/org/apache/camel/converter/stream/test.xml"; + + @Test + public void testStreamCaching() throws Exception { + getMockEndpoint("mock:paris").expectedMessageCount(0); + getMockEndpoint("mock:madrid").expectedMessageCount(0); + getMockEndpoint("mock:london").expectedMessageCount(1); + getMockEndpoint("mock:other").expectedMessageCount(1); + + File file = new File(TEST_FILE); + FileInputStreamCache cache = new FileInputStreamCache(file); + template.sendBody("direct:start", cache); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:start") + .setVariable("foo").simple("${body}") + .choice() + .when().simple("${variable:foo} contains 'Paris'") + .to("mock:paris") + .when().simple("${variable:foo} contains 'London'") + .to("mock:london") + .otherwise() + .to("mock:other") + .end() + .choice() + .when().simple("${variable:foo} contains 'Paris'") + .to("mock:paris") + .when().simple("${variable:foo} contains 'Madrid'") + .to("mock:madrid") + .otherwise() + .to("mock:other") + .end(); + } + }; + } +} diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/ThrottlerMethodCallTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/ThrottlerMethodCallTest.java index 16f59048b60..14aa716a72f 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/ThrottlerMethodCallTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/ThrottlerMethodCallTest.java @@ -70,7 +70,7 @@ public class ThrottlerMethodCallTest extends ContextTestSupport { } @Test - public void testConfigurationWithMethodCallExpression() { + public void testConfigurationWithMethodCallExpression() { for (int i = 0; i < messageCount; i++) { executor.execute(() -> template.sendBody("direct:expressionMethod", "<message>payload</message>")); } diff --git a/core/camel-core/src/test/java/org/apache/camel/util/ObjectHelperTest.java b/core/camel-core/src/test/java/org/apache/camel/util/ObjectHelperTest.java index e05d74e0ecf..b0fd15074a1 100644 --- a/core/camel-core/src/test/java/org/apache/camel/util/ObjectHelperTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/util/ObjectHelperTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.util; +import java.io.File; import java.io.InputStream; import java.lang.reflect.Method; import java.net.URL; @@ -44,6 +45,7 @@ import org.apache.camel.component.bean.MyOtherFooBean.AbstractClassSize; import org.apache.camel.component.bean.MyOtherFooBean.Clazz; import org.apache.camel.component.bean.MyOtherFooBean.InterfaceSize; import org.apache.camel.component.bean.MyStaticClass; +import org.apache.camel.converter.stream.FileInputStreamCache; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.support.CamelContextHelper; import org.apache.camel.support.DefaultMessage; @@ -124,6 +126,54 @@ public class ObjectHelperTest { } } + @Test + void testContainsStreamCaching() throws Exception { + try (CamelContext context = new DefaultCamelContext()) { + context.start(); + TypeConverter tc = context.getTypeConverter(); + + File file = new File("src/test/resources/org/apache/camel/util/quote.txt"); + FileInputStreamCache data = new FileInputStreamCache(file); + + assertTrue(ObjectHelper.typeCoerceContains(tc, data, "foo", true)); + assertTrue(ObjectHelper.typeCoerceContains(tc, data, "FOO", true)); + assertFalse(ObjectHelper.typeCoerceContains(tc, data, "FOO", false)); + + assertTrue(ObjectHelper.typeCoerceContains(tc, data, "foo", true)); + assertTrue(ObjectHelper.typeCoerceContains(tc, data, "FOO", true)); + assertFalse(ObjectHelper.typeCoerceContains(tc, data, "FOO", false)); + + assertTrue(ObjectHelper.typeCoerceContains(tc, "foo", "foo", true)); + assertFalse(ObjectHelper.typeCoerceContains(tc, data, "xyz", true)); + assertFalse(ObjectHelper.typeCoerceContains(tc, data, "xyz", true)); + assertFalse(ObjectHelper.typeCoerceContains(tc, "foo", "xyz", true)); + } + } + + @Test + void testEqualsStreamCaching() throws Exception { + try (CamelContext context = new DefaultCamelContext()) { + context.start(); + TypeConverter tc = context.getTypeConverter(); + + File file = new File("src/test/resources/org/apache/camel/util/foo.txt"); + FileInputStreamCache data = new FileInputStreamCache(file); + + assertTrue(ObjectHelper.typeCoerceEquals(tc, data, "foo", true)); + assertTrue(ObjectHelper.typeCoerceEquals(tc, data, "FOO", true)); + assertFalse(ObjectHelper.typeCoerceEquals(tc, data, "FOO", false)); + + assertTrue(ObjectHelper.typeCoerceEquals(tc, data, "foo", true)); + assertTrue(ObjectHelper.typeCoerceEquals(tc, data, "FOO", true)); + assertFalse(ObjectHelper.typeCoerceEquals(tc, data, "FOO", false)); + + assertTrue(ObjectHelper.typeCoerceEquals(tc, "foo", "foo", true)); + assertFalse(ObjectHelper.typeCoerceEquals(tc, data, "xyz", true)); + assertFalse(ObjectHelper.typeCoerceEquals(tc, data, "xyz", true)); + assertFalse(ObjectHelper.typeCoerceEquals(tc, "foo", "xyz", true)); + } + } + @Test void testContainsStringBuilder() throws Exception { try (CamelContext context = new DefaultCamelContext()) { diff --git a/core/camel-core/src/test/resources/org/apache/camel/util/foo.txt b/core/camel-core/src/test/resources/org/apache/camel/util/foo.txt new file mode 100644 index 00000000000..19102815663 --- /dev/null +++ b/core/camel-core/src/test/resources/org/apache/camel/util/foo.txt @@ -0,0 +1 @@ +foo \ No newline at end of file diff --git a/core/camel-core/src/test/resources/org/apache/camel/util/quote.txt b/core/camel-core/src/test/resources/org/apache/camel/util/quote.txt new file mode 100644 index 00000000000..06564a61da9 --- /dev/null +++ b/core/camel-core/src/test/resources/org/apache/camel/util/quote.txt @@ -0,0 +1,2 @@ +Hello foo how are you? +Thank you I am going to the bar now. \ No newline at end of file diff --git a/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java index c1876c76482..3c0d30cb902 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java @@ -16,6 +16,8 @@ */ package org.apache.camel.support; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -45,7 +47,9 @@ import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.Ordered; import org.apache.camel.RuntimeCamelException; +import org.apache.camel.StreamCache; import org.apache.camel.TypeConverter; +import org.apache.camel.WrappedFile; import org.apache.camel.util.ReflectionHelper; import org.apache.camel.util.Scanner; import org.apache.camel.util.StringHelper; @@ -145,15 +149,28 @@ public final class ObjectHelper { } // convert left to right - Object value = converter.tryConvertTo(rightValue.getClass(), leftValue); - final boolean isEqualLeftToRight = org.apache.camel.util.ObjectHelper.equal(value, rightValue, ignoreCase); - if (isEqualLeftToRight) { - return true; - } + StreamCache sc = null; + try { + if (leftValue instanceof StreamCache) { + sc = (StreamCache) leftValue; + } + Object value = converter.tryConvertTo(rightValue.getClass(), leftValue); + final boolean isEqualLeftToRight = org.apache.camel.util.ObjectHelper.equal(value, rightValue, ignoreCase); + if (isEqualLeftToRight) { + return true; + } - // convert right to left - value = converter.tryConvertTo(leftValue.getClass(), rightValue); - return org.apache.camel.util.ObjectHelper.equal(leftValue, value, ignoreCase); + // convert right to left + if (rightValue instanceof StreamCache) { + sc = (StreamCache) rightValue; + } + value = converter.tryConvertTo(leftValue.getClass(), rightValue); + return org.apache.camel.util.ObjectHelper.equal(leftValue, value, ignoreCase); + } finally { + if (sc != null) { + sc.reset(); + } + } } private static boolean booleanStringComparison(Boolean leftBool, String rightValue) { @@ -964,14 +981,32 @@ public final class ObjectHelper { */ public static boolean typeCoerceContains( TypeConverter typeConverter, Object collectionOrArray, Object value, boolean ignoreCase) { + + // unwrap file + if (collectionOrArray instanceof WrappedFile<?> wf) { + collectionOrArray = wf.getBody(); + } + if (collectionOrArray instanceof StreamCache sc) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + sc.writeTo(bos); + collectionOrArray = bos.toByteArray(); + } catch (IOException e) { + // ignore + } finally { + sc.reset(); + } + } // favor String types + if (value instanceof StringBuffer || value instanceof StringBuilder) { + value = value.toString(); + } if (collectionOrArray instanceof StringBuffer || collectionOrArray instanceof StringBuilder) { collectionOrArray = collectionOrArray.toString(); } - if (value instanceof StringBuffer || value instanceof StringBuilder) { - value = value.toString(); + if (collectionOrArray instanceof byte[] arr) { + collectionOrArray = new String(arr); } - if (collectionOrArray instanceof Collection) { Collection<?> collection = (Collection<?>) collectionOrArray; if (ignoreCase) {