Using ZeroMQ from Clojure

Published January 26, 2013

I'm quite the JVM noob with only 6 months of Clojure experience, and no prior JVM experience. After some hacking and tweaking, I managed to figure out how to use ZeroMQ from Clojure via Leiningen (the build tool). ZeroMQ is a native library that is packaged in a Java API via JNI, so the steps required weren't obvious to me.

These steps are slightly opinionated, but they should be easily modifiable if your opinion differs from mine.

Step 0: Install ZeroMQ

Do it! My development box is currently a Mac running OS X, so I used Homebrew. Your package manager of choice probably has a package for ZeroMQ you can use.

Step 1: Configure and build JZMQ

git clone git://github.com/zeromq/jzmq.git
cd jzmq
./autogen.sh
./configure --prefix=$HOME/myproject/jzmq-2.1.0-SNAPSHOT
make
make install

The actual path you specify with --prefix should be to the same folder as where you intend to put your Leiningen project. You'll see why that is in a second.

These steps are also basically copied from the README at https://github.com/zeromq/jzmq.

Step 2: Install the jar you just built into your local maven repo

mvn install:install-file -DgroupId=org.zeromq -DartifactId=jzmq \
-Dversion=2.1.0-SNAPSHOT -Dpackaging=jar \
-Dfile=$HOME/myproject/jzmq-2.1.0-SNAPSHOT/share/java/zmq.jar 

Leiningen hates free floating jars, but installing the jar into your local maven repo (typically ~/.m2) fools Leiningen into thinking it isn't actually a free floating jar.

Note that this jar has a relationship to the dynamic libraries you built with the "make" above, so you can't put this jar into a shared repository. The jar is local to your machine.

Step 3: Set up Leiningen

(defproject myproject "1.0.0"
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [org.zeromq/jzmq "2.1.0-SNAPSHOT"]]
  :profiles {:dev {:jvm-opts ["-Djava.library.path=../jzmq-2.1.0-SNAPSHOT/lib"]}}
  ...)

As you can see, we assume you put JZMQ into the same folder as your leiningen project, by referring to it via "../". This has two benefits:

You don't hardcode the absolute path into your project.clj. The actual path of a default JZMQ install will differ from system to sytem. Some might have it in /usr/local, some in $HOME/.brew/whatever, and so on. With a relative setup, you don't enforce any specific directory structure other than requiring that zeromq is in the same folder as your Leiningen project.

You get the specific version of JZMQ your project requires. If you use an absolute path, say /usr/local, you risk getting a different version of JZMQ than you intended, since you might have installed it into /usr/local via a package manager that might or might not install the version you expect it to.

Step 4: Use it!

lein repl
...
user=> (import [org.zeromq ZMQ ZMQ$Context ZMQ$Socket])
org.zeromq.ZMQ$Socket
user=>

Most Clojure wrappers around Java APIs are just, well, Clojure wrappers, so I prefer to use the Java APIS directly in almost all cases, unless the Clojure wrapper adds some value beyond just wrapping. Java interop is really easy with Clojure though, I recommend trying it out.


Questions or comments?

Feel free to contact me on Twitter, @augustl, or e-mail me at august@augustl.com.