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 2021/11/11 20:07:45 UTC

[camel] 01/02: CAMEL-17187: camel-xpath - Allow to configure preCompile on @XPath annotation

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-3.11.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit b46027700c196540750426f2b1f230d8a4f4c1db
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Nov 11 20:54:35 2021 +0100

    CAMEL-17187: camel-xpath - Allow to configure preCompile on @XPath annotation
---
 .../org/apache/camel/language/xpath/xpath.json     |  2 +-
 .../org/apache/camel/language/xpath/XPath.java     | 16 ++++
 .../xpath/XPathAnnotationExpressionFactory.java    | 24 +++++-
 .../org/apache/camel/model/language/xpath.json     |  2 +-
 .../camel/model/language/XPathExpression.java      |  2 +-
 .../org/apache/camel/component/xslt/MyXPath.java   |  1 -
 .../BeanWithXPathInjectionPreCompileTest.java      | 97 ++++++++++++++++++++++
 7 files changed, 139 insertions(+), 5 deletions(-)

diff --git a/components/camel-xpath/src/generated/resources/org/apache/camel/language/xpath/xpath.json b/components/camel-xpath/src/generated/resources/org/apache/camel/language/xpath/xpath.json
index bb86008..715db4a 100644
--- a/components/camel-xpath/src/generated/resources/org/apache/camel/language/xpath/xpath.json
+++ b/components/camel-xpath/src/generated/resources/org/apache/camel/language/xpath/xpath.json
@@ -22,7 +22,7 @@
     "saxon": { "kind": "attribute", "displayName": "Saxon", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to use Saxon." },
     "factoryRef": { "kind": "attribute", "displayName": "Factory Ref", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "References to a custom XPathFactory to lookup in the registry" },
     "objectModel": { "kind": "attribute", "displayName": "Object Model", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The XPath object model to use" },
-    "logNamespaces": { "kind": "attribute", "displayName": "Log Namespaces", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to log namespaces which can assist during trouble shooting" },
+    "logNamespaces": { "kind": "attribute", "displayName": "Log Namespaces", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to log namespaces which can assist during troubleshooting" },
     "headerName": { "kind": "attribute", "displayName": "Header Name", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of header to use as input, instead of the message body" },
     "threadSafety": { "kind": "attribute", "displayName": "Thread Safety", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable thread-safety for the returned result of the xpath expression. This applies to when using NODESET as the result type, and the returned set has multiple elements. In this situation there can be thread-safety issues if you process th [...]
     "preCompile": { "kind": "attribute", "displayName": "Pre Compile", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to enable pre-compiling the xpath expression during initialization phase. pre-compile is enabled by default. This can be used to turn off, for example in cases the compilation phase is desired at the starting phase, such as if the application is  [...]
diff --git a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPath.java b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPath.java
index 48a1352..922259f 100644
--- a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPath.java
+++ b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPath.java
@@ -58,4 +58,20 @@ public @interface XPath {
      * be applied to the body instead.
      */
     String headerName() default "";
+
+    /**
+     * Whether to log namespaces which can assist during troubleshooting
+     */
+    boolean logNamespaces() default false;
+
+    /**
+     * Whether to enable pre-compiling the xpath expression during initialization phase. pre-compile is enabled by
+     * default.
+     *
+     * This can be used to turn off, for example in cases the compilation phase is desired at the starting phase, such
+     * as if the application is ahead of time compiled (for example with camel-quarkus) which would then load the xpath
+     * factory of the built operating system, and not a JVM runtime.
+     */
+    boolean preCompile() default true;
+
 }
diff --git a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathAnnotationExpressionFactory.java b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathAnnotationExpressionFactory.java
index d05d6b1..26aa250 100644
--- a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathAnnotationExpressionFactory.java
+++ b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathAnnotationExpressionFactory.java
@@ -42,6 +42,8 @@ public class XPathAnnotationExpressionFactory extends DefaultAnnotationExpressio
         }
 
         XPathBuilder builder = XPathBuilder.xpath(xpath, resultType);
+        builder.preCompile(isPreCompile(annotation));
+        builder.setLogNamespaces(isLogNamespaces(annotation));
         NamespacePrefix[] namespaces = getExpressionNameSpacePrefix(annotation);
         if (namespaces != null) {
             for (NamespacePrefix namespacePrefix : namespaces) {
@@ -71,7 +73,7 @@ public class XPathAnnotationExpressionFactory extends DefaultAnnotationExpressio
      * null if the annotation's method is not found.
      * 
      * @return If the annotation has the method 'header' then the name of the header we want to apply the XPath
-     *         expression to. Otherwise null will be returned
+     *         expression to. Otherwise, null will be returned
      */
     protected String getHeaderName(Annotation annotation) {
         String headerValue = null;
@@ -82,4 +84,24 @@ public class XPathAnnotationExpressionFactory extends DefaultAnnotationExpressio
         }
         return headerValue;
     }
+
+    protected boolean isLogNamespaces(Annotation annotation) {
+        // in case @XPath is extended in a custom annotation then it may not have the method
+        try {
+            return (boolean) getAnnotationObjectValue(annotation, "logNamespaces");
+        } catch (Exception e) {
+            // Do Nothing
+        }
+        return false;
+    }
+
+    protected boolean isPreCompile(Annotation annotation) {
+        // in case @XPath is extended in a custom annotation then it may not have the method
+        try {
+            return (boolean) getAnnotationObjectValue(annotation, "preCompile");
+        } catch (Exception e) {
+            // Do Nothing
+        }
+        return false;
+    }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xpath.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xpath.json
index 0750b86..2055454 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xpath.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/xpath.json
@@ -18,7 +18,7 @@
     "saxon": { "kind": "attribute", "displayName": "Saxon", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to use Saxon." },
     "factoryRef": { "kind": "attribute", "displayName": "Factory Ref", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "References to a custom XPathFactory to lookup in the registry" },
     "objectModel": { "kind": "attribute", "displayName": "Object Model", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The XPath object model to use" },
-    "logNamespaces": { "kind": "attribute", "displayName": "Log Namespaces", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to log namespaces which can assist during trouble shooting" },
+    "logNamespaces": { "kind": "attribute", "displayName": "Log Namespaces", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to log namespaces which can assist during troubleshooting" },
     "headerName": { "kind": "attribute", "displayName": "Header Name", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of header to use as input, instead of the message body" },
     "threadSafety": { "kind": "attribute", "displayName": "Thread Safety", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable thread-safety for the returned result of the xpath expression. This applies to when using NODESET as the result type, and the returned set has multiple elements. In this situation there can be thread-safety issues if you process th [...]
     "preCompile": { "kind": "attribute", "displayName": "Pre Compile", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to enable pre-compiling the xpath expression during initialization phase. pre-compile is enabled by default. This can be used to turn off, for example in cases the compilation phase is desired at the starting phase, such as if the application is  [...]
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/language/XPathExpression.java b/core/camel-core-model/src/main/java/org/apache/camel/model/language/XPathExpression.java
index 6dbf80b..7c8cbe2 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/language/XPathExpression.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/language/XPathExpression.java
@@ -170,7 +170,7 @@ public class XPathExpression extends NamespaceAwareExpression {
     }
 
     /**
-     * Whether to log namespaces which can assist during trouble shooting
+     * Whether to log namespaces which can assist during troubleshooting
      */
     public void setLogNamespaces(String logNamespaces) {
         this.logNamespaces = logNamespaces;
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/xslt/MyXPath.java b/core/camel-core/src/test/java/org/apache/camel/component/xslt/MyXPath.java
index 07ca1d2..a0d363f 100644
--- a/core/camel-core/src/test/java/org/apache/camel/component/xslt/MyXPath.java
+++ b/core/camel-core/src/test/java/org/apache/camel/component/xslt/MyXPath.java
@@ -17,7 +17,6 @@
 package org.apache.camel.component.xslt;
 
 // START SNIPPET: example
-// START SNIPPET: example
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/BeanWithXPathInjectionPreCompileTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/BeanWithXPathInjectionPreCompileTest.java
new file mode 100644
index 0000000..00586f3
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/BeanWithXPathInjectionPreCompileTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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 org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.language.xpath.XPath;
+import org.apache.camel.spi.Registry;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class BeanWithXPathInjectionPreCompileTest extends ContextTestSupport {
+    private static final Logger LOG = LoggerFactory.getLogger(BeanRouteTest.class);
+    protected MyBean myBean = new MyBean();
+
+    @Test
+    public void testSendMessage() throws Exception {
+        String expectedBody = "<env:Envelope xmlns:env='http://www.w3.org/2003/05/soap-envelope'><env:Body>"
+                              + "<foo>bar</foo></env:Body></env:Envelope>";
+
+        template.sendBodyAndHeader("direct:in", expectedBody, "foo", "bar");
+
+        assertEquals(expectedBody, myBean.body, "bean body: " + myBean);
+        assertEquals("bar", myBean.foo, "bean foo: " + myBean);
+    }
+
+    @Test
+    public void testSendTwoMessages() throws Exception {
+        // 1st message
+        String expectedBody = "<env:Envelope xmlns:env='http://www.w3.org/2003/05/soap-envelope'><env:Body>"
+                              + "<foo>bar</foo></env:Body></env:Envelope>";
+
+        template.sendBodyAndHeader("direct:in", expectedBody, "foo", "bar");
+
+        assertEquals(expectedBody, myBean.body, "bean body: " + myBean);
+        assertEquals("bar", myBean.foo, "bean foo: " + myBean);
+
+        // 2nd message
+        String expectedBody2 = "<env:Envelope xmlns:env='http://www.w3.org/2003/05/soap-envelope'><env:Body>"
+                               + "<foo>baz</foo></env:Body></env:Envelope>";
+
+        template.sendBodyAndHeader("direct:in", expectedBody2, "foo", "baz");
+
+        assertEquals(expectedBody2, myBean.body, "bean body: " + myBean);
+        assertEquals("baz", myBean.foo, "bean foo: " + myBean);
+    }
+
+    @Override
+    protected Registry createRegistry() throws Exception {
+        Registry answer = super.createRegistry();
+
+        answer.bind("myBean", myBean);
+        return answer;
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:in").bean("myBean");
+            }
+        };
+    }
+
+    public static class MyBean {
+        public String body;
+        public String foo;
+
+        @Override
+        public String toString() {
+            return "MyBean[foo: " + foo + " body: " + body + "]";
+        }
+
+        public void read(String body, @XPath(value = "/soap:Envelope/soap:Body/foo/text()", preCompile = false) String foo) {
+            this.foo = foo;
+            this.body = body;
+            LOG.info("read() method called on " + this);
+        }
+    }
+}