Interactive Programming with Clojure, Compojure, Google App Engine and Emacs

Clojure is lisp running on the JVM. Interactive programming with Clojure is fun. Just hit C-c C-c to compile a defun in Emacs. If you come from Java, you will appreciate this: No long Build-Deploy-Run cycles anymore. But if you want to write Clojure programs for Google App Engine, it looks like interactive programming is not possible. Really?! No! This tutorial shows, how to develop applications interactively lisp-style with Clojure, Compojure, Google App Engine and Emacs.

The Google App Engine SDK includes a development server (based on Jetty) that allows you to test your application in an environment that is close to the real one. This is nice for a pre-flight check but for every change you make in the code you must stop the devserver, recompile and start the server again. You can't develop your code incrementally and interactively as you're used to in a lispy language.

We'll presume that Emacs and Clojure is configured, working with Compojure is familiar and that the App Engine SDK has been downloaded and installed.

John Hume created a nice little clojure binding for appengine that we'll use here.

This is the example servlet:
; a basic application using the google app engine
; when this file is created it will create a class that extends
; javax.servlet.http.HttpServlet which can be mapped in the
; applications web.xml.

(ns helloworld
  (:gen-class :extends javax.servlet.http.HttpServlet)
  (:use compojure.http compojure.html)
  (:require [appengine-clj.datastore :as ds])
  (:import [ Query]))

(defn index
  (let [items (ds/find-all (Query. "item"))]
      [:h1 (str "Hello World. There are " (count items) " in the database.")]
      [:a {:href "/new"} "Create another one"])))

(defn new
    (ds/create {:kind "item" :text "something"})
    (redirect-to "/")))

(defroutes helloworld
  (GET "/" index)
  (GET "/new" new))

(defservice helloworld)

This file compiles to a servlet class that can be used in app engine (see the introduction post about clojure on app engine for a more detailed example). If you navigate your browser to "/" when the devserver is running you'll see a page displaying the amount of "item" entities in the datastore. If you click on the link you'll create a new entity and are redirected to the index page.

In order to develop the code more interactively, we'll create another file that will start a Jetty server. We'll call this file start0.clj:

; starting the application in a jetty.
; the original application is decorated by a function that sets up the
; app engine services.

(ns start0
  (:use helloworld)
  (:use compojure.server.jetty compojure.http compojure.control))

(defn start-it
    (run-server {:port 9090} "/*" (servlet helloworld))))

Compiling this file with C-c C-k in Emacs and calling the function start-it in the REPL will start the Jetty. The application is available on port 9090.

If you view the application in the browser, there's a problem.

More work is needed. The services aren't initialized yet. We'll fix this in start.clj:

; starting the application in a jetty.
; the original application is decorated by a function that sets up the
; app engine services.

(ns start
  (:use helloworld)
  (:use compojure.server.jetty compojure.http compojure.control))

(defmacro with-app-engine
  "testing macro to create an environment for a thread"
    `(with-app-engine env-proxy ~body))
  ([proxy body]
    `(last (doall [( ~proxy)

(defn login-aware-proxy
  "returns a proxy for the google apps environment that works locally"
  (let [email (:email (:session request))]
    (proxy [$Environment] []
      (isLoggedIn [] (boolean email))
      (getAuthDomain [] "")
      (getRequestNamespace [] "")
      (getDefaultNamespace [] "")
      (getAttributes [] (java.util.HashMap.))
      (getEmail [] (or email ""))
      (isAdmin [] true)
      (getAppId [] "local"))))

(defn environment-decorator
  "decorates the given application with a local version of the app engine environment"
    (fn [request]
      (with-app-engine (login-aware-proxy request)
      (application request))))

(defn init-app-engine
  "Initialize the app engine services."
    (init-app-engine "/tmp"))
    (proxy [] [( dir)]))))

;; make sure every thread has the environment set up

(defn start-it
    (run-server {:port 8080} "/*" (servlet (environment-decorator helloworld)))))

Before running jetty, local test implementations of the services are registered. Also, every request is wrapped by a function that registers the services for the current thread. Now the application runs as expected:

Google includes test implementations of the services in the jar files "appengine-local-runtime.jar" and "appengine-api-stubs.jar". So these two must be in the classpath of the clojure that is run from Emacs/SLIME. Be careful not to include these two jars in the war file that you deploy to the app engine servers. The application won't run!

Now that you've got a setup like this, incremental changes to the code are very simple: Try to change the text in the function "index" and just recompile the function with C-c C-c. The change should appear immediately if you reload the page in your browser.

Now you can work interactivly with Clojure and Google App Engine!