Kerflyn's Blog

Well… It's a blog!

Homemade context management

leave a comment »

A stateless application component is a component that looks like a mathematical function. By definition, a mathematical function always returns the same result providing that you enter the same parameters. If a component follows such a definition, it is easier to test, because the component is decoupled from the rest of the application. Moreover, it is safer in case of concurrency.

If you want application components as stateless as possible, you have to manage a context, in order to export and manipulate the component state outside of the component. A context can be a complex structure that contains all the configuration elements needed by a component or more to run.

There are two kinds of context:

  • The application context is a context that is built during the load time and that do not change during the application life. Spring and Java CDI have such an approach.
  • The session context is a context that is built dynamically at a specific phase during the runtime.

In this post, we see this last case of context: the session context.

Interface

A context can be seen as a set of objects. Each object is identified by a name.

Here is an interface to represent contexts:

public interface Context {
    void register(String name, Object o);
    Object get(String name);
    <T> T get(String name, Class<T> contract);
    <T> T get(Class<T> contract);
    Context getParent();
}

Contexts can be organized in a hierarchical structure. A context has a parent context. All registered objects in the parent context is accessible to the child context. This help not to redefine abusively previously registered objects.

There are three methods to get a registered object in a context or its parent:

  • get(name) is the easiest one, but its drawback is to return an Object and not something more specific like a String, a Date, a BigDecimal, or anything else. So, if you know what it is, you have to cast the result. Otherwise, the result might not be directly useful.
  • get(name, contract) applies a check on the returned type according to the contract. The contract may be a class, a super-class, or an interface. What is return is of the type of the contract. Notice that get(name, Object.class) is the same as get(name).
  • get(contract) searches the first registered object which type matches or inherits of the given contract.

Implementation

Here is the class to generate contexts.

public class Contexts {
    public static final NULL = new NullContextImpl();

    private Contexts() {
        throw new UnsupportedOperationException();
    }

    public static Context create() {
        return new ContextImpl(NULL);
    }

    public static Context create(Context parent) {
        return new ContextImpl(parent);
    }

    /* Implementation of context classes (see later...) */
}

You can get or create three types of contexts:

  • A null context that is the parent of all contexts.
  • A top-level context with create(), which is a context without parent (more exactly, a context which parent is the null context).
  • An ordinary context with create(parent), which has a parent context.

A null context is defined. In fact, it is the top parent of all contexts. The null context is built according to the Null Object pattern. Every get() method throws a NoSuchElementException.

private static class NullContextImpl implements Context {
    public Context getParent() { return this; }

    public void register(String name, Object o) {
        throw new UnsupportedOperationException();
    }

    public Object get(String name) {
        throw new NoSuchElementException();
    }

    public <T> T get(String name, Class<T> contract) {
        throw new NoSuchElementException();
    }

    public <T> T get(Class<T> contract) {
        throw new NoSuchElementException();
    }
}

Here is a simple implementation of a context. It is not intended to be thread-safe nor to be scalable. In order to be improved, cache can be used in the method get(contract).

private static class ContextImpl implements Context {

    private final Context parent;

    private final Map<String, Object> objects;

    public ContextImpl(Context parent) {
        this.parent = parent;
        this.objects = new HashMap<String, Object>();
    }

    public void register(String name, Object o) {
        objects.put(name, o);
    }

    public Object get(String name) {
        if (objects.containsKey(name)) {
            return objects.get(name);
        }
        return parent.get(name);
    }

    public <T> T get(String name, Class<T> contract) {
        if (objects.containsKey(name)) {
            return contract.cast(objects.get(name));
        }
        return parent.get(name, contract);
    }

    public <T> T get(Class<T> contract) {
        for(Object o : objects.values()) {
            if (contract.isInstance(o)) {
                return (T) o;
            }
        }
        return parent.get(contract);
    }

    public Context getParent() { return parent; }
}

We can notice that for each get() method, either the method internally succeeds or it calls the get() method of the parent. And as the null context is the parent of every context, if each get() fails, a NoSuchElementException is thrown.

Example

Here is some examples that use our context implementation:

Context context = Context.create();

context.register("message-en", "hello");
context.register("message-fr", "salut");

System.out.println(context.get("message-en"));               // display: hello
System.out.println(context.get("message-fr", String.class)); // display: salut
System.out.println(context.get(String.class));               // display "hello" or "salut"

Now, let’s get a more concrete example. Suppose that you manage message persistency through a DAO. We have a DAO based on the interface MessageDao and using the XML medium. Here is the preparation of the DAO in a main context.

public void init() {
    Context mainContext = Context.create();

    MessageDao messageDao = new XmlMessageDao(file);
    mainContext.register("messageDao", messageDao);
}

In the main context, the element lifespan is the same as the main component one.

Now, to call the sub-component, we have to prepare a specific context. But we do not want to copy/paste the main context. So, we set the parent of the newly created context as the main context and build the new context.

public void callComponent() {
    Context context = Context.create(mainContext);

    // specific context composition for the sub-component call...

    component.call(context);
}

Now, we wanted to use the DAO in a sub-component.

public void call(Context context) {
    // create an entity
    Message myMessage = new Message():

    // save it
    context.get(MessageDao.class).save(myMessage);
}

For the last line above, we can notice that if an object is a singleton inside a context, there are different ways to access it, especially if it is part of an inheritance tree. Indeed, we might use the class XmlMessageDao, instead of MessageDao. But in this case, the use of MessageDao is better because it helps to decoupled the application from the implementation of its components.

Annexe

Written by fsarradin

2010/09/15 at 01:24

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: