el-next@uel.java.net

Re: Proposal: ELProcessor

From: Christoph Beck <beck_at_odysseus.de>
Date: Mon, 15 Mar 2010 23:41:04 +0100

Kin-man Chung 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);
>
> 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.
>
So far I'm with you. However, I would try to avoid changing the existing
ELFactory API. The processor stuff should be a new layer on top of the
existing API without cyclic dependencies between the two layers.

> 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);
> }
>
It may be a good idea to identifiy classes from the API that are pretty
"useless" to the end user. The ELProcessor should hide these classes
rather than expose them. More or less "useless" classes are:
- FunctionMapper (the user doesn't want to resolve functions; she wants
to add methods)
- VariableMapper (the user doesn't want to resolve variables; she might
want to add variables, but maybe it's better to have a setVariable(...)
method for this)
- ELContext (the user doesn't want to resolve properties, etc)
- ExpressionFactory (the user evaluates expressions through the
ELProcessor API and the ExpressionFactory requires an ELContext at hand)
- ValueExpression, MethodExpression (same as above)
So, I would suggest that these classes are candidates to _not_ appear in
the ELProcessor interface.

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

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

I'm not sure we need a getter.
>
> 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);
> }
>
We already have ELResolver and ELResolver.getValue(context, null,
property) does exactly this, so why invent a new thing? Also, the name
BeanLookup could be misleading as this would be used to resolve any
top-level identifiers. Not sure if it's worth having this.

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

OK, currently I have no clear idea of how to make resolving
identifiers/properties easier. This definitely needs further discussion.
>
> 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?
>
Here's what I could think of:

interface ELProcessorBuilder {
    void addVariable(String name, String expression);
    void addVariable(String name, Object value);
    void addFunction(String name, Method method);

    ELProcessor buildProcessor();
}

interface ELProcessor {
    void setPropertyResolver(PropertyResolver resolver);
    void setTypeConverter(TypeConverter converter);

    Object getValue(String expression, Class<?> type);
    void setValue(String expression, Object value);
    Object invoke(String expression, Object[] params, Class<?> returnType);
}

class DefaultProcessorBuilder implements ELProcessorBuilder {
  // convenience implementation
}


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: el-next-unsubscribe_at_uel.dev.java.net
> For additional commands, e-mail: el-next-help_at_uel.dev.java.net
>