el-next@uel.java.net

Re: Making EL easier to use

From: Pete Muir <pmuir_at_redhat.com>
Date: Thu, 4 Mar 2010 18:51:31 +0000

On 4 Mar 2010, at 01:22, Kin-man Chung wrote:

> Let's take a step backward and talk about the need for getting the
> current ELContext, Expressionfactory, and ELResolver.
>
> 1. We need an instance of ExpressionFactory because it is need to
> create an ELExpression. However, we already have the static method
>
> ExpressionFactory.newInstance(), which can be used to get an
> ExpressionFactory.

Ok great, I wasn't aware this existed!

I still think we need a listener to allow decoration of the ExpressionFactory by extensions (I need to do this in the CDI RI to allow me to listen to the evaluation of EL expression to set up and tear down contexts around the evaluation).

>
> 2. An ELResolver is needed for EL expression parsing and evaluation, but
> it is part of the ELContext, and not something that the user needs to
> worry about, except maybe to insert custom ELResolvers. Also, it is
> not too useful to listen to the creation of every ELResolvers. For
> instance, in JSP, the default ELResolver is a composite ELResolver,
> containing 6 or 7 ELResolvers, and there is no need to listen to each
> of them.

I would argue that we can't know all the weird and wonderful ways in which people will use EL. Firing an "event" for each created listener isn't expensive, and would allow a lot of flexibility.

I'll try to come up with some concrete use cases.

>
> 3. The current EL already has a ELContextListener and a ELContextEvent,
> which can be used to obtain the ELContext when it is first created.
> Granted the registration of such listeners are done not in the EL api,
> but in JSP or JSF. I agree that we can probably use a mechanism
> like what you've described for discovering such listeners, if EL is used
> outside of JSP or JSF. Of course, details need to be worked out.

Yes, I have used this before, but like you say, the registration of a listener is not standard (for EL) right now. It also doesn't allow decoration of the ELContext, as there is no setter method on the event.

>
> 4. The question I am not clear about is what we can assume about the
> bean container that EL works in. Both JSP and JSF has an object
> (javax.servlet.jsp.JspApplicationContext and javax.faces.application.Application resp.) we can go get the instances
> of ExpressionFactory, ELContext, and ELResolver. Should we just
> formalize this and have such an object in EL instead?

Yes, I think we need such a construct. We also need to discuss how a framework or user can obtain this object.

> I know Jason has
> reservations about adding more instance, but we only one such instance
> in an application, and it is really not too bad. An added benefit is
> that such an object can be created earlier in the application life
> cycle, such as in a servlet listener, so that EL can be used there.

Yes, this is a key requirement!

>
>> 2) An easier way to evaluate expressions.
>> Above I have used the current ExpressionFactory/ELContext, however I think there is strong argument for a simple way to evaluate expressions that uses the "default" ELContext and ExpressionFactory I defined above:
>> interface ExpressionEvaluator {
>> <T> T getValue(String valueExpression, Class<T> expectedType);
>> <T> T invoke(String methodExpression, Class<T> expectedReturnType, Object... parameters);
>> }
>
> Just to be clear, the valueExpression and methodExpression are the raw
> EL expression in String, such as "#{mybean.name}", right? If so, I
> am inclined to agree.

Exactly.

>
> Note also that getValue can also be used to invoke a method, if we use
> the syntax #{myBean.myMethod("foo")}.
>
> Can such methods be added to ExpressionFactory instead of a new interface?

Yes, it could. My reasoning for putting it on a new interface was to split it from the "infrastructural" concerns of creating expressions. But I'm not sure that is needed now.

>
>> A quick note here - I believe it is necessary to still provide the expectedType parameter, as EL coerces types. If we were prepared to relax this requirement, and allow a CCE at runtime, you could remove this type parameter and rely on type inference. Unfortunately Java's type inference, especially through reflection, just isn't robust enough to be able to guarantee to always infer the type parameter. (Or perhaps I am wrong, I would be delighted to be shown otherwise!).
>> If we provided such a convenience class, we could add it to the ELFactory --
>> ExpressionEvaluator getExpressionEvaluator();
>> then someone could do in their client code:
>> Foo foo = ELFactory.getExpressionEvaluator().getValue("#{bar.foo}", Foo.class);
>
> Are expectedType used for type coercion after the expression evaluation?
> Can ExpressionFactor.coerceToType() be used instead?

Yes, that seems like a reasonable tradeoff, if someone wants to explicitly coerce the type, then they call:

Bar bar = fact.coerceToType(fact.getValue("#{foo.bar}"));

or, if they are happy to use type inference

Bar bar = fact.getValue("#{foo.bar}");

I would envisage frameworks using the former, and users using the latter on the whole. I would suggest we do a little prototyping of client here, and check that java really can infer the type in enough situations, that people aren't always having to use the (ugly):

fact.<Bar>getValue("#{foo.bar}");

syntax a lot...

(From my memory, it should normally be ok, unless people are using a lot of generics).