3

My android-appengine-endpoint project is setup entirely by android studio. Therefore I don't mess around with anything: I just let android studio add my AppEngine module to my android project. Now I am getting a java.lang.NoClassDefFoundError. Do I have to set somethings in gradle or configure somethings in android studio to get rid of this error? Basically when I try to test my app I get the exception.

Code:

Note: Instead of calling ofy inside my endpoint class, UserDao is a place where all the ofy data access calls are made. You can compare my code to this tutorial

public class UserDao extends OfyService {
    public static void persist(User user) {
        ofy().save().entities(user).now();
    }
    public static User getById(long id) {
        return ofy().load().type(User.class).id(id).now();
    }
    public static List<User> getByTwitterId(Long twitterId) {
        return ofy().load().type(User.class).filter("twitterId", twitterId).list();
    }
}

public class OfyService {
    public OfyService() {}

    static {
        ObjectifyService.register(User.class);
        ObjectifyService.register(Food.class);
        ObjectifyService.register(Pet.class);
    }

    public static Objectify ofy() { return ObjectifyService.ofy(); }

    public static ObjectifyFactory factory() {
        return ObjectifyService.factory();
    }
}

The actual error as seen from the App Engine Console:

com.google.api.server.spi.SystemService invokeServiceMethod: exception occurred while calling backed method
java.lang.NoClassDefFoundError: Could not initialize class com.mycompany.server.data.dao.UserDao
    at com.mycompany.server.utils.Authenticator.verifyAccount(Authenticator.java:22)
    at com.mycompany.server.endpoint.server.getFood(server.java:105)
    at sun.reflect.GeneratedMethodAccessor22.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:44)
    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.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: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 java.lang.Thread.run(Thread.java:745)

Every post I see here seem to be talking about WEB-INF\lib. Well Android Studio has not created any lib directory inside my WEB-INF. So both Android Studio and App Engine are from Google. Therefore I am at a lost there.

My webapp/WEB-INF has only three files in it:

  • web.xml
  • logging.properties
  • appengine-web.xml

Here is an image of my dependencies in case it helps:

enter image description here

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Katedral Pillon
  • 14,534
  • 25
  • 99
  • 199
  • I write the actual app engine source: entities, endpoints, ofy. I just don't decide what goes inside `WEB-INF`. As for UserDao, it is an OfyService subclass, not an entity. So I don't know what's going on there. – Katedral Pillon Oct 11 '15 at 09:47
  • The only difference with UserDao (vs the other OfyService subclasses) is that one of its static methods throw an exception: `IllegalStateException` – Katedral Pillon Oct 11 '15 at 09:49
  • UserDao is **not** an entity. It does not have the `@Entity` annotation. By subclass I mean `public class UserDao extends OfyService` where OfyService is simply a wrapper for `ObjectifyService`, I am simply following the best practice recommended structure. – Katedral Pillon Oct 11 '15 at 17:22
  • I refactor the UserDao class and remove the Exception. That did it. I was having one of the satic methods throw an exception in a certain case. That was the cause of the problem. – Katedral Pillon Oct 11 '15 at 18:41
  • In the stack-trace I see `com.googlecode.objectify.ObjectifyFilter`, and I conclude that Gradle has downloaded Objectify without problems. What class is missing then? – naXa stands with Ukraine Oct 12 '15 at 19:48
  • If I remove UserDao and use OfyService directly, I still get the NoClassDefFoundError except this time it's for OfyService (of course). So really the problem is with Objectify setup. And @naXa, I was able to verify that the jars are indeed downloaded to `.../build/exploded-app/WEB-INF/lib` – Katedral Pillon Oct 12 '15 at 19:59
  • It seems that it's failing during class initialization - most likely a static block of `UserDao`. – naXa stands with Ukraine Oct 12 '15 at 20:05
  • Could you debug the static block and tell where does it fail? – naXa stands with Ukraine Oct 12 '15 at 20:10
  • I remove the UserDao class and directly use the OfyService class and I still get the error. And the OfyService is exactly what the docs say I am supposed to use. I will step through it. And see if I get more specific insight, if that's what you mean by debug. I have posted the entire OfyService code and the entire UserDao code. There isn't much to step-through there, but I'll give it a go. – Katedral Pillon Oct 12 '15 at 20:16
  • What kind of HTTP request is causing that stack trace to occur? A Cloud Endpoints API call? A normal request? – Nick Oct 12 '15 at 20:34
  • Cloud Endpoint call from android phone. – Katedral Pillon Oct 12 '15 at 20:40
  • I have added an image of all my dependencies, in case it helps. Maybe they are interfering with each other somehow, I don't really know. – Katedral Pillon Oct 12 '15 at 20:42
  • I break the static method into pieces, and the crash happens when I call `OfyService.ofy()` – Katedral Pillon Oct 12 '15 at 20:51
  • When you call `ofy()` method for the first time, the static block that registers classes executes. Does it fail to register any class? – naXa stands with Ukraine Oct 12 '15 at 21:44
  • @naXa inside the static registration block, I place a logging before all registrations and a logging after all registrations. The before line is printed, the after line is not. – Katedral Pillon Oct 12 '15 at 22:58
  • after days of trying to get this bug to go away, I seem to be the one going away. Many thanks to everyone for trying to help. I will start recoding my server using the datastore native api instead of objectify. Thanks for all the support. – Katedral Pillon Oct 13 '15 at 01:12

2 Answers2

1

As I find out from our discussion Objectify fails to register classes for an unknown reason.

Perhaps,

  1. you have incorrect annotations on one of your data classes (provide a source code of User, Food, Pet for further investigation);
  2. or call OfyService methods from another thread at the same time (which is unlikely).
naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
1

Since you are using Cloud Endpoint, do this:

  1. In your Endpoint class, create a constructor.
  2. In the constructor, call new OfyService().

The reason this may work is that you will have loaded all your objectify related classes way way before you need to use them (i.e. way before a client makes a first call). Right now you are loading them right before you need them . And delays in the App-Engine class loader may be what's killing you. The sad thing is, the structure you are using is very popular and so most people use it that way: and yet there is no guarantee that it will always work. It's a race condition thing. App-engine can be very slow to load classes. Lucky (or unlucky) for you, you find the problem early. People should give you some upvotes for your pains :).

Konsol Labapen
  • 2,424
  • 2
  • 15
  • 12
  • At least you're properly feeding your garbage collector since this solution creates an OfyService object just for it ;-). – konqi Oct 13 '15 at 16:09
  • It is an interesting concept, sci-fi-ishly speaking: An entity that exist as a mere catalyst, nothing more. – Konsol Labapen Oct 13 '15 at 17:27