5 Replies Latest reply on Jun 17, 2014 7:43 AM by cvarona

    Unable to inject ServletContext in ApplicationScoped bean

    cvarona

      Hi,

       

      I have a ServletContextListener like this in my web application:

       

      @WebListener
      public class Initialization implements ServletContextListener {
      
          @Inject
          @Any
          private Event<RtConf> _rtConfEvent;
      
          public void contextInitialized( ServletContextEvent sce ) {
      
                // rtConf retrieval from db, nothing worth mentioning
                RtConf rtConf = ... ;
                _rtConfEvent.fire( rtConf );
          }
          ...
      }
      

       

      I'm testing this in jetty 9.2 and Glassfish 4; _rtConfEvent won't get injected in jetty 9.1, even if the console output reads like this:

       

      2014-06-16 18:02:09.739 INFO  - jetty-9.1.0.v20131115

      2014-06-16 18:02:10.909 INFO  - Initialize Weld using ServletContainerInitializer

      ...

      2014-06-16 18:02:11.051 INFO  - WELD-000900: 2.2.1 (Final)

      2014-06-16 18:02:11.203 INFO  - WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.

      2014-06-16 18:02:11.260 WARN  - WELD-001700: Interceptor annotation class javax.ejb.PostActivate not found, interception based on it is not enabled

      2014-06-16 18:02:11.260 WARN  - WELD-001700: Interceptor annotation class javax.ejb.PrePassivate not found, interception based on it is not enabled

      2014-06-16 18:02:11.347 INFO  - Jetty 7.2+ detected, CDI injection will be available in Listeners, Servlets and Filters.

       

      These rt conf events are meant to make a certain configuration available to several application scoped components. In other words, I leverage the CDI event propagation mechanism in order to both create application wide components and transfer them the configuration. These components are beans like this:

       

      @ApplicationScoped
      public class RtConfAwareImpl implements RtConfAware {
      
          @Inject
          private ServletContext _sctx;
          public static final Collection<ConfigAttribute> PERMIT_ALL = Collections.<ConfigAttribute>singletonList( new SecurityConfig( "permitAll" ) );
          public static final Collection<ConfigAttribute> AUTHENTICATED = Collections.<ConfigAttribute>singletonList( new SecurityConfig( "authenticated" ) );
      
          public void onRtConfActivation( @Observes RtConf pConf ) {
              // Do something with the provided conf and the injected servlet context
          }
      }
      

       

      This servlet context injection fails:

      18:30:46.987 [main] ERROR RT.COMP1 - WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped

      org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped

          at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:687) ~[weld-servlet-2.2.1.Final.jar:2014-05-09 12:21]

          at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:79) ~[weld-servlet-2.2.1.Final.jar:2014-05-09 12:21]

          at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:99) ~[weld-servlet-2.2.1.Final.jar:2014-05-09 12:21]

          at org.jboss.weld.proxies.ServletContext$1191556859$Proxy$_$$_WeldClientProxy.getAttribute(Unknown Source) ~[weld-servlet-2.2.1.Final.jar:?]

      By debugging I can see the event gets notified within a Request scope. I think that's quite surprising, for the RequestScoped annotation javadoc states that

      The request scope is active:

      • during the service() method of any servlet in the web application, during the doFilter() method of any servlet filter and when the container calls any ServletRequestListener or AsyncListener,
      • during any Java EE web service invocation,
      • during any remote method invocation of any EJB, during any asynchronous method invocation of any EJB, during any call to an EJB timeout method and during message delivery to any EJB message-driven bean, and
      • during any message delivery to a MessageListener for a JMS topic or queue obtained from the Java EE component environment.

      According to the JEE7 tutorial

      Predefined beans are injected with dependent scope and the predefined default qualifier @Default

      Since the event is originating at a Listener and is targeting an ApplicationScoped bean I would expect the ServletContext to be smoothly injected. It isn't, and quite to my surprise I've found by debugging that Weld's ServletContextBean#getScope is defined like this:

       

           @Override
           public Class<? extends Annotation> getScope() {
               return RequestScoped.class;
           }
      

      which might or might not exert some influence on what's happening to me.

       

      Am I doing something wrong? Is it ok to expect the injection works at that point? I've googled quite a bit and have been unable to find anybody attempting (and failing) to do something like this. Any help will be much appreciated.

        • 1. Re: Unable to inject ServletContext in ApplicationScoped bean
          mkouba

          Hi Cesar,

          first, the request context is active when the container calls any ServletRequestListener, not a ServletContextListener. Also the CDI spec does not define a scope for a ServletContext bean (see also 3.8. Additional built-in beans) - so it's implementation-specific. As to the Java EE 7 tutorial I'm not so sure what "predefined beans" really stands for. If it stands for all built-in beans it's probably wrong.

           

          I confirm that listener injection does not work on Jetty 9.1. I will create a new issue.


          Now back to your problem. I wouldn't inject a ServletContext but instead create a composite event payload for RtConf and ServletContext (available from ServletContextEvent).

          1 of 1 people found this helpful
          • 2. Re: Unable to inject ServletContext in ApplicationScoped bean
            mkouba

            I've just verified that listener injection works on Jetty 9.1.1.v20140108 and later. Could you confirm my findings?

            • 3. Re: Re: Unable to inject ServletContext in ApplicationScoped bean
              cvarona

              Hi Martin,

               

              I've debugged again and you're right, I had misunderstood how it actually works: it's not that a request scoped context gets created during the servlet context initialization or event notification, it's that Weld's ServletContextBean requires a request scoped context that of course cannot be found anywhere.

               

              I personally think it's quite striking that I cannot inject a so application-wide object like ServletContext unless a request scoped context is present. As for the predefined beans the JEE7 tutorial speaks of are those same built-in-beans in the spec.

               

              Regarding the (obvious) solution you propose, it's something I considered when I first encounted this problem, but which I'd like to avoid if possible. I've tried the following:

               

              1. I've modificed my servlet context listener like this:

               

              public class Initialization implements ServletContextListener {
              
                  @Inject
                  @Any
                  private Event<RtConf> _rtConfEvent;
              
                  @Inject
                  @Any
                  private Event<ServletContext> _servletContextPropagator;
              
                  public void contextInitialized( ServletContextEvent sce ) {
                    _servletContextPropagator.fire( sce.getServletContext() );
                    _rtConfEvent.fire( rtConf );
                    ...
              

               

              2. And I have added this method to the bean that needs to get the ServletContext injected:

               

                   ...
                   private ServletContext _sctx;
                   public void onServletContextActivation( @Observes ServletContext pServletContext ) {
                      _sctx = pServletContext;
                   }
                   ...
              

              Quite to my surprise two events with ServletContext payload are generated and notified the same observing instance. The first one is generated at Weld's HttpContextLifecycle, the second one originates at my _servletContextPropagator.fire invocation.

               

              Whether the first event propagation sticks to the cdi spec and is thus something I can count on in every cdi-enabled container I cannot tell.

               

              Regards

              • 4. Re: Re: Unable to inject ServletContext in ApplicationScoped bean
                mkouba
                • 5. Re: Re: Unable to inject ServletContext in ApplicationScoped bean
                  cvarona

                  Thanks a lot for the info. Since @Initialized(ApplicationScoped.class) qualified events triggering is part of CDI I'll rely on this and remove my manual event propagation.

                   

                  As for the jetty version you mention I've been unable to find it and give it a try. I'm preparing my app to run on a standalone jetty 9.1.5.v20140505 (so far I've been using the nice gretty plugin), I'll let you know when I got it right. Anyway, there would be nothing unusual in 9.1.1.v20140108 displaying some minor tweak that makes Event injectable (probably again).

                   

                  Regards