el-next@uel.java.net

Proposal: ELProcessor

From: Kin-man Chung <Kin-Man.Chung_at_Sun.COM>
Date: Thu, 11 Mar 2010 15:41:29 -0800

 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);

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);
    }

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.

2. I am not sure we need or even should allow setter for ExpressionFactory.

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)

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.

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.


Comments?