
org.apache.commons.jexl3.package-info Maven / Gradle / Ivy
Show all versions of commons-jexl3 Show documentation
/*
* 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.
*/
/**
* Provides a framework for evaluating JEXL expressions.
*
* - Introduction
* - Brief Example
* - Using JEXL
* - Configuring JEXL
* - Customizing JEXL
* - Extending JEXL
*
* Introduction
*
* JEXL is a library intended to facilitate the implementation of dynamic and scripting features in applications
* and frameworks.
*
* A Brief Example
*
* In its simplest form, JEXL merges an
* {@link org.apache.commons.jexl3.JexlExpression}
* with a
* {@link org.apache.commons.jexl3.JexlContext} when evaluating expressions.
* An Expression is created using
* {@link org.apache.commons.jexl3.JexlEngine#createExpression(String)},
* passing a String containing valid JEXL syntax. A simple JexlContext can be created using
* a {@link org.apache.commons.jexl3.MapContext} instance;
* a map of variables that will be internally wrapped can be optionally provided through its constructor.
* The following example, takes a variable named 'car', and
* invokes the checkStatus() method on the property 'engine'
*
*
* // Create a JexlEngine (could reuse one instead)
* JexlEngine jexl = new JexlBuilder().create();
* // Create an expression object equivalent to 'car.getEngine().checkStatus()':
* String jexlExp = "car.engine.checkStatus()";
* Expression e = jexl.createExpression( jexlExp );
* // The car we have to handle coming as an argument...
* Car car = theCarThatWeHandle;
* // Create a context and add data
* JexlContext jc = new MapContext();
* jc.set("car", car );
* // Now evaluate the expression, getting the result
* Object o = e.evaluate(jc);
*
* Using JEXL
* The API is composed of three levels addressing different functional needs:
*
* - Dynamic invocation of setters, getters, methods and constructors
* - Script expressions known as JEXL expressions
* - JSP/JSF like expression known as JXLT expressions
*
* Important note
* The public API classes reside in the 2 packages:
*
* - org.apache.commons.jexl3
* - org.apache.commons.jexl3.introspection
*
*
* The following packages follow a "use at your own maintenance cost" policy; these are only intended to be used
* for extending JEXL.
* Their classes and methods are not guaranteed to remain compatible in subsequent versions.
* If you think you need to use directly some of their features or methods, it might be a good idea to check with
* the community through the mailing list first.
*
*
* - org.apache.commons.jexl3.parser
* - org.apache.commons.jexl3.scripting
* - org.apache.commons.jexl3.internal
* - org.apache.commons.jexl3.internal.introspection
*
* Dynamic Invocation
*
* These functionalities are close to the core level utilities found in
* BeanUtils.
* For basic dynamic property manipulations and method invocation, you can use the following
* set of methods:
*
*
* - {@link org.apache.commons.jexl3.JexlEngine#newInstance}
* - {@link org.apache.commons.jexl3.JexlEngine#setProperty}
* - {@link org.apache.commons.jexl3.JexlEngine#getProperty}
* - {@link org.apache.commons.jexl3.JexlEngine#invokeMethod}
*
* The following example illustrate their usage:
*
* // test outer class
* public static class Froboz {
* int value;
* public Froboz(int v) { value = v; }
* public void setValue(int v) { value = v; }
* public int getValue() { return value; }
* }
* // test inner class
* public static class Quux {
* String str;
* Froboz froboz;
* public Quux(String str, int fro) {
* this.str = str;
* froboz = new Froboz(fro);
* }
* public Froboz getFroboz() { return froboz; }
* public void setFroboz(Froboz froboz) { this.froboz = froboz; }
* public String getStr() { return str; }
* public void setStr(String str) { this.str = str; }
* }
* // test API
* JexlEngine jexl = new JexlBuilder().create();
* Quux quux = jexl.newInstance(Quux.class, "xuuq", 100);
* jexl.setProperty(quux, "froboz.value", Integer.valueOf(100));
* Object o = jexl.getProperty(quux, "froboz.value");
* assertEquals("Result is not 100", Integer.valueOf(100), o);
* jexl.setProperty(quux, "['froboz'].value", Integer.valueOf(1000));
* o = jexl.getProperty(quux, "['froboz']['value']");
* assertEquals("Result is not 1000", Integer.valueOf(1000), o);
*
* Expressions and Scripts
*
* If your needs require simple expression evaluation capabilities, the core JEXL features
* will most likely fit.
* The main methods are:
*
*
* - {@link org.apache.commons.jexl3.JexlEngine#createScript}
* - {@link org.apache.commons.jexl3.JexlScript#execute}
* - {@link org.apache.commons.jexl3.JexlEngine#createExpression}
* - {@link org.apache.commons.jexl3.JexlExpression#evaluate}
*
* The following example illustrates their usage:
*
* JexlEngine jexl = new JexlBuilder().create();
* JexlContext jc = new MapContext();
* jc.set("quuxClass", quux.class);
* JexlExpression create = jexl.createExpression("quux = new(quuxClass, 'xuuq', 100)");
* JelxExpression assign = jexl.createExpression("quux.froboz.value = 10");
* JexlExpression check = jexl.createExpression("quux[\"froboz\"].value");
* Quux quux = (Quux) create.evaluate(jc);
* Object o = assign.evaluate(jc);
* assertEquals("Result is not 10", Integer.valueOf(10), o);
* o = check.evaluate(jc);
* assertEquals("Result is not 10", Integer.valueOf(10), o);
*
* Unified Expressions and Templates
*
* If you are looking for JSP-EL like and basic templating features, you can
* use Expression from a JxltEngine.
*
* The main methods are:
*
* - {@link org.apache.commons.jexl3.JxltEngine#createExpression}
* - {@link org.apache.commons.jexl3.JxltEngine.Expression#prepare}
* - {@link org.apache.commons.jexl3.JxltEngine.Expression#evaluate}
* - {@link org.apache.commons.jexl3.JxltEngine#createTemplate}
* - {@link org.apache.commons.jexl3.JxltEngine.Template#prepare}
* - {@link org.apache.commons.jexl3.JxltEngine.Template#evaluate}
*
* The following example illustrates their usage:
*
* JexlEngine jexl = new JexlBuilder().create();
* JxltEngine jxlt = jexl.createJxltEngine();
* JxltEngine.Expression expr = jxlt.createExpression("Hello ${user}");
* String hello = expr.evaluate(context).toString();
*
* JexlExpression, JexlScript, Expression and Template: summary
* JexlExpression
*
* These are the most basic form of JexlEngine expressions and only allow for a single command
* to be executed and its result returned. If you try to use multiple commands, it ignores
* everything after the first semi-colon and just returns the result from
* the first command.
*
*
* Also note that expressions are not statements (which is what scripts are made of) and do not allow
* using the flow control (if, while, for), variables or lambdas syntactic elements.
*
* JexlScript
*
* These allow you to use multiple statements and you can
* use variable assignments, loops, calculations, etc. More or less what can be achieved in Shell or
* JavaScript at its basic level. The result from the last command is returned from the script.
*
* JxltEngine.Expression
*
* These are ideal to produce "one-liner" text, like a 'toString()' on steroids.
* To get a calculation you use the EL-like syntax
* as in ${someVariable}. The expression that goes between the brackets
* behaves like a JexlScript, not an expression. You can use semi-colons to
* execute multiple commands and the result from the last command is
* returned from the script. You also have the ability to use a 2-pass evaluation using
* the #{someScript} syntax.
*
* JxltEngine.Template
*
* These produce text documents. Each line beginning with '$$' (as a default) is
* considered JEXL code and all others considered as JxltEngine.Expression.
* Think of those as simple Velocity templates. A rewritten MudStore initial Velocity sample looks like this:
*
*
* <html>
* <body>
* Hello ${customer.name}!
* <table>
* $$ for(var mud : mudsOnSpecial ) {
* $$ if (customer.hasPurchased(mud) ) {
* <tr>
* <td>
* ${flogger.getPromo( mud )}
* </td>
* </tr>
* $$ }
* $$ }
* </table>
* </body>
* </html>
*
* JEXL Configuration
*
* The JexlEngine can be configured through a few parameters that will drive how it reacts
* in case of errors.
* These configuration methods are embedded through a {@link org.apache.commons.jexl3.JexlBuilder}.
*
* Static & Shared Configuration
*
* Both JexlEngine and JxltEngine are thread-safe, most of their inner fields are final; the same instance can
* be shared between different threads and proper synchronization is enforced in critical areas (introspection caches).
*
*
* Of particular importance is {@link org.apache.commons.jexl3.JexlBuilder#loader(java.lang.ClassLoader)} which indicates
* to the JexlEngine being built which class loader to use to solve a class name;
* this directly affects how JexlEngine.newInstance and the 'new' script method operates.
*
*
* This can also be very useful in cases where you rely on JEXL to dynamically load and call plugins for your application.
* To avoid having to restart the server in case of a plugin implementation change, you can call
* {@link org.apache.commons.jexl3.JexlEngine#setClassLoader} and all the scripts created through this engine instance
* will automatically point to the newly loaded classes.
*
*
* You can state what can be manipulated through scripting by the {@link org.apache.commons.jexl3.annotations.NoJexl}
* annotation that completely shield classes and methods from JEXL introspection.
* The other configurable way to restrict JEXL is by using a
* {@link org.apache.commons.jexl3.introspection.JexlSandbox} which allows finer control over what is exposed; the sandbox
* can be set through {@link org.apache.commons.jexl3.JexlBuilder#sandbox(org.apache.commons.jexl3.introspection.JexlSandbox)}.
*
*
* {@link org.apache.commons.jexl3.JexlBuilder#namespaces} extends JEXL scripting by registering your own classes as
* namespaces allowing your own functions to be exposed at will.
*
* This can be used as in:
* {@code
* public static MyMath {
* public double cos(double x) {
* return Math.cos(x);
* }
* }
* Map funcs = new HashMap();
* funcs.put("math", new MyMath());
* JexlEngine jexl = new JexlBuilder().namespaces(funcs).create();
* JexlContext jc = new MapContext();
* jc.set("pi", Math.PI);
* JexlExpression e = JEXL.createExpression("math:cos(pi)");
* o = e.evaluate(jc);
* assertEquals(Double.valueOf(-1),o);
* }
*
* If the namespace is a Class and that class declares a constructor that takes a JexlContext (or
* a class extending JexlContext), one namespace instance is created on first usage in an
* expression; this instance lifetime is limited to the expression evaluation.
*
*
* JexlEngine and JxltEngine expression caches can be configured as well. If you intend to use JEXL
* repeatedly in your application, these are worth configuring since expression parsing is quite heavy.
* Note that all caches created by JEXL are held through SoftReference; under high memory pressure, the GC will be able
* to reclaim those caches and JEXL will rebuild them if needed. By default, a JexlEngine does create a cache for
* "small" expressions and a JxltEngine does create one for Expression.
*
* {@link org.apache.commons.jexl3.JexlBuilder#cache(int)} will set how many expressions can be simultaneously cached by the
* JEXL engine. JxltEngine allows to define the cache size through its constructor.
*
*
* {@link org.apache.commons.jexl3.JexlBuilder#debug(boolean)}
* makes stack traces carried by JExlException more meaningful; in particular, these
* traces will carry the exact caller location the Expression was created from.
*
* Dynamic Configuration
*
* Those configuration options can be overridden during evaluation by implementing a
* {@link org.apache.commons.jexl3.JexlContext}
* that also implements {@link org.apache.commons.jexl3.JexlEngine.Options} to carry evaluation options.
* An example of such a class exists in the test package.
*
*
* {@link org.apache.commons.jexl3.JexlBuilder#strict} or {@link org.apache.commons.jexl3.JexlEngine.Options#isStrict}
* configures when JEXL considers 'null' as an error or not in various situations;
* when facing an unreferenceable variable, using null as an argument to an arithmetic operator or failing to call
* a method or constructor. The lenient mode is close to JEXL-1.1 behavior.
*
*
* {@link org.apache.commons.jexl3.JexlBuilder#silent} or {@link org.apache.commons.jexl3.JexlEngine.Options#isSilent}
* configures how JEXL reacts to errors; if silent, the engine will not throw exceptions
* but will warn through loggers and return null in case of errors. Note that when non-silent, JEXL throws
* JexlException which are unchecked exception.
*
*
* Implementing a {@link org.apache.commons.jexl3.JexlContext.NamespaceResolver} through a JexlContext - look at
* JexlEvalContext in the test directory
* as an example - allows to override the namespace resolution and the default namespace map defined
* through {@link org.apache.commons.jexl3.JexlBuilder#namespaces}.
*
* JEXL Customization
*
* The {@link org.apache.commons.jexl3.JexlContext}, {@link org.apache.commons.jexl3.JexlBuilder} and
* {@link org.apache.commons.jexl3.JexlEngine.Options} are
* the most likely interfaces you'll want to implement for customization. Since they expose variables and options,
* they are the primary targets. Before you do so, have a look at JexlEvalContext in the test directory
* and {@link org.apache.commons.jexl3.ObjectContext} which may already cover some of your needs.
*
*
* {@link org.apache.commons.jexl3.JexlArithmetic}
* is the class to derive if you need to change how operators behave or add types upon which they
* operate.
* There are 3 entry points that allow customizing the type of objects created:
*
*
* - array literals: {@link org.apache.commons.jexl3.JexlArithmetic#arrayBuilder}
* - map literals: {@link org.apache.commons.jexl3.JexlArithmetic#mapBuilder}
* - set literals: {@link org.apache.commons.jexl3.JexlArithmetic#setBuilder}
* - range objects: {@link org.apache.commons.jexl3.JexlArithmetic#createRange}
*
*
* You can also overload operator methods; by convention, each operator has a method name associated to it.
* If you overload some in your JexlArithmetic derived implementation, these methods will be called when the
* arguments match your method signature.
* For example, this would be the case if you wanted '+' to operate on arrays; you'd need to derive
* JexlArithmetic and implement '{@code public Object add(Set;> x, Set;> y)}' method.
* Note however that you can not change the operator precedence.
* The list of operator / method matches is described in {@link org.apache.commons.jexl3.JexlOperator}:
*
*
* You can also add methods to overload property getters and setters operators behaviors.
* Public methods of the JexlArithmetic instance named propertyGet/propertySet/arrayGet/arraySet are potential
* overrides that will be called when appropriate.
* The following table is an overview of the relation between a syntactic form and the method to call
* where V is the property value class, O the object class and P the property identifier class (usually String or Integer).
*
* Property Accessors
*
* Expression
* Method Template
*
*
* foo.property
* public V propertyGet(O obj, P property);
*
*
* foo.property = value
* public V propertySet(O obj, P property, V value);
*
*
* foo[property]
* public V arrayGet(O obj, P property, V value);
*
*
* foo[property] = value
* public V arraySet(O obj, P property, V value);
*
*
*
* You can also override the base operator methods, those whose arguments are Object which gives you total
* control.
*
* Extending JEXL
* If you need to make JEXL treat some objects in a specialized manner or tweak how it
* reacts to some settings, you can derive most of its inner-workings. The classes and methods are rarely private or
* final - only when the inner contract really requires it. However, using the protected methods
* and internal package classes imply you might have to re-adapt your code when new JEXL versions are released.
*
* {@link org.apache.commons.jexl3.internal.Engine} can be
* extended to let you capture your own configuration defaults wrt cache sizes and various flags.
* Implementing your own cache - instead of the basic LinkedHashMap based one - would be
* another possible extension.
*
*
* {@link org.apache.commons.jexl3.internal.Interpreter}
* is the class to derive if you need to add more features to the evaluation
* itself; for instance, you want pre- and post- resolvers for variables or nested scopes for
* for variable contexts.
*
*
* {@link org.apache.commons.jexl3.internal.introspection.Uberspect}
* is the class to derive if you need to add introspection or reflection capabilities for some objects, for
* instance adding factory based support to the 'new' operator.
* The code already reflects public fields as properties on top of Java-beans conventions.
*
*/
package org.apache.commons.jexl3;