el-next@uel.java.net

Re: Proposal: ELProcessor

From: Kin-man Chung <Kin-Man.Chung_at_Sun.COM>
Date: Wed, 17 Mar 2010 14:56:37 -0700

On 03/15/10 15:41, Christoph Beck wrote:
> 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.
>
But I'm not talking about ExpressionFactory, which I agree that we
should not change for this purpose.

>> 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.
>
I agree with you in spirit about information hiding, but the ultimate
question is really this: what is ELProcess? Is it just for
casual users who would only want to evalute EL expressions, but nothing
else, or is it something that allows for full EL customization? You
seems to think it's the former, while I the later.

For instance, if we take away FunctionMapper, then EL functions cannot
be used in the expression. I want to allow the user to be able to
supply her own FunctionMapper, so that EL functions can be used.

Also, we should allow for deferred EL expressions, in that EL parsing
and evaluation do not happen at the same time. Would you disallow this
also?


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

Writing your own ELResolver is definitely non-trivial, not that it is
technically hard, but that it requires a lot of typing and boiler-plate
filling. This is one of the things people have been complaining
about. BeanLoopup and BeanNameELResolver are proposed to solve this
problem.

> BeanLookup could be misleading as this would be used to resolve any
> top-level identifiers. Not sure if it's worth having this.
>
BeanNameLoopup? getTopLevelBeans? Arg, I'm no good with names! :-)

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

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

How are these for? Let me guess. Maybe I can do

     elProcessorBuilder.addVariable("x", "#{myBean}");
     elProcessor.getValue("#{x.foo}"); // returns myBean.foo ?

but how do I use the other two methods?

Also, why are they not also in ELProcessor?

> ELProcessor buildProcessor();
> }

Now who builds ELProcessorBuilder?

>
> interface ELProcessor {
> void setPropertyResolver(PropertyResolver resolver);
What is this?

> void setTypeConverter(TypeConverter converter);
>
> Object getValue(String expression, Class<?> type);
> void setValue(String expression, Object value);

Yes, I think this can be useful.

> Object invoke(String expression, Object[] params, Class<?> returnType);
> }
>
> class DefaultProcessorBuilder implements ELProcessorBuilder {
> // convenience implementation
> }
>
This discussion is useful, but we'll need to get into more details. It
helps to include some examples for using new proposed features, to make
things clearer.

-Kin-man
>
>> ---------------------------------------------------------------------
>> 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
>>
>
>
> ---------------------------------------------------------------------
> 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
>