Introduction

Author’s note: this is an article that I co-authored
with a colleague, Robert van der Steen. It has also been
published in our company newsletter.

Many of the applications we write for our clients nowadays use the service paradigm: a dedicated and often reusable component within the application that is responsible for a particular task or process. Such components are often written and used in such a way that a component is instantiated once and reused often within the runtime of the application (such as a web service or a Spring managed bean).

Using components in this way is a very resource-friendly way of constructing an application; resource-intensive objects are created only once and reused instead of having to be recreated and reinitialized for each request. However, they do make it necessary to pay special attention to issues that arise in reuse and concurrent use — specifically state management. A common technique to avoid such state problems is to build stateless components.

Unfortunately, mistakes happen in software engineering. Sometimes team members introduce state to shared components without thinking about it. This article discusses using the Memento pattern as an easy way of transforming a stateful into a stateless service.

The problem

In order to illustrate the problem sketched above, let’s introduce an example. This example is an adaptation of a piece of code I stumbled upon in the codebase of a project:

                public class SpringManagedServiceClass {
                        private String oneField;
                        private String anotherField;

                        public boolean anOperation(final String input) {
                                oneField = someOperationOnInput(input);
                                anotherField = somethingElseInvolvingInput(input);
                                totalOperationSucceeded = operationOnTwoStrings(oneField, anotherField);
                                return totalOperationSucceeded;
                        }

                        public String getOneField() {
                                return oneField;
                        }

                        public String getAnotherField() {
                                return anotherField;
                        }
                }

The code above evolved from a class that was not managed by Spring and was instead instantiated each time the service was needed. In that form having state at the class level was no problem: each object was used once and then discarded. This meant it was also thread safe for a client of the service object to extract state using the getters. However, when moved over to Spring the class became a de facto singleton and therefore not thread safe. The person who converted the class to a Spring bean did not recognize this and so introduced a problem by allowing concurrent access to unguarded state.

A quick fix to this problem might be to move back from Spring to instantiation at use. Unfortunately, in this case the class had evolved to depend on a database connection managed by Spring, so moving away from Spring support would be a lot of work.

Introducing the Memento

The Memento pattern is a rather simple pattern that externalizes a classes (visible) state. The way it works is by following these steps:

  1. Take a class C which has fields (i.e. objects with state).
  2. Introduce a new class CMemento.
  3. Move all the fields from C to CMemento. Make them private.
  4. Introduce constructors, getters and setters into CMemento as needed.
  5. In C, in your service methods, initialize a new instance of CMemento.
  6. Refactor C to use the variables in CMemento instead of the variables C used to have itself.
  7. Refactor the relevant methods of C to return a CMemento instead of their existing return types.
  8. Refactor CMemento and C so that the previous return values of the methods of C are placed in the CMemento objects.
  9. Finally, refactor the relevant methods of C again to place their previous return values in their CMemento objects before returning these objects.

The CMemento classes take over the state of the class C in this pattern. C can instantiate a single CMemento instance or as many instances of CMemento as it likes and so create a set of states for itself. Each of these CMemento instances can be the “current” state of any object of type C at any time, so instances of C can roll back to a previous state easily or even swap states.

The Memento pattern is actually not meant to address the problem discussed in this article; it is meant to enable rolling back to a previous state. Fortunately for us the idea can be reused easily to solve our problem as well. Instead of being swappable however, we will refactor our service object to create a Memento object (an object to hold the externally visible state). The public service method will then return this Memento object and the service class will “abandon” its “state” after that, making the service completely stateless.

The solution applied to the example

First, let’s introduce our Memento class:

                public class ServiceMemento {
                        private String oneField;
                        private String anotherField;
                        private boolean serviceResult;

                        public ServiceMemento(final String oneField, final String anotherField, final boolean serviceResult) {
                                 this.oneField = oneField;
                                 this.anotherField = anotherField;
                                 this.serviceResult = serviceResult;
                        }

                        public String getOneField() {
                                 return oneField;
                        }

                        public String getAnotherField() {
                                 return anotherField;
                        }

                        public boolean getServiceResult() {
                                 return serviceResult;
                        }
                 }

Now we can refactor our original service class and replace its state with a Memento object:

                public class SpringManagedServiceClass {
                        public ServiceMemento anOperation(final String input) {
                                String oneField = someOperationOnInput(input);
                                String anotherField = somethingElseInvolvingInput(input);
                                boolean totalOperationSucceeded = operationOnTwoStrings(oneField, anotherField);
                                ServiceMemento result = new ServiceMemento(oneField, anotherField, totalOperationSucceeded);
                                return result;
                        }
                }

After this refactoring all the client code will of course have to be refactored as well. However, all the state access has been replaced by getter calls to the new Memento object, so that refactoring is not very difficult.

The most important thing is that using the Memento has allowed us transform our service class into a thread safe, stateless service class rather easily. The price for this was introducing a new class. This is not as clean a solution as we would have had if the service class had been designed to be thread safe from the very start. But it is an easy fix to implement so it allows us to avoid a lot of (potentially difficult) rework. Also, this Memento class will do nothing but hold the result of the work done by the service class. So at least instantiating and having multiple instances of this class will not be as expensive as having to create multiple instances of the service class.

Summary

This article discussed using the Memento pattern as a quick fix for a thread safety problem in service classes. It covered the problem of accidentally introducing concurrent access problems in singleton classes by introducing state into those classes and then showed how that state can be externalized into a Memento class to make the service class stateless once more.

Using the Memento pattern to solve thread safety issues
Tagged on: