„Freiheit oder Tod“

Clojure Web Framework Compojure

January 31st, 2010 posted in blog
I’ve been playing around with clojure this weekend and after I learned the basics of the language I wanted to see what possibilities I have to do web development using clojure because it’s one of my main interests for now and the last few years.

I googled for “clojure web framework” and found compojure and though I had no idea about its quality I tried it out.

Compojure embeds the jetty http server and though you are not limited to jetty it’s a good default choice and my primary goal is also to embed jetty because I want to have it behind a reverse proxy like pound so I have the maximum flexibility. Jetty is fast, solid and easy to use.

To get yourself started quickly, create a directory where you will store all required jar files for your compojure web application like “/home/andreas/clojure/jars/”. You’ll need the following files: You now should have the following files:
  • clojure-contrib.jar
  • commons-fileupload-1.2.1.jar
  • grizzly-http-servlet-1.9.10.jar
  • jetty-util-6.1.15.jar
  • clojure.jar
  • commons-io-1.4.jar
  • grizzly-http-webserver-1.9.10.jar
  • servlet-api-2.5-20081211.jar
  • commons-codec-1.3.jar
  • compojure.jar
  • jetty-6.1.15.jar
Regarding the wikibooks hello world your hello world could look like this:

(ns hello-world
  (:use compojure))
 
(defroutes greeter
  (GET "/"
    (html [:h1 "Hello World"])))
 
(run-server {:port 8080}
  "/*" (servlet greeter))
I am using gedit with the external tools plugin which is not activated by default on ubuntu but you can simply enable it in the preferences. This plugin is very useful for what I need because for this example we need a simple script that calls clojure.

My script for executing/compiling a clojure file looks like this:

#!/bin/bash
for f in /home/andreas/clojure/jars/*.jar; do
    CLASSPATH=$CLASSPATH:$f
done
java -cp $CLASSPATH clojure.main $GEDIT_CURRENT_DOCUMENT_NAME

If you try out the hello world example and execute it you can open your webbrowser at “localhost:8080″ and you’ll see your hello world message.

Now if you want to distribute your web app to some customer’s server you probably don’t want to publish the source code (depends :P ), so whether you create a war file which would expect a running application server being there set up or you are going the route to have a standalone clojure/java app running that embeds jetty and runs in the background as a so called daemon. In this case you need to use a dedicated port just for this app but using pound it can give you a great flexibility (app servers are flexible, too; it simply depends).

In the case you want to create a standalone jar file that you can start with java -jar you have to modify your hello world example and your buildscript:

the hello world example modified

(ns main
	(:gen-class)
	(:use compojure))
 
(defroutes greeter
	(GET "/"
		(html [:h1 "Hello World"])))
 
(defn -main
	[]
	(run-server {:port 8080}
		"/*" (servlet greeter)))


the script to build a jar file

#!/bin/bash
for f in /home/andreas/clojure/jars/*.jar; do
    CLASSPATH=$CLASSPATH:$f
done
 
echo "Main-Class: main" > $GEDIT_CURRENT_DOCUMENT_DIR/Manifest.txt
echo "Class-Path: ." >> $GEDIT_CURRENT_DOCUMENT_DIR/Manifest.txt
 
rm -rf $GEDIT_CURRENT_DOCUMENT_DIR/classes/*
mkdir -p $GEDIT_CURRENT_DOCUMENT_DIR/classes
 
for f in /home/andreas/clojure/jars/*.jar; do
    unzip -u $f -d $GEDIT_CURRENT_DOCUMENT_DIR/classes/ > /tmp/foo
done
 
CLASSPATH=$CLASSPATH:classes/
 
rm -rf $GEDIT_CURRENT_DOCUMENT_DIR/classes/META-INF
 
java -cp $CLASSPATH clojure.main -e "(compile 'main)" > /tmp/foo
jar cmf $GEDIT_CURRENT_DOCUMENT_DIR/Manifest.txt $GEDIT_CURRENT_DOCUMENT_NAME.jar -C classes . > /tmp/foo
java -jar $GEDIT_CURRENT_DOCUMENT_NAME.jar
Gedit has a tiny bug that prevents the java process to be killed after execution. Kill the process manually and you can start the jar with “java -jar main.clj.jar”. Rename the file if you wish to do so.

serving static files
by default your static files are expected to reside in “public”.

(defroutes greeter
	(GET "/"
		(html (greet "andreas")))
	(GET "/*" 
    	(or (serve-file (params :*)) :next)) 
  	(ANY "*" 
    	(page-not-found)))

implement your favicon

(defroutes greeter
	(GET "/favicon.ico"
		(redirect-to "/fav.ico"))
	(GET "/"
		(html (greet "andreas")))
	(GET "/*" 
    	(or (serve-file (params :*)) :next)) 
  	(ANY "*" 
    	(page-not-found)))

It only works if you serve static files as shown in this example.

get GET parameters

(GET "/greet"
		(html (greet (params :user))))


For web development you probably want to read config values from a json file:

1.) set up lein (see this text)
2.) clone the clojure-json repository:
git clone git://github.com/danlarkin/clojure-json.git

3.) cd to clojure-json and issue:
lein jar

This will create a jar that you have to put in your jar folder or add it to your classpath.

Let’s assume the following json file:

{
	"database": {
		"host": "127.0.0.1",
		"user": "postgres",
		"pass": "secret",
		"name": "mydatabase"
	},
	"email": {
		"user": "foo@bar.com",
		"pass": "mysecret"
	}
}


Let’s assume you want to get the value of the database host:

(ns my.config)
(require '(org.danlarkin [json :as json]))
 
(defn read-custom-config
  [file]
  (json/decode-from-str (slurp file)))
 
(println (-> (read-custom-config "config.json") :database :host))


You can also write it like this:

(ns my.config)
(require '(org.danlarkin [json :as json]))
 
(defn read-custom-config
  [file]
  (json/decode-from-str (slurp file)))
 
(println (:host (:database (read-custom-config "config.json"))))


For now that’s all I know about compojure, too :) .
 
follow comments via rss
  1. 3 Responses to “Clojure Web Framework Compojure”

  2. By Dan Waldron on Jan 31, 2010

    I must say this is a great article i enjoyed reading it keep the good work :)

  1. 2 Trackback(s)

  2. Feb 2, 2010: Tweets die Clojure Web Framework Compojure – Schipplock - Man On The Silver Mountain - schipplock.de erwähnt -- Topsy.com
  3. Feb 14, 2010: Clojure contrib.sql timestamp in postgresql – Schipplock - Man On The Silver Mountain - schipplock.de

Post a Comment