7 Replies Latest reply on May 20, 2010 12:40 PM by Pete Muir

    Weld ignoring Servlets, and Google App Engine

    Jeff Schnitzer Newbie

      I'm using Weld on Google App Engine without JSF, using the weld-servlet.jar.  I have a simple project with RESTeasy and CDI working.  I've made a small proxy servlet which should, in theory, allow me to CDI-inject any other servlet.


      There's one problem:  Weld ignores anything derived from HttpServlet.


      I presume this is in deference to the container, which Weld expects to manage and inject itself.  However, the GAE container is not CDI-enabled and probably won't be for quite some time.


      Is there some way to make Weld scan and register HttpServlet-derived objects?


      You can see my CDIProxyServlet in this test project:


      http://code.google.com/p/scratchmonkey/source/browse/appengine/weld/src/test/CDIProxyServlet.java


      It's quite trivial.  You can see that Weld ignores anything HttpServlet derived by watching the registered bean count of the BeanManager (spit out to debug on startup) - add extends HttpServlet to the Thing bean and notice that the count goes down.


      Thanks,
      Jeff Schnitzer

        • 1. Re: Weld ignoring Servlets, and Google App Engine
          Ales Justin Master

          This is related GAE + Weld + Servlets issue.


          For my playground app I hacked this workaround:


          public class WeldServlet extends HttpServlet
          {
             /** The request handler */
             private RequestHandler handler;
          
             @Override
             @SuppressWarnings("unchecked")
             public void init(ServletConfig config) throws ServletException
             {
                super.init(config);
          
                String wrapperClass = config.getInitParameter("request-handler");
                if (wrapperClass == null)
                   throw new IllegalArgumentException("Missing handler class parameter");
          
                ServletContext context = config.getServletContext();
                BeanManager manager = (BeanManager) context.getAttribute(BeanManager.class.getName());
                if (manager == null)
                   throw new IllegalArgumentException("No Weld manager present");
          
                try
                {
                   ClassLoader cl = RequestHandler.class.getClassLoader();
                   Class<?> tmp = cl.loadClass(wrapperClass);
                   if (RequestHandler.class.isAssignableFrom(tmp) == false)
                      throw new ServletException("Illegal handler class, wrong type: " + tmp);
          
                   Class<RequestHandler> clazz = (Class<RequestHandler>) tmp;
          
                   InjectionTarget<RequestHandler> it = manager.createInjectionTarget(manager.createAnnotatedType(clazz));
                   CreationalContext<RequestHandler> cc = manager.createCreationalContext(null);
                   handler = it.produce(cc);
                   it.inject(handler, cc);
                }
                catch (ServletException e)
                {
                   throw e;
                }
                catch (Exception e)
                {
                   throw new ServletException(e);
                }
             }
          
             protected final void handle(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
                handler.handle(req, resp);
             }
          
             @Override
             protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
                handle(req, resp);
             }
          
             @Override
             protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
             {
                handle(req, resp);
             }
          }
          


          • 2. Re: Weld ignoring Servlets, and Google App Engine
            Jeff Schnitzer Newbie

            Ales Justin wrote on May 13, 2010 22:34:


            This is related GAE + Weld + Servlets issue.


            Funny, I starred your issue earlier today.


            RequestHandler is your own custom interface, right?  I don't have that luxury because I'm using GWT rpc servlets, which must actually extend GWT's RemoteServiceServlet.  I need to get Weld to register a servlet for injection one way or another.  I'm going to try doing it manually.


            It looks like there's a lot of code in WeldBootstrap that I should be able to follow and figure out where the exclusion is being made for servlets... but I'm lost.  Can someone tell me roughly where the exclusion is being made?  There aren't any javadocs on internal methods :-(


            Thanks,
            Jeff

            • 3. Re: Weld ignoring Servlets, and Google App Engine
              Nicklas Karlsson Master

              in


              org.jboss.weld.bootstrap.AbstractBeanDeployer
              



              there is a method


              isTypeManagedBeanOrDecoratorOrInterceptor(WeldClass<?>)
              



              that does something like


              !servletApiAbstraction.SERVLET_CLASS.isAssignableFrom(javaClass)
              



              which might be the offender.


              Can you use an extension + SPI to make injection available?

              • 4. Re: Weld ignoring Servlets, and Google App Engine
                Jeff Schnitzer Newbie

                Nicklas Karlsson wrote on May 14, 2010 07:31:


                Can you use an extension + SPI to make injection available?



                It doesn't look like there is a convenient way to surgically edit that behavior out, but it wasn't too hard to take Ales' code and make it into a generic CDIProxyServlet:


                package test;
                
                import java.io.IOException;
                import java.util.Iterator;
                import java.util.logging.Logger;
                
                import javax.enterprise.context.spi.CreationalContext;
                import javax.enterprise.inject.spi.Bean;
                import javax.enterprise.inject.spi.BeanManager;
                import javax.enterprise.inject.spi.InjectionTarget;
                import javax.servlet.ServletException;
                import javax.servlet.ServletRequest;
                import javax.servlet.ServletResponse;
                import javax.servlet.http.HttpServlet;
                
                /**
                 * <p>Servlet which proxies to another servlet which was loaded using CDI and thus receives
                 * proper dependency injection. You can use this on containers (like GAE) which do not support
                 * CDI for Servlets.</p>
                 * 
                 * <p>Use an init-param which defines the actual class of the servlet to load.</p>
                 */
                public class CDIProxyServlet extends HttpServlet
                {
                     private static final long serialVersionUID = 1L;
                     
                     /** */
                     @SuppressWarnings("unused")
                     private static final Logger log = Logger.getLogger(CDIProxyServlet.class.getName());
                     
                     /** */
                     public static final String PROXY_FOR_INIT_PARAM_NAME = "realServletClass";
                     
                     /** The actual, CDI-managed servlet we wrap */
                     HttpServlet actual;
                
                     /** */
                     @Override
                     @SuppressWarnings("unchecked")
                     public void init() throws ServletException
                     {
                          BeanManager mgr = (BeanManager)this.getServletContext().getAttribute(BeanManager.class.getName());
                          
                          //log.finest("BeanManager is " + mgr);
                          
                          String className = this.getServletConfig().getInitParameter(PROXY_FOR_INIT_PARAM_NAME);
                          try
                          {
                               Class<?> clazz = Class.forName(className);
                               Iterator<Bean<?>> it = mgr.getBeans(clazz).iterator();
                               
                               if (it.hasNext())
                               {
                                    Bean<?> servletBean = it.next();
                                    
                                    if (it.hasNext())
                                         throw new ServletException("Too many managed beans for " + clazz);
                                    
                                    this.actual = (HttpServlet)servletBean.create((CreationalContext)mgr.createCreationalContext(servletBean));
                               }
                               else
                               {
                                    InjectionTarget targ = mgr.createInjectionTarget(mgr.createAnnotatedType(clazz));
                                    CreationalContext cc = mgr.createCreationalContext(null);
                                    this.actual = (HttpServlet)targ.produce(cc);
                                    targ.inject(this.actual, cc);
                                    
                                    //log.fine("Injected " + this.actual);
                               }
                          }
                          catch (ClassNotFoundException e)
                          {
                               throw new ServletException(e);
                          }
                     }
                
                     /** */
                     @Override
                     public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
                     {
                          this.actual.service(req, res);
                     }
                }
                



                Big thanks to both of you.  I now have CDI working more-or-less as advertised with Resteasy and GWT services in GAE.

                • 5. Re: Weld ignoring Servlets, and Google App Engine
                  Pete Muir Master

                  Nicklas Karlsson wrote on May 14, 2010 07:31:


                  in

                  org.jboss.weld.bootstrap.AbstractBeanDeployer
                  



                  there is a method

                  isTypeManagedBeanOrDecoratorOrInterceptor(WeldClass<?>)
                  



                  that does something like

                  !servletApiAbstraction.SERVLET_CLASS.isAssignableFrom(javaClass)
                  



                  which might be the offender.



                  This is a bug in Weld. Please file an issue and we will fix (in fact, I thought we had an open issue, but I can't find it).

                  • 7. Re: Weld ignoring Servlets, and Google App Engine
                    Pete Muir Master

                    Nicklas Karlsson wrote on May 19, 2010 13:57:


                    https://jira.jboss.org/browse/WELD-492 ?


                    Yep, thanks Nik!