el-next@uel.java.net

Re: Proposal: ELProcessor

From: Kin-man Chung <Kin-Man.Chung_at_Sun.COM>
Date: Wed, 17 Mar 2010 11:04:15 -0700

On 03/15/10 15:40, Jason Porter wrote:
> On Thu, Mar 11, 2010 at 16:41, Kin-man Chung <Kin-Man.Chung_at_sun.com> wrote:
>> From the discussions we;ve had so far, it has been clear that we need a
>> simpler API for getting an ExpressionFactory and ELContext, outside of JSP
>> or
>> JSF. We'll need an object to setup a defualt for ELContext
>> ELResolvers, etc. I'll call it ELProcessor, because it also can be
>> used to simplify EL parsing and evaluations.
>>
>> Here's my tentative proposal. I'm trying to keep it simple and use
>> a design that seems to fit in the current EL. Therefore there are no
>> CDI extensions, or listeners. :-)
>>
>> Use example:
>>
>> ELProcessor elp = new ELProcessor();
>> boolean t = elp.getValue("${'2 > 1'}", Boolean.class);
>
> I really like how simple that is. +1
>
>
>> An alternative is to make ELProcessor an interface instead of a
>> concrete class, and use a ELFactory to create an instance of
>> ELProcessor, as suggested by Pete in one of his mails.
>>
>> interface ELProcessor {
>>
>> ExpressionFactory getExpressionFactory();
>> void setExpressionFactory(ExpressionFactory ef); // Note 2
>>
>> ELParseContext getELParseContext();
>> void setELParseContext(ELParseContext elpc);
>>
>> ELEvaluationContext getELEvaluationContext();
>> void setELEvaluationContext(ELEvaluationContext elec);
>>
>> ELResolver getELResolver();
>> void setELResolver(ELResolver elr);
>>
>> // May also need getter/setters for function mapper and variables
>>
>> // Add an user defined ELResolver
>> void addELResolver(ELResolver elr);
>>
>> // See note 4 below for BeanLookup
>> void setBeanLookup(BeanLookup blu);
>>
>> ValueExpression createValueExpression(String expression,
>> Class expectedType);
>> Object getValue(String expression);
>> Object getValue(String expression,
>> expectedReturnType);
>> MethodExpression createMethodExpression(String expression,
>> Class expectedReturnType,
>> Class[] extectedParamTypes);
>> Object invoke(String expression, Object[] params, Class returnType);
>> }
>
> This helps keep the API fairly small (at least the currently discussed
> changes). I like having the interface, perhaps having a base abstract
> class or something the provides the basic functionality so creating
> your own ELProcessor isn't as involved. Such things in a base class
> would be create*Expression as well as invoke. If we don't feel like we
> gain a whole lot with that approach I'm fine without it, I don't think
> this is something a typical user would be doing for every application
> they create.
>
If we use an interface, we'll still need to provide a default
implementation. Then we are faced with the problem of how it can be
loaded without the user knowing about it. This is precisely the
problem CDI tries to solve. I am not ready to introduce injections
in EL, yet.

Let me think about this more.

>> Notes:
>>
>> 1. The life time of ELProcessor instances should be controlled by
>> the application, not EL. The application can use a single instance
>> of ELProcessor for the entire application, or use an instance of
>> shorter life. For instance, JSP and JSF applications can use one
>> for application, and one in each of the servlet listeners.
>
> I completely agree with this. How the client ends up using EL, and
> it's lifecycle really doesn't need to be in spec. I think this would
> also allow more flexibility in how EL is used and could even give it
> some application context and life time, both good things.
>
>> 2. I am not sure we need or even should allow setter for ExpressionFactory.
>
> Not really sure this buys us anything either.
>
>> 3. Objects in ELProcessor has the following defaults:
>>
>> a. ExpressionFactory: singleton from ExpressionFactory.newInstance().
>> b. ELParseContext: with null function mapper and variable mapper.
>> c. ELEvalutaionContext: with the default ELResolver, below
>> d. ELResolver: a composite ELResolver, consists of:
>> MapELResolver,
>> ResourceBundleELResolver,
>> ListELResolver,
>> ArrayELResolver,
>> BeanELResolver,
>> BeanNameELResolver (proposed, see below)
>
> +1
>
>> 4. Expressions such as #{foo.bar} will need a way to resolve "foo". I think
>> bean discovery is the job of the bean containers, and not EL. Therefore I
>> propose the following:
>>
>> interface BeanLookup {
>> Object getBean(String name);
>> }
>>
>> The method ELProcessor.setBeanLookup() let the application or container
>> specify how to lookup a bean with the registered name.
>>
>> As an example, in the servlet listener, EL can be used to evaluate
>> expressions involving an request scope object "foo":
>>
>> ELProcessor elp;
>> elp.setBeanLookup(new BeanLookup {
>> Object getBean(String name) {
>> return request.getAttribute(name);
>> }
>> });
>> elp.getValue("#{foo.bar}");
>>
>> BeanNameELResolver is a (proposed) ELResolver that uses the BeanLookup
>> instance to resolve beans with names.
>
> I like it, it gives us a nice, clean way to add additional resolvers
> for simple cases. If we have this, is there a need for the full
> ELResolver (besides backwards compatibility)? If we're going to have
> a BeanNameELResolver, would it make sense to have a MethodELResolver,
> seems like a logic split of the current ELResolver.
>
What I am doing here is to provide a simple API for the most common use
case: that of finding the top level beans. We'll still need ELResolver
not only for backwards compatibility, but also to allow full
customization of EL property resolution. For instance, you can write
an ELResolver that returns a value of 0xff0000 for the expression
#{color.red}. If you want this, you'll still need to write you own
ELResolver and plug it into EL. I still want to make doing such things
possible.

The default ELBeanResolver already handles method calls for the methods
that it can find (via reflection) in a beans. If you want an EL method
expression, such as #{bean.doSuch()}, to do more than calling the method
in a bean (i.e. to make doSuch do something even if the method doSuch
does not exist), you'll need to write your own ELResolver. But I don't
this is common.

>> 5. Of course we can also have different ELProcessors with slightly different
>> defaults. The question is whether we need to specify them in the
>> EL api, or just left as an implementation for the bean container.
>> Ideally, the container should generate the appropiate ELProcessor
>> depending
>> on life cycle events of the applications, but that'll require tight
>> coupling between EL and the container.
>
> Seems to me if there could be a broad usage of any other ELProcessor
> then it would be appropriate to add them to the spec. What those
> ELProcessors might be I'm not sure.
>
As an example, we can have a ELProcessor for a servlet filter, with a
default ELResolver that picks up beans in the request scope.

-Kin-man