You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2020/10/18 09:18:31 UTC

[camel] branch master created (now a270d2e)

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

acosentino pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git.


      at a270d2e  CAMEL-15697: camel-joor - Camel expression langauge using jOOR runtime java compiled.

This branch includes the following new commits:

     new a270d2e  CAMEL-15697: camel-joor - Camel expression langauge using jOOR runtime java compiled.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[camel] 01/01: CAMEL-15697: camel-joor - Camel expression langauge using jOOR runtime java compiled.

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit a270d2ed4db9419046bd448c57d7e887cffc37c2
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Oct 18 11:02:30 2020 +0200

    CAMEL-15697: camel-joor - Camel expression langauge using jOOR runtime java compiled.
---
 .../apache/camel/catalog/docs/joor-language.adoc   |   7 +-
 .../joor/JoorAnnotationExpressionFactory.java      |  18 +--
 .../apache/camel/language/joor/JoorCompiler.java   | 115 +++++++++++++++++
 .../apache/camel/language/joor/JoorExpression.java | 136 ++++-----------------
 .../apache/camel/language/joor/JoorLanguage.java   |  33 +++--
 .../src/main/java/org/apache/camel/Exchange.java   |   2 +
 .../modules/languages/pages/joor-language.adoc     |   7 +-
 .../java/org/apache/camel/itest/jmh/JoorTest.java  |   3 -
 8 files changed, 177 insertions(+), 144 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/joor-language.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/joor-language.adoc
index 0088364..1fe0ca5 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/joor-language.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/joor-language.adoc
@@ -78,7 +78,7 @@ And then in the 2nd statement we return a value whether the user is `null` or no
 [source,java]
 ----
 from("seda:orders")
-  .transform().joor("Object user = message.getHeader(\"user\"); return user != null ? \"User: \" + user : \"No user exists\";")
+  .transform().joor("var user = message.getHeader(\"user\"); return user != null ? \"User: \" + user : \"No user exists\";")
   .to("seda:user");
 ----
 
@@ -87,7 +87,7 @@ Notice how we have to quote strings in strings, and that is annoying, so instead
 [source,java]
 ----
 from("seda:orders")
-  .transform().joor("Object user = message.getHeader('user'); return user != null ? 'User: ' + user : 'No user exists';")
+  .transform().joor("var user = message.getHeader('user'); return user != null ? 'User: ' + user : 'No user exists';")
   .to("seda:user");
 ----
 
@@ -130,6 +130,9 @@ The code that you can write is therefore limited to a number of Java statements.
 The supported runtime is intended for Java standalone, Spring Boot, Camel Quarkus and other microservices runtimes.
 It is not supported in OSGi, Camel Karaf or any kind of Java Application Server runtime.
 
+jOOR does not support runtime compilation with Spring Boot using _fat jar_ packaging (https://github.com/jOOQ/jOOR/issues/69),
+it works with exploded classpath.
+
 == Dependencies
 
 To use scripting languages in your camel routes you need to add a
diff --git a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorAnnotationExpressionFactory.java b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorAnnotationExpressionFactory.java
index 6039c01..0d3e623 100644
--- a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorAnnotationExpressionFactory.java
+++ b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorAnnotationExpressionFactory.java
@@ -30,21 +30,15 @@ public class JoorAnnotationExpressionFactory extends DefaultAnnotationExpression
             CamelContext camelContext, Annotation annotation,
             LanguageAnnotation languageAnnotation, Class<?> expressionReturnType) {
 
-        String expression = getExpressionFromAnnotation(annotation);
-        JoorExpression answer = new JoorExpression(expression);
-
-        if (expressionReturnType != null) {
-            answer.setResultType(expressionReturnType);
-        }
-
+        Object[] params = new Object[3];
+        params[1] = expressionReturnType;
         if (annotation instanceof Joor) {
             Joor joorAnnotation = (Joor) annotation;
-            answer.setPreCompile(joorAnnotation.preCompile());
-            answer.setSingleQuotes(joorAnnotation.singleQuotes());
+            params[0] = joorAnnotation.preCompile();
+            params[2] = joorAnnotation.singleQuotes();
         }
-
-        answer.init(camelContext);
-        return answer;
+        String expression = getExpressionFromAnnotation(annotation);
+        return camelContext.resolveLanguage("joor").createExpression(expression, params);
     }
 
 }
