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
in the bean
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
is 0 or more expressions, separated by commas. Semantics for the new syntax: The expression
is evaluated to represent a bean object. The expression
is evaluated and coerced to a string. This string or
is the name of the method in
. The
are the actual parameters for the method invocation. If the expression is a ValueEpxression, then calling its
causes the method to be invoked. If the expression is a MethodExpression, then calling its
causes the method to be invoked, and the parameters
for the
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
in the current
, as to be described next. The
, 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
.
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
* "<init>"or "<clinit>" 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;
}
...
}
|