javacdiweldjava-websocket

WebSocket endpoint and CDI injection: No active contexts for scope RequestScoped


I want to inject a @RequestScoped CDI bean in my Java EE 7 WebSocket endpoint.

However I am getting error WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped.

What am I doing wrong and why it is not possible?

@Named
@RequestScoped
public class Storage {

}

Which I @Inject in the endpoint like this:

@ServerEndpoint("/serverpush")
public class ContratoEndpoint {

    @Inject
    private Storage storage;

}

And I am getting the following stack trace:

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:689)
    at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:90)
    at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:165)
    at org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63)
    at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:83)
    at org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance(ProxyMethodHandler.java:125)

Solution

  • As @John mentioned, RequestContext is not active in WebSocket methods. Instead of using Deltaspike (which is a good option), you can also write your own Interceptor to activate/deactivate weld RequestContext.

    As you are using Wildfly, you can use weld as a provided dependency :

    <dependency>
        <groupId>org.jboss.weld</groupId>
        <artifactId>weld-core</artifactId>
        <version>2.2.12.Final</version>
        <scope>provided</scope>
    </dependency>
    

    Then you can define an InterceptorBinding @RequestContextOperation :

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import javax.interceptor.InterceptorBinding;
    
    @InterceptorBinding
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD, ElementType.TYPE })
    public @interface RequestContextOperation
    {
    
    }
    

    And the corresponding RequestContextInterceptor where we activate/deactivate the RequestContext:

    import javax.inject.Inject;
    import javax.interceptor.AroundInvoke;
    import javax.interceptor.Interceptor;
    import javax.interceptor.InvocationContext;
    
    import org.jboss.weld.context.RequestContext;
    import org.jboss.weld.context.unbound.Unbound;
    
    @Interceptor
    @RequestContextOperation
    public class RequestContextInterceptor {
    
        /** The RequestContext */
        @Inject
        @Unbound
        private RequestContext m_requestContext;
    
        /**
         * 
         * @param p_invocationContext
         * @return
         * @throws Exception
         */
        @AroundInvoke
        public Object activateRequestContext(final InvocationContext p_invocationContext) throws Exception {
            try {
                m_requestContext.activate();
                return p_invocationContext.proceed();
            } finally {
                m_requestContext.invalidate();
                m_requestContext.deactivate();
            }
        }
    }
    

    You can then use the @RequestContextOperation annotation on your class or on a specific method :

    @ServerEndpoint("/serverpush")
    public class ContratoEndpoint {
    
        @Inject
        private Storage storage;
    
        @OnMessage
        @RequestContextOperation
        public String handleMessage(String message){
    
            // Here the @RequestScoped bean is valid thanks to the @RequestContextOperation InterceptorBinding
            storage.yourMethod();
            ....
        }
    
    }