This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push:
new a270d2e CAMEL-15697: camel-joor - Camel expression langauge using
jOOR runtime java compiled.
a270d2e is described below
commit a270d2ed4db9419046bd448c57d7e887cffc37c2
Author: Claus Ibsen <[email protected]>
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);