1

My original HttpSessionListener code:

public class SessionListener implements HttpSessionListener {
  @Override
  public void sessionDestroyed(HttpSessionEvent event) {
    final Object user = event.getSession().getAttribute("user");
    if (user != null && user insteaceof User) {
      UserUtils.deleteUser((User) user);
    }
  }
}

and my web.xml

<filter>
  <filter-name>ObjectifyFilter</filter-name>
  <filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>ObjectifyFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

When a session timeout event happens it throws:

WARNING: Problem scavenging sessions java.lang.IllegalStateException: You have not started an Objectify context. You are probably missing the ObjectifyFilter. If you are not running in the context of an http request, see the ObjectifyService.run() method. at com.googlecode.objectify.ObjectifyService.ofy(ObjectifyService.java:44) at com.learnkeeper.server.OfyService.ofy(OfyService.java:61) at com.learnkeeper.server.UserUtils.deleteUser(UserUtils.java:28) at com.learnkeeper.server.SessionListener.sessionDestroyed(SessionListener.java:36) at org.mortbay.jetty.servlet.AbstractSessionManager.removeSession(AbstractSessionManager.java:669) at org.mortbay.jetty.servlet.AbstractSessionManager$Session.timeout(AbstractSessionManager.java:926) at org.mortbay.jetty.servlet.HashSessionManager.scavenge(HashSessionManager.java:285) at org.mortbay.jetty.servlet.HashSessionManager.access$000(HashSessionManager.java:44) at org.mortbay.jetty.servlet.HashSessionManager$2.run(HashSessionManager.java:219) at java.util.TimerThread.mainLoop(Timer.java:555) at java.util.TimerThread.run(Timer.java:505)

I tried that (from this post How to resolve "You have not started an Objectify context" in JUnit?):

public class SessionListener implements HttpSessionListener {
  private Closeable closeable;
  @Override
  public void sessionDestroyed(HttpSessionEvent event) {
    final Object user = event.getSession().getAttribute("user");
    if (user != null && user instanceof User) {
      closeable = OfyService.begin();
      UserUtils.deleteUser((User) user);
      closeable.close();
    }
  }
}

And here is my OfyService class:

class OfyService {
  static {
    // Register all my Entities classes
    ObjectifyService.register(User.class);
    ...
  }

  public static Closeable begin() {
    return ObjectifyService.begin();
  }

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

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

but same stacktrace :(

So what did I miss? thx

EDIT

to follow-up with @stickfigure

So I cleaned my project and re-run my use case and I get this stacktrace now:

WARNING: Problem scavenging sessions java.lang.NullPointerException: No API environment is registered for this thread. at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppId(DatastoreApiHelper.java:132) at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppIdNamespace(DatastoreApiHelper.java:148) at com.google.appengine.api.datastore.Key.(Key.java:96) at com.google.appengine.api.datastore.Key.(Key.java:78) at com.google.appengine.api.datastore.KeyFactory.createKey(KeyFactory.java:54) at com.google.appengine.api.datastore.KeyFactory.createKey(KeyFactory.java:47) at com.googlecode.objectify.util.DatastoreUtils.createKey(DatastoreUtils.java:86) at com.googlecode.objectify.impl.KeyMetadata.getRawKey(KeyMetadata.java:187) at com.googlecode.objectify.impl.Keys.rawKeyOf(Keys.java:36) at com.googlecode.objectify.impl.Keys.keyOf(Keys.java:29) at com.googlecode.objectify.impl.LoaderImpl.entity(LoaderImpl.java:121) at com.learnkeeper.server.UserUtils.deleteUser(UserUtils.java:28) at com.learnkeeper.server.SessionListener.sessionDestroyed(SessionListener.java:40) at org.mortbay.jetty.servlet.AbstractSessionManager.removeSession(AbstractSessionManager.java:669) at org.mortbay.jetty.servlet.AbstractSessionManager$Session.timeout(AbstractSessionManager.java:926) at org.mortbay.jetty.servlet.HashSessionManager.scavenge(HashSessionManager.java:285) at org.mortbay.jetty.servlet.HashSessionManager.access$000(HashSessionManager.java:44) at org.mortbay.jetty.servlet.HashSessionManager$2.run(HashSessionManager.java:219) at java.util.TimerThread.mainLoop(Timer.java:555) at java.util.TimerThread.run(Timer.java:505)

Community
  • 1
  • 1
Freddy Boucher
  • 1,387
  • 1
  • 16
  • 27

2 Answers2

1

I don't see any reason why that code should fail, although it can be written more elegantly:

public class SessionListener implements HttpSessionListener {
  @Override
  public void sessionDestroyed(HttpSessionEvent event) {
    final Object user = event.getSession().getAttribute("user");
    if (user != null && user instanceof User) {
      try (Closable closable = OfyService.begin()) {
        UserUtils.deleteUser((User) user);
    }
  }
}

I have many variations of this code in my applications and there are examples in the test cases - actually, all of Objectify's test cases rely on this pattern.

I would like to see the exact stacktrace generated when you run this code. It should be quite impossible to get that stacktrace if you have called begin() properly. You can look at the code in ObjectifyService.ofy() - it is quite simple. Doublecheck that the code you have deployed is the code you think you have deployed.


UPDATE: The new stacktrace is quite different, and indicates that GAE is not set up to perform API calls from that listener callback. It has nothing to do with Objectify; this is now a question for Google. I suggest writing a new question that focuses on that aspect and tagging it with GAE-related tags.

That said, my general advice is to avoid relying on this callback. Aside from technical issues like this, I would not trust it to be executed consistently in a distributed environment like GAE. If you want to expire an object, put a datestamp on it and cull anything older than a week (or whatever is reasonable).

stickfigure
  • 13,458
  • 5
  • 34
  • 50
0

Google App Engine Doesn't support session listeners. Session listeners may get invoke in local, but No API environment is registered for this thread. In production listeners will not even invoke.

Source @Ramesh V https://stackoverflow.com/a/11152125/421563

Anyway thx @stickfigure

Community
  • 1
  • 1
Freddy Boucher
  • 1,387
  • 1
  • 16
  • 27