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 2016/07/31 08:02:39 UTC

camel git commit: CAMEL-10189: camel-jsonpath - Add support for simple functions

Repository: camel
Updated Branches:
  refs/heads/master 136db7f25 -> 492402cb7


CAMEL-10189: camel-jsonpath - Add support for simple functions


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/492402cb
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/492402cb
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/492402cb

Branch: refs/heads/master
Commit: 492402cb77b98d2b60dd583b794f520da3e5f72e
Parents: 136db7f
Author: Claus Ibsen <da...@apache.org>
Authored: Sun Jul 31 10:02:28 2016 +0200
Committer: Claus Ibsen <da...@apache.org>
Committed: Sun Jul 31 10:02:28 2016 +0200

----------------------------------------------------------------------
 .../camel/builder/ExpressionClauseSupport.java  | 35 ++++++++
 .../model/language/JsonPathExpression.java      | 19 +++++
 .../camel-jsonpath/src/main/docs/jsonpath.adoc  | 58 ++++++++++++++
 .../org/apache/camel/jsonpath/JsonPath.java     |  5 ++
 .../JsonPathAnnotationExpressionFactory.java    |  1 +
 .../apache/camel/jsonpath/JsonPathEngine.java   | 43 +++++++++-
 .../camel/jsonpath/JsonPathExpression.java      | 14 +++-
 .../jsonpath/JsonPathWithSimpleCBRTest.java     | 84 ++++++++++++++++++++
 8 files changed, 255 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/492402cb/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java b/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
index 9e1224c..16eedced 100644
--- a/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
+++ b/camel-core/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
@@ -354,6 +354,22 @@ public class ExpressionClauseSupport<T> {
      * expression</a>
      *
      * @param text the expression to be evaluated
+     * @param suppressExceptions whether to suppress exceptions such as PathNotFoundException
+     * @param allowSimple whether to allow in inlined simple exceptions in the json path expression
+     * @return the builder to continue processing the DSL
+     */
+    public T jsonpath(String text, boolean suppressExceptions, boolean allowSimple) {
+        JsonPathExpression expression = new JsonPathExpression(text);
+        expression.setSuppressExceptions(suppressExceptions);
+        expression.setAllowSimple(allowSimple);
+        return expression(expression);
+    }
+
+    /**
+     * Evaluates a <a href="http://camel.apache.org/jsonpath.html">Json Path
+     * expression</a>
+     *
+     * @param text the expression to be evaluated
      * @param resultType the return type expected by the expression
      * @return the builder to continue processing the DSL
      */
@@ -382,6 +398,25 @@ public class ExpressionClauseSupport<T> {
     }
 
     /**
+     * Evaluates a <a href="http://camel.apache.org/jsonpath.html">Json Path
+     * expression</a>
+     *
+     * @param text the expression to be evaluated
+     * @param suppressExceptions whether to suppress exceptions such as PathNotFoundException
+     * @param allowSimple whether to allow in inlined simple exceptions in the json path expression
+     * @param resultType the return type expected by the expression
+     * @return the builder to continue processing the DSL
+     */
+    public T jsonpath(String text, boolean suppressExceptions, boolean allowSimple, Class<?> resultType) {
+        JsonPathExpression expression = new JsonPathExpression(text);
+        expression.setSuppressExceptions(suppressExceptions);
+        expression.setAllowSimple(allowSimple);
+        expression.setResultType(resultType);
+        setExpressionType(expression);
+        return result;
+    }
+
+    /**
      * Evaluates a <a href="http://commons.apache.org/jxpath/">JXPath expression</a>
      *
      * @param text the expression to be evaluated

http://git-wip-us.apache.org/repos/asf/camel/blob/492402cb/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java b/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
index 59bfa33..8c9f786 100644
--- a/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
+++ b/camel-core/src/main/java/org/apache/camel/model/language/JsonPathExpression.java
@@ -44,6 +44,8 @@ public class JsonPathExpression extends ExpressionDefinition {
     private Class<?> resultType;
     @XmlAttribute @Metadata(defaultValue = "false")
     private Boolean suppressExceptions;
+    @XmlAttribute @Metadata(defaultValue = "true")
+    private Boolean allowSimple;
 
     public JsonPathExpression() {
     }
@@ -78,6 +80,17 @@ public class JsonPathExpression extends ExpressionDefinition {
         return suppressExceptions;
     }
 
+    public Boolean getAllowSimple() {
+        return allowSimple;
+    }
+
+    /**
+     * Whether to allow in inlined simple exceptions in the json path expression
+     */
+    public void setAllowSimple(Boolean allowSimple) {
+        this.allowSimple = allowSimple;
+    }
+
     /**
      * Whether to suppress exceptions such as PathNotFoundException.
      */
@@ -109,6 +122,9 @@ public class JsonPathExpression extends ExpressionDefinition {
         if (suppressExceptions != null) {
             setProperty(expression, "suppressExceptions", suppressExceptions);
         }
+        if (allowSimple != null) {
+            setProperty(expression, "allowSimple", allowSimple);
+        }
         super.configureExpression(camelContext, expression);
     }
 
@@ -120,6 +136,9 @@ public class JsonPathExpression extends ExpressionDefinition {
         if (suppressExceptions != null) {
             setProperty(predicate, "suppressExceptions", suppressExceptions);
         }
+        if (allowSimple != null) {
+            setProperty(predicate, "allowSimple", allowSimple);
+        }
         super.configurePredicate(camelContext, predicate);
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/492402cb/components/camel-jsonpath/src/main/docs/jsonpath.adoc
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/docs/jsonpath.adoc b/components/camel-jsonpath/src/main/docs/jsonpath.adoc
index cf1eeee..9f35bc1 100644
--- a/components/camel-jsonpath/src/main/docs/jsonpath.adoc
+++ b/components/camel-jsonpath/src/main/docs/jsonpath.adoc
@@ -102,6 +102,64 @@ And in XML DSL:
 
 This option is also available on the�`@JsonPath`�annotation.
 
+[[JSonPath-InlineSimple]]
+Inline Simple exceptions
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+*Available as of Camel 2.18*
+
+Its now possible to inlined Simple language expressions in the JSonPath expression using the simple syntax ${xxx}.
+An example is shown below:
+
+[source,java]
+---------------------------------------------------
+from("direct:start")
+  .choice()
+    .when().jsonpath("$.store.book[?(@.price < ${header.cheap})]")
+      .to("mock:cheap")
+    .when().jsonpath("$.store.book[?(@.price < ${header.average})]")
+      .to("mock:average")
+    .otherwise()
+      .to("mock:expensive");
+---------------------------------------------------
+
+And in XML DSL:
+
+[source,xml]
+--------------------------------------------------------------------------
+<route>
+  <from uri="direct:start"/>
+  <choice>
+    <when>
+      <jsonpath>$.store.book[?(@.price < ${header.cheap})]</jsonpath>
+      <to uri="mock:cheap"/>
+    </when>
+    <when>
+      <jsonpath>$.store.book[?(@.price < ${header.average})]</jsonpath>
+      <to uri="mock:average"/>
+    </when>
+    <otherwise>
+      <to uri="mock:expensive"/>
+    </otherwise>
+  </choice>
+</route>
+--------------------------------------------------------------------------
+
+You can turn off support for inlined simple expression by setting the option allowSimple to false as shown:
+
+[source,java]
+---------------------------------------------------
+.when().jsonpath("$.store.book[?(@.price < 10)]", false, false)
+---------------------------------------------------
+
+And in XML DSL:
+
+[source,xml]
+--------------------------------------------------------------------------
+<jsonpath allowSimple="false">$.store.book[?(@.price < 10)]</jsonpath>
+--------------------------------------------------------------------------
+
+
 [[JSonPath-JSonPathinjection]]
 JSonPath injection
 ~~~~~~~~~~~~~~~~~~

http://git-wip-us.apache.org/repos/asf/camel/blob/492402cb/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPath.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPath.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPath.java
index 0bca294..bbb399f 100644
--- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPath.java
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPath.java
@@ -46,6 +46,11 @@ public @interface JsonPath {
     boolean suppressExceptions() default false;
 
     /**
+     * Whether to allow in inlined simple exceptions in the json path expression
+     */
+    boolean allowSimple() default true;
+
+    /**
      * To configure the json path options to use
      */
     Option[] options() default {};

http://git-wip-us.apache.org/repos/asf/camel/blob/492402cb/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAnnotationExpressionFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAnnotationExpressionFactory.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAnnotationExpressionFactory.java
index 8c314c5..1909f16 100644
--- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAnnotationExpressionFactory.java
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathAnnotationExpressionFactory.java
@@ -41,6 +41,7 @@ public class JsonPathAnnotationExpressionFactory extends DefaultAnnotationExpres
             JsonPath jsonPathAnnotation = (JsonPath) annotation;
 
             answer.setSuppressExceptions(jsonPathAnnotation.suppressExceptions());
+            answer.setAllowSimple(jsonPathAnnotation.allowSimple());
 
             Option[] options = jsonPathAnnotation.options();
             answer.setOptions(options);

http://git-wip-us.apache.org/repos/asf/camel/blob/492402cb/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
index e619a97..58eebf3 100644
--- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathEngine.java
@@ -20,6 +20,8 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import com.jayway.jsonpath.Configuration;
 import com.jayway.jsonpath.Configuration.Defaults;
@@ -28,19 +30,28 @@ import com.jayway.jsonpath.Option;
 import com.jayway.jsonpath.internal.DefaultsImpl;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
 import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.component.file.GenericFile;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class JsonPathEngine {
 
+    private static final Logger LOG = LoggerFactory.getLogger(JsonPathEngine.class);
+
+    private static final Pattern SIMPLE_PATTERN = Pattern.compile("\\$\\{[^\\}]+\\}", Pattern.MULTILINE);
+    private final String expression;
     private final JsonPath path;
     private final Configuration configuration;
 
     public JsonPathEngine(String expression) {
-        this(expression, false, null);
+        this(expression, false, true, null);
     }
 
-    public JsonPathEngine(String expression, boolean suppressExceptions, Option[] options) {
+    public JsonPathEngine(String expression, boolean suppressExceptions, boolean allowSimple, Option[] options) {
+        this.expression = expression;
+
         Defaults defaults = DefaultsImpl.INSTANCE;
         if (options != null) {
             Configuration.ConfigurationBuilder builder = Configuration.builder().jsonProvider(defaults.jsonProvider()).options(options);
@@ -55,10 +66,36 @@ public class JsonPathEngine {
             }
             this.configuration = builder.build();
         }
-        this.path = JsonPath.compile(expression);
+
+        boolean hasSimple = false;
+        if (allowSimple) {
+            // is simple language embedded
+            Matcher matcher = SIMPLE_PATTERN.matcher(expression);
+            if (matcher.find()) {
+                hasSimple = true;
+            }
+        }
+        if (hasSimple) {
+            this.path = null;
+        } else {
+            this.path = JsonPath.compile(expression);
+            LOG.debug("Compiled static JsonPath: {}", expression);
+        }
     }
 
     public Object read(Exchange exchange) throws IOException, InvalidPayloadException {
+        if (path == null) {
+            Expression exp = exchange.getContext().resolveLanguage("simple").createExpression(expression);
+            String text = exp.evaluate(exchange, String.class);
+            JsonPath path = JsonPath.compile(text);
+            LOG.debug("Compiled dynamic JsonPath: {}", expression);
+            return doRead(path, exchange);
+        } else {
+            return doRead(path, exchange);
+        }
+    }
+
+    private Object doRead(JsonPath path, Exchange exchange) throws IOException, InvalidPayloadException {
         Object json = exchange.getIn().getBody();
 
         if (json instanceof GenericFile) {

http://git-wip-us.apache.org/repos/asf/camel/blob/492402cb/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
index 5a8bbe6..37eeab1 100644
--- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
+++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathExpression.java
@@ -31,6 +31,7 @@ public class JsonPathExpression extends ExpressionAdapter implements AfterProper
 
     private Class<?> resultType;
     private boolean suppressExceptions;
+    private boolean allowSimple = true;
     private Option[] options;
 
     public JsonPathExpression(String expression) {
@@ -59,6 +60,17 @@ public class JsonPathExpression extends ExpressionAdapter implements AfterProper
         this.suppressExceptions = suppressExceptions;
     }
 
+    public boolean isAllowSimple() {
+        return allowSimple;
+    }
+
+    /**
+     * Whether to allow in inlined simple exceptions in the json path expression
+     */
+    public void setAllowSimple(boolean allowSimple) {
+        this.allowSimple = allowSimple;
+    }
+
     public Option[] getOptions() {
         return options;
     }
@@ -91,7 +103,7 @@ public class JsonPathExpression extends ExpressionAdapter implements AfterProper
 
     public void init() {
         try {
-            engine = new JsonPathEngine(expression, suppressExceptions, options);
+            engine = new JsonPathEngine(expression, suppressExceptions, allowSimple, options);
         } catch (Exception e) {
             throw new ExpressionIllegalSyntaxException(expression, e);
         }

http://git-wip-us.apache.org/repos/asf/camel/blob/492402cb/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathWithSimpleCBRTest.java
----------------------------------------------------------------------
diff --git a/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathWithSimpleCBRTest.java b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathWithSimpleCBRTest.java
new file mode 100644
index 0000000..eb9bbf9
--- /dev/null
+++ b/components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathWithSimpleCBRTest.java
@@ -0,0 +1,84 @@
+/**
+ * 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.jsonpath;
+
+import java.io.File;
+
+import org.apache.camel.builder.FluentProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.junit.Test;
+
+public class JsonPathWithSimpleCBRTest extends CamelTestSupport {
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .choice()
+                        .when().jsonpath("$.store.book[?(@.price < ${header.cheap})]")
+                            .to("mock:cheap")
+                        .when().jsonpath("$.store.book[?(@.price < ${header.average})]")
+                            .to("mock:average")
+                        .otherwise()
+                            .to("mock:expensive");
+            }
+        };
+    }
+    
+    @Test
+    public void testCheap() throws Exception {
+        getMockEndpoint("mock:cheap").expectedMessageCount(1);
+        getMockEndpoint("mock:average").expectedMessageCount(0);
+        getMockEndpoint("mock:expensive").expectedMessageCount(0);
+
+        FluentProducerTemplate fluent = new FluentProducerTemplate(context);
+        fluent.withHeader("cheap", 10).withHeader("average", 30).withBody(new File("src/test/resources/cheap.json"))
+                .to("direct:start").send();
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testAverage() throws Exception {
+        getMockEndpoint("mock:cheap").expectedMessageCount(0);
+        getMockEndpoint("mock:average").expectedMessageCount(1);
+        getMockEndpoint("mock:expensive").expectedMessageCount(0);
+
+        FluentProducerTemplate fluent = new FluentProducerTemplate(context);
+        fluent.withHeader("cheap", 10).withHeader("average", 30).withBody(new File("src/test/resources/average.json"))
+                .to("direct:start").send();
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testExpensive() throws Exception {
+        getMockEndpoint("mock:cheap").expectedMessageCount(0);
+        getMockEndpoint("mock:average").expectedMessageCount(0);
+        getMockEndpoint("mock:expensive").expectedMessageCount(1);
+
+        FluentProducerTemplate fluent = new FluentProducerTemplate(context);
+        fluent.withHeader("cheap", 10).withHeader("average", 30).withBody(new File("src/test/resources/expensive.json"))
+                .to("direct:start").send();
+
+        assertMockEndpointsSatisfied();
+    }
+
+}