el-next@uel.java.net

Re: Making EL easier to use

From: Kin-man Chung <Kin-Man.Chung_at_Sun.COM>
Date: Wed, 03 Mar 2010 17:22:00 -0800

Sorry for the late reply. :-)

On 02/20/10 03:53, Pete Muir wrote:
> On 19 Feb 2010, at 23:35, Jason Porter wrote:
>
>>>>> We can do something similar to bootstrap API in Bean Validation, but it
>>>>> still remains a fact that components of a ELContext, such as ELResolver
>>>>> need to be specified somehow. We can, for instance, have a default
>>>>> ELResolver that includes the common ELResolvers, such a BeanELResolver
>>>>> etc, but user must still need to be able to use a map to resolve his/her
>>>>> bean. For instance, in the expression #{foo.bar.baz}, s/he can rely on
>>>>> the default ELResolver to resolve the operator ".", but s/he must
>>>>> still tell us how foo should be resolved.
>>>> Bean registration with the resolver is something I was thinking about
>>>> the other day because Ed had mentioned how nice it would be to use EL
>>>> everywhere (+1 from me as well). Would we want resolution for all
>>>> classes, or only managed classes? If it were all classes then what was
>>>> done with CDI and the default name would seem to work (unless you had
>>>> classes with the same name but in a different package, then we run
>>>> into complications).
>>> I am not familiar with how CDI's default name works. Can you give me
>>> a brief description? Thanks.
>> In CDI the default @Named is the simple name of the class with the
>> first letter lower case.
>
> IMO we should concentrate less on bean registration and more on how you obtain the ELContext which has the relevant ELResolvers registered.
>
Alright, let's talk about that first.

> From my experience integrating Seam, CDI, jBPM etc. into EL, the ELResolver is an excellent SPI for the bean container to use. Combine this with using decoration of the various EL interfaces, and you can pretty much do anything you need. I'm interested to hear from other people who have integrated EL into a bean container as to whether this is their experience too.
>
> What is really hard is to obtain the ELContext with the resolvers registered, and the various decorations applied. However, this is more about the platform in which EL is used.
>
>>> Perhaps a discussion on what classes are
>>>> available for resolution is needed. If it's only managed classes then
>>>> we're kind of back to square one unless there's a way to obtain all
>>>> the managed classes from the various environments.
>>>>
>>> But even with managed beans, we can still provide simpler APIs for
>>> plugging them in, see below.
>>>
>>>>> I already have ExpressionFactory. Can we combine it with ELContext,
>>>>> to make things simpler?
>>>> I'm all for simplifying the API as much as possible. One of the
>>>> things we could do to help maintain backwards compatibility and still
>>>> make it simple for the use case outside of the view layer is to
>>>> provide a shortcut entry point. The shortcut would have to use a
>>>> context that's already been started and have a valid ELResolver but
>>>> something as simple as ExpressionFactory.parse(String) or possibly
>>>> ExpressionFactory.parse(String, returnType) would probably go a long
>>>> way into making EL easier to use outside of a managed context.
>>>>
>>> Right.
>>>
>>> For managed beans, we can provide a simpler way for resolving them
>>> without the need for a ELResolver. Something along the following
>>> lines.
>>>
>>> Map myBeans; // User managed beans;
>>> ExpressFactory fact = ExpressFactory.newInstance();
>>> // Plug a collectons of beans into the default ELResolver
>>> fact.addBeans(myBeans);
>>> // Or to add a bean
>>> fact.addBean("emplyee100", new Employee("Joe Smith"));
>>> String result = fact.evaluate("${employee100.name}");
>>>
>>> This would really simplify things, especially for common use cases.
>
> Just to establish some terminology early on, are you referring to managed beans as defined by the EE spec? Or more to java objects?
>
Java objects managed by the user.

> Have we got a lot of feedback that people want to use EL with java objects without any bean container?
>
Some, but not a lot. Agreed though that beans in bean container should
be our initial concern.

