by Luke VanderHart
You want to deploy a Clojure web application built using Ring as a standard web archive (WAR) file in a commonly used Java EE container such as Tomcat, JBoss, or WebLogic.
Assuming you are using Ring or a framework based on Ring (such as Compojure), the easiest way to structure your project to build as a WAR file is to use the lein-ring plug-in for Leiningen. Say that your project has a Ring handler function defined in a namespace called warsample.core,[1] like so:
(ns warsample.core)
(defn handler [request]
{:status 200
:headers {"content-type" "text/html"}
:body "<h1>Hello, world!</h1>"})
To configure the project with lein-ring, add the following key/value pairs to your Leiningen project.clj file:
:plugins [[lein-ring "0.8.8"]]
:ring {:handler warsample.core/handler}
You’ll also need to make sure that your application declares a
dependency on the javax.servlet/servlet-api library. Most web app libraries
do include a transitive dependency, which you can verify by running
lein deps :tree. If no other library you’re using includes it, you
can include it yourself by adding [javax.servlet/servlet-api "2.5"]
to the :dependencies key in project.clj.
The :plugins key specifies that the project uses the lein-ring plug-in, and the map under the :ring key specifies configuration options specific to lein-ring. The only required option is :handler, which indicates the var name of the application’s primary Ring handler function.
lein-ring provides a handy way to run your application locally, for development and testing. At the command line, simply type:
$ lein ring server
An embedded Jetty server will be started, serving your Ring application (on port 3000 by default, though you can change this in the lein-ring options). It will also open your operating system’s default browser to that page.
Once you think the application is running correctly, you can build a WAR file using the lein ring war or lein ring uberwar commands. Both take the name of the WAR file to emit:
$ lein ring war warsample.war
$ lein ring uberwar warsample-with-deps.war
lein ring war builds a WAR file containing only your application code, not any transitive dependencies, whereas lein ring uberwar will build a WAR file containing bundled JAR files for every dependency as well.
Both these commands will generate all the necessary configuration and wiring (such as a WEB-INF directory and a web.xml file) before building the WAR. See the discussion section for some options you can pass to lein ring that will influence how these artifacts are generated.
After issuing the WAR build command, you will find the WAR file you created in your project’s target directory. This is a perfectly normal WAR file that you can deploy just as if it were a standard J2EE WAR. Every application server is different, so check the documentation for your preferred system to see how to deploy a WAR file. If you have an operations team responsible for production deployments, you will definitely want to check with them to make sure you adhere to their processes and best practices.
It is crucial to understand the difference between a bare WAR file generated using lein ring war and an "uberwar" generated by lein ring uberwar, and when to use each.
A bare WAR file does not contain any of your project’s dependencies; it contains only the application code itself. This means that your program will not work unless you make sure that each and every JAR file your program depends on, including Clojure itself, is present on your web application’s shared library path. Exactly how to do this depends on the application server you’re using—you’ll have to refer to your system’s documentation to determine how to make them available.
An "uberwar," on the other hand, includes all the JARs your program depends on in the WAR archive as a bundled library under the WEB-INF/lib subfolder. Compliant application servers are capable of running each application (each deployed WAR file) in its own class loader context and will make the bundled JARs available only to their applications.
Typically, an uberwar is a safer choice. It spares you from much of the effort of manually curating your libraries, and better reflects how your application’s classpath probably looked in development.
The cost of an uberwar, however, is that a single library may be loaded multiple times if it is bundled by multiple applications. If you are running 10 applications, all of which use (say) Compojure, the server will actually load the Compojure code into the JVM’s class space 10 times, once for each application. Some organizations running resource-constrained or high-performance deployments prefer to ensure that there is minimal redundancy in application dependencies. If this is the case, then you may have to fall back to using a non-uber WAR file and managing your dependencies in your application server’s shared library pool by hand.
Although modern J2EE application servers do a pretty good job of keeping the classpaths and bundled libraries of different applications isolated from one another, you do have to be careful of the scenario where your application depends on a library that is part of the core J2EE platform, such as JDBC, the Servlet API, various XML libraries like JAX-*, StAX, JMS, etc.
These classes are usually provided by the application container itself, and if your application refers to them, those references will resolve to the instance provided by the container rather than the version your application has bundled. If they are exactly the same, well and good; but if there is a version mismatch that includes breaking changes in the class API, you may encounter cryptic errors as your application tries to call into classes that are different than the ones it was built against.
In this scenario, you will need to reconcile the dependency versions used by your application container and your application to make sure they are compatible.
lein-ring provides some additional options you can set in the :ring configuration map in project.clj to fine-tune how WAR files are generated. For an exhaustive description, see the lein-ring project page.
A few of the more useful ones are shown in lein-ring WAR options.
Key | Description | Default |
---|---|---|
:war-exclusions |
A sequence of regexes of files to exclude from the target WAR |
All hidden files |
:servlet-class |
The name of the generated Servlet class |
|
:servlet-name |
The name of the servlet in web.xml |
The name of the handler function |
:url-pattern |
The URL of the servlet mapping in web.xml |
/* |
:web-xml |
A specific web.xml file to use instead of the generated one |
If you aren’t using Ring, or if you have a good reason not to use the lein-ring plug-in, you can still create a WAR file, but the process is much more hands-on. Fortunately, a WAR file is essentially a JAR file with a different extension and some additional internal structure and configuration files, so you can use the standard lein jar tool to generate one—provided you add the following files at the appropriate locations in the archive.
You’ll also need to define some AOT classes implementing javax.servlet.Servlet yourself, and have these call into your Clojure application. Then you’ll need to wire them up to the application server using a deployment descriptor (web.xml).
The structure of a WAR file is:
<war root> |-- <static resources> |-- WEB-INF |-- web.xml |-- <app-server-specific deployment descriptors> |-- lib | |-- <bundled JAR libraries> |-- classes |-- <AOT compiled .class files for servlets, etc.> |-- <.clj source files>
A full explanation of all of these elements is beyond the scope of this recipe. For more information, see Oracle’s J2EE tutorial on packaging web archives.
Other web server libraries (for example, Pedestal Server) that include tooling for Leiningen will also often have a utility for building WAR files—check the documentation of the library you’re using.
-
lein-ring's project page
-
Oracle’s J2EE tutorial