Caching in XPages – not as straightforward as you would believe

Well, you may know that I am a fan of MVC (Model-View-Controller) pattern (see more here).

One tempting feature of using an MVC pattern is that it is pretty straightforward to implement a caching mechanism. This can easily be done in the facade layer of the model. And just imagine what happens if all of the data is ready in memory – and you only need to go to the disk/database when some of the data is updated. I can assure you that this means SPEED….

But as with so many things – more wants more. Wouldn’t it be nice to preload all data on start of the HTTP server? Then you would avoid the first user hitting the delay of loading data from the database – it will already be ready in memory.

This article is about my experiences with trying to obtain this. And I must warn you – I have not yet found the “silver bullet”. But I have discovered a number of mechanisms that may help others understand the underlying features of XPages.

My custom cache – take 1

Ok, how does one create a cache? Basically, you need a way to reference the same memory – no matter from where you do it. I started out using a managed bean in the application scope. As long as the application scope “lives” the data is cached in memory.

I do this by having a “DataBean” – set up as a managed bean in faces-config.xml:

 <managed-bean>
    <managed-bean-name>Data</managed-bean-name>
    <managed-bean-class>dk.dalsgaarddata.demo.bean.DataBean</managed-bean-class>
    <managed-bean-scope>application</managed-bean-scope>
  </managed-bean>

And here is a snippet of the code. Since it is a managed bean it implements Serializable and you can see that I use lazy initialization for setting the facade bean:

public class DataBean implements Serializable {
  private static final long serialVersionUID = 1L;
  private PersonCRUDFacade personFacade = null;
  public PersonCRUDFacade getPersonFacade() {
    if (null == personFacade) {
      personFacade = new PersonCRUDFacade();
    }
    return personFacade;
  }}

In the facade bean I have implemented the cache as a HashMap – with the key of the data as key of the entry in the HashMap. Here is a snippet from the facade bean:

public class PersonCRUDFacade implements Serializable {
   private static final long serialVersionUID = 1L;
   private Map<String, Person> persons = null;
   private PersonDAO getDao() {
     return new DominoPersonDAO();  // Not serialized - handles access to the Domino database
  }
   private Map<String, Person> getCachedPersons() {
     if (null == persons) {
       System.out.println("read persons into cache...");
       persons = getDao().getAllPersons();   // With key also as key to the hash map
     }
     return persons;
   }
 
   public List<Person> getAllPersons() {
     return sortByName(getCachedPersons());
   }
    public Person findPerson(String key) {
     return getCachedPersons().get(key);
   }
   :
   :
 }

The above are just small snippets. I have left out some of the details (like reading in the cache as Anonymous – but using sessionAsSigner). But hopefully you get the idea.

So the next thing I did was to create an XPage (preload.xsp) that basically reads all the data that I want to cache (by referencing the various facade beans through Expression Language – and simply just returning the size of the map). And then I added the following to notes.ini (through a configuration document, of course):

XPagesPreload=1
XPagesPreloadDB=demo/mvc.nsf/preload.xsp

Restart the server, and looking at the console we can see that data is loaded into the cache. Great! But when we open the application – we can see that the data is loaded into cache again…. What is happening???

My custom cache – take 2

My first thought was that for some reason the application scope was not the right place to put the cache – since it somehow was cleared after preload and before I opened the application. My next thought therefore was to create a true singleton object (using an enum which seems to be best practice) and cache the facade beans in there. And a small snippet to give you the idea:

public enum CacheContainer {
  INSTANCE;
  private PersonCRUDFacade personFacade = new PersonCRUDFacade();
  private CacheContainer() {
    System.out.println("Instantiating enum CacheContainer...");
  }
  public PersonCRUDFacade getPersonFacade() {
    if (null == personFacade) {
      System.out.println("Create personFacade...");
      personFacade = new PersonCRUDFacade();
    }
    return personFacade;
  }
  :
  :
}

In our DataBean we can use the singleton object this way:

public PersonCRUDFacade getPersonFacade() {
 return CacheContainer.INSTANCE.getPersonFacade();
}

Back to testing with preload – … and the same result…!  The cache is re-read after preload when the application is accessed afterwards.

My custom cache – take 3

Ok, so how can we then create a “true” caching mechanism? Well, the ServerBean (part of the OpenNTF Domino API) comes to our rescue. This is the true server side caching mechanism. It implements ConcurrentHashMap and as such is ideal for the purpose. So off I went implementing the ServerBean approach. This is a snippet from the DataBean class:

public PersonCRUDFacade getPersonFacade() {
  PersonCRUDFacade personFacade = (PersonCRUDFacade) ServerBean.getCurrentInstance().get(PersonCRUDFacade.class.getName());
  if (null == personFacade) {
    personFacade = new PersonCRUDFacade();
    ServerBean.getCurrentInstance().put(PersonCRUDFacade.class.getName(), personFacade);
  }
  return personFacade;
}

