Use Guice Injection in a Java GAE Backend Servlet
Asked Answered
H

1

6

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

Hypoglossal answered 20/12, 2012 at 3:44 Comment(5)
Are you handling the /_ah/start/ call in your code? The backend will not serve any requests until it gets a 200 OK response from the /_ah/start/ call.Dode
Yes I have...issue is beyond /_ah/startHypoglossal
@Hypoglossal Have you found a solution?Nydianye
Sorry, unfortunately I am no longer working on this project, so I cannot tell whether the development team found a solution or not. Looks like this issue is still hot in the world, hopefully someone can post an answer sooner than later.Hypoglossal
I'm having the same issue. It seems to be due to the fact that App Engine Backends completely ignores filters in web.xml. code.google.com/p/googleappengine/issues/…Ornery
A
1

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");
  }
}
Alleyn answered 22/8, 2014 at 23:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.