Search code examples
javascopecdiweldcustom-scope

Use injections in custom scope


Is it possible to use a @SessionScoped bean as a field in a custom scope's Context ?

I am writing a custom scope ("ScreenScoped") with CDI, so that it approximately has the same behavious than CDI's @ViewScoped (I do this because the latter is not WebSphere-compatible). So far my scope acts like as @ApplicationScoped would. I would like to use my @SessionScoped NavigationHandler class, which is called everytime a user clicks a link or button, to determine when my ScreenScoped life-cycle ends. However I do get an error when I try to use an @Injected field.

public class ScreenContext
    implements Context
{
    @Inject
    private NavigationHandler navigationHandler;
    ...
}

A NullPointerException appears because of this @Inject:

16:55:07,492 SEVERE [javax.enterprise.resource.webcontainer.jsf.application] (http-localhost/127.0.0.1:8443-10) Error Rendering View[/page/hello.xhtml]: javax.el.ELException: /page/hello.xhtml @24,58 visible="#{helloController.popupshowed}": java.lang.NullPointerException

...

Caused by: java.lang.NullPointerException
    at com.mypackage.scope.screenscope.ScreenContext.get(ScreenContext.java:38) [myproject.jar:]

Line 38 is the first time I call the injected field:

System.out.println("Navigation is used: " + navigationHandler.getUserId());

Solution

  • You can't inject a Bean in Context. You need to use the BeanManager to access the NavigationHandler bean.

    Your context is registered via CDI Extension observing AfterBeanDiscovery event of CDI lifecycle. That's here that you pass the BeanManager to the context :

    public void afterBeanDiscovery (@Observes final AfterBeanDiscovery event, final BeanManager beanManager)
    {
        event.addContext(new ScreenContext(beanManager));
    }
    

    And then in your ScreenContext implementation you can get the NavigationHandler bean (in myMethod below) using the BeanManager:

    public class ScreenContext implements Context
    {
    
       private final BeanManager m_beanManager;
    
       public ScreenContext(final BeanManager p_BeanManager)
       {
          m_beanManager = p_BeanManager;
       }
    
       public void myMethod()
       {
          NavigationHandler NavigationHandlerBean = getNavigationHandler();
          ...
          ...
       }
    
       private NavigationHandler getNavigationHandler()
       {
          final Set<Bean<?>> beans = m_beanManager.getBeans(NavigationHandler.class);
          final Bean<?> bean = m_beanManager.resolve(beans);
    
          return (NavigationHandler) m_beanManager.getReference(bean, NavigationHandler.class, m_beanManager.createCreationalContext(bean));
       }