Expression Language Maintenance Release Summary

The main goal for this MR is to introduce a new syntax for method invocations
in EL.

Background and Motivations

The current EL allows for creation of a MethodExpression, such as

<h:commandButton action="#{trader.buy}" value="buy"/>

Here the method

buy
in the bean
trader
is specified in EL. The method
signature used to identify the method is not specified in EL.
In JSP, that information is specified in a TLD (tag library descriptor). The
actual invocation of the method, and the specification of the actual parameters
for it does not happen in the EL, and can only be done in Java.

Although this is sufficiently useful for specifying JSF actions, it is somewhat
lacking in a general purpose expression language. Ideally, evaluation of EL
expressions should include method calls with parameters. Also, the added
parentheses to a method call provides a nice visual clue to EL. The proposed
MR attempts to address such issues. The above example can then be written as

<h:commandButton action="#{trader.buy('JAVA')}" value="buy"/>

To invoke methods in EL, we can now write

<c:out value="${myBean.foo().bar('abc')}"/>

Modifications to EL spec

EL Operators [] and .

New Syntax:

expr-a[expr-b](<parameters>)
expr-a.identifier-b(<parameters>)

where

<parameters>
is 0 or more expressions, separated by commas.

Semantics for the new syntax:
The expression

expr-a
is evaluated to represent a bean object. The
expression
expr-b
is evaluated and coerced to a string. This string
or
identifier-b
is the name of the method
in
expr-a
.
The
<parameters>
are the actual parameters for the method invocation.

If the expression is a ValueEpxression, then calling its

getValue

causes the method to be invoked. If the expression is a
MethodExpression, then calling its
invoke
causes the
method to be invoked, and the parameters
params
for the
invoke

will be ignored, since those specified in EL will be used.

To be more precise, the behavior for invoking the method specified
in the above EL expression depends on the

ELResolver
in the current
ELContext
, as to be described next. The
BeanELResolver
, also to be
described, provides a default behavior for invoking methods in a
bean object. The user can use a custom EL resolver to make calls
to "methods" that do not exist in a class. This allows
for creation of macros, and can lead to some interesting applications.

MethodExpression

public abstract class MethodExpression extends Expression {
...
    /**
     * If a String literal is specified as the expression, returns the
     * String literal coerced to the expected return type of the method
     * signature. An <code>ELException</code> is thrown if
     * <code>expectedReturnType</code> is void or if the coercion of the String
literal
     * to the <code>expectedReturnType</code> yields an error (see Section "1.18 Type
     * Conversion" of the EL specification).
     *
     * If not a String literal, evaluates the expression
     * relative to the provided context, invokes the method that was
     * found using the supplied parameters, and returns the result of
     * the method invocation.
     *
     * Any parameters passed to this method is ignored if isLiteralText()
     * or isParametersProvided() is true.
     *
     * @param context The context of this evaluation.
     * @param params The parameters to pass to the method, or
     *     <code>null</code> if no parameters.
     * @return the result of the method invocation (<code>null</code> if
     *     the method has a <code>void</code> return type).
     * @throws NullPointerException if context is <code>null</code>
     * @throws PropertyNotFoundException if one of the property
     *     resolutions failed because a specified variable or property
     *     does not exist or is not readable.
     * @throws MethodNotFoundException if no suitable method can be found.
     * @throws ELException if a String literal is specified and
     * expectedReturnType of the MethodExpression is void or if the coercion of
the String literal
     * to the expectedReturnType yields an error (see Section "1.18 Type
     * Conversion").
     * @throws ELException if
     * an exception was thrown while performing
     *     property or variable resolution. The thrown exception must be
     *     included as the cause property of this exception, if
     *     available.  If the exception thrown is an
     *     <code>InvocationTargetException</code>, extract its
     *     <code>cause</code> and pass it to the
     *     <code>ELException</code> constructor.
     */
    public abstract Object invoke(ELContext context, Object[] params);

    /**
     * Return whether this MethodExpression was created with parameters.
     *
     * <p>This method must return <code>true</code> if and only if
     * parameters are specified in the EL, using the
     * expr-a.expr-b(...) syntax.</p>
     *
     * @return <code>true</code> if the MethodExpression was created with
     *    parameters, <code>false</code> otherwise.
     */
    public boolean isParmetersProvided() {
        return false;
    }
...
}

ELResolver

The evaluation of bean properties can be customized with user specified
EL Resolvers. Similarly, the evaluation of method invocations should be
allowed to be customized with user specified EL Resolvers. A new method
is now added to

ELResolver
.