So back to testing the preload etc. And now we get an error – which seems a little strange when you look at it:

javax.faces.el.EvaluationException: Error getting property 'persons' from bean of type dk.dalsgaarddata.demo.bean.DataBean: java.lang.ClassCastException: dk.dalsgaarddata.demo.dao.facade.PersonCRUDFacade incompatible with dk.dalsgaarddata.demo.dao.facade.PersonCRUDFacade

But what does that mean??? That seems to say that the same class is not compatible with itself…???? What is going on????

The explanation

Following the above error I tried to put a String with a timestamp into the cache – and retrieve that at the same time as I tried to retrieve the PersonCRUDFacade instance. And guess what – the String object was cached quite all right. This led to the explanation of what was going on.

First, the singleton approach would probably have worked quite as good as the ServerBean approach. The problem lies in classloaders….

This has been pointed out by Nathan Freeman and Philip Riand – both very competent and way above my level of knowledge on XPages.

So when you preload an XPage it is done in one classloader. When you open an XPage in application it is done in its own classloader…. i.e. a DIFFERENT classloader – and they cannot share the same objects…. And what happens if the application scope times out? Well, the classloader unloads all classes and start from scratch when a new user hits the application – and in our case that means they have to re-read data into the cache…. (well with the ServerBean implementation we would get the ClassX is incompatible with ClassX error)

Sigh….

What can you do??

I am currently working with this approach:

  • Do not try to preload a specific XPage – it won’t help you
  • Call the application via a URL that will preload the data, e.g. as mention via a special preload XPage. I do this from a DDM probe – which checks that the application (and server) is alive – I even check every 15 minutes which is shorter than my application timeout, so my data is cached while the server is running….

This is not ideal – but it works. The only other solution would be to have the class (facade bean) outside of the NSF. You could put it in a JAR and wrap it up as an OSGi plugin (see the steps here) – but that makes deployment a totally different game – and you may not want to do that.

I hope to have saved you some of the hassle that I have been through by sharing the above information.

Happy coding!

2 Responses to Caching in XPages – not as straightforward as you would believe

  1. Nice article, got me thinking.
    There are three approaches that come to my mind:

    1) The-first-one-takes-one-for-the-team approach: You keep your cache inside the application scope, but don’t load anything. When the first user wants something, you would do a full lookup and spin of a thread that loads the rest.

    2) The-first-one-takes-one-for-the-team-but-we-gave-him-padding approach: When the data that shall be cached changes you serialize a Java object into a compact format and save it into a mime document. When the first user needs something, that one document is loaded and deserialized. This should be blinding fast. You could cheat with known UNIDs to not rely on the NIF

    3) Why-not-use-a-servlet approach: Your cache is implemented as servlet that you actually talk to via HTTP and get a JSON object back which you deserialize (GSON is good at that). No classloader headache and a servlet doesn’t time out. A variation is mq or a pipe

    In all cases, don’t simply HashMap. The Guava library (part of the last OpenNTF API build) has a wonderful cache object. Why not use that one?

    • Hi Stephan

      Thanks for your comments.

      I appreciate that there are many different approaches to caching. The nature of this application is that it has quite a lot of meta data (e.g. 14-15.000 GPS points) that we want to be available for quick reference – also for the first user. Otherwise, I tend to follow the “lazy initialization” method which I suppose resembles your first approach – though I really like the idea of spinning off a thread to load the remaining data.

      In addition to the meta data we are going to return a number of statistics on all the user data that we have. Therefore, we also cache them – and just do all the calculations on data in memory.

      I am caching Java objects in memory using a HashMap where I have the same key as to the data in the database. I am curious as to why you think I should not use this approach? I have had several discussions about this with Nathan Freeman – and actually changed it based on his advice. When I need a different sort order I just sort it on request. It is blistering fast. Look ups are naturally as fast as they can get.

      I have been reluctant to serialize the entire cache to disk as I was afraid of the overhead – on the other hand I have not tried it.

      I would think that the servlet solution is similar to serialization – in the sense that data would need to be transformed to another format whenever I require/update the data – or perhaps I have not fully understood what you are suggesting?

      The main problem that I see is that I cannot work with the native objects once the application scope has timed out (i.e. the classloader has unloaded). Using a ServerBean is still “surviving” this – but I cannot read my data back. Perhaps I could get an event triggered when this happens and serialize the entire cache – and just reload it from there when a new classloader requires it…?

      I don’t need the cache to time out – all access to data goes through the facade layer in my beans – all updates are written to the cache as well as to the database.

      I have also recently learned that the Guava library has become part of the OpenNTF Domino Api (that I have used from the beginning of this project). I will have to go and learn about that – and what it could provide for this application – though I am not sure it will help solving the classloader issue.

      /John