Domino REST Service Plugins and Pre-Loading Caches
Last week I had an email from Mat Newman passing on a query for a REST servlet plugin. The person was noting that when sending a
tell http osgi ss command to the server, the plugin was showing as “<<LAZY>>” and only changed to “<<ACTIVE>>” after the first call, when the code in the Activator runs. This is standard behaviour and as XPages developers move from SSJS to Java and start to gain more experience of Java, lazy loading becomes a natural performance optimisation approach. So a plugin that’s lazy-loaded starts to make a lot of sense.
But I suspected that what might be required was to run code immediately, maybe to preload some cached content into the plugin. I don’t think I’ve blogged about that explicitly before, but it’s something I’d already provided code for in ODA Starter Servlet.
It’s something that’s not as straightforward in an NSF – you can load something into
applicationScope, but it’s not straightforward to cache it earlier than the first call. John Dalsgaard has done a lot of work in that area, encountering issues with different ClassLoaders that mean it’s stored in a “different”
applicationScope. And once cached,
applicationScope gets dumped periodically, which also makes sense to help manage memory.
But pre-caching content is something I’m doing for my session with John Jardin at IBM Think. In terms of running code straightaway, that can be achieved by checking the “Activate this plug-in when one of it’s classes are loaded” checkbox on the plugin.xml.
But, as I found in previous projects, this means the code runs before ODA starts up, so you can’t kick of a Xots task at this point. The alternative approach I use is an HttpService. Steps are:
- Add another extension in the
type="com.ibm.xsp.adapter.serviceFactory". This points to a class that extends
HttpServiceFactory(which manages one or more HttpService). Here’s the plugin.xml of the ODA Starter Servlet.
getServices()method of the
HttpServiceFactorytakes an array of
HttpServiceclasses. That’s commented out of the
HttpServiceFactoryclass the ODA Starter Servlet. Note, again this code runs immediately, so before ODA gets initialised, so I can’t schedule a Xots task here.
HttpServicehas a static
createInstance()method which initialises it. The
checkTimeout()method then runs after 30 seconds and thereafter every 30 seconds afterwards. This is where the magic happens. In my real-world examples, like my demo at IBM Think, I use a
hasRunvariable to know if I should kick it off or not, so this code only runs once. Note, I immediately set
true, because if there’s a critical issue, there’s no point trying again. And if it takes more than 30 seconds to complete (unlikely with what I’m running),
checkTimeout()would get triggered again and run the code again.
- This is where the Xots task gets kicked off. In my demo, I’m loading a
ConcurrentHashMapof all ToDo database instances (stores) into memory, for easy access. It’s using Xots to run this as a background task, just for convenience and streamlining. There’s then the potential for a call to the REST service before the server has been up for 30 seconds, so before this task has run. So the code is aborting if stores is not null. My
getStores()method, through which everything is running to integrate with this map of stores, is also calling
loadStores()if stores has not yet been initialised. So I’m covering both bases.
I’m using a
ConcurrentHashMap for the demo there aren’t intended to be lost of stores and it’s only intended for dev servers, so there are no memory issues. When you start using this approach on a production application, where the cached data could get larger, you need a more scalable approach. Bear in mind we’re now outside the XPages runtime which has mechanisms for managing memory size, garbage collection and leakage. The challenges and approaches are standard Java development ones. So if I had concerns about the size and memory usage, I’d change it to use Google Guava and a
LoadingCache so the size or duration could be managed. Google Guava has documentation about that and the GraphNSF functionality in ODA uses it extensively to manage the entries held in the in-memory graph.