Jactl
Since Camel 4.22
Camel has support for using Jactl.
Jactl is a scripting language with syntax borrowed from Groovy with a little Perl mixed in.
It compiles to bytecode for fast execution speed while providing a secure, locked down, sandbox environment out-of-the-box.
If you need Jactl scripts to have access to Java classes you can configure which classes are permitted, to provide fine-grained control over what the scripts are allowed to do. If no security is required, users can open it up so that Jactl scripts have access to all classes.
Dependency
To enable the use of Jactl scripts in your Camel routes you will need to add a dependency on camel-jactl.
For example with Maven, in the dependencies section of your pom.xml:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jactl</artifactId>
<version>x.y.z</version>
</dependency> Usage
import static org.apache.camel.language.jactl.JactlLanguage.jactl;
public class MyRouteBuilder extends RouteBuilder {
@Override
public void configure() {
from("direct:orders")
.filter(jactl("body.amount > 100 && body.status == 'NEW'"))
.to("direct:big-orders");
}
}
// Or via the Language SPI:
Language jactl = camelContext.resolveLanguage("jactl");
Predicate p = jactl.createPredicate("body.age > 20");
Expression e = jactl.createExpression("'Hello ' + body.name"); Results
To return a result from a multi-line script, you can use return or, if there is no return statement, the result of the last expression in the script will be returned:
def cost = body.item.count * body.item.cost
cost > 10000 // return true if high cost Standard Variables
Scripts see these variables:
| Variable | Type | Description |
|---|---|---|
|
| The message body. |
|
| The headers of the message. |
|
| The exchange variables. |
If withAllowContextMapAll(true) is configured, these variables are also available:
| Variable | Type | Description |
|---|---|---|
|
| The message. |
|
| Deprecated The Out message (only for InOut message exchange pattern). |
|
| If failure then this has the cause. |
|
| The Exchange itself. |
|
| The exchange properties. |
|
| The Camel Context. |
The types of the request, response, exception, exchange, and camelContext variables are not standard supported types in Jactl so if you need to invoke methods on these objects you should configure this using createWithHostAccess() or createWithHostAccess(predicate). |
Security
The sandbox security model of Jactl, by default, prevents access to any class outside the Jactl provided types. If you want to allow Jactl scripts to invoke methods on objects of other types you need to use createWithHostAccess() or createWithHostAccess(predicate).
createWithHostAccess()
If the parameterless createWithHostAccess() is used, it completely opens up the sandbox and any method on any object can be invoked from a Jactl script.
createWithHostAccess() should only be used in scenarios where you have complete trust in the scripts being invoked. |
createWithHostAccess(Predicate<String> predicate)
When a predicate is supplied to createWithHostAccess(Predicate<String>) it will be invoked with the full Java classname and should return true if the class is a permitted class.
Whenever there is a method invocation in a script, the predicate is invoked twice:
-
The classname of the runtime type (e.g. "com.acme.ApplicationContext") of the object is passed to the predicate to see if we are allowed to invoke methods on it. The predicate should return true if it is an allowed class.
-
The class where the method is declared is passed to the predicate to make sure that not only the object’s class is permitted but the class in which the method is implemented is also permitted (this could be the same class, or it could be a base class if the method is inherited).
Binding the Jactl Language
To configure withAllowContextMapAll(boolean) or createWithHostAccess() you should bind the jactl language to the configured JactlLanguage instance before the first usage.
For example:
Predicate<String> predicate = name -> name.startsWith("com.acme.");
JactlLanguage jactl = JactlLanguage.createWithHostAccess(predicate)
.withAllowContextMapAll(true);
camelContext.getRegistry().bind("jactl", jactl); For more control over configuration of the Jactl language, pass your own JactlContext to the JactlLanguage constructor:
Predicate<String> predicate = name -> name.startsWith("com.acme.");
JactlContext context = JactlContext.create()
... various setters ...
.build();
JactlLanguage jactl = new JactlLanguage(context);
camelContext.getRegistry().bind("jactl", jactl); Script Cache
Compiled scripts are cached by script text in a cache that defaults to 1000 entries but whose size can be overridden with the camel-jactl.maxCacheSize system property. Note that all scripts create Java classes which are kept by the class loader owned by the JactlContext so don’t keep dynamically creating new scripts as you will eventually run out of memory.
Similarities with Groovy
Jactl and Groovy have many similarities in terms of syntax and simple scripts written in one language can often be run without change in the other.
For example, here are some of the things that are the same:
-
//and/* */style commends. -
Semicolons are optional unless there are multiple statements on the same line.
-
Closures:
{ x,y → x + y}, the implicititvariable, and trailing closure-call syntaxlist.map{ it * 2 }. -
deffor dynamic types -
Numeric literals are
intby default unless a suffix ofLforlongorDfor double is used. -
Numeric literals with decimal points default to
BigDecimalvalues (calledDecimalin Jactl). -
Hex values are prefixed with
0x. -
Binary values are prefixed with
0b. -
Jactl has support for the standard
boolean,int,long,double,String,List, andMaptypes. -
Jactl also supports
bytevalues but in Jactlbytevalues are unsigned. -
List literals
[1,2,3]and Map literals[a:1, b:2]are the same as in Groovy. -
Double-quoted interpolation strings:
"Value is ${a * a + 2 * b + c}". -
Triple quoted strings for multi-line strings.
-
/…/is another way to delimit interpolated strings - useful when used as regex patterns. -
=~operator for doing a regex find. -
?.(safe navigation),?:(Elvis/default value),<⇒(spaceship/comparator),==/!=, and===/!==(identity comparison) are the same. -
asfor coercion. -
infor membership tests. -
Truthiness:
0,null,'',[], and[:]are all false, everything else is true. -
List and Map object support
+,-,<<,+=, and support subscript access as well as null-safe subscript?[.
Please see the Jactl Language Guide for more information about Jactl, the language.