Java Unified Expression Language
Java Unified Expression Language
Table of contents
1 Welcome to JUEL!................................................................................................................................. 2
2 JUEL Guide............................................................................................................................................ 2
2.1 Getting Started................................................................................................................................3
2.2 Basic Classes...................................................................................................................................3
2.2.1
Expression Factory.................................................................................................................... 3
2.2.2
Value Expressions......................................................................................................................5
2.2.3
Method Expressions...................................................................................................................6
2.3 Utility Classes..................................................................................................................................7
2.3.1
Simple Context.......................................................................................................................... 8
2.3.2
Simple Resolver.........................................................................................................................8
2.4 Plugin JUEL...................................................................................................................................... 9
2.5 Advanced Topics............................................................................................................................. 9
2.6 Specification Issues....................................................................................................................... 12
3 JUEL Project.........................................................................................................................................14
3.1 History of Changes........................................................................................................................14
1. Welcome to JUEL!
JUEL is an implementation of the Unified Expression Language (EL), specified as part of the JSP 2.1 standard
(JSR-245), which has been introduced in JEE5. Additionally, JUEL 2.2 implements the JSP 2.2 maintenance
release specification for full JEE6 compliance.
Motivation
Once, the EL started as part of JSTL. Then, the EL made its way into the JSP 2.0 standard. Now, though
part of JSP 2.1, the EL API has been separated into package javax.el and all dependencies to the core
JSP classes have been removed.
In other words: the EL is ready for use in non-JSP applications!
Features
JUEL provides a lightweight and efficient implementation of the Unified Expression Language.
High Performance Parsing expressions is certainly the expected performance bottleneck. JUEL uses a
hand-coded parser which is up to 10 times faster than the previously used (javacc) generated parser!
Once built, expression trees are evaluated at highest speed.
Pluggable Cache Even if JUELs parser is fast, parsing expressions is relative expensive. Therefore, it's
best to parse an expression string only once. JUEL provides a default caching mechanism which should
be sufficient in most cases. However, JUEL allows to plug in your own cache easily.
Small Footprint JUEL has been carefully designed to minimize memory usage as well as code size.
Method Invocations JUEL supports method invocations as in ${foo.matches('[0-9]+')}. Methods are
resolved and invoked using the EL's resolver mechanism. As of JUEL 2.2, method invocations are enabled
by default.
VarArg Calls JUEL supports Java 5 VarArgs in function and method invocations. E.g., binding
String.format(String, String...) to function format allows for ${format('Hey %s','Joe')}. As of
JUEL 2.2, VarArgs are enabled by default.
Pluggable JUEL can be configured to be transparently detected as EL implementation by a Java runtime
environment or JEE application server. Using JUEL does not require an application to explicitly reference
any of the JUEL specific implementation classes.
Status
JUEL is considered production stable. The code is well tested (80% coverage) and feature complete.
Availability
JUEL is licensed under the Apache License 2.0.
Requirements
JUEL requires Java 5 or later.
2. JUEL Guide
This guide gives a brief introduction to JUEL. However, this is not an EL tutorial. Before using JUEL,
we strongly recommend to get familiar with the Unified EL basics by taking a look at the specification
document, which is available here.
The JUEL guide divides into the following sections:
Copyright 2006-2014 Odysseus Software GmbH. All rights reserved. 2
Java Unified Expression Language
Expression Cache
Each factory instance uses its own expression cache. Caching expressions can be an important issue,
because parsing is relative expensive. An expression cache maps expression strings to their parsed
representations (trees).
JUEL provides a caching interface which allows applications to use their own caching mechanism. However,
in most scenarios, JUEL's default implementation (based on java.util.concurrent.ConcurrentHashMap
and java.util.concurrent.ConcurrentLinkedQueue) should be fine.
Note
The caching mechanism has been rewritten for JUEL 2.2.5 to improve performance.
The default constructor uses a maximum cache size of 1000. You may specify a different value - say 5000
- by specifying the javax.el.cacheSize property.
java.util.Properties properties = new java.util.Properties();
properties.put("javax.el.cacheSize", "5000");
javax.el.ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl(properties);
Using your own caching mechanism is covered in the Advanced Topics secion.
Type Conversions
Type conversions are performed at several points while evaluating expressions.
Operands are coerced when performing arithmetic or logical operations
Value expression results are coerced to the expected type specified at creation time
For literal method expressions the text is coerced to the type specified at creation time
For non-literal method expressions the last property is coerced to a method name
Composite expression coerce their sub-expressions to strings before concatenating them
All these coercions are done following the same rules. The specification describes these coercion rules
in detail. It supports converting between string, character, boolean, enumeration and number types.
Additionally, the conversion of strings to other types is supported by the use of (Java Beans) property
editors. The EL makes the coercion rules available to client applications via the expression factory method
ExpressionFactoryImpl.coerceToType(Object, Class<?>)
whose return type is Object.
JUEL can be configured to use alternative coercion rules as described in the Advanced Topics secion.
Factory Configuration
The factory may be configured via property files. The mechanism described here is used when an
expression factory is created without specifying properties. The lookup procedure for properties is as
follows:
1. JAVA_HOME/lib/el.properties - If this file contains property javax.el.ExpressionFactory whose value
is de.odysseus.el.ExpressionFactoryImpl, its properties are loaded and taken as default properties.
2. System.getProperties() - if the previous rule (1) did not match and system property
javax.el.ExpressionFactory is set to de.odysseus.el.ExpressionFactoryImpl, the system properties
are taken as default properties.
3. el.properties anywhere on your classpath - These properties may override the default properties from
(1) or (2).
Having this, the following properties are read:
javax.el.cacheSize - expression cache size (default is 1000)
javax.el.methodInvocations - set to true to allow method invocations. Please refer to the Advanced
Topics section for more on this.
javax.el.nullProperties - set to true to resolve null properties. Please refer to the Advanced Topics
section for more on this.
javax.el.varArgs - set to true to allow vararg function/method calls in your expressions.
The factory class also provides constructors which let you explicitly pass your properties. If you just want
to switch between JEE5 and JEE6 style, JUEL provides enum constants to use as profiles.
A value expression is called an lvalue expression if its expression string is an eval expression (#{...} or
${...}) consisting of a single identifier or a nonliteral prefix (function, identifier or nested expression),
followed by a sequence of property operators (. or []). All other value expressions are called non-lvalue
expressions.
For non-lvalue expressions
getType(ELContext) method will always return null.
isReadOnly(ELContext) method will always return true.
setValue(ELContext, Object) method will always throw an exception.
Depending on your application's scenario, there may be several ways to register JUEL as default EL
implementation.
Place the JUEL JARs into directory JRE_HOME/lib/ext. This will make JUEL available globally for all
applications running in that environment.
You may simply drop juel-impl-2.2.x.jar and juel-spi-2.2.x.jar into your /WEB-INF/lib directory.
This will result in using JUEL as EL implementation for that particular web application.
Of course you can also add the jar files to your classpath manually.
Please refer to the section on Factory Configuration on how to configure an expression factory via property
files.
Expression Trees
An expression tree refers to the parsed representation of an expression string. The basic classes and
interfaces related to expression trees are contained in package de.odysseus.el.tree. We won't cover all
the tree related classes here. Rather, we focus on the classes that can be used to provide a customized
tree cache and builder.
1. Tree This class represents a parsed expression string.
2. TreeBuilder General interface containing a single build(String) method. A tree builder must be
thread safe. The default implementation is de.odysseus.el.tree.impl.Builder.
3. TreeCache General interface containing methods get(String) and put(String, Tree). A tree cache
must be thread safe, too. The default implementation is de.odysseus.el.tree.impl.Cache.
4. TreeStore This class just combines a builder and a cache and contains a single get(String) method.
The expression factory uses its tree store to create tree expressions. The factory class provides a
constructor which takes a tree store as an argument.
To enable (disable) expressions using method invocations, you may set property
javax.el.methodInvocations
to true (false).
Method invocations are enabled in profile JEE6 (default) and disabled in JEE5.
Enabling/Disabling VarArgs
The EL specification does not support function calls with variable argument lists. That is, if we
bind String.format(String, Object...) to function str:format, the expression ${str:format('Hey
%s','Joe')} will not work.
To enable (disable) VarArgs in function and method invocations, you may set property
javax.el.varArgs
to true (false).
VarArg invocations are enabled in profile JEE6 (default) and disabled in JEE5.
Tree Expressions
In the basics section, we already presented the TreeValueExpression and TreeMethodExpression classes,
which are used to represent parsed expressions.
Equality
As for all objects, the equals(Object) method is used to test for equality. The specification notes that two
expressions of the same type are equal if and only if they have an identical parsed representation.
This makes clear, that the expression string cannot serve as a sufficient condition for equality testing.
Consider expression string ${foo}. When creating tree expressions from that string using different variable
mappings for foo, these expressions must not be considered equal. Similar, an expression string using
function invocations may be used to create tree expressions with different function mappings. Even worse,
${foo()} and ${bar()} may be equal if foo and bar referred to the same method at creation time.
To handle these requirements, JUEL separates the variable and function bindings from the pure parse tree.
The tree only depends on the expression string and can therefore be reused by all expressions created from
a string. The bindings are then created from the tree, variable mapper and function mapper. Together, the
tree and bindings form the core of a tree expression.
When comparing tree expressions, the trees are structurally compared, ignoring the names of functions
and variables. Instead, the corresponding methods and value expressions bound to them are compared.
Serialization
As required by the specification, all expressions have to be serializable. When serializing a tree expression,
the expression string is serialized, not the tree. On deserialization, the tree is rebuilt from the expression
string.
tell us if '\' is used to escape other characters in literal expressions, too. Consequently, JUEL treats '\'
as escape character only when immediately followed by '${' and '#{'.
Note
Expression \\${ evaluates to '\${', whereas \$ evaluates to '\$' and \\ evaluates to '\\'.
3. In section 1.3, "Literals", the specification states that "Quotes only need to be escaped in a string value
enclosed in the same type of quote". This suggests that, e.g., "You could escape a single quote in a
double-quoted string, but it's not necessary". JUEL guesses that you can't and that the above should
read as "Quotes can only be escaped in a string value enclosed in the same type of quote".
Note
The '\' in expression ${'\"'} doesn't escape the double quote.
4. From section 1.2.1.2, "Eval-expressions as method expressions", it is clear that a single identifier
expression, e.g. ${foo}, can be used as a method expression. However, the specification doesn't tell how
to evaluate such a method expression! Unfortunately, there's no obvious guess, here... JUEL evaluates
method expression ${foo} as follows (let paramTypes be the supplied expected method parameter types,
returnType the expected return type):
Evaluate ${foo} as a value expression
If the result is an instance of java.lang.reflect.Method
if the method is not static, then error.
if the method's parameter types do not match the paramTypes, then error.
if returnType is not null and the method's return type does not match returnType, then error.
If MethodExpression.invoke(...) was called, invoke the found method with the parameters
passed to the invoke method.
If MethodExpression.getMethodInfo(...) was called, construct and return a new MethodInfo
object.
JUEL 2.2.6 and later: If the result is an instance of javax.el.MethodExpression
If MethodExpression.invoke(...) was called, delegate to invoke(...) on the found method
expression.
If MethodExpression.getMethodInfo(...) was called, delegate to getMethodInfo(...) on the
found method expression.
Otherwise, error
5. Section 1.6, "Operators [] and .", describes the semantics of base[property]. If property is null, the
speciification states not to resolve null on base. Rather, null should be returned if getValue(...) has
been called and a PropertyNotFoundException should be thrown else. As a consequence, it would not
be possible to resolve null as a key in a map. We think that this restriction is not really wanted and
more generally, that property == null should not even have been treated as a special case. We have
made an enhancement request, hoping that this will be changed in the future. Because this has been
explicitly stated in the spec, we had to implement it this way. However, JUEL's builder supports a feature
NULL_PROPERTIES to let you resolve null like any other property value.
Note
Assume that map resolves to a java.util.Map. Further assume that feature NULL_PROPERTIES is enabled.
Evaluating ${base[null]} as an rvalue (lvalue) will get (put) the value for key null in that map.
3. JUEL Project
3.1. History of Changes
Implemented changes from JSR 245, maintenance release 2. This replaces JUELs proprietary API for
method invocations. The BeanELResolver class now provides a generic default for method invocations
using reflection. Method invocations are disabled/enabled via the javax.el.methodInvocations
property.