8

I am building a test clojure/ring project to learn how it works. I created an app I call "junkapp" and it has really one handler

(defn handler [request]
  {:status 200
  :headers {"Content-type" "text/html"}
  :body "Hello World"})

And also one call to wrap-resource for static content

(def app
  (wrap-resource handler "public"))

So then in my project.clj I have a reference to lein-ring and also set :handler to my junkapp.core/app

:plugins [[lein-ring "0.8.5"]]
:ring {:handler junkapp.core/app}

when I run this with lein run, everything works as expected. A call to / returns "Hello World" and a call to /test.html returns the contents of resources/public/test.html.

But then I tried to build it into a war file with

lein ring uberwar junkapp.war

and put it under the webapps/ dir of a tomcat7 server. Now when I go to any path under junkapp (so /junkapp/, /junkapp/foo, /junkapp/test.html) it always returns "Hello World" and I can't seem to make it reference the static content at all. In googling I see people just saying to use compojure.route/resources but as I am learning I'd like it to work like this and then add in more libraries later. What's going on here?

MichaelB
  • 1,092
  • 2
  • 16
  • 32

2 Answers2

2

I think what's happening here is that there's some code in wrap-resources here, specifically this line:

(or ((head/wrap-head #(resource-request % root-path)) request)
  (handler request))))

What happens is that when built as a war file, it doesn't understand that WEB-INF/classes/ is the root of the path it should be using to serve up the static content. So it's looking for public/test.html somewhere else (perhaps the root of the .war?) and so this "or" is false so it falls through to calling the handler directly.

I am not sure of a fix for this as I am not totally sure the internal working of how tomcat is handling this internally... that is, I dont know where it is looking to find the base path.

juan.facorro
  • 9,791
  • 2
  • 33
  • 41
MichaelB
  • 1,092
  • 2
  • 16
  • 32
  • It has something to do with ServletConext. See this mailing list thread: https://groups.google.com/forum/#!topic/clojure-web-dev/dg_lVfi1y-w – noahlz Jun 27 '13 at 02:42
  • Yea, that basically says what I said also... that WEB-INF/classes should be the base of the classpath. So it should be able to find public/test.html underneath it. For some reason it isn't. – MichaelB Jun 27 '13 at 20:48
  • This might have additional hints: https://github.com/weavejester/compojure-example/issues/1#issuecomment-1538577 - interested in the resolution, if there is one. – noahlz Jul 05 '13 at 14:03
1

From my handler.clj (I'm using compojure and lib-noir)

; defroutes and route/* being from Compojure
(defroutes app-routes
  (route/resources "/")
  (route/not-found "Not Found"))

(def all-routes [admin-routes home-routes blog-routes app-routes])
(def app (-> (apply routes all-routes)
; etc. etc.
(def war-handler (middleware/war-handler app))

Now I don't know the particulars of how WARs are expected to behave, but lets note this war-handler from lib-noir:

(defn war-handler
  "wraps the app-handler in middleware needed for WAR deployment:
  - wrap-resource
  - wrap-file-info
  - wrap-base-url"
  [app-handler]
  (-> app-handler
      (wrap-resource "public")
      (wrap-file-info)
      (wrap-base-url)))

Example application CMS: https://github.com/bitemyapp/neubite/

Compojure (routing): https://github.com/weavejester/compojure

Bag of useful utilities harvested post-Noir: https://github.com/noir-clojure/lib-noir

Chris Allen
  • 194
  • 5