Implementing Persistent Sessions in a Legacy Tomcat Application

Tim O'Malley
Delivery.com Engineering
5 min readFeb 1, 2016

When I started my company back in 2011, my co-founder and I built our e-commerce website using Apache’s Tomcat. We used the built-in session manager that stored sessions in memory, but eventually we couldn’t accept the downsides associated with that. This post is not really a how-to guide for persistent sessions; the actual process is relatively straightforward and detailed elsewhere. Instead, it’s a list of problems that came up while implementing persistent sessions, thoughts on how they could have been avoided, and how we dealt with those problems while avoiding a massive rewrite.

Good practices when using sessions

Before we get into the actual implementation of persistent sessions, there are a few guidelines that bear mentioning. If we had we followed the advice below while building our application, the entire process could have been a whole lot easier.

Don’t store things in session

I realize this is like one of those StackOverflow answers that say, “You just shouldn’t be doing what you’re asking for help to do.” But seriously, just avoid storing stuff in session. Sessions are clients’ state and we know not to store client state on the backend if we’re trying to follow REST principles. Most things are either not client state and should be persisted to the database, or they’re absolutely state and should be managed on the client side.

Only use sessions in the controllers

Our application followed a typical 3-tiered architecture:

controllers -> business logic (or “middle layer”) -> persistence layer

We had one object (called ClientSession for the remainder of this post) that stored all the session data. Once you realize that sessions are client state, it becomes clear that they should be used exclusively in the controllers. There should not be a middle layer function that takes in a ClientSession. We didn’t follow this rule 100% and ended up with middle layer classes that took ClientSession as a parameter, manipulated data on it (java objects are essentially passed by reference even though that isn’t technically true), and returned something else. Don’t do this:

Implementing Serializable

Things you need to do before using Tomcat’s persistent session manager:

  1. Any object that will be persisted into the session needs to implement java.io.Serializable. This makes up about 95% of the work you’ll have to do.
  2. Any member of an object implementing Serializable needs to also implement Serializable or be marked as transient, in which case you have to deal with the fact that it won’t be there after deserialization.

An alternative title for this post could be “How implementing Serializable made me rethink a lot of things.” Here are a few things I wish I knew going in:

Don’t implement Serializable on extendable classes

When I first looked at our ClientSession, it didn’t seem too daunting to make all the members implement Serializable:

Only two objects :-)

Awesome! Only a couple classes need to implement Serializable. I did those two classes, then went to run my code and immediately saw a bunch of NotSerializableExceptions. But why? I implemented Serializable in all my objects. I dug into it further and added the comments below:

Yeah… it’s a lot more than two objects :-(

So I went about implementing Serializable class by class until I stopped throwing NotSerializableException during every run of my code. Most of the classes were small and only contained a few native types, making it more tedious than anything. For the bigger ones, I took a different approach:

Store ids, not objects

If you’re storing an object in session that is also in your database (which probably means you’re violating the first point of this post), you can do the following:

  1. Mark the large object as transient.
  2. Add a member for the id of the large object.
  3. Update the getter for the large object to load the large object from the db or some 2nd level cache. You need to do this because you’ll lose the large object during serialization.
  4. (Optional) Deprecate the large object so people stop using it by default.

Alternatively I could have overridden readObject and writeObject in SomeLargeObject to do the same thing (store only the id during serialization, then during deserialization load the object using LargeObjectGateway). That seemed like overkill for what I needed to do, and also would have prevented someone from serializing the object before it’s been persisted into the database.

Make sure hashCode() and equals() are implemented properly

Some implementations of Tomcat’s Session Manager have optimizations for only updating the persisted objects when things change. Because of this, you need to make sure hashCode() and equals() are properly implemented for any persisted objects, or the session manager won’t know that your object has changed and will skip writing the new versions to the db.

Other considerations

Again, 95% of the work here was related to implementing Serializable. Besides that, I should mention:

Don’t use custom classes to load application configuration

Our app used a custom class to load properties such as database credentials, which were encrypted in a properties file. Because session management is handled on the Tomcat level (and not on the application level), the Tomcat session manager doesn’t have access to our custom property reader class. We had to pass in the database passwords a different way — a major annoyance that required some infra changes.

Possible issues with default values for members?

A couple members of ClientSession had default values and all of our code assumed those values were never null. After implementing persistent sessions, I somehow started getting random null pointer exceptions when accessing those members. I set a breakpoint in the setter method because I assumed some code was explicitly setting those to null, but it never happened. Then somehow the member would end up null again. I just removed the assumption of non-null values, but that was weird and I wish I could figure out how it was happening.

More helpful posts

  1. Five things you didn’t know about Java Object Serialization discusses other pitfalls and gotchas, including how Serialization can prevent refactoring of Serializable classes.
  2. Implementing Serializable has a couple good pointers about inner classes and container classes.

Hopefully this post helped with some practical examples of how you should structure your code to allow for serialization and persistent sessions. As I’m relatively new to serialization in Java, there may be things I’ve missed or you disagree with. Feel free to comment below or on Twitter at @timomall and I’d love to discuss.

--

--