>> It's not bad, and the only thing I don't like about it is having to
>> add the instances, but you'd either have to instantiate each class
>> when it's resolved and toss that instance into a cache or something or
>> come up with some way to 1) find the instances of the resolved class
>> and 2) figure out how to differentiate which instance the user wants
>> to use, neither of which (AFAIK) are easy to do.
>>
>> If we create instances of the class upon resolution then we'd need to
>> control the lifetime of those classes. Without a context duration for
>> the class that's kind of arbitrary, especially in an SE environment (I
>> guess it could be the lifetime of a thread and that cache could be
>
> Jason, I think we are making the same point, just with different words - you are saying that such an interface as above isn't useful in a world dominated by bean containers.
>
> Top of my list for making EL easier to use would be:
>
> 1) Provide a good way to get hold of the ELContext for the current environment. IMO we should leave exactly what ELContext is provided here up to the environment (for example the EE spec might want to define this, as might the Servlet spec OR a framework such as Weld in Java SE might define what this means for that environment). The environment could either return an existing ELContext, or could bootstrap a new one - up to the environment.
>
> My first thoughts on this would be to provide a Factory class inside the API:
>
> class ELFactory {
>
> public ELContext getCurrent() {
> // load the ELContext
> }
>
> public ExpressionFactory() {
> // load the expression factory
> }
>
> }
>
> However I don't really like this that much, as it either requires using a service loader (problem with multiple implementations on the classpath) or implementation-specific APIs.
>
> 1a) Provide a nice way to listen to ELContext creation, register resolvers, and decorate stuff as needed. To do this, I think we can take a leaf out of the design of portable extensions for CDI (http://docs.jboss.org/cdi/spec/1.0/html/spi.html#initevents), and allow the user to register "extensions" that will modify the expression factory, the resolvers and the elcontext as they are created:
>
> interface ProcessExpressionFactory {
>
> public ExpressionFactory getExpressionFactory();
>
> public void setExpressionFactory(ExpressionFactory expressionFactory);
>
> }
>
> interface ProcessELContext {
>
> public ELContext getELContext();
>
> public void setELContext(ELContext ctx);
>
> }
>
> interface ProcessELResolver {
>
> public ELResolver getELResolver();
>
> public void setELResolver(ELResolver resolver);
>
> }
>
> An extension is defined liked:
>
> class WeldExtension implements Extension {
>
> void observe(ProcessELResolver resolver) {
> // work
> }
>
> }
>
> I would suggest a simple algorithm to find the relevant methods to call - each method is checked, and if the method takes a single parameter of type {ProcessExpressionFactory, ProcessELContext, ProcessELResolver}, it is registered as a callback for that processing that extension. Order in which the methods and extensions are called is undefined.
>
> interface Extension {}
>
> The Extension interface is just defined to allow a service provider to be registered:
>
> META-INF/services/javax.el.Extension:
> org.jboss.weld.el.WeldExtension
>
> This now allows an easy way for a user to obtain the ELContext and ExpressionFactory, whilst maintaining the flexibility EL has for the bean container.
>

Let's take a step backward and talk about the need for getting the
current ELContext, Expressionfactory, and ELResolver.

1. We need an instance of ExpressionFactory because it is need to
create an ELExpression. However, we already have the static method

ExpressionFactory.newInstance(), which can be used to get an
ExpressionFactory.

2. An ELResolver is needed for EL expression parsing and evaluation, but
it is part of the ELContext, and not something that the user needs to
worry about, except maybe to insert custom ELResolvers. Also, it is
not too useful to listen to the creation of every ELResolvers. For
instance, in JSP, the default ELResolver is a composite ELResolver,
containing 6 or 7 ELResolvers, and there is no need to listen to each
of them.

3. The current EL already has a ELContextListener and a ELContextEvent,
which can be used to obtain the ELContext when it is first created.
Granted the registration of such listeners are done not in the EL api,
but in JSP or JSF. I agree that we can probably use a mechanism
like what you've described for discovering such listeners, if EL is used
outside of JSP or JSF. Of course, details need to be worked out.

4. The question I am not clear about is what we can assume about the
bean container that EL works in. Both JSP and JSF has an object
(javax.servlet.jsp.JspApplicationContext and
javax.faces.application.Application resp.) we can go get the instances
of ExpressionFactory, ELContext, and ELResolver. Should we just
formalize this and have such an object in EL instead? I know Jason has
reservations about adding more instance, but we only one such instance
in an application, and it is really not too bad. An added benefit is
that such an object can be created earlier in the application life
cycle, such as in a servlet listener, so that EL can be used there.

> 2) An easier way to evaluate expressions.
>
> Above I have used the current ExpressionFactory/ELContext, however I think there is strong argument for a simple way to evaluate expressions that uses the "default" ELContext and ExpressionFactory I defined above:
>
> interface ExpressionEvaluator {
>
> <T> T getValue(String valueExpression, Class<T> expectedType);
>
> <T> T invoke(String methodExpression, Class<T> expectedReturnType, Object... parameters);
>
> }
>

Just to be clear, the valueExpression and methodExpression are the raw
EL expression in String, such as "#{mybean.name}", right? If so, I
am inclined to agree.

Note also that getValue can also be used to invoke a method, if we use
the syntax #{myBean.myMethod("foo")}.

Can such methods be added to ExpressionFactory instead of a new interface?

> A quick note here - I believe it is necessary to still provide the expectedType parameter, as EL coerces types. If we were prepared to relax this requirement, and allow a CCE at runtime, you could remove this type parameter and rely on type inference. Unfortunately Java's type inference, especially through reflection, just isn't robust enough to be able to guarantee to always infer the type parameter. (Or perhaps I am wrong, I would be delighted to be shown otherwise!).
>
> If we provided such a convenience class, we could add it to the ELFactory --
>
> ExpressionEvaluator getExpressionEvaluator();
>
> then someone could do in their client code:
>
> Foo foo = ELFactory.getExpressionEvaluator().getValue("#{bar.foo}", Foo.class);
>

Are expectedType used for type coercion after the expression evaluation?
Can ExpressionFactor.coerceToType() be used instead?

-Kin-man

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