Running a Clojure REPL on OpenShift

Any Clojure programmer will tell you that you need a REPL (Read-Eval-Print-Loop) to develop and debug applications. Interactive development is one of the great benefits of the LISP world. It gets used rather more than shells in other languages such as Ruby and Python.

OpenShift app development is generally done locally then pushed to the cloud. But what if the deployed app doesn’t run for some reason? What you want to do is fire up a REPL and debug the app. While that should be possible in OpenShift (since you can ssh directly into your application) it’s not quite as straight-forward as you might hope.

Simply running lein repl in your project directory gives a stack trace:

Exception in thread "Thread-1" java.net.BindException: Permission denied
    at java.net.PlainSocketImpl.socketBind(Native Method)
    at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:376)
    at java.net.ServerSocket.bind(ServerSocket.java:376)
    at java.net.ServerSocket.(ServerSocket.java:237)
    at clojure.tools.nrepl.server$start_server.doInvoke(server.clj:127)
    at clojure.lang.RestFn.invoke(RestFn.java:457)
    at leiningen.repl$fn__4138.invoke(repl.clj:90)
    at clojure.lang.Delay.deref(Delay.java:33)
    at clojure.core$deref.invoke(core.clj:2080)
    at leiningen.repl$repl$fn__4172.invoke(repl.clj:175)
    at clojure.lang.AFn.applyToHelper(AFn.java:159)
    at clojure.lang.AFn.applyTo(AFn.java:151)
    at clojure.core$apply.invoke(core.clj:601)
    at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1771)
    at clojure.lang.RestFn.invoke(RestFn.java:425)
    at clojure.lang.AFn.applyToHelper(AFn.java:163)
    at clojure.lang.RestFn.applyTo(RestFn.java:132)
    at clojure.core$apply.invoke(core.clj:605)
    at clojure.core$bound_fn_STAR_$fn__3984.doInvoke(core.clj:1793)
    at clojure.lang.RestFn.invoke(RestFn.java:397)
    at clojure.lang.AFn.run(AFn.java:24)
    at java.lang.Thread.run(Thread.java:722)

There are two issues: The first is that, by default, leiningen tries to bind to locahost and find a random available port. The second is that OpenShift appears not to allow multiple Java threads in the same shell.

My workaround is to put a couple of bash scripts into the $OPENSHIFT_REPO_DIR/bin/ directory along with lein. The first (which I’ve named repl-server) sets up a headless server using the $OPENSHIFT_INTERNAL_IP for the host and a high open port number (35000 in this case):

#!/bin/bash
# Script to start nrepl server with leiningen
#
export HTTP_CLIENT="wget --no-check-certificate -O"
export LEIN_REPL_PORT=35000
export LEIN_REPL_HOST=$OPENSHIFT_INTERNAL_IP
export HOME=$OPENSHIFT_DATA_DIR/home
export LEIN_JVM_OPTS=-Duser.home=$HOME

$OPENSHIFT_REPO_DIR/bin/lein repl :headless >${OPENSHIFT_DIY_LOG_DIR}/repl.log 2>&1 &
disown

Then in lein-connect I have the following:

#!/bin/bash
# Script to connect to headless nrepl server with leiningen
#
export HTTP_CLIENT="wget --no-check-certificate -O"
export LEIN_REPL_PORT=35000
export LEIN_REPL_HOST=$OPENSHIFT_INTERNAL_IP
export HOME=$OPENSHIFT_DATA_DIR/home
export LEIN_JVM_OPTS=-Duser.home=$HOME

$OPENSHIFT_REPO_DIR/bin/repl-server
$OPENSHIFT_REPO_DIR/bin/lein repl :connect $LEIN_REPL_HOST:$LEIN_REPL_PORT

And now, after making the two scripts executable:
chmod +x repl-server lein-connect
we have the expected result:

../bin/lein-connect 
REPL-y 0.1.9
Clojure 1.4.0
    Exit: Control+D or (exit) or (quit)
Commands: (user/help)
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
          (user/sourcery function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
Examples from clojuredocs.org: [clojuredocs or cdoc]
          (user/clojuredocs name-here)
          (user/clojuredocs "ns-here" "name-here")
user=>

with any server errors being recorded in ${OPENSHIFT_DIY_LOG_DIR}/repl.log.

Happy debugging 🙂

About simonholgate

I'm CEO of Sea Level Research Ltd (www.sealevelresearch.com) - a Liverpool, UK based startup that uses machine learning to predict sea level surges and optimise shipping movements into and out of port. I'm an oceanographer and I'm also a Clojure developer who is interested in democracy and Big Data.
This entry was posted in Clojure, openshift and tagged , , , , . Bookmark the permalink.

1 Response to Running a Clojure REPL on OpenShift

  1. Pingback: OpenShift PaaS Week in Review - February 10, 2013 - Blog Import Demo Site

Leave a comment