8 Replies Latest reply on Jan 9, 2013 8:36 PM by jervisliu

    How to inject into a Servlet?

    jervisliu

      Hi, I've had following configuration in my web.xml, but I still got null for the injection.

       

        <servlet>
          <servlet-name>ErraiServlet</servlet-name>
          <servlet-class>org.jboss.errai.bus.server.servlet.DefaultBlockingServlet</servlet-class>
          <init-param>
            <param-name>service-locator</param-name>
            <param-value>org.jboss.errai.cdi.server.CDIServiceLocator</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
        </servlet>
      
        <servlet-mapping>
          <servlet-name>ErraiServlet</servlet-name>
          <url-pattern>*.erraiBus</url-pattern>
        </servlet-mapping> 
          
        <listener>
          <listener-class>org.jboss.errai.container.CDIServletStateListener</listener-class>
        </listener>
      
        <resource-env-ref>
          <description>Object factory for the CDI Bean Manager</description>
          <resource-env-ref-name>BeanManager</resource-env-ref-name>
          <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
        </resource-env-ref>
      
        <resource-env-ref>
          <description>Object factory for the Errai Service</description>
          <resource-env-ref-name>ErraiService</resource-env-ref-name>
          <resource-env-ref-type>org.jboss.errai.bus.server.service.ErraiService</resource-env-ref-type>
        </resource-env-ref>
       
      

       

       

       

      Check https://github.com/droolsjbpm/guvnor/blob/guvnor-ng/guvnor-ng/guvnor-showcase/guvnor-webapp/src/main/webapp/WEB-INF/web.xml for a complete version of web.xml. And below is my Servlet (see https://github.com/droolsjbpm/guvnor/blob/guvnor-ng/guvnor-ng/guvnor-editors/guvnor-m2repo-editor/guvnor-m2repo-editor-backend/src/main/java/org/kie/guvnor/m2repo/backend/server/FileServlet.java)

       

       

      public class FileServlet extends HttpServlet {
          @Inject
          private M2RepoService m2RepoService;
      }
      

       

       

      Am I still missing anything? Thanks.

       

      Cheers,

      Jervis

        • 1. Re: How to inject into a Servlet?
          jfuerth

          Hi Jervis,

           

          I have just tried the following:

           

          1. Created a new cdi-quickstart project based on Errai 2.2.0.CR2

          2. Added the following class:

           

          package com.foo.erraicdi.server;

           

          import java.io.IOException;

          import javax.inject.Inject;

          import javax.servlet.ServletException;

          import javax.servlet.http.HttpServlet;

          import javax.servlet.http.HttpServletRequest;

          import javax.servlet.http.HttpServletResponse;

           

          public class InjectableServlet extends HttpServlet {

            @Inject SimpleCDIService simpleCdiService;

            @Override

            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            resp.setContentType("text/plain");

            resp.getWriter().append("The injected service is " + simpleCdiService);

            resp.getWriter().flush();

            }

          }

           

          3. Added the following to src/main/jetty/WEB-INF/web.xml and src/main/jboss7/WEB-INF/web.xml

          4. Executed "mvn clean package -Pjboss7"

          5. Deployed the generated webapp at target/errai-cdi-with-servlet-injection.war in JBoss AS 7.1.1.Final

          6. Went to http://127.0.0.1:8080/injectableServlet

           

          And it was good. I got the message "The injected service is com.foo.erraicdi.server.SimpleCDIService@6755b6."

           

          Then I tried in the embedded Jetty 6 that comes with GWT Dev Mode:

           

          7. Executed "mvn clean package -Pjetty"

          8. Executed "mvn gwt:run"

          9. Visited http://127.0.0.1:8888/injectableServlet

           

          And the injected value was missing. I got the message "The injected service is null."

           

          So it seems that Weld was able to inject into servlets under AS7 but not under Jetty 6. There may be a weld configuration problem in the CDI quickstart project. I'm not sure. Are you experiencing this problem in the Dev Mode embedded Jetty server or in AS7?

           

          Cheers,

          Jonathan

          • 2. Re: How to inject into a Servlet?
            jfuerth

            Update:

             

            I tried switching back to the standard weld-servlet listener (org.jboss.weld.environment.servlet.Listener) in web.xml, and now injection into the servlet is working properly in GWT Dev Mode's Jetty server.

             

            It seems the problem of @Inject not working in the servlet was indeed due to a bug in Errai.

             

            If your app works properly with org.jboss.weld.environment.servlet.Listener instead of org.jboss.errai.container.CDIServletStateListener, then this workaround should fix things for you.

             

            I've filed ERRAI-462 to track this issue.

             

            -Jonathan

            • 3. Re: How to inject into a Servlet?
              jervisliu

              Jonathan, thanks for the reply. I noticed that we have following comment in web.xml regarding CDIServletStateListener

               

                <listener>

                  <!--

                  NOTE: The orig servlet listener has been patched,

                  until a solution to the bean validation problems in hosted mode is found.

               

                  <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>

              -->   

               

                 <listener-class>org.jboss.errai.container.CDIServletStateListener</listener-class>

                </listener>

               

              So what is this "bean validation problems" that org.jboss.errai.container.CDIServletStateListener patched for? The reason why I am asking is because if I switch back to org.jboss.weld.environment.servlet.Listener, Guvnor failed to start using mvn gwt:run, and I found following errors in the log, and they seemed to be related to bean validation:

               

              00:00:02.013 [WARN] Failed startup of context org.jboss.errai.cdi.server.gwt.JettyLauncher$WebAppContextWithReload@6c6c5b8d{/,d:\svn\drools\guvnor-latest\guvnor-ng\guvnor-showcase\guvnor-webapp\target\guvnor-webapp-6.0.0-SNAPSHOT} org.jboss.weld.exceptions.DeploymentException: Exception List with 2 exceptions:
              Exception 0 :
              org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001436 Normal scoped bean class org.kie.guvnor.guided.rule.backend.server.util.BRDRTPersistence is not proxyable because it has a private no-args constructor private org.kie.guvnor.guided.rule.backend.server.util.BRDRTPersistence() - Managed Bean [class org.kie.guvnor.guided.rule.backend.server.util.BRDRTPersistence] with qualifiers [@Any @Default].
                   at org.jboss.weld.util.Proxies.getUnproxyableClassException(Proxies.java:224)
                   at org.jboss.weld.util.Proxies.getUnproxyableTypeException(Proxies.java:180)
                   at org.jboss.weld.util.Proxies.getUnproxyableTypesExceptionInt(Proxies.java:195)
                   at org.jboss.weld.util.Proxies.getUnproxyableTypesException(Proxies.java:169)
                   at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:147)
                   at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:163)
                   at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:382)
                   at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
                   at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:380)
                   at org.jboss.weld.environment.servlet.Listener.contextInitialized(Listener.java:182)
                   at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:543)
                   at org.mortbay.jetty.servlet.Context.startContext(Context.java:136)
                   at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1220)
                   at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:513)
                   at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:448)
                   at org.jboss.errai.cdi.server.gwt.JettyLauncher$WebAppContextWithReload.doStart(JettyLauncher.java:486)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
                   at org.mortbay.jetty.handler.RequestLogHandler.doStart(RequestLogHandler.java:115)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
                   at org.mortbay.jetty.Server.doStart(Server.java:222)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.jboss.errai.cdi.server.gwt.JettyLauncher.start(JettyLauncher.java:692)
                   at com.google.gwt.dev.DevMode.doStartUpServer(DevMode.java:509)
                   at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1093)
                   at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:836)
                   at com.google.gwt.dev.DevMode.main(DevMode.java:311)
              Exception 0 :
              org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [BuildService] with qualifiers [@Default] at injection point [[parameter 3] of [constructor] @Inject public org.kie.guvnor.projecteditor.backend.server.ProjectEditorServiceImpl(IOService, Paths, BuildService, Event<Messages>, KModuleEditorContentHandler, GroupArtifactVersionModelContentHandler)]
                   at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:311)
                   at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
                   at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143)
                   at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:163)
                   at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:382)
                   at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
                   at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:380)
                   at org.jboss.weld.environment.servlet.Listener.contextInitialized(Listener.java:182)
                   at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:543)
                   at org.mortbay.jetty.servlet.Context.startContext(Context.java:136)
                   at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1220)
                   at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:513)
                   at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:448)
                   at org.jboss.errai.cdi.server.gwt.JettyLauncher$WebAppContextWithReload.doStart(JettyLauncher.java:486)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
                   at org.mortbay.jetty.handler.RequestLogHandler.doStart(RequestLogHandler.java:115)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
                   at org.mortbay.jetty.Server.doStart(Server.java:222)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.jboss.errai.cdi.server.gwt.JettyLauncher.start(JettyLauncher.java:692)
                   at com.google.gwt.dev.DevMode.doStartUpServer(DevMode.java:509)
                   at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1093)
                   at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:836)
                   at com.google.gwt.dev.DevMode.main(DevMode.java:311)

                   at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:394)
                   at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
                   at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:380)
                   at org.jboss.weld.environment.servlet.Listener.contextInitialized(Listener.java:182)
                   at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:543)
                   at org.mortbay.jetty.servlet.Context.startContext(Context.java:136)
                   at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1220)
                   at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:513)
                   at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:448)
                   at org.jboss.errai.cdi.server.gwt.JettyLauncher$WebAppContextWithReload.doStart(JettyLauncher.java:486)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
                   at org.mortbay.jetty.handler.RequestLogHandler.doStart(RequestLogHandler.java:115)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
                   at org.mortbay.jetty.Server.doStart(Server.java:222)
                   at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:39)
                   at org.jboss.errai.cdi.server.gwt.JettyLauncher.start(JettyLauncher.java:692)
                   at com.google.gwt.dev.DevMode.doStartUpServer(DevMode.java:509)
                   at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1093)
                   at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:836)
                   at com.google.gwt.dev.DevMode.main(DevMode.java:311)

               

              Thanks,

              Jervis

              • 4. Re: How to inject into a Servlet?
                cbrock

                Those aren't Bean Validation errors.They are plain Weld errors that mean what they mean -- that is if you're enabling Weld to manage all classes in your application classpath (which is the default behaviour of Weld). If you're using a normal-scoped bean in CDI, you are required to make it proxyable. ie. You can't have an @ApplicationScoped bean which has a private constructor. This is simply not permitted by the spec, because it means that it cannot be proxied to handle narrower-scope injection as well as cycle resolution.

                • 5. Re: How to inject into a Servlet?
                  jfuerth

                  Hi Jervis,

                   

                  Just following up on this: have you tried creating a public constructor for org.kie.guvnor.guided.rule.backend.server.util.BRDRTPersistence? It seems like that should solve the problem.

                  • 6. Re: How to inject into a Servlet?
                    jervisliu

                    Yes, the workaround (using org.jboss.weld.environment.servlet.Listener) works once I fixed BRDRTPersistence. Though I am not sure what kind of bean validation problems I may run into if I switch back to use org.jboss.weld.environment.servlet.Listener?

                     

                    Thanks,

                    Jervis

                    • 7. Re: How to inject into a Servlet?
                      jfuerth

                      We investigated this thoroughly yesterday, and have determined that the CDIServletStateListener is no longer necessary. I tracked down the history and found out it was forked from Weld 1.0.0.Beta1! The only difference from the upstream Weld Listener class was that it skipped the validation phase at startup. But since then, the official Weld version has changed a lot. This explains why @Inject wasn't working in Servlets for you, and also why you weren't being warned about the unproxyable BRDRTPersistence class.

                       

                      For the 2.2.0.Final release, all demos, archetypes, and documentation will be using org.jboss.weld.environment.servlet.Listener. We've removed CDIServletStateListener entirely in the Errai 3.0 branch.

                       

                      -Jonathan

                      • 8. Re: How to inject into a Servlet?
                        jervisliu

                        Thats all I need to know, thanks Jonathan!

                         

                         

                        Jervis