Use Guice Injection in a Java GAE Backend Servlet - google-app-engine

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

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");
}
}

Related

Given BlobKey of video how to get url

Normally, to get a url from a BlobKey, I simply do
public static String getUrlFromKey(BlobKey blobKey) {
if(null == blobKey)return null;
ServingUrlOptions options = ServingUrlOptions.Builder.withBlobKey(blobKey).secureUrl(false);
String servingUrl = ImagesServiceFactory.getImagesService().getServingUrl(options);
return servingUrl;
}
But I am now getting an exception and I am convince that the reason is because this time the Blobkey is for a video instead of an image. So how do I get the url given the Blobkey of a video?
For some background, I am using a Cloud Endpoint App-Engine server for my Android app. after uploading a video to the Blobstore, I need to be able to watch it from my android device. So if there is no way to get the url the way I can with an image, is there a way to serve it from an endpoint method?
Error:
com.google.appengine.api.images.ImagesServiceFailureException:
at com.google.appengine.api.images.ImagesServiceImpl.getServingUrl(ImagesServiceImpl.java:284)
at com.mycompany.utils.BlobstoreUtils.getUrlFromKey(BlobstoreUtils.java:21)
at com.mycompany.servlet.VideoUploadCallback.doPost(VideoUploadCallback.java:44)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.googlecode.objectify.ObjectifyFilter.doFilter(ObjectifyFilter.java:48)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:123)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:37)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.JdbcMySqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
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 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.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:260)
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 com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:78)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:147)
at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:457)
at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:437)
at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:444)
at com.google.tracing.CurrentContext.runInContext(CurrentContext.java:230)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:308)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:300)
at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:441)
at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:235)
at java.lang.Thread.run(Thread.java:745)
From the documentation:
To serve blobs, you must include a blob download handler as a path in
your application. This handler should pass the blob key for the
desired blob to blobstoreService.serve(blobKey, res);. In this
example, the blob key is passed to the download handler as the URL
argument (req.getParameter('blob-key')). In practice, the download
handler can get the blob key by any means you choose, such as through
another method or user action.
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
blobstoreService.serve(blobKey, res);
Look at using Google Cloud Storage instead of Blobstore. Then you can serve your videos directly - simply by passing the URL of a video, without going through your application.

JDO on Google app engine

I'm having difficulties getting JDO to work with my GAE app. When I try to execute the following line of code
getPeristenceManager().makePersistent(report.toClient());
I get an exception
The class "fi.aurinko.server.jdo.Client" is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data/annotations for the class are not found.
org.datanucleus.exceptions.ClassNotPersistableException: The class "fi.aurinko.server.jdo.Client" is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data/annotations for the class are not found.
at org.datanucleus.ObjectManagerImpl.assertClassPersistable(ObjectManagerImpl.java:5460)
at org.datanucleus.ObjectManagerImpl.persistObjectInternal(ObjectManagerImpl.java:1802)
at org.datanucleus.ObjectManagerImpl.persistObjectWork(ObjectManagerImpl.java:1745)
at org.datanucleus.ObjectManagerImpl.persistObject(ObjectManagerImpl.java:1602)
at org.datanucleus.api.jdo.JDOPersistenceManager.jdoMakePersistent(JDOPersistenceManager.java:731)
at org.datanucleus.api.jdo.JDOPersistenceManager.makePersistent(JDOPersistenceManager.java:756)
at fi.aurinko.server.api.ReportApi.report(ReportApi.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:115)
at com.google.api.server.spi.SystemService.invokeServiceMethod(SystemService.java:359)
at com.google.api.server.spi.SystemServiceServlet.execute(SystemServiceServlet.java:160)
at com.google.api.server.spi.SystemServiceServlet.doPost(SystemServiceServlet.java:118)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
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.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:127)
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:63)
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.DevAppServerModulesFilter.doDirectRequest(DevAppServerModulesFilter.java:366)
at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectModuleRequest(DevAppServerModulesFilter.java:349)
at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:116)
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:98)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:491)
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)
The class is not enhanced? I assumed that my maven plugin would take care of that.
<datanucleus-core.version>4.1.0-m1</datanucleus-core.version>
<datanucleus-api-jdo.version>4.1.0-m1</datanucleus-api-jdo.version>
<jdo-api.version>3.0.1</jdo-api.version>
<datanucleus-appengine.version>2.1.2</datanucleus-appengine.version>
...
<plugin>
<groupId>org.datanucleus</groupId>
<artifactId>maven-datanucleus-plugin</artifactId>
<version>3.2.0-m1</version>
<dependencies>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-core</artifactId>
<version>${datanucleus-core.version}</version>
</dependency>
<dependency>
<groupId>org.datanucleus</groupId>
<artifactId>datanucleus-api-jdo</artifactId>
<version>${datanucleus-api-jdo.version}</version>
</dependency>
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo-api</artifactId>
<version>${jdo-api.version}</version>
</dependency>
</dependencies>
<configuration>
<fork>false</fork>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
and this is the class I'm trying to persist
#PersistenceCapable
public class Client {
#PrimaryKey
private String macAddress;
public String getMacAddress() {
return macAddress;
}
public void setMacAddress(String macAddress) {
this.macAddress = macAddress;
}
}
any idea how to fix this problem?

Google App Engine - calling new Gson() creates "java.lang.VerifyError: Cannot inherit from final class"

