I needed to create an Unmanaged Extension in Neo4j but the prospect of using Java gave me shudders.
I have been programming in Clojure for a little while now. Given that Clojure is a JVM language, I decided to try to write the extension in that. Neo4j uses the JAX-RS API for supporting user supplied code. Luckily O’Reilly’s excellent book on Clojure, Clojure Programming had an example of a JAX-RS class. I love it when a plan comes together.
I modified that code to get what I needed. I hit across two issues:
-
A nasty
deftype
namespace not loading bug in Clojure. It seems to be an old one. But found a work around on Stack Overflow. -
Neo4j 2.0 documentation stated that a transaction is required only when the graph is being written to. Nope. I’ve highlighted it to them and as ever they’ve quickly fixed it. I was trying to get a node by its ID and I was getting a
NotInTransactionException
. Anyway its not needed for this exercise.
Here is my working code (I’ve tried to stay close to the Neo4j provided example; my only deviation being calling another function from the function which directly handles the GET
:
(ns jaxtest.core
(:import (java.nio.charset Charset)
(javax.ws.rs Path PathParam Produces GET)
(javax.ws.rs.core Context Response)))
(defn dummy [] "Hello World")
(definterface HelloWorld
(hello [^org.neo4j.graphdb.GraphDatabaseService database ^long node-id]))
(deftype ^{Path "/helloworld"} HelloWorldResource []
HelloWorld
(^{GET true
Produces ["text/plain"]
Path "/{nodeId}"}
hello
[this ^{Context true} database ^{PathParam "nodeId"} node-id]
(require 'jaxtest.core)
(let [result (-> (str (dummy) ", nodeId=" node-id)
(.getBytes (Charset/forName "UTF-8")))]
(-> (Response/ok)
(.entity result)
.build))))
So now I have Java, Scala (for Cypher) and Clojure (for my extension) all running in the same JVM.
JVM ftw! Woot!!