I recently spent a good amount of time trying to figure out why I could not retreive an object from a session that I had previously stored.  After doing a little thinking, I came up with the answer and I hope this saves someone else an immense amount of time.  There is one setup item that needs to be done before you can start using the standard HttpSession objects in your App Engine project.  This line needs to go in your appengine-web.xml file:

<sessions-enabled>true</sessions-enabled>

If you don’t you may find yourself with only read-access to the session object.

In addition, you need to make sure that all your objects that are you going to persist to the session implement the java.io.Serializable interface.  This is particularly important and this is what I failed to realize until I struggled with this for 2 hours.  The reason the object needs to be serializable is because App Engine stores session data in the datastore and memcache.  Any objects referenced by the value you put in the session must be serializable, so the entire object graph is available.  What I found interesting is that it must commit the session data in an transactionally based manner because I had also stored a String in the session and that wasn’t persisted either.  If the object isn’t serializable, the app will NOT fail in a local development machine, but will fail when deployed to the cloud.

A bit of sample code:

public void doGet(HttpServletRequest req, HttpServletResponse resp) {
  HttpSession session = reg.getSession(true);
  // passing in a boolean to getSession() will allow you to inspect if a session
  // already exists.  if you pass in true, the session will be created by default
  // whether one exists already or not.
  // if you pass in false, if a session doesn't exist, you will be return NULL and no
  // session will be created.  if there is a previously existing session, it will return
  // that session.
  String name = (String)session.getAttribute('name');
  session.setAttribute("age", 25);
}

You can check to see that everything is being stored correctly if you look on your machine, you should see a cookie for the domain your working in (dev: localhost, prod: appname.appspot.com) with the key JSESSIONID.  The value for JSESSIONID should match what is in your _ah_SESSION table in the App Engine datastore. You can visually inspect the bytes in the session as well.

There is a small gotcha between the standard J2EE HttpSession and the GAE HttpSession.  There is a difference in when services manipulate objects stored in the session, in that case your changes will be lost when another service will get the object from the session.  The fix for this is invoking setAttribute again after having modified the Person object in the session. This workaround will solve all the inconsistencies but has a pretty important trade-off, every setAttribute will trigger a new serialization and write to the datastore.

Lastly, depending on the utilization of your application, you may find yourself with more than a few sessions.  That is because when the session is written to the datastore, the expiration of the session is set to  24 * 60 * 60 * 1,000 = System.currentTimeMillis() + 86,400,000.   The _expires field is updated each time the session is active, so that could be quite a bit of data storage.  There is currently no automatic removal of expired sessions in GAE.

One last note: remember that App Engine is a distributed architecture so a difference from J2EE is that you are never guaranteed the same application server instance during request processing as the previous request.  While the object is being serialized correctly in memcache, you still have to call setAttribute() every time due to the fact that memory is not shared.

« »