vrijdag 23 juli 2010

Deploying a Clojure web-application as a WAR file

After working my way trough several tutorials, a book on Clojure and the API docs I managed to produce a web-application using Compojure. If you're like me and made it this far you might be wondering; how do I deploy my application?

This post will walk you through deploying your application as a WAR file on Apache Tomcat 6 using the Apache HTTP Server as proxy complete with virtual hosting and static file serving. The chosen operating system is Ubuntu, keep this in mind if your system differs. Debian / Ubuntu might have some different locations / tools for managing Apache and Tomcat.

Step 1, creating a WAR file



To create the WAR file we will use Leiningen and the leiningen-war plugin. Open your project.clj file and add the following; uk.org.alienscience/leiningen-war "0.0.3" to; :dev-dependencies. My project.clj looks like:

(defproject myapp "1.0.0-SNAPSHOT"
;; Removed some lines for clarity
:dev-dependencies [[swank-clojure "1.2.0"]
[uk.org.alienscience/leiningen-war "0.0.3"]]
;; The rest of your config
:namespaces [myapp.servlet])


Please lookup the most recent available version on Clojars before copying the code.

This plugin will create a WAR file when we run: lein uberwar. Before we can do this we need to create a servlet class. Add a file called servlet.clj to your project in src/myapp/servlet.clj (change myapp as needed).

Within this file add the following lines:

(ns myapp.servlet
(:gen-class :extends javax.servlet.http.HttpServlet)
(:require [compojure.route :as route])
(:use ring.util.servlet
[myapp.core :only [application]]))

(defservice application)


In this case "application" should be the name you chose in core.clj for your defroutes. If you want to have Ring middle-ware as well either wrap it here or create a "def" which you can reference instead.

Now you can compile the servlet class with: lein compile. Before the WAR file can be created a web.xml file also needs to be setup. Create a file called src/web.xml with the following contents.

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
  <display-name>My App</display-name>

  <servlet>
    <servlet-name>myapp</servlet-name>
    <servlet-class>myapp.servlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>myapp</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

</web-app>

Create the WAR with: lein uberwar. Using uberwar makes sure all your dependencies are included.

Step 2, getting the packages


I will be assuming you already have a working Java installation. Enter the following command line to install the dependencies:

sudo apt-get install tomcat6 apache2


Step 3, configure Tomcat


Since I want my application to be available at mydomain.example.com I need to setup Tomcat for virtual hosting. To do this open /etc/tomcat6/server.xml. Search for the &lthost name=".. and add one below like:

<Host name=" com="" appbase="myapp">
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">



You will also need to enable the connector for the AJP protocol. This will be used for interaction between Tomcat and Apache. To do this uncomment the line which says; <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Your web-application needs to be placed in a new directory within the Tomcat installation (as defined with "appBase"). Create this directory with the following commands:

sudo mkdir /var/lib/tomcat6/myapp
sudo chown tomcat6:tomcat6 /var/lib/tomcat6/myapp


Step 4, configuring Apache



Apache has a module specifically designed for proxy-ing traffic to Tomcat. To enable this module run: sudo a2enmod proxy_ajp. Now we can create a virtual host configuration file.

Using your editor of choice open: /etc/apache2/site-available/myapp.example.com. Paste in something like:

<VirtualHost *:80>
ServerName myapp.example.com
ErrorLog /var/log/apache2/myapp.error.log
CustomLog /var/log/apache2/myapp.log combined

Alias /static/ "/var/myapp/static/"
<Directory /var/myapp/static/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>

<Proxy *>
AddDefaultCharset Off
Order deny,allow
Allow from all
</Proxy>

ProxyPass /static !
ProxyPass / ajp://localhost:8009/
ProxyPassReverse / ajp://localhost:8009/
</VirtualHost>


The /var/myapp/static directory will contain the applications static files. You can put this anywhere you want as long as it's readable for Apache.

Now enable the virtual host with: sudo a2ensite myapp.example.com.

Step 5, install application and load



All we have to do now is to install the WAR file. Copy or move the WAR file created in step 1 to /var/lib/tomcat6/myapp/ROOT.war. Make sure it's name will be ROOT.war. This tells Tomcat to use it to "mount" you application on /. Otherwise it will mount it on /name-of-warfile.

The last step is to restart all Tomcat and Apache:

sudo /etc/init.d/tomcat6 restart
sudo /etc/init.d/apache2 restart


If everything went well you should now be able to visit myapp.example.com to see your application. To diagnose any problems look in the files specified in /var/log/tomcat6/catalina.out and the ones specified in your Apache virtual host.