It’s been about a year since I last blogged about XOTS. Just to recap, XOTS is a method of running background multi-threaded tasks coded within an NSF. It’s basically DOTS tasklets within XPages. The tasklet extends Runnable if you just want to call it and let it run in the background, or Callable if you want to wait for it to complete and return a value. There are, by default, ten XOTS threads so up to ten tasklets can be running simultaneously. That doesn’t mean you can’t kick off more. The others are just queued up behind it and will start once a thread is available. But there was a limitation last year, that any XPages elements that you wanted to use inside the tasklet needed passing into the constructor. Was. Because there have been some recent developments.

But before explaining all about that, there’s an aspect I realised I didn’t cover in the original blog posts, which was error handling. Obviously without access to the XPages runtime, it’s not possible to use XspOpenLogUtil.logError(), as in XPages. Instead there has been a method, XotsUtil.handleException(final Throwable t, final Database currDb) method that should be used to log errors. However, when I hit an error in a Xots task earlier in the week, I realised that in 2.0.0 there was a bug in BaseOpenLogItem.writeToLog() because it was checking for the current database, but that threw a NullPointerException. That’s now been fixed. So XotsUtil is still the way to handle errors from XOTS. But if you’re using a XOTS task from XPages, don’t use XotsUtil.handleException(final Throwable t, final Database currDb).

That’s because I finally worked out how to pass everything related to the XPages runtime into a XOTS tasklet. The code is in my development branch on Stash and is working nicely in tests. This brings me back to the topic of the annotations on XOTS. Again, to recap, a XOTS tasklet needs the annotation @Tasklet with various parameters. The one covered before was @Tasklet(session = Tasklet.Session.CLONE), to clone the current user’s session. I’ve moved away from using SessionAsSigner, so I wouldn’t use Tasklet.Session.SIGNER. And since the native server session is always included anyway, there’s no real need to specify session = Tasklet.Session.NATIVE if you want to process the tasklet as the server. So Tasklet.Session.CLONE is probably the only one I would typically use.

However, there are other annotations. The one of relevance here is Tasklet.Context, with the options Tasklet.Context.XSPBARE and Tasklet.Context.XSPSCOPED. If set to XSPBARE, when the tasklet is run, an object XotsXspContext is created into which is passed the facesContext and XSPContext objects. This will be of relevance for scheduled tasklets, where you want access to the XPages runtime but there’s no point interacting with the scope maps. If set to XSPSCOPED then the XotsXspContext object also includes the four scope maps – applicationScope, sessionScope, viewScope and requestScope. As a point of best practice, I would recommend using requestScope and viewScope only for read activities. After all, if you’re using a Callable then you don’t need to write to anywhere, just return an object. And if you’re using a Runnable then there’s rarely any point using anything below sessionScope because you can’t guarantee the user will still be on the same page.

This brings me to the other key requirement for leveraging the XotsXspContext. Because we’re storing this XotsXspContext in the tasklet, whereas tasklets previously used implements Callable or implements Runnable, the tasklets now need to use extends AbstractXotsXspCallable or extends AbstractXotsXspRunnable. If the tasklets don’t extend these classes, you will not be able to use getContext() to retrieve the XotsXspContext from which to get all the XPages runtime objects.

This finally brings me back to error handling and OpenLog. XotsXspContext extends XotsContext, just as AbstractXotsXspRunnable extends AbstractXotsRunnable and AbstractXotsXspCallable extends AbstractXotsCallable. That’s because XotsContext holds the current database path, OpenLog path and tasklet class. These are used by OpenLog to know where to log to (picking from xsp.properties or notes.ini), which database to log as running in, and which tasklet class name to log as running from. So if you’re not wanting an XPages context but are running from within a database and want to use OpenLog error handling, you should extend AbstractXotsRunnable or AbstractXotsCallable. Then, instead of XotsUtil.handleException(final Throwable t, final Database currDb), you should use XotsUtil.handleException(final Throwable t, final XotsContext xotsContext). There are also two other helper methods – getOpenLogItem(final XotsContext xotsContext) and getOpenLogItem(final Database currDb), to be used if you want to log events or use any of other OpenLog methods. The methods that take a Database as the parameter are still there for Runnables or Callables triggered from non-NSF contexts, for example in a plugin.

All of this functionality will be included in the next release of ODA, which we’re aiming to have out in around a month. I also hope to have scheduled XOTS working for then as well, so stay tuned for that. But before getting to that, I’ll also do a blog post about functionality available for configuring and troubleshooting XOTS, which will come next. As ever, if you have suggestions for features or are interested in testing, get in touch (issues can be logged on OpenNTF’s JIRA project for ODA).

4 thoughts on “XOTS: Background and MultiThreaded Tasks the OpenNTF Domino API Way (Part Four)”

  1. Hi Paul,
    I find your blog extremely useful. Thank you for sharing your expertise.
    I have a question. How can the number of XOTS threads be changed from the default value of ten ? What precautions would you advise if I were to change the number of XOTS threads in my server ?

    1. There is the potential to use a configuration database. However, the code currently just still uses the setting in the code of 10 rather than using what’s been changed in the configuration. The only way currently would be to modify the source code.

      I’m not totally sure of the impact of changing it. I did see the HTTP task on my local Domino server going quite high recently when XOTS threads were running, but didn’t notice that before, haven’t had any issues in a production application that’s been running for over a year, and didn’t have time to investigate further. I’m not sure how it scales and don’t have much experience of multi-threading in Java web applications outside Domino, I’m afraid.

  2. Hello Mr. Withers, I can not find any examples on how to use XOTS for running long scheduled tasks in a domino server.
    There is any way to use the OpenNTF Domino API inside a lotus notes java agent?

    1. There’s a demo database with samples here https://openntf.org/main.nsf/project.xsp?r=project/OpenNTF%20Domino%20API%20Demo%20Database. Also, this blog post about more recent functionality still in development includes code that contributes a Xots task https://www.intec.co.uk/awesome-new-functionality-oda-part-two/. Attaching external jars to Java agents is not recommended – it can lead to memory leaks because they are not always cleaned up correctly. It may be possible to use the core org.openntf.domino jar via an agent if it’s dropped in the jvm\lib\ext folder, but I don’t think any of the development team use that, so we’re unlikely to have the experties to help if anything unexpected occurs. In terms of scheduled processes using ODA, ideas are in the works. There is an ODA channel in the OpenNTF Slack chat, if you’re not part of it, you can get an invite via the “Slack” button on the OpenNTF site https://openntf.org/main.nsf/.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.