Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Exploded wars #48

wants to merge 1 commit into from

3 participants


Hi James,

I have added a :war-explode key to the options for lein-ring. If true, the output of lein ring war and lein ring uberwar will be output to a directory, analogous to mvn war:exploded.

I decided to retain the usage of JarOutputStream for minimal fuss while assembling the jar. To keep things clean and atomic I shifted the task to first build the jar in a temporary location and then ether copy or unzip it to the final location depending on the value of :war-explode.

Please let me know what you think, and thanks for all of your amazing work.



Sorry it's taken me so long to get around to reviewing this. Could you squash the "Typo" commit into "Adding documentation", and maybe the "Style" commit could be squashed into "Better handling..."


Sorry, not quite up to speed on best practices for pull requests. I rebased everything into a single commit and push --force'd. Please let me know if you'd like me to make any other changes.


Again, apologies for the lateness of my response. My to-do list has been long of late.

I've taken another look at this, and I think a better solution might be to have an :exploded command line option, rather than a configuration option. Also, the temporary directory thing should be removed, as the target directory already contains intermediate files.


Closing because of lack of activity, please reopen if you'd like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 14, 2012
  1. @diegs
This page is out of date. Refresh to see the latest.
@@ -152,6 +152,15 @@ The following war-specific options are supported:
* `:web-xml` -
web.xml file to use in place of auto-generated version (relative to project root).
+* `:war-explode` -
+ If true, war will be created in "exploded" form, i.e. as a directory. Defaults
+ to false.
+* `:war-name` -
+ This has the same effect as specifying the filename as an argument to `lein
+ ring war` or `lein ring uberwar`. Note: `lein ring uberwar` also respects
+ the `:uberjar` key in `project.clj`.
These keys should be placed under the `:ring` key in `project.clj`,
and are optional values. If not supplied, default values will be used instead.
12 src/leiningen/ring/uberwar.clj
@@ -5,8 +5,11 @@
[ :as io]))
(defn default-uberwar-name [project]
- (or (:uberjar-name project)
- (str (:name project) "-" (:version project) "-standalone.war")))
+ (or (get-in project [:ring :war-name])
+ (:uberjar-name project)
+ (if (war/explode-war? project)
+ (str (war/basic-war-name project) "-standalone"))
+ (str (war/basic-war-name project) "-standalone.war")))
(defn get-classpath [project]
(if-let [get-cp (resolve 'leiningen.core.classpath/get-classpath)]
@@ -47,12 +50,13 @@
([project war-name]
(ensure-handler-set! project)
(let [project (war/add-servlet-dep project)
- result (compile/compile project)]
+ result (compile/compile project)]
(when-not (and (number? result) (pos? result))
(let [war-path (war/war-file-path project war-name)]
(war/compile-servlet project)
(if (war/has-listener? project)
(war/compile-listener project))
- (write-uberwar project war-path)
+ (war/make-war project write-uberwar war-path)
(println "Created" war-path)
52 src/leiningen/ring/war.clj
@@ -9,13 +9,22 @@
(:import [java.util.jar Manifest
- [ BufferedOutputStream
+ [ BufferedOutputStream
+ File
- ByteArrayInputStream]))
+ ByteArrayInputStream]
+ [ ZipFile]))
+(defn explode-war? [project]
+ (get-in project [:ring :war-explode]))
+(defn basic-war-name [project]
+ (str (:name project) "-" (:version project)))
(defn default-war-name [project]
(or (get-in project [:ring :war-name])
- (str (:name project) "-" (:version project) ".war")))
+ (if (explode-war? project) (basic-war-name project))
+ (str (basic-war-name project) ".war")))
(defn war-file-path [project war-name]
(let [target-dir (or (:target-dir project) (:target-path project))]
@@ -218,13 +227,44 @@
[(:resources-path project)] (:resource-paths project))
:when path]
(dir-entry war-stream project "WEB-INF/classes/" path))
- (dir-entry war-stream project "" (war-resources-path project))
- war-stream))
+ (dir-entry war-stream project "" (war-resources-path project))))
(defn add-servlet-dep [project]
(update-in project [:dependencies]
conj ['ring/ring-servlet "1.1.0"]))
+(defn delete-recursive [file]
+ (if (.isDirectory file)
+ (dorun (map delete-recursive (.listFiles file))))
+ (.delete file))
+(defn unzip-entry [zip-file output-directory zip-entry]
+ (let [entry-path (.getName zip-entry)
+ output-file (io/file output-directory entry-path)]
+ (if-let [parent (.getParentFile output-file)]
+ (.mkdirs parent))
+ (with-open [data (.getInputStream zip-file zip-entry)]
+ (io/copy data output-file))))
+(defn unzip [input-path output-path]
+ (let [input-file (ZipFile. input-path)
+ zip-entries (enumeration-seq (.entries input-file))
+ output-directory (io/file output-path)]
+ (dorun (map (partial unzip-entry input-file output-directory) zip-entries))))
+(defn explode [tmp-path war-path]
+ (delete-recursive (io/file war-path))
+ (unzip tmp-path war-path))
+(defn make-war [project write-war-fn war-path]
+ (let [tmp-path (File/createTempFile (basic-war-name project) ".war")]
+ (write-war-fn project tmp-path)
+ (if (explode-war? project)
+ (explode tmp-path war-path)
+ (io/copy tmp-path war-path))
+ (io/delete-file tmp-path)
+ war-path))
(defn war
"Create a $PROJECT-$VERSION.war file."
@@ -238,6 +278,6 @@
(compile-servlet project)
(if (has-listener? project)
(compile-listener project))
- (write-war project war-path)
+ (make-war project write-war war-path)
(println "Created" war-path)
Something went wrong with that request. Please try again.