public abstract class ELResolver {
    ...
    /**
     * Attemps to resolve and invoke the given <code>method</code> on the given
     * <code>base</code> object.
     *
     * <p>If this resolver handles the given (base, method) pair,
     * the <code>propertyResolved</code> property of the
     * <code>ELContext</code> object must be set to <code>true</code>
     * by the resolver, before returning. If this property is not
     * <code>true</code> after this method is called, the caller should ignore
     * the return value.</p>
     *
     * <p>A default implementation is provided that returns null so that
     * existing classes that extend ELResolver can continue to function.</p>
     *
     * @param context The context of this evaluation.
     * @param base The bean on which to invoke the method
     * @param method The simple name of the method to invoke.
     *     Will be coerced to a <code>String</code>.
     * @param paramTypes An array of Class objects identifying the
     *     method's formal parameter types, in declared order.
     *     Use an empty array if the method has no parameters.
     *     Can be <code>null</code>, in which case the method's formal
     *     parameter types are assumed to be unknown.
     * @param params The parameters to pass to the method, or
     *     <code>null</code> if no parameters.
     * @return The result of the method invocation (<code>null</code> if
     *     the method has a <code>void</code> return type).
     * @throws MethodNotFoundException if no suitable method can be found.
     * @throws ELException if an exception was thrown while performing
     *     (base, method) resolution.  The thrown exception must be
     *     included as the cause property of this exception, if
     *     available.  If the exception thrown is an
     *     <code>InvocationTargetException</code>, extract its
     *     <code>cause</code> and pass it to the
     *     <code>ELException</code> constructor.
     */
    public Object invoke(ELContext context,
                         Object base,
                         Object method,
                         Class<?>[] paramTypes,
                         Object[] params) {
        return null;
    }
    ...

BeanELResolver

The implementation of the "invoke" method in BeanELResolver provides a
default implementation for resolving the method invocations in a bean.

public class BeanELResolver extends ELResolver {
...
    /**
     * If the base object is not <code>null</code>, invoke the method, with
     * the given parameters on this bean.  The return value from the method
     * is returned.
     *
     * <p>If the base is not <code>null</code>, the
     * <code>propertyResolved</code> property of the <code>ELContext</code>
     * object must be set to <code>true</code> by this resolver, before
     * returning. If this property is not <code>true</code> after this
     * method is called, the caller should ignore the return value.</p>
     *
     * <p>The provided method object will first be coerced to a
     * <code>String</code>.  The methods in the bean is then examined and
     * an attempt will be made to selct one for invocation.  If no suitable
     * can be found, a <code>MethodNotFoundException</code> is thrown.
     *
     * If the given paramTypes is not <code>null</code>, select the method
     * with the given name and parameter types.
     *
     * Else select the method with the given name that has the same number
     * of parameters.  If there are more than one such method, the method
     * selection process is undefined.
     *
     * Else select the method with the given name that takes a variable
     * number of arguments.
     *
     * Note the resolution for overloaded methods will likely be clarified
     * in a future version of the spec.
     *
     * The provide parameters are coerced to the correcponding parameter
     * types of the method, and the method is then invoked.
     *
     * @param context The context of this evaluation.
     * @param base The bean on which to invoke the method
     * @param method The simple name of the method to invoke.
     *     Will be coerced to a <code>String</code>.  If method is
     *     "&lt;init&gt;"or "&lt;clinit&gt;" a MethodNotFoundException is
     *     thrown.
     * @param paramTypes An array of Class objects identifying the
     *     method's formal parameter types, in declared order.
     *     Use an empty array if the method has no parameters.
     *     Can be <code>null</code>, in which case the method's formal
     *     parameter types are assumed to be unknown.
     * @param params The parameters to pass to the method, or
     *     <code>null</code> if no parameters.
     * @return The result of the method invocation (<code>null</code> if
     *     the method has a <code>void</code> return type).
     * @throws MethodNotFoundException if no suitable method can be found.
     * @throws ELException if an exception was thrown while performing
     *     (base, method) resolution.  The thrown exception must be
     *     included as the cause property of this exception, if
     *     available.  If the exception thrown is an
     *     <code>InvocationTargetException</code>, extract its
     *     <code>cause</code> and pass it to the
     *     <code>ELException</code> constructor.
     */

    public Object invoke(ELContext context,
                         Object base,
                         Object method,
                         Class<?>[] paramTypes,
                         Object[] params) {

        if (base == null || method == null) {
            return null;
        }
        Method m = findMethod(base, method.toString(), paramTypes, params);
        Object ret = invokeMethod(m, base, params);
        context.setPropertyResolved(true);
        return ret;
    }
...
}