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 7b0e75a CAMEL-15713: camel-joor - Added support for dependency injection via #bean:id style and optimized to invoke method without reflection via interface. 7b0e75a is described below commit 7b0e75ae9f637d136df071d552730316047197ca Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Wed Oct 21 07:38:31 2020 +0200 CAMEL-15713: camel-joor - Added support for dependency injection via #bean:id style and optimized to invoke method without reflection via interface. --- .../apache/camel/catalog/docs/joor-language.adoc | 69 ++++++++++++++++++++ .../camel-joor/src/main/docs/joor-language.adoc | 69 ++++++++++++++++++++ .../apache/camel/language/joor/JoorCompiler.java | 75 +++++++++++++++++----- .../apache/camel/language/joor/JoorExpression.java | 23 ++++--- .../org/apache/camel/language/joor/JoorMethod.java | 30 +++++++++ .../language/joor/JoorBeanInjectTransformTest.java | 51 +++++++++++++++ .../camel/language/joor/JoorBeanInjectVarTest.java | 51 +++++++++++++++ .../org/apache/camel/language/joor/MyEchoBean.java | 28 ++++++++ .../src/test/resources/log4j2.properties | 4 +- .../modules/languages/pages/joor-language.adoc | 69 ++++++++++++++++++++ 10 files changed, 441 insertions(+), 28 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 f03d480..3d0653c 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 @@ -91,6 +91,75 @@ And then the function can be shortened: var user = bodyAs(MyUser); ---- +== Dependency Injection + +The Camel jOOR language allows dependency injection by referring to beans by their id from the Camel registry. +For optimization purposes then the beans are injected once in the constructor and the scopes are _singleton_. +This requires the injected beans to be _thread safe_ as they will be reused for all processing. + +In the jOOR script you declare the injected beans using the syntax `#bean:beanId`. + +For example suppose we have the following bean + +[source,java] +---- +public class MyEchoBean { + + public String echo(String str) { + return str + str; + } + + public String greet() { + return "Hello "; + } +} +---- + +And this bean is registered with the name `myBean` in the Camel registry. + +The jOOR script can then inject this bean directly in the script where the bean is in use: + +[source,java] +---- +from("direct:start") + .transform().joor("'Hello ' + #bean:myEcho.echo(bodyAs(String))") + .to("mock:result"); +---- + +Now this code may seem a bit magically, but what happens is that the `myEcho` bean is injected via a constructor, +and then called directly in the script so its as fast as possible. + +Under the hod Camel jOOR generates the following source code that is compiled once: + +[source,java] +---- +public class JoorScript1 implements org.apache.camel.language.joor.JoorMethod { + + private MyEchoBean myEcho; + + public JoorScript1(CamelContext context) throws Exception { + myEcho = context.getRegistry().lookupByNameAndType("myEcho", MyEchoBean.class); + } + + @Override + public Object evaluate(CamelContext context, Exchange exchange, Message message, Object body, Optional optionalBody) throws Exception { + return "Hello " + myEcho.echo(bodyAs(exchange, String.class)); + } +} +---- + +You can also store a reference to the bean in a variable which would more resemble how you would code in Java + +[source,java] +---- +from("direct:start") + .transform().joor("var bean = #bean:myEcho; return 'Hello ' + bean.echo(bodyAs(String))") + .to("mock:result"); +---- + +Notice how we declare the bean as if its a local variable via `var bean = #bean:myEcho`. When doing this we must use +a different name as `myEcho` is the variable used by the dependency injection and therefore we use _bean_ as name in the script. + == Auto imports The jOOR language will automatic import from: diff --git a/components/camel-joor/src/main/docs/joor-language.adoc b/components/camel-joor/src/main/docs/joor-language.adoc index f03d480..3d0653c 100644 --- a/components/camel-joor/src/main/docs/joor-language.adoc +++ b/components/camel-joor/src/main/docs/joor-language.adoc @@ -91,6 +91,75 @@ And then the function can be shortened: var user = bodyAs(MyUser); ---- +== Dependency Injection + +The Camel jOOR language allows dependency injection by referring to beans by their id from the Camel registry. +For optimization purposes then the beans are injected once in the constructor and the scopes are _singleton_. +This requires the injected beans to be _thread safe_ as they will be reused for all processing. + +In the jOOR script you declare the injected beans using the syntax `#bean:beanId`. + +For example suppose we have the following bean + +[source,java] +---- +public class MyEchoBean { + + public String echo(String str) { + return str + str; + } + + public String greet() { + return "Hello "; + } +} +---- + +And this bean is registered with the name `myBean` in the Camel registry. + +The jOOR script can then inject this bean directly in the script where the bean is in use: + +[source,java] +---- +from("direct:start") + .transform().joor("'Hello ' + #bean:myEcho.echo(bodyAs(String))") + .to("mock:result"); +---- + +Now this code may seem a bit magically, but what happens is that the `myEcho` bean is injected via a constructor, +and then called directly in the script so its as fast as possible. + +Under the hod Camel jOOR generates the following source code that is compiled once: + +[source,java] +---- +public class JoorScript1 implements org.apache.camel.language.joor.JoorMethod { + + private MyEchoBean myEcho; + + public JoorScript1(CamelContext context) throws Exception { + myEcho = context.getRegistry().lookupByNameAndType("myEcho", MyEchoBean.class); + } + + @Override + public Object evaluate(CamelContext context, Exchange exchange, Message message, Object body, Optional optionalBody) throws Exception { + return "Hello " + myEcho.echo(bodyAs(exchange, String.class)); + } +} +---- + +You can also store a reference to the bean in a variable which would more resemble how you would code in Java + +[source,java] +---- +from("direct:start") + .transform().joor("var bean = #bean:myEcho; return 'Hello ' + bean.echo(bodyAs(String))") + .to("mock:result"); +---- + +Notice how we declare the bean as if its a local variable via `var bean = #bean:myEcho`. When doing this we must use +a different name as `myEcho` is the variable used by the dependency injection and therefore we use _bean_ as name in the script. + == Auto imports The jOOR language will automatic import from: 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 index 4168ff3..84ad995 100644 --- 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 @@ -16,18 +16,18 @@ */ package org.apache.camel.language.joor; -import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.Map; -import java.util.Optional; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; 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.CamelContextHelper; import org.apache.camel.support.ScriptHelper; import org.apache.camel.support.service.ServiceSupport; import org.apache.camel.util.StopWatch; @@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory; public class JoorCompiler extends ServiceSupport implements StaticService { + private static final Pattern BEAN_INJECTION_PATTERN = Pattern.compile("(#bean:)([A-Za-z0-9-_]*)"); private static final Pattern BODY_AS_PATTERN = Pattern.compile("(optionalBodyAs|bodyAs)\\(([A-Za-z0-9.$]*)(.class)\\)"); private static final Pattern BODY_AS_PATTERN_NO_CLASS = Pattern.compile("(optionalBodyAs|bodyAs)\\(([A-Za-z0-9.$]*)\\)"); private static final Pattern HEADER_AS_PATTERN @@ -53,7 +54,7 @@ public class JoorCompiler extends ServiceSupport implements StaticService { private static final Logger LOG = LoggerFactory.getLogger(JoorCompiler.class); private static final AtomicInteger UUID = new AtomicInteger(); - private Set<String> imports; + private Set<String> imports = new TreeSet<>(); private Map<String, String> aliases; private int counter; private long taken; @@ -82,17 +83,19 @@ public class JoorCompiler extends ServiceSupport implements StaticService { } } - public Method compile(CamelContext camelContext, String script, boolean singleQuotes) { + public JoorMethod compile(CamelContext camelContext, String script, boolean singleQuotes) { StopWatch watch = new StopWatch(); - Method answer; + JoorMethod answer; String className = nextFQN(); String code = evalCode(camelContext, className, script, singleQuotes); try { - LOG.trace(code); + if (LOG.isDebugEnabled()) { + LOG.debug("Compiling code:\n\n" + code + "\n"); + } Reflect ref = Reflect.compile(className, code); - answer = ref.type().getMethod("evaluate", CamelContext.class, Exchange.class, Message.class, Object.class, - Optional.class); + Class<?> clazz = ref.type(); + answer = (JoorMethod) clazz.getConstructor(CamelContext.class).newInstance(camelContext); } catch (Exception e) { throw new JoorCompilationException(className, code, e); } @@ -112,6 +115,12 @@ public class JoorCompiler extends ServiceSupport implements StaticService { // trim text script = script.trim(); + script = staticHelper(script); + script = alias(script); + Set<String> scriptImports = new LinkedHashSet<>(); + Map<String, Class> scriptBeans = new HashMap<>(); + script = evalDependencyInjection(camelContext, scriptImports, scriptBeans, script); + // wrap text into a class method we can call StringBuilder sb = new StringBuilder(); sb.append("package ").append(qn).append(";\n"); @@ -132,19 +141,38 @@ public class JoorCompiler extends ServiceSupport implements StaticService { } sb.append("\n"); } + for (String i : scriptImports) { + sb.append("import "); + sb.append(i); + sb.append(";\n"); + } sb.append("\n"); - sb.append("public class ").append(name).append(" {\n"); + sb.append("public class ").append(name).append(" implements org.apache.camel.language.joor.JoorMethod {\n"); sb.append("\n"); + + // local beans variables + for (Map.Entry<String, Class> entry : scriptBeans.entrySet()) { + sb.append(" private ").append(entry.getValue().getSimpleName()).append(" ").append(entry.getKey()).append(";\n"); + } + sb.append("\n"); + + // constructor to lookup beans + sb.append(" public ").append(name).append("(CamelContext context) throws Exception {\n"); + for (Map.Entry<String, Class> entry : scriptBeans.entrySet()) { + sb.append(" ").append(entry.getKey()).append(" = ").append("context.getRegistry().lookupByNameAndType(\"") + .append(entry.getKey()).append("\", ").append(entry.getValue().getSimpleName()).append(".class);\n"); + } + sb.append(" }\n"); + sb.append("\n"); + + sb.append(" @Override\n"); sb.append( - " public static Object evaluate(CamelContext context, Exchange exchange, Message message, Object body, Optional optionalBody) throws Exception {\n"); + " public Object evaluate(CamelContext context, Exchange exchange, Message message, Object body, Optional optionalBody) throws Exception {\n"); sb.append(" "); if (!script.contains("return ")) { sb.append("return "); } - script = staticHelper(script); - script = alias(script); - if (singleQuotes) { // single quotes instead of double quotes, as its very annoying for string in strings String quoted = script.replace('\'', '"'); @@ -163,6 +191,21 @@ public class JoorCompiler extends ServiceSupport implements StaticService { return sb.toString(); } + private String evalDependencyInjection( + CamelContext camelContext, Set<String> scriptImports, Map<String, Class> scriptBeans, String script) { + Matcher matcher = BEAN_INJECTION_PATTERN.matcher(script); + while (matcher.find()) { + String id = matcher.group(2); + Object bean = CamelContextHelper.mandatoryLookup(camelContext, id); + Class<?> type = bean.getClass(); + scriptImports.add(type.getName()); + scriptBeans.put(id, type); + script = matcher.replaceFirst(id); + matcher = BEAN_INJECTION_PATTERN.matcher(script); + } + return script; + } + private String staticHelper(String script) { Matcher matcher = BODY_AS_PATTERN.matcher(script); script = matcher.replaceAll("$1(exchange, $2$3)"); @@ -191,7 +234,7 @@ public class JoorCompiler extends ServiceSupport implements StaticService { } private static String nextFQN() { - return "org.apache.camel.language.joor.compiled.JoorLanguage" + UUID.incrementAndGet(); + return "org.apache.camel.language.joor.compiled.JoorScript" + 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 594d5ec..32e0af4 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 @@ -16,19 +16,18 @@ */ package org.apache.camel.language.joor; -import java.lang.reflect.Method; import java.util.Optional; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; +import org.apache.camel.ExpressionEvaluationException; import org.apache.camel.support.ExpressionAdapter; -import org.apache.camel.support.ObjectHelper; public class JoorExpression extends ExpressionAdapter { private final String text; private JoorCompiler compiler; - private Method compiled; + private JoorMethod method; private Class<?> resultType; private boolean preCompile = true; @@ -77,17 +76,21 @@ public class JoorExpression extends ExpressionAdapter { @Override public Object evaluate(Exchange exchange) { - Method method = compiled; - if (method == null) { - method = compiler.compile(exchange.getContext(), text, singleQuotes); + JoorMethod target = this.method; + if (target == null) { + target = 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 body = exchange.getIn().getBody(); // in the rare case the body is already an optional - boolean optional = body instanceof Optional; - Object out = ObjectHelper.invokeMethod(method, null, exchange.getContext(), exchange, exchange.getIn(), - body, optional ? body : Optional.ofNullable(body)); + Optional<?> optional = body instanceof Optional ? (Optional<?>) body : Optional.ofNullable(body); + Object out; + try { + out = target.evaluate(exchange.getContext(), exchange, exchange.getIn(), body, optional); + } catch (Exception e) { + throw new ExpressionEvaluationException(this, exchange, e); + } if (out != null && resultType != null) { return exchange.getContext().getTypeConverter().convertTo(resultType, exchange, out); } else { @@ -100,7 +103,7 @@ public class JoorExpression extends ExpressionAdapter { super.init(context); if (preCompile) { - this.compiled = compiler.compile(context, text, singleQuotes); + this.method = compiler.compile(context, text, singleQuotes); } } diff --git a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorMethod.java b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorMethod.java new file mode 100644 index 0000000..ade1503 --- /dev/null +++ b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorMethod.java @@ -0,0 +1,30 @@ +/* + * 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.util.Optional; + +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.Message; + +public interface JoorMethod { + + Object evaluate(CamelContext context, Exchange exchange, Message message, Object body, Optional<?> optionalBody) + throws Exception; + +} diff --git a/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorBeanInjectTransformTest.java b/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorBeanInjectTransformTest.java new file mode 100644 index 0000000..5d7282a --- /dev/null +++ b/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorBeanInjectTransformTest.java @@ -0,0 +1,51 @@ +/* + * 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 org.apache.camel.BindToRegistry; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.junit.jupiter.api.Test; + +public class JoorBeanInjectTransformTest extends CamelTestSupport { + + @BindToRegistry("myEcho") + private MyEchoBean myEchoBean = new MyEchoBean(); + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .transform().joor("#bean:myEcho.greet() + #bean:myEcho.echo(bodyAs(String))") + .to("mock:result"); + } + }; + } + + @Test + public void testHello() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Hello CamelCamel", "Hello WorldWorld"); + + template.sendBody("direct:start", "Camel"); + template.sendBody("direct:start", "World"); + + assertMockEndpointsSatisfied(); + } + +} diff --git a/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorBeanInjectVarTest.java b/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorBeanInjectVarTest.java new file mode 100644 index 0000000..ab15c01 --- /dev/null +++ b/components/camel-joor/src/test/java/org/apache/camel/language/joor/JoorBeanInjectVarTest.java @@ -0,0 +1,51 @@ +/* + * 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 org.apache.camel.BindToRegistry; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.junit.jupiter.api.Test; + +public class JoorBeanInjectVarTest extends CamelTestSupport { + + @BindToRegistry("myEcho") + private MyEchoBean myEchoBean = new MyEchoBean(); + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .transform().joor("var bean = #bean:myEcho; return 'Hello ' + bean.echo(bodyAs(String))") + .to("mock:result"); + } + }; + } + + @Test + public void testHello() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Hello CamelCamel", "Hello WorldWorld"); + + template.sendBody("direct:start", "Camel"); + template.sendBody("direct:start", "World"); + + assertMockEndpointsSatisfied(); + } + +} diff --git a/components/camel-joor/src/test/java/org/apache/camel/language/joor/MyEchoBean.java b/components/camel-joor/src/test/java/org/apache/camel/language/joor/MyEchoBean.java new file mode 100644 index 0000000..f484326 --- /dev/null +++ b/components/camel-joor/src/test/java/org/apache/camel/language/joor/MyEchoBean.java @@ -0,0 +1,28 @@ +/* + * 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; + +public class MyEchoBean { + + public String echo(String str) { + return str + str; + } + + public String greet() { + return "Hello "; + } +} diff --git a/components/camel-joor/src/test/resources/log4j2.properties b/components/camel-joor/src/test/resources/log4j2.properties index e4e6691..0383652 100644 --- a/components/camel-joor/src/test/resources/log4j2.properties +++ b/components/camel-joor/src/test/resources/log4j2.properties @@ -24,7 +24,7 @@ appender.out.type = Console appender.out.name = out appender.out.layout.type = PatternLayout appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n -logger.springframework.name = org.springframework -logger.springframework.level = WARN +logger.joor.name = org.apache.camel.language.joor +logger.joor.level = DEBUG rootLogger.level = INFO rootLogger.appenderRef.file.ref = file diff --git a/docs/components/modules/languages/pages/joor-language.adoc b/docs/components/modules/languages/pages/joor-language.adoc index 441f32e..fda65dd 100644 --- a/docs/components/modules/languages/pages/joor-language.adoc +++ b/docs/components/modules/languages/pages/joor-language.adoc @@ -93,6 +93,75 @@ And then the function can be shortened: var user = bodyAs(MyUser); ---- +== Dependency Injection + +The Camel jOOR language allows dependency injection by referring to beans by their id from the Camel registry. +For optimization purposes then the beans are injected once in the constructor and the scopes are _singleton_. +This requires the injected beans to be _thread safe_ as they will be reused for all processing. + +In the jOOR script you declare the injected beans using the syntax `#bean:beanId`. + +For example suppose we have the following bean + +[source,java] +---- +public class MyEchoBean { + + public String echo(String str) { + return str + str; + } + + public String greet() { + return "Hello "; + } +} +---- + +And this bean is registered with the name `myBean` in the Camel registry. + +The jOOR script can then inject this bean directly in the script where the bean is in use: + +[source,java] +---- +from("direct:start") + .transform().joor("'Hello ' + #bean:myEcho.echo(bodyAs(String))") + .to("mock:result"); +---- + +Now this code may seem a bit magically, but what happens is that the `myEcho` bean is injected via a constructor, +and then called directly in the script so its as fast as possible. + +Under the hod Camel jOOR generates the following source code that is compiled once: + +[source,java] +---- +public class JoorScript1 implements org.apache.camel.language.joor.JoorMethod { + + private MyEchoBean myEcho; + + public JoorScript1(CamelContext context) throws Exception { + myEcho = context.getRegistry().lookupByNameAndType("myEcho", MyEchoBean.class); + } + + @Override + public Object evaluate(CamelContext context, Exchange exchange, Message message, Object body, Optional optionalBody) throws Exception { + return "Hello " + myEcho.echo(bodyAs(exchange, String.class)); + } +} +---- + +You can also store a reference to the bean in a variable which would more resemble how you would code in Java + +[source,java] +---- +from("direct:start") + .transform().joor("var bean = #bean:myEcho; return 'Hello ' + bean.echo(bodyAs(String))") + .to("mock:result"); +---- + +Notice how we declare the bean as if its a local variable via `var bean = #bean:myEcho`. When doing this we must use +a different name as `myEcho` is the variable used by the dependency injection and therefore we use _bean_ as name in the script. + == Auto imports The jOOR language will automatic import from: