jsr372-experts@javaserverfaces-spec-public.java.net

[jsr372-experts] [PROPOSAL] CDI integration for UIComponent instances in JSF 2.3

From: Leonardo Uribe <leonardo.uribe_at_irian.at>
Date: Wed, 21 Dec 2016 19:10:45 -0500

Hi

I have some good news to tell.

After some attempts and using Arjan trick in Omnifaces, and based on the
previous discussion I was able to code a consistent solution using a
CDI centric approach. I made this example work:

@Named
@SessionScoped
public class UserSessionBean implements Serializable
{

    @Inject
    @ResolveComponent(value="updateButton")
    private HtmlCommandButton button;

    // ...
}

This change things drastically, from an scenario where this cannot be
included to other where we could do it once for all.

Please not the trick has two requeriments to be considered succesful:

- The injected object must be the final component class
  (HtmlCommandButton)
- The injected object must be a normal scope proxy that avoids
  inteference with JSF state saving.

And the solution proposed comply with these two requeriments.

I'll describe how the solution works, because I considered this
relevant to be included in JSF 2.3 scope. The code are two CDI extensions:
one for the internal @ViewTransientScope and the other for
@ResolveComponent. The spec changes are just include the annotation:

/**
 *
 */
@Qualifier
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE, ElementType.METHOD,
               ElementType.PARAMETER, ElementType.FIELD})
public @interface ResolveComponent
{
    public String value() default "";
}

To be able to inject different component instances, we need to generate
one producer per component class and search expression.

The code aims to found injections of @ResolveComponent and collect the
component classes:

public <T> void collect(@Observes ProcessManagedBean<T> event)

and then create one dynamic producer per component class and
expression in:

public void afterBean(
    @Observes AfterBeanDiscovery afterBeanDiscovery,
              BeanManager beanManager)

The producer is a class implementing Bean<UIComponent> with to
important things:

- getBeanClass() returns the component class we need to instantiate the
  proxy.
- getScope() returns @ViewTransientScope, which is an internal
  annotation that relies on UIViewRoot transient map used to control
  the lifecycle of the proxy instances.
- The bean should hold the "expression" or "id" that this proxy
  depends, because when the proxy is resolved, the access to the
  InjectionPoint is lost.

The last thing is use JSF 2.3 Search Expression API to define
expressions or id chains that resolve the right component
instance, like it was mentioned previously.

I also discovered that fix "binding" has another problem that prevent
it to be fixed. The problem is proxy classes does not have information
about injection points when they are resolved and you can't add
producer instances at runtime or pass the necessary information to
locate the component to the proxy.

So, in this moment the CDI centric solution is the one that looks
viable to be included officially in JSF 2.3.

WDYT? Could this solution be included in JSF 2.3? What needs to be
done to include it? Do you support this feature?

regards,

Leonardo Uribe