Skip to content

Developing RunJava Addins for Domino

Most Domino developers use Windows because that's the only platform Domino Designer runs on. For most of my application development life, my main device has been a Dell laptop of some variety for this reason. For almost a decade now I've also been running a Windows Domino server because Domino Designer local preview is not an effective test environment for a Domino web application. If you're using source control you are also usually testing locally unless you're developing cloud functions. So for development, you typically want a Domino server, and if you're using Domino Designer, the easiest server install to develop against is a Windows Domino server. If you want Linux on Windows and you're using Windows Professional, Docker is a sensible approach, if you take some time to understand port access from Docker.

The downside of developing against a local Domino server comes when running Java applications that need NSF access. The challenge here is that you can run your Domino server, or you can run your Java application which talks to NSFs either directly from your IDE or from a JAR file - but you can't run both at the same time. That's because only one process on Windows can talk to the NSFs. You can access a Domino session by starting a NotesThread, you can get the username it's running as, but as soon as you try to access any NSF, it will return null.

The solution to this is to run the Java application from within the Domino runtime. One method for doing that is as an OSGi plugin, as I blogged about on the Vaadin blog and in my XPages to Web App series some years ago. But if your web application framework is more recent, it either expects a more up-to-date version of the servlet framework or includes its own server. For the former, Jesse Gallagher's work to run Websphere Liberty Profile from Domino's HTTP stack is the best approach. If your web framework packages its own server, for example with Spring Boot or Vert.x, it makes little sense to load a new HTTP server from within the Domino HTTP stack. For this, the best option is the RunJava task in Domino.

The RunJava task is, from what I've read, unsupported. However, it's pretty widely used, most notably with the ISpy task. If you're building and deploying the application yourself, it's probably also best to look at Andy Brunner's Domino-JAddin project. If you're not, you'll need your own RunJava addin, coded from scratch. The best resource for this is Julian Robichaux's example. After all, if you can't trust code from a Lifetime IBM Champion and HCL Grandmaster, then whose code can you trust and why do you expect your users to trust your code?

There are a few peculiarities / anachronisms to be aware of when using it though. Firstly, when you issue the command tell runjava Foo, it will look for a Java class in the default package called Foo.java or for a Java class lotus.notes.addins.foo.Foo.java. Typically, this means creating a Java class called Foo.java which your (modern) IDE will tell you is discouraged (polite term, aka bad practice). This dates back to the pre-Java 5 days, before Java code was put into packages to avoid name collisions as Java became more widely adopted. So you will want to make sure you have a specific name in use. And it is via tell runjava... that you start the task. It's important to note that the RunJava addin name passed with tell runjava... is case sensitive. So tell runjava foo or tell runjava FOO will fail, only tell runjava Foo will work.

To check if it's running, you will look at the Domino server tasks via sh ta. The name of the task, set in the constructor via the setName() method and defined in a variable called progName in Julian's example, is what it will appear as if you issue the Domino server console command sh ta.

However, to interact with it, you interact with the Domino message queue. The message queue name passed when you call mq.create() is what is important here. It will typically be a unique name added to MSG_Q_PREFIX (which is "MQ$"), is the qname variable in Julian's example, and it's this unique name which you use to interact with the task from the Domino console. So if you set it as MSG_Q_PREFIX + "FOO", you will use tell foo... to interact with it. So to end the task, you'll issue the Domino console command tell foo quit. The name used to interact with the MessageQueue is should be lower case. So you use tell foo..., not tell FOO. The critical part here is to ensure you call mq.open(), otherwise the MessageQueue will not start listening for it.

When the addin is running there's a loop that picks up messages from the MessageQueue every 500 milliseconds and, if it's not an error message, passes it to a Java method to process the message. It's this method that the tell foo... Domino console command routes to. "quit" and "exit" are automatically handled, so don't need addressing here. But you can add methods for every other console command you want to expose, as well as what to return if the admin issues tell foo or tell foo help.

As soon as you issue tell foo quit, the message queue message will become ERR_MQ_QUITTING and the loop will break. Once out of the loop, it should be coded to run your doCleanup() method to perform whatever cleanup is required. If you are starting Domino threads, it's critical this closes them down. Also, if using something like Vert.x which requires all verticles to be closed before shutting down, this is where you need to run Vert.x's undeploy() method to shut down the verticles. If that's not done, you will not be able to restart the RunJava addin again before quitting the Domino server. Also the Domino server will not terminate gracefully and will need to be killed.

Also, it appears that the RunJava task doesn't wait long before quitting the RunJava addin itself. I took a convoluted development route to successfully ending my RunJava addin and I don't know what the RunJava integration is actually doing, but I don't think promise-based quitting would work. If your doCleanup method doesn't shut down everything cleanly before it finishes, you've also got the same issue that the Domino server will not terminate gracefully.

Of course RunJava only works on a Windows Domino server, not on a Windows Notes Client. For that, as far as I'm aware, there's no way to run your Java application and the Notes Client at the same time. On non-Windows platforms, however, this isn't an issue. Linux can run a Java application and the Domino server at the same time. And the same is true for the Mac Client.

One big caveat for using RunJava is that it is running a Java application. As such, if notes.ini is set with the Java debug variables, when you call load runjava Foo, it will start debug listening on the Java debug port. If Domino HTTP is running already or a Java agent is used, you will have a conflict because it's already listening on that port and Domino will crash. As far as I can tell, there's no way round that.