How To Integrate Clojure Web Applications in Apache

DeploymentClojureWarCompojure

Deployment Problem Overview


Note

Given this OP was written about two years ago, rather than ask the same question again, I am wondering if step-by-step instructions exist, so that I can integrate a Noir or other Clojure web application into Apache, whether it's Jetty, Tomcat, or something else. Similar instructions exist for Django, and I think I understand that Python is being run in Django's case as an engine rather than a ring framework, so things are more complicated with Clojure web applications.

End Note

I'm deeply in love with Clojure, and Compojure seems like a neat web framework.

But it all fell apart when I wanted to deploy my application on a regular application server like Tomcat as a WAR. I actually had to write custom Ring handlers for static files and resources that would work on both local Jetty and Tomcat because the stock handlers didn't, and I had to prepend the context root everywhere manually.

I am negatively amazed that I had to write all this code to create the simplest web application conceivable that would run on both Jetty and Tomcat. I have only three possible explanations for this:

  1. Nobody uses Clojure/Compojure for anything other than local development with Jetty, i.e. in production
  2. Everybody deploys Clojure/Compojure apps on a dedicated Jetty without context root (LIke people do it with Node.js apps)
  3. There is a really simple way to circumvent the problems I ran into I wasn't aware of

Which of these is in your opinion the case? Or is it something else?

Edit:

Please note that creating a war file is a no-brainer with Maven/Leiningen and not what I mean. I'm wondering that I have to write so much code to make Compojure work with Tomcat, basic stuff like static file serving and context root awareness that should work out of the box.

Deployment Solutions


Solution 1 - Deployment

I use a combination of the following to make this fairly painless:

Cake (incl. the deploy command)

A Cake template for webprojects developed by Lau Jensen.

Vagrant (Ruby VM(Virtualbox) management tool, which relies on Chef or Puppet)

VPS (from Slicehost)

The key part is the webdev template that Lau made. The webdev folder should be placed in the ~/.cake/templates. To create a new project based on it use:

cake new webdev *projectname*

Pls note that the template includes log4j and Java mail which can/should be excluded if not needed. It further assumes you are using Enlive and Moustache but changing that to Compojure/Hiccup is trivial if that is your poison.

The template takes care of serving the app from jetty in development (you just eval server.clj) and works as a war when running under Tomcat. Routes remain identical if deployed to the server as ROOT.war under Tomcat. All static files should be located in the resources dir. Jetty will serve them from there (thanks to the Ring file middleware). In production these are moved to the root of the webapp and served from there by Tomcat(the web.xml takes care of that).

The devbox folder contains a Vagrantfile and cookbooks necessary to create a Virtualbox VM with Tomcat installed. I use cake to deploy the .war file to the /home/vagrant dir (this is controlled from the definition of the dev context in project.clj). The .war file is symlinked into Tomcat's webapps dir (/var/lib/tomcat6/webapps) as ROOT.war. For further info on how to use Vagrant please see the Vagrant site.

This gist shows an example of how to adapt the project.clj to use the cake deploy command. The example creates two contexts @dev and @prod which you can deploy to using:

cake deploy @dev / cake delpoy @prod

I have collected the Cake webdev template and the Vagrant files in this zip.

Solution 2 - Deployment

People are deploying Compojure apps to non-Jetty servlet containers.

Check out:

Also check out lein-war

Solution 3 - Deployment

I've had some success using leiningen-war to generate a generic war file (assuming you are using leiningen, of course). It allows you to specify locations for static html, the location of a web.xml and other resources in your project.clj file.

It wasn't too difficult for me to produce a generic war file that I was able to deploy to JBoss (running Tomcat as a servlet container) but I think you have to be pretty familiar with the web.xml format. I'm more comfortable with authoring my own web.xml so that may account for my liking this approach more.

It appears that the person behind leiningen-war is recommending lein-ring now. I've started looking at that but so far I haven't been able to get a generic war file from it as easily.

I agree though that accounting for production deployment is a weakness here.

Solution 4 - Deployment

I am using Noir, a web framework built on top of Ring and Compojure.

I have created project using lein noir new my-proj. Then I created my-proj/web directory and added following lines to

project.clj:    
    :compile-path "web/WEB-INF/classes"
    :library-path "web/WEB-INF/lib"
    :ring {:handler project.server/handler}

I have set my-proj/web directory as context root during development for Tomcat.

For static file serving, I put stuff under my-proj/resources/public directory. For accessing (read/write) files through code, :servlet-context from ring request header can be used. With above settings, contextual path would be: (.getRealPath (ring-request-header :servlet-context) "/WEB-INF/classes/myfile.txt"). Myfile.txt is under my-proj/resources.

Solution 5 - Deployment

If you're deploying on Google App Engine, here's a great blog http://compojureongae.posterous.com/

You may still get some useful tips even if you're not deploying to GAE.

Solution 6 - Deployment

I have released in production (for over 6 months now) a Compojure+Vaadin application. Packaging has been done using the lein-war plugin and I did not encounter any major problems.

The application is named halo, contained in a halo.war file, the context is /halo and the tomcat server is also running Hudson, and another custom made application. Vaadin is resolving all my static files properly, CSS, images... I mainly used that blog post to write the little glue needed for it.

On another note, I am also running Noir on Heroku and haven't had any problems regarding packaging and deployment, and this is in production too.

This is not directly related to your Compojure question, but Clojure in production in web applications, yes, definitely.

Solution 7 - Deployment

If you are using a ring-based server (compojure, noir/lib-noir, luminus, etc.), and want to deploy as an uberjar, to avoid

"Failed to load Main-Class manifest attribute from your-uberjar.jar"

simply create the uberjar with lein ring uberjar. Note the 'ring' addition to lein uberjar. This is assuming you're using the lein-ring plugin.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfutlibView Question on Stackoverflow
Solution 1 - DeploymentmacView Answer on Stackoverflow
Solution 2 - DeploymentDon JacksonView Answer on Stackoverflow
Solution 3 - DeploymentstandView Answer on Stackoverflow
Solution 4 - Deploymentuser954311View Answer on Stackoverflow
Solution 5 - DeploymentMatthew BostonView Answer on Stackoverflow
Solution 6 - DeploymentNicolas ModrzykView Answer on Stackoverflow
Solution 7 - DeploymentGreg BView Answer on Stackoverflow