This works when debugging locally but once deployed any servlet call to new Gson() produces this error. I've not been able to google a fix for it. What will fix the issue?
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Gson gson = new Gson();
resp.setContentType("text/plain");
resp.getWriter().println("Service started.");
}
Uncaught exception from servlet
java.lang.VerifyError: Cannot inherit from final class
at com.google.appengine.runtime.Request.process-753f125056d26ae3(Request.java)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:809)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at java.lang.ClassLoader.loadClass(ClassLoader.java:374)
at com.selectshotgolf.game.SelectShotServlet.doGet(SelectShotServlet.java:17)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.googlecode.objectify.ObjectifyFilter.doFilter(ObjectifyFilter.java:48)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
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 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.HttpConnection.handle(HttpConnection.java:404)
at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:438)
at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:445)
at com.google.tracing.CurrentContext.runInContext(CurrentContext.java:220)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:309)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:301)
at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:442)
at java.lang.Thread.run(Thread.java:724)
This also works fine locally or deployed:
import com.google.gson.Gson;
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
new JsonObject();
resp.setContentType("text/plain");
resp.getWriter().println("Service started.");
}
*Edit
I let Eclipse update itself a moment ago and after a restart I had to reset the App Engine Java Build Path. Once reset I now had the option to depend on either com.google.gson.Gson or the repackaged.
This still throws the error above:
import com.google.gson.GsonBuilder;
public <T> T fromJson(String paramName, Class<T> clazz){
if(gson == null)
gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
return gson.fromJson(req.getParameter(paramName), clazz);
}
but the following code works fine remotely but with the disturbing message:
"Use of com.google.appengine.repackaged may result in your app breaking without warning."
yikes
import com.google.appengine.repackaged.com.google.gson.GsonBuilder;
public <T> T fromJson(String paramName, Class<T> clazz){
if(gson == null)
gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
return gson.fromJson(req.getParameter(paramName), clazz);
}
Are you sure your imports are correct?
Keep in mind that App Engine SDK contains repackaged version of Gson.
com.google.appengine.repackaged.com.google.gson.Gson
Add your own Gson dependency and make sure the imports point to this dependency not repackaged version.
We faced the similar issue for some other jars. Reason was mutually conflicting jar versions of Jackson libraries needed for serialization purposes.
What We did:
Added enforcer plugin in our (Maven) project
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<configuration>
<rules><dependencyConvergence/></rules>
</configuration>
</plugin>
Ran enforcer on project
$ mvn enforcer:enforce
Took remedial steps based on enforcer's recommendations
Given ours was a pretty big project, we had around 20 odd jars in conflicts for different versions from different dependency graphs. Attended one at a time and esp. those we had doubt at.
This paid off. We were back at running the app perfectly fine.

NoClassDefFoundError when adding new font in iText on AppEngine

I have an appengine java project that includes pdf creation at some point. The pdf document has special font that I'm trying to include with:
BaseFont bf = BaseFont.createFont("resources/AlexBrush-Regular.ttf", "", BaseFont.EMBEDDED);
Code runs perfect locally but when deployed I get the NoClassDefFoundError (stack trace below). Did any one succeeded in adding custom font to iText on appengine? I'm using iTextg-5.4.4
java.lang.NoClassDefFoundError: java.nio.MappedByteBuffer is a restricted class. Please see the Google App Engine developer's guide for more details.
at com.google.apphosting.runtime.security.shared.stub.java.nio.channels.FileChannel_.map(FileChannel.java)
at com.itextpdf.text.io.MappedChannelRandomAccessSource.open(MappedChannelRandomAccessSource.java:104)
at com.itextpdf.text.io.FileChannelRandomAccessSource.<init>(FileChannelRandomAccessSource.java:71)
at com.itextpdf.text.io.RandomAccessSourceFactory.createBestSource(RandomAccessSourceFactory.java:212)
at com.itextpdf.text.io.RandomAccessSourceFactory.createBestSource(RandomAccessSourceFactory.java:193)
at com.itextpdf.text.pdf.RandomAccessFileOrArray.<init>(RandomAccessFileOrArray.java:147)
at com.itextpdf.text.pdf.TrueTypeFont.process(TrueTypeFont.java:625)
at com.itextpdf.text.pdf.TrueTypeFont.<init>(TrueTypeFont.java:369)
at com.itextpdf.text.pdf.BaseFont.createFont(BaseFont.java:705)
at com.itextpdf.text.pdf.BaseFont.createFont(BaseFont.java:621)
at com.itextpdf.text.pdf.BaseFont.createFont(BaseFont.java:456)
at ccfb.server.servlets.PaypalCheckoutServlet.doPost(PaypalCheckoutServlet.java:156)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:125)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.JdbcMySqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
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 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.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:266)
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 com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:146)
at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:446)
at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:435)
at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:442)
at com.google.tracing.CurrentContext.runInContext(CurrentContext.java:186)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:306)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:298)
at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:439)
at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)
at java.lang.Thread.run(Thread.java:724)
As you know, you should avoid all file system related operations on GAE. As you can read on the iTextG page, some methods in iTextG are to be used only on Android, not on GAE. I quote:
We have not removed them since they are useful on an Android device,
but it doesn't make sense to use them in the context of Google App
Engine.
This means that you need to use a byte[] if you want to create a BaseFont instance. You can't use the createFont() method that takes a path to a font program. Instead you need to use the createFont() method that accepts the bytes of the font program.

Sitebricks and Channel Presence Service (GAE); Also having trouble with normal servlets

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
If anyone still cares...
Problem was that I could use neither Sitebricks nor Guice Injection for a Servlet that handles POST requests to Channel Presence Service URIs. Once I removed both, the servlet worked like a charm.

Resources