MQTT with Lotus Notes

Previously, I’ve used Apache Ant with MQTT Publish/Subscribe to keep track of overnight processes. Since then (2 years – amazing) The list of things talking to the sturdy RSMB broker has grown steadily. What it has lacked up ’til now was a means to get Lotus Notes messages from LotusScript agents.

One of the issues with Notes is that you can’t open network connections, so normal TCP/IP games are out. However, you can run java agents and they can load external libraries. The majority of agents are however written in Notes’ own scripting language,  LotusScript, and that’s the situation here. I didn’t want to rewrite a load of stuff and I wanted something that could be dropped into an existing agent.

What I didn’t know about was the star of this show:  LS2J the “LotusScript to Java Bridge”  which allow you to interact with methods and properties of a java class from LS as if they were native. It’s a clever bit of kit, with a few caveats and the following bits of code show how it works for a simple MQTT class.

If you want to download it, I’ve wrapped all this up in a single NSF for convenience.

Java Class

So, first off you need an empty database and a new Java Script Library entry. I’ve called mine MQTT_Notes. You’ll need to use ‘Edit Project’ to pull in the wmqtt.jar file from the IA92 Java Client into the database.

I’ve kept the java side very simple. There are three methods:  initialize, execute and disconnect to send a QoS 0 publication to a broker. You’ll also notice I’m not doing any try/catch error handling. This is because the errors are being sent up to the LS level above.

The code is listed below, any real java coders, please feel free to tut and shake your heads.

import com.ibm.mqtt.MqttClient;
import com.ibm.mqtt.MqttException;

public class MQTT_Notes {
    private final static boolean CLEAN_START = true;
    private final static boolean RETAINED = false;
    private final static short KEEP_ALIVE = 30;
    private final static short QOS = 0;
    private String name="mqttnotes";
    private String url="tcp://localhost:1882";
    private String topic="mqttnotes";
    private String payload="hello from notes";
    private MqttClient client=null;

public void initialize(String strURL, String strName) throws MqttException {
    name = strName;
    url = strURL;
    client = new MqttClient(url);
    client.connect(name, CLEAN_START, KEEP_ALIVE );
}

public void execute(String strTopic, String strPayload) throws MqttException {
    topic = strTopic;
    payload = strPayload;
    if (client != null) {
        client.publish(topic,payload.getBytes(),QOS,RETAINED);
    }
}

public void disconnect() throws MqttException {
    if (client != null) {
        client.disconnect();
    }
}

}

LotusScript Class

Next up, you’ll need a new LotusScript Library. I’ve called mine Class_MQTT. This wraps up the calls into LS2J and our java class  into a handy drop-in LotusScript class. The key lines are in the Options section:

  1. UseLSX “*javacon” which loads the LS2J bridge code.
  2. Use “Java_MQTT” which loads our java code from above.

The code is a standard LS Class, called MQTTSimple, with the addition of some new types.

  1. JavaSession is the link to the Java machine. It lets us find the classes we want to use. In this case “MQTT_Notes”.
  2. JavaClass is the handle to the class.
  3. JavaObject is the handle to the running object, using the class.
  4. JavaError lets us get errors from the underlying java into the LS On Error model.

Notice that there is an explicit Delete method in the code which is called when the LS object is destroyed to make sure the MQTT connection is closed. The code is listed below:

'Class_MQTT:

Option Public
Option Explicit
Uselsx "*javacon"
Use "Java_MQTT"

Class MQTTSimple
Private MQTTObj As JavaObject
Private MQTTError As JavaError
Private js As JavaSession

Sub New(url As String, title As String)
    Set js = New JavaSession
    Dim MQTTClass As JavaClass

    On Error Goto ErrHandler
    'Get our java class
    Set MQTTClass = js.GetClass ("MQTT_Notes")
    'Instantiate it.
    Set MQTTObj = MQTTClass.CreateObject ()
    'Call our initialize method to get a MQTT connection
    'Takes connection url and connection handle
    Call MQTTObj.Initialize (url, title)
    Exit Sub

ErrHandler:
    Set MQTTError = js.getLastJavaError()
    Print MQTTError.ErrorMsg
    Call js.ClearJavaError()
    Resume Next
End Sub

Sub Delete
    'Called as the object is destroyed.
    'Makes sure MQTT connection is shut.
    On Error Goto ErrHandler
    Call MQTTObj.disconnect()
    Exit Sub

ErrHandler:
    Set MQTTError = js.getLastJavaError()
    Print MQTTError.ErrorMsg
    Call js.ClearJavaError()
    Resume Next
End Sub

Sub Exec(topic As String, payload As String)
    'Publish given payload to given topic
    Call MQTTObj.execute(topic,payload)
    Exit Sub

ErrHandler:
    Set MQTTError = js.getLastJavaError()
    Print MQTTError.ErrorMsg
    Call js.ClearJavaError()
    Resume Next
End Sub

End Class

This code gives us a new LS MQTTSimple class which takes a URL and connection name on creation.
It has one visible method, exec, which takes a topic and payload string.

Notice that the errorhandling in the code uses the standard LS error handler, but gets it’s error information from the underlying java.
It’s not quiet seamless though as you have to cancel the error yourself.

Test Script

Lastly, there is a test script to test it all out. To use the MQTT class, simply add it into the Options section. The little agent below opens the mail file of the user and publishes it’s size in Mb. Assuming you have a broker running and some sort of client subscribed, the following should work. I used the RSMB broker on my laptop on port 1882  and the IA92 client subscribed to the Notes topic.

Option Public
Option Explicit
Use "Class_MQTT"

Sub Initialize
    Dim mqtt As MQTTSimple
    Dim db As NotesDatabase
    'Start the connection: URL and Connection string
    Set mqtt = New MQTTSimple("tcp://localhost:1882","NotesAgent")
    'Open user's mail file
    Set db = New NotesDatabase( "", "" )
    Call db.OpenMail
    'Publish database size in Mb to MQTT in the Notes topic at Qos0
    Call mqtt.Exec("Notes", "Mail database " & Format$(db.Size/1000000,"000,###")) )
End Sub

This is of course a bare bones system, but I can see a lot of uses: Mail-in databases, overnight agents, maintenance tasks etc. The best thing is that MQTT allows me to tie in all these disparate systems easily.