Extending PartialViewContext with a RequestScoped bean in JSF

Viewed 76

In a JSF project, we wrote our own PartialViewContext to listen to some events fired by pages beans:

@RequestScoped
public class OurPartialViewContext extends PartialViewContextWrapper
{
    ...

    // called by cdi
    @SuppressWarnings("unused")
    private void listenForUpdate(@Observes OurRefreshEvent event)
    {
        ...

And we wrote the matching factory, injecting it:

public class OurPartialViewContextFactory extends PartialViewContextFactory
{
    @Inject
    private OurPartialViewContext customPartialViewContext;
    ...

Problem is that in the newest versions of JSF, the empty constructor for PartialViewContextWrapper is deprecated, asking us to use another constructor with the wrapped object in parameter.

Currently, our PartialViewContext needs to be tied to the request scope, in order to be modified during the request by the observed events and to be used by a custom PartialResponseWriter we also wrote.

So our PartialViewContext currently both:

  • must have an empty constructor, as it is a @RequestScoped bean;
  • should not have an empty constructor, as it is deprecated for PartialViewContextWrapper which it inherits from.

How could we find a solution there?

We tried to remove it from the scope and build it in the Factory with a simple new OurPartialViewContext(), but then the @Observes methods are never called.

1 Answers

You are required to pass the wrapped instance into the constructor and to use getWrapped() over all place in delegate methods. Otherwise your application will most probably not work when you install other JSF libraries which also ship with their own PartialViewContext implementation such as OmniFaces and PrimeFaces. You would be effectively completely skipping the functionality of their PartialViewContext implementation. This mistake was previously observed in too many custom implementations of factory-provided classes. Hence the urge to mark the default constructor as @Deprecated so that the developers are forced to use the proper design pattern.

Your specific issue can be solved by simply refactoring the listenForUpdate() method into a fullworthy request scoped CDI bean, which you then inject in the factory who in turn ultimately passes it into the constructor of your PartialViewContext implementation.

Thus, so:

@RequestScoped
public class OurEventObserver {

    public void listenForUpdate(@Observes OurRefreshEvent event) {
        // ...
    }

}
public class OurPartialViewContextFactory extends PartialViewContextFactory {

    @Inject
    private OurEventObserver observer;

    public OurPartialViewContextFactory(PartialViewContextFactory wrapped) {
        super(wrapped);
    }

    @Override
    public PartialViewContext getPartialViewContext(FacesContext context) {
        PartialViewContext wrapped = getWrapped().getPartialViewContext(context);
        return new OurPartialViewContext(wrapped, observer);
    }

}
public class OurPartialViewContext extends PartialViewContextWrapper {

    private OurEventObserver observer;

    public OurPartialViewContext(PartialViewContext wrapped, OurEventObserver observer) {
        super(wrapped);
        this.observer = observer;
    }

    // ...
}

Inside any of the overridden methods of OurPartialViewContext you can simply access the state of the observer, provided that the listenForUpdate() modifies some instance variables representing the state.

Related