In my Java Google App Engine server application, I would like to enable Channel Presence servlets in order to track connections/disconnections to/from my channels using the Channel API (as described here. I have already edited my WEB-INF/appengine-web.xml
file like described.
Most servlets in my application use Sitebricks, instead of classes extending HttpServlet, to provide an easy way for me to create REST endpoints in my app. BUT, it seems like using Sitebricks in my class, like so, does not work because I get WARNING: No file found for: /_ah/channel/connected/
when the URL is hit:
@At("/_ah/channel")
@Service
public class MyChannelPresenceServlet {
...
@Post("/connected")
public void connectedMethod() {
...
}
@Post("/disconnected")
public void disconnectedMethod() {
...
}
...
}
Is there any way for me to use Sitebricks at all to provide a REST URL endpoint, for Channel Presence, for /-ah/channel/connected/
and /_ah/channel/disconnected/
?
BONUS QUESTION
Let's say that Sitebricks is not the way to go and, instead, I need to stick to normal servlet classes that extends HttpServlet
and are configured via WEB-INF/web.xml
. This other solution still does not work for me. Let's say that I were to put this into my web.xml file:
<servlet>
<servlet-name>channel_connect</servlet-name>
<servlet-class>com.example.PresenceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>channel_connect</servlet-name>
<url-pattern>/_ah/channel/connected/</url-pattern>
</servlet-mapping>
In this example, com.example.PresenceServlet is a child of HttpServlet and overrides the doPost(HttpServletRequest, HttpServletResponse) method from the parent. I STILL run into problems:
WARNING: /_ah/channel/connected/
java.lang.InstantiationException: com.ea.pogosocial.rest.ChannelServiceServlet
at java.lang.Class.newInstance0(Class.java:357)
at java.lang.Class.newInstance(Class.java:325)
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.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:66)
at com.google.sitebricks.SitebricksFilter.doFilter(SitebricksFilter.java:88)
at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
at com.google.sitebricks.HiddenMethodFilter.doFilter(HiddenMethodFilter.java:75)
at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
at com.googlecode.objectify.cache.AsyncCacheFilter.doFilter(AsyncCacheFilter.java:59)
at com.googlecode.objectify.ObjectifyFilter.doFilter(ObjectifyFilter.java:49)
at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
at com.google.inject.servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:118)
at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:113)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.appstats.AppstatsFilter.doFilter(AppstatsFilter.java:141)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:110)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:61)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97)
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:380)
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.content(HttpConnection.java:938)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:755)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
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)
All help much appreciated. Especially from a Google employee.
(PS: I am using GAE SDK 1.7.2. I am eagerly awaiting the release of 1.7.3)
Update
There was one more experiment I tried. In the <servlet>
tag of web.xml for my servlet class, I added this tag in order to force my servlet to be loaded upon server startup (which, by the way, is done using mvn gae:run
in my example). Now I get this output
WARNING: /_ah/channel/connected/: javax.servlet.UnavailableException: java.lang.InstantiationException: com.ea.pogosocial.rest.ChannelServiceServlet
Hmm that is too bad. It looks as though GAE introspects the servlet chain to try and discover a registered handler for /_ah/channel/*
Disclaimer: I am the creator of both Sitebricks and Guice Servlet so I find this rather frustrating. The one time I did implement a channel setup in GAE I managed to do it using the Channel API directly: https://github.com/dhanji/crosstalk/blob/master/src/main/java/com/wideplay/crosstalk/web/RoomPage.java