el-next@uel.java.net

Re: Proposal: ELProcessor

From: Jason Porter <lightguard.jp_at_gmail.com>
Date: Mon, 15 Mar 2010 15:40:21 -0700

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.

> 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.

> 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.

-- 
Jason Porter
Real Programmers think better when playing Adventure or Rogue.
PGP key id: 926CCFF5
PGP fingerprint: 64C2 C078 13A9 5B23 7738 F7E5 1046 C39B 926C CFF5
PGP key available at: keyserver.net, pgp.mit.edu