Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Guice Injection in a Java GAE Backend Servlet

I have a dilemma in my GAE Java app: I am trying to write a servlet that will be handled by GAE backends and it must also use Guice dependency injection to initiate all the servlet's fields. I have tried multiple approaches but none seems to be doing the trick for me.

I first tried to use Sitebricks in the servlet, but it seemed that doing so results in GAE being unable to detect the class as a legit servlet when the backend URL is being queried: a 404 Not Found is always returned from the backend. Despite this, I know for sure that the class is annotated correctly with Sitebricks annotations because the same URI path works fine with the frontend instance.

Next, I tried to use Guice Servlet Extension (see this link); in my class that creates the global Injector, I used this binding:

return Guice.createInjector(..., new ServletModule() {
    @Override
    protected void configureServlets() {
        serve("/backend/*").with(MyBackend.class);
    }
}, ...);

Unfortunately, the same exact issue as with Sitebricks happens: I am returned a 404 error on the backend but not on the frontend.

The last feasible approach would be to configure the servlet-mapping in web.xml; the traditional approach. This finally allows my backend to detect the URI mapping (which follows along with what is writen in Google's documentation of Java GAE backends: "Backends share the servlets defined in web.xml with the main application version."). Unfortunately, I would not be able to take advantage of Guice injection using this procedure and hence it is extremely undesirable for me to use web.xml.

I even tried combining the second and third approach: use both the Guice Servlet Extension and web.xml. This resuled in a 500 error being returned from the backend with this error:

java.lang.InstantiationException: com.example.MyBackend
    at java.lang.Class.newInstance0(Class.java:340)
    at java.lang.Class.newInstance(Class.java:308)
    at org.mortbay.jetty.servlet.Holder.newInstance(Holder.java:153)
    at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:428)
    at org.mortbay.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:339)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.google.appengine.tools.development.BackendServersFilter.doRedirectedServerRequest(BackendServersFilter.java:292)
    at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:106)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94)
    at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:327)
    at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126)
    at com.google.appengine.tools.development.BackendServers.forwardToServer(BackendServers.java:56)
    at com.google.appengine.tools.development.BackendServersFilter.doServerRedirect(BackendServersFilter.java:237)
    at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:100)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:383)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

20-Dec-2012 3:23:53 AM com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: /backend/example/rest/call
java.lang.InstantiationException: com.example.MyBackend
    at java.lang.Class.newInstance0(Class.java:340)
    at java.lang.Class.newInstance(Class.java:308)
    at org.mortbay.jetty.servlet.Holder.newInstance(Holder.java:153)
    at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:428)
    at org.mortbay.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:339)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.google.appengine.tools.development.BackendServersFilter.doRedirectedServerRequest(BackendServersFilter.java:292)
    at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:106)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94)
    at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:327)
    at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126)
    at com.google.appengine.tools.development.BackendServers.forwardToServer(BackendServers.java:56)
    at com.google.appengine.tools.development.BackendServersFilter.doServerRedirect(BackendServersFilter.java:237)
    at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:100)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:383)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

One final thing to note: I configured my backends.xml file to have <public>true</public>, but I am testing these HTTP request calls by querying the backend from the frontend of my GAE app. Also, note that my frontend instance is properly retrieving the backend address using BackendService (javadoc here).

Other related Stackoverflow questions I have asked before

  • Is Sitebricks compatible with GAE backends (Java)
  • Sitebricks and Channel Presence Service (GAE); Also having trouble with normal servlets
like image 859
ecbrodie Avatar asked Dec 20 '12 03:12

ecbrodie


1 Answers

Backends have been deprecated in favor of Modules.

I'm working on some sample code using Jersey w/ Guice, it's still in progress (I haven't started pruning things yet), but here are some excerpts.

from pom.xml:

<!-- [START Guice] -->
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>${guice.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.inject.extensions</groupId>
            <artifactId>guice-servlet</artifactId>
            <version>${guice.version}</version>
        </dependency>
<!-- [END Guice] -->
<!-- [START Jersey] -->
        <!-- Jersey -->
      <dependency>
          <groupId>org.glassfish.jersey.core</groupId>
          <artifactId>jersey-server</artifactId>
          <version>${jersey.version}</version>
      </dependency>
      <dependency>
          <groupId>org.glassfish.jersey.containers</groupId>
          <artifactId>jersey-container-servlet</artifactId>
          <version>${jersey.version}</version>
      </dependency>
      <dependency>
          <groupId>org.glassfish.jersey.ext</groupId>
          <artifactId>jersey-mvc-jsp</artifactId>
          <version>${jersey.version}</version>
      </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>${jersey.version}</version>
        </dependency>
      <dependency>
          <groupId>org.glassfish.hk2</groupId>
          <artifactId>guice-bridge</artifactId>
          <version>2.2.0</version>
      </dependency>
<!-- [END Jersey] -->

from JerseyConfiguration.java:

public class JerseyConfiguration extends ResourceConfig {
    private final Logger log = Logger.getLogger(getClass().getName());

    @Inject
    public JerseyConfiguration(ServiceLocator serviceLocator, ServletContext servletContext) {
        log.info("Creating JerseyConfiguration");
        packages("com.example.gettingstarted");

        GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
        GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
        guiceBridge.bridgeGuiceInjector((Injector) servletContext.getAttribute(Injector.class.getName()));
    }
}

From GettingStartedServletContextListener:

public class GettingStartedServletContextListener extends
        GuiceServletContextListener {

    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new GettingStartedModule());
    }
}

From GettingStartedModule.java:

public class GettingStartedModule extends ServletModule {

  @Override
  protected void configureServlets() {
      serve("/hi").with(HelloWorldServlet.class);
  }
}

From HelloWorldServlet.java:

@SuppressWarnings("serial")
@Singleton
public class HelloWorldServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
          throws ServletException, IOException {
      resp.setContentType("text/plain");
      resp.getWriter().println("Hello, old friend");
  }
}
like image 150
Les Vogel - Google DevRel Avatar answered Nov 15 '22 01:11

Les Vogel - Google DevRel