1.22 Using MQTT in Common Lisp with ABCL and the Eclipse Paho client.
=====================================================================
2013-07-10
I have recently written some posts about using Clojure to take
advantage of Java libraries. The idea of using a Lisp language that can
take advantage of existing Java infrastructure is something that can
however be achieved in different ways, Clojure being but one of them.
On the Scheme side I know at least GNU Kawa
(
https://www.gnu.org/software/kawa/) and SISC (
http://sisc-scheme.org/),
and on the Common Lisp side Armed Bear Common Lisp (ABCL). Since I lean
towards Common Lisp more than Scheme, I have made some experiences with
ABCL (including some comparisons with Clojure in terms of syntax which I
will likely post about since the original content, sent to the ABCL
mailing list as a pastebin link, is no longer accessible).
This is the simple MQTT example, which I previously wrote in Clojure,
written in Common Lisp and using ABCL Java interop (gist here
(
https://gist.github.com/fsmunoz/5850260#file-abcl-mqtt-lisp)):
(defpackage #:abcl-mqtt
(:use #:cl))
(require :abcl-contrib)
(require :jss)
(in-package #:abcl-mqtt)
(defparameter broker-url "tcp://m2m.eclipse.org:1883") ; Eclipse sandbox server, see
http://m2m.eclipse.org/sandbox.html for details on use
(defparameter mqtt-topic-name "abcltest")
(defparameter mqtt-message (or (car EXT:*COMMAND-LINE-ARGUMENT-LIST*) ; If there is no command-line argument then use a quote from Pessoa's Tobacco Shop
"But at least, out of my bitterness at what I'll never be, There's the quick calligraphy of these lines, The broken archway to the Impossible."))
(defparameter client-id "FSM-ABCL-Test")
(defun mqtt-callback ()
"Function called after delivery confirmation"
(jss::jinterface-implementation "org.eclipse.paho.client.mqttv3.MqttCallback"
"connectionLost"
(lambda (cause)
(print cause))
"messageArrived"
(lambda (topic message) ;; not used
(print (format nil "Topic: ~A" (#"getName" topic)))
(print (format nil "Message: ~A" (#"getPayload" message))))
"deliveryComplete"
(lambda (token)
(print "*** DELIVERY COMPLETE ***"))))
(defun mqtt-connect (topic broker-url client-id)
"Establishes a MQTT connection to TOPIC; returns the mqtt client object"
(let ((mqtt-conn-options (jss::new 'MqttConnectOptions))
(mqtt-client (jss::new 'MqttClient broker-url client-id)))
(#"setCleanSession" mqtt-conn-options jss::+true+)
(#"setKeepAliveInterval" mqtt-conn-options 30)
(print (format nil "Connected to ~A" broker-url))
(#"setCallback" mqtt-client (mqtt-callback))
(#"connect" mqtt-client mqtt-conn-options)
mqtt-client))
(defun mqtt-create-message (message)
"Creates a MQTT message from MESSAGE, a string"
(let ((mqtt-message (jss::new 'MqttMessage (#"getBytes" message))))
(#"setQos" mqtt-message 0)
(#"setRetained" mqtt-message jss::+false+)
mqtt-message))
(defun mqtt-publish (topic message)
"Publishes MESSAGE to TOPIC"
(print (format nil "*** PUBLISHING TO TOPIC ~A ***" (#"toString" topic)))
(#"waitForCompletion" (#"publish" topic message)))
(defun testmq ()
"Main demo function, creates the connection and sends the message"
(let* ((client (mqtt-connect mqtt-topic-name broker-url client-id))
(topic (#"getTopic" client mqtt-topic-name))
(message (mqtt-create-message mqtt-message)))
(mqtt-publish topic message)
(sleep 0.2)
(#"disconnect" client)))
;; Make it all work
(testmq)
(cl-user::quit)
The use of ABCL Java facilities is of course essential to use the the
Java libraries (see Hans Hübner's post about using ABCL to parse Excel
files using Apache POI
(
https://netzhansa.blogspot.com/2013/03/dealing-with-excel-files-from-common.html)
for another example of the usefulness of this), so those parts aren't
portable to other CL implementations, but the rest of the code is. This
means that by using ABCL one can use CL - which is a standartised
language with multiple implementations - for the code, and that even
with non-portable parts (which can be isolated) the bulk of the code is
implementation independent.
Additionally (and at least equally important) it allows the use of
Common Lisp wherever a JVM exists, which in itself is an advantage to
those who know the language and do not want to program in Clojure for
whatever reason. There are several Java interop options built into ABCL
- the example above uses JSS 3
(
http://abcl-dev.blogspot.com/2012/02/jss-3.html), but there are also
the included primitives and even Rich Hickey's pre-Clojure framework
JFLI (
http://abcl-dev.blogspot.com/2012/05/jfli-added-to-abcl-110.html).