diff --git a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
new file mode 100644
index 0000000..81752de
--- /dev/null
+++ b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
@@ -0,0 +1,115 @@
+/*
+ * 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.language.joor;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.StaticService;
+import org.apache.camel.support.ScriptHelper;
+import org.apache.camel.support.service.ServiceSupport;
+import org.apache.camel.util.StopWatch;
+import org.joor.Reflect;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JoorCompiler extends ServiceSupport implements StaticService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(JoorCompiler.class);
+    private static final AtomicInteger UUID = new AtomicInteger();
+    private int counter;
+    private long taken;
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+        if (counter > 0) {
+            LOG.info("jOOR language compiled {} scripts in {} millis", counter, taken);
+        }
+    }
+
+    public Method compile(CamelContext camelContext, String script, boolean singleQuotes) {
+        StopWatch watch = new StopWatch();
+
+        Method answer;
+        String className = nextFQN();
+        String code = evalCode(camelContext, className, script, singleQuotes);
+        try {
+            LOG.trace(code);
+            Reflect ref = Reflect.compile(className, code);
+            answer = ref.type().getMethod("evaluate", CamelContext.class, Exchange.class, Message.class, Object.class);
+        } catch (Exception e) {
+            throw new JoorCompilationException(className, code, e);
+        }
+
+        counter++;
+        taken += watch.taken();
+        return answer;
+    }
+
+    private String evalCode(CamelContext camelContext, String fqn, String script, boolean singleQuotes) {
+        String qn = fqn.substring(0, fqn.lastIndexOf('.'));
+        String name = fqn.substring(fqn.lastIndexOf('.') + 1);
+
+        // reload script
+        script = ScriptHelper.resolveOptionalExternalScript(camelContext, script);
+
+        // trim text
+        script = script.trim();
+
+        //  wrap text into a class method we can call
+        StringBuilder sb = new StringBuilder();
+        sb.append("\n");
+        sb.append("package ").append(qn).append(";\n");
+        sb.append("\n");
+        sb.append("import org.apache.camel.*;\n");
+        sb.append("\n");
+        sb.append("public class ").append(name).append(" {\n");
+        sb.append("\n");
+        sb.append("\n");
+        sb.append(
+                "    public static Object evaluate(CamelContext context, Exchange exchange, Message message, Object body) throws Exception {\n");
+        sb.append("        ");
+        if (!script.contains("return ")) {
+            sb.append("return ");
+        }
+        if (singleQuotes) {
+            // single quotes instead of double quotes, as its very annoying for string in strings
+            String quoted = script.replace('\'', '"');
+            sb.append(quoted);
+        } else {
+            sb.append(script);
+        }
+        if (!script.endsWith("}") && !script.endsWith(";")) {
+            sb.append(";");
+        }
+        sb.append("\n");
+        sb.append("    }\n");
+        sb.append("}\n");
+        sb.append("\n");
+
+        return sb.toString();
+    }
+
+    private static String nextFQN() {
+        return "org.apache.camel.language.joor.compiled.JoorLanguage" + UUID.incrementAndGet();
+    }
+
+}
diff --git a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorExpression.java b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorExpression.java
index 62dcaf9..39aaddf 100644
--- a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorExpression.java
+++ b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorExpression.java
@@ -17,28 +17,17 @@
 package org.apache.camel.language.joor;
 
 import java.lang.reflect.Method;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
-import org.apache.camel.Message;
 import org.apache.camel.support.ExpressionAdapter;
-import org.apache.camel.support.ScriptHelper;
-import org.joor.Reflect;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.camel.support.ObjectHelper;
 
 public class JoorExpression extends ExpressionAdapter {
 
-    private static final AtomicInteger COUNTER = new AtomicInteger();
-    private static final Logger LOG = LoggerFactory.getLogger(JoorExpression.class);
-    private static Boolean java8;
-
     private final String text;
-    private String className;
-    private String code;
-    private Reflect compiled;
-    private Method method;
+    private JoorCompiler compiler;
+    private Method compiled;
 
     private Class<?> resultType;
     private boolean preCompile = true;
@@ -53,6 +42,14 @@ public class JoorExpression extends ExpressionAdapter {
         return "joor:" + text;
     }
 
+    public JoorCompiler getCompiler() {
+        return compiler;
+    }
+
+    public void setCompiler(JoorCompiler compiler) {
+        this.compiler = compiler;
+    }
+
     public boolean isPreCompile() {
         return preCompile;
     }
@@ -79,25 +76,18 @@ public class JoorExpression extends ExpressionAdapter {
 
     @Override
     public Object evaluate(Exchange exchange) {
-        try {
-            Reflect ref = compiled;
-            if (ref == null) {
-                this.className = nextFQN();
-                this.code = evalCode(exchange.getContext(), className, text);
-                LOG.trace(code);
-                ref = compile(className, code);
-                method = ref.type().getMethod("evaluate", CamelContext.class, Exchange.class, Message.class, Object.class);
-            }
-            // optimize as we call the same method all the time so we dont want to find the method every time as joor would do
-            // if you use its call method
-            Object out = method.invoke(null, exchange.getContext(), exchange, exchange.getIn(), exchange.getIn().getBody());
-            if (out != null && resultType != null) {
-                return exchange.getContext().getTypeConverter().convertTo(resultType, exchange, out);
-            } else {
-                return out;
-            }
-        } catch (Exception e) {
-            throw new JoorExpressionEvaluationException(this, className, code, exchange, e);
+        Method method = compiled;
+        if (method == null) {
+            method = compiler.compile(exchange.getContext(), text, singleQuotes);
+        }
+        // optimize as we call the same method all the time so we dont want to find the method every time as joor would do
+        // if you use its call method
+        Object out = ObjectHelper.invokeMethod(method, null, exchange.getContext(), exchange, exchange.getIn(),
+                exchange.getIn().getBody());
+        if (out != null && resultType != null) {
+            return exchange.getContext().getTypeConverter().convertTo(resultType, exchange, out);
+        } else {
+            return out;
         }
     }
 
@@ -105,87 +95,9 @@ public class JoorExpression extends ExpressionAdapter {
     public void init(CamelContext context) {
         super.init(context);
 
-        if (java8 == null) {
-            java8 = getJavaMajorVersion() == 8;
-            if (java8) {
-                throw new UnsupportedOperationException("Java 8 is not supported. Use Java 11 or higher");
-            }
-        }
-
         if (preCompile) {
-            this.className = nextFQN();
-            this.code = evalCode(context, className, text);
-            LOG.debug(code);
-            try {
-                this.compiled = compile(className, code);
-                this.method = compiled.type().getMethod("evaluate", CamelContext.class, Exchange.class, Message.class,
-                        Object.class);
-            } catch (NoSuchMethodException e) {
-                throw new JoorCompilationException(className, code, e);
-            }
-        }
-    }
-
-    private Reflect compile(String fqn, String code) {
-        try {
-            return Reflect.compile(fqn, code);
-        } catch (Exception e) {
-            throw new JoorCompilationException(fqn, code, e);
+            this.compiled = compiler.compile(context, text, singleQuotes);
         }
     }
 
-    private String evalCode(CamelContext camelContext, String fqn, String text) {
-        String qn = fqn.substring(0, fqn.lastIndexOf('.'));
-        String name = fqn.substring(fqn.lastIndexOf('.') + 1);
-
-        // reload script
-        text = ScriptHelper.resolveOptionalExternalScript(camelContext, text);
-
-        // trim text
-        text = text.trim();
-
-        //  wrap text into a class method we can call
-        StringBuilder sb = new StringBuilder();
-        sb.append("\n");
-        sb.append("package ").append(qn).append(";\n");
-        sb.append("\n");
-        sb.append("import org.apache.camel.*;\n");
-        sb.append("\n");
-        sb.append("public class ").append(name).append(" {\n");
-        sb.append("\n");
-        sb.append("\n");
-        sb.append(
-                "    public static Object evaluate(CamelContext context, Exchange exchange, Message message, Object body) throws Exception {\n");
-        sb.append("        ");
-        if (!text.contains("return ")) {
-            sb.append("return ");
-        }
-        if (singleQuotes) {
-            // single quotes instead of double quotes, as its very annoying for string in strings
-            String quoted = text.replace('\'', '"');
-            sb.append(quoted);
-        } else {
-            sb.append(text);
-        }
-        if (!text.endsWith("}") && !text.endsWith(";")) {
-            sb.append(";");
-        }
-        sb.append("\n");
-        sb.append("    }\n");
-        sb.append("}\n");
-        sb.append("\n");
-
-        return sb.toString();
-    }
-
-    private static int getJavaMajorVersion() {
-        String javaSpecVersion = System.getProperty("java.specification.version");
-        return javaSpecVersion.contains(".")
-                ? Integer.parseInt(javaSpecVersion.split("\\.")[1]) : Integer.parseInt(javaSpecVersion);
-    }
-
-    private static String nextFQN() {
-        return "org.apache.camel.language.joor.compiled.JoorLanguage" + COUNTER.incrementAndGet();
-    }
-
 }
diff --git a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorLanguage.java b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorLanguage.java
index 3b8c703..e658cce 100644
--- a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorLanguage.java
+++ b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorLanguage.java
@@ -22,15 +22,13 @@ import org.apache.camel.StaticService;
 import org.apache.camel.spi.annotations.Language;
 import org.apache.camel.support.ExpressionToPredicateAdapter;
 import org.apache.camel.support.LanguageSupport;
-import org.apache.camel.util.StopWatch;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.camel.support.service.ServiceHelper;
 
 @Language("joor")
 public class JoorLanguage extends LanguageSupport implements StaticService {
 
-    private static final Logger LOG = LoggerFactory.getLogger(JoorLanguage.class);
-    private long taken;
+    private static Boolean java8;
+    private final JoorCompiler compiler = new JoorCompiler();
 
     private boolean preCompile = true;
     private Class<?> resultType;
@@ -68,13 +66,10 @@ public class JoorLanguage extends LanguageSupport implements StaticService {
     @Override
     public Expression createExpression(String expression) {
         JoorExpression exp = new JoorExpression(expression);
+        exp.setCompiler(compiler);
         exp.setResultType(resultType);
         exp.setSingleQuotes(singleQuotes);
-
-        StopWatch watch = new StopWatch();
         exp.init(getCamelContext());
-        taken += watch.taken();
-
         return exp;
     }
 
@@ -86,6 +81,7 @@ public class JoorLanguage extends LanguageSupport implements StaticService {
     @Override
     public Expression createExpression(String expression, Object[] properties) {
         JoorExpression exp = new JoorExpression(expression);
+        exp.setCompiler(compiler);
         exp.setPreCompile(property(boolean.class, properties, 0, preCompile));
         exp.setResultType(property(Class.class, properties, 1, resultType));
         exp.setSingleQuotes(property(boolean.class, properties, 2, singleQuotes));
@@ -95,13 +91,24 @@ public class JoorLanguage extends LanguageSupport implements StaticService {
 
     @Override
     public void start() {
-        // noop
+        if (java8 == null) {
+            java8 = getJavaMajorVersion() == 8;
+            if (java8) {
+                throw new UnsupportedOperationException("Java 8 is not supported. Use Java 11 or higher");
+            }
+        }
+        ServiceHelper.startService(compiler);
     }
 
     @Override
     public void stop() {
-        if (taken > 0) {
-            LOG.info("jOOR language compilations took {} millis", taken);
-        }
+        ServiceHelper.stopService(compiler);
     }
+
+    private static int getJavaMajorVersion() {
+        String javaSpecVersion = System.getProperty("java.specification.version");
+        return javaSpecVersion.contains(".")
+                ? Integer.parseInt(javaSpecVersion.split("\\.")[1]) : Integer.parseInt(javaSpecVersion);
+    }
+
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/Exchange.java b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
index 07bf559..20dc6a5 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Exchange.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
@@ -291,6 +291,8 @@ public interface Exchange {
      */
     Object getProperty(String name);
 
+
+
     /**
      * Returns a property associated with this exchange by name
      *
diff --git a/docs/components/modules/languages/pages/joor-language.adoc b/docs/components/modules/languages/pages/joor-language.adoc
index 716daea..4756f01 100644
--- a/docs/components/modules/languages/pages/joor-language.adoc
+++ b/docs/components/modules/languages/pages/joor-language.adoc
@@ -80,7 +80,7 @@ And then in the 2nd statement we return a value whether the user is `null` or no
 [source,java]
 ----
 from("seda:orders")
-  .transform().joor("Object user = message.getHeader(\"user\"); return user != null ? \"User: \" + user : \"No user exists\";")
+  .transform().joor("var user = message.getHeader(\"user\"); return user != null ? \"User: \" + user : \"No user exists\";")
   .to("seda:user");
 ----
 
@@ -89,7 +89,7 @@ Notice how we have to quote strings in strings, and that is annoying, so instead
 [source,java]
 ----
 from("seda:orders")
-  .transform().joor("Object user = message.getHeader('user'); return user != null ? 'User: ' + user : 'No user exists';")
+  .transform().joor("var user = message.getHeader('user'); return user != null ? 'User: ' + user : 'No user exists';")
   .to("seda:user");
 ----
 
@@ -132,6 +132,9 @@ The code that you can write is therefore limited to a number of Java statements.
 The supported runtime is intended for Java standalone, Spring Boot, Camel Quarkus and other microservices runtimes.
 It is not supported in OSGi, Camel Karaf or any kind of Java Application Server runtime.
 
+jOOR does not support runtime compilation with Spring Boot using _fat jar_ packaging (https://github.com/jOOQ/jOOR/issues/69),
+it works with exploded classpath.
+
 == Dependencies
 
 To use scripting languages in your camel routes you need to add a
diff --git a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/JoorTest.java b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/JoorTest.java
index 20f55e7..d0f78b0 100644
--- a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/JoorTest.java
+++ b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/JoorTest.java
@@ -41,9 +41,6 @@ import org.openjdk.jmh.runner.options.TimeValue;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Tests a Simple operator expression
- */
 public class JoorTest {
 
     private static final Logger LOG = LoggerFactory.getLogger(JoorTest.class);