compiling clojure for a servlet container (tomcat)

265 views
Skip to first unread message

toddg

unread,
Jun 20, 2010, 12:36:55 PM6/20/10
to Clojure
1. Loading .clj files
Is it possible to load up .clj files from the classpath of an
arbitrary java app? For example, could you proxy HttpServlet and run
your servlet as a .clj from within a servlet container? If not, and
you have to gen-class the clojure servlet class, could the servlet
bootstrap the clojure environment and proceed to load additional .clj
files off the classpath?

Essentially, I'm trying to figure out the easiest path to incorporate
clojure code into an existing java+tomcat environment (although I
think this is interesting in many other cases, too.)

2. Spring integration
Along this note, someone asked a question awhile back about
integrating spring with clojure. Is it possible to autowire either
proxy or gen-class clojure classes? If so, does anyone have any
experience with this scenario? It would be very cool to be able to
progressively re-write a java app by progressively replacing java
implementations with clojure implementations as a way to evangelize
the language at work.

3. Multi-Module Project Dependencies
Lastly on this topic, I've been attempting to create a multi-module
project in Intellij that uses both java and clojure. Typically, you
set module foo to depend on module bar, and the classpath gets
configured properly. What I'm seeing is that laclojure (or whatever)
is not compiling the .clj files to .class files, so my output dir is
empty. As a result, there is nothing for foo to depend on. How is this
supposed to work? Should I be copying the .clj files to the output
dir? Or do I need to gen-class everything and only use .class files?
Basically this is an extension of #1 above.

4. Ant tasks for building not working on head
I sent a note to the task maintainer, Ivan Chernetsky, on this, but I
thought I'd see if anyone else out there is using this. The last
thread I read on this indicated that the tasks only worked w/ Clojure
1.0, and that he was thinking of getting it to work with HEAD. My
vote: +1.

-Todd

p.s.

If this is a double post, my apologies. I sent this from my email
client yesterday, and did not see it come up in the list, so I'm
resending via the google groups web page.

Adrian Cuthbertson

unread,
Jun 21, 2010, 2:38:04 AM6/21/10
to clo...@googlegroups.com
> 1. Loading .clj files
> Is it possible to load up .clj files from the classpath of an
> arbitrary java app? For example, could you proxy HttpServlet and run
> your servlet as a .clj from within a servlet container?

Hi Todd, here's a pattern for doing what you want;

1.) Create svlt/Svlt.clj as below
2.) Compile it separately using
(binding [*compile-path* "./tmp"] (compile 'svlt.Svlt))
3.) cp ./tmp/* [tomcat]/myctx/WEB-INF/classes/
4.) cp clojure.jar to myctx/WEB-INF/lib
It is better to have clojure.jar in each context's WEB-INF
as then class loading works correctly.
5.) Create your app clj functions in say myapp/clfns.clj
(as below) and copy this (not compiled)
to myctx/WEB-INF/classes/myapp/clfns.clj
6.) Add to myctx/WEB-INF/web.xml;
<servlet>
<servlet-name>Svlt</servlet-name>
<servlet-class>svlt.Svlt</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Svlt</servlet-name>
<url-pattern>/svlt/*</url-pattern>
7.) Open http://whatever/myctx/svlt/myapp.clfns/html-hi

; -------------------- svlt/Svlt.clj
(ns svlt.Svlt
(import (javax.servlet.http HttpServletRequest
HttpServletResponse))
(:gen-class :extends javax.servlet.http.HttpServlet))

(def re-ipath #"\/(.*)\/(.*)")

(defn -service
[this #^HttpServletRequest req #^HttpServletRequest rsp]
(let [ipath (.getPathInfo req)
g (re-matches re-ipath ipath)
_ (when (nil? g) (throw (java.io.IOException.
(str "Invalid svlt ipath (parse): " ipath))))
[_ ns-sym req-fn-nm] g
ns-sym (symbol ns-sym)
found-ns (find-ns ns-sym)
found-ns (if (nil? found-ns)
(let [n (create-ns ns-sym)] (require ns-sym) n)
found-ns)
_ (when (nil? found-ns) (throw (java.io.IOException.
(str "Namespace not found for: " ns-sym))))
req-fn (get (ns-publics ns-sym) (symbol req-fn-nm)) ]
(req-fn req rsp)))

; --------------------- myapp/clfns.clj
(ns myapp.clfns
(:import (javax.servlet.http HttpServletRequest
HttpServletResponse)))

(defn html-hi
[#^HttpServletRequest req #^HttpServletResponse rsp]
(.setContentType rsp "text/html")
(with-open [wtr (java.io.PrintWriter. (.getOutputStream rsp))]
(.println wtr "<html><body><p>Hi</p></body></html>"))))

Notes;
- to update, simply copy new app clj file/s to WEB-INF/classes and
reload the context.
- Borrow http://github.com/weavejester/hiccup for some cool html
generation stuff.
- you should be able to adapt the above (namespace requiring) to your
other java integration needs.

-Rgds, Adrian.

Alex Ott

unread,
Jun 21, 2010, 8:48:30 AM6/21/10
to clo...@googlegroups.com
Hello


AC> Notes;
AC> - to update, simply copy new app clj file/s to WEB-INF/classes and
AC> reload the context.
AC> - Borrow http://github.com/weavejester/hiccup for some cool html
AC> generation stuff.
AC> - you should be able to adapt the above (namespace requiring) to your
AC> other java integration needs.

You can use Compojure to build webapp in more convenient way.

It's also possible to use Leiningen + lein-war plugin to build .war file
with all dependencies inside.

Another possibility is use maven to build .war. I have simple example of
Compojure + war at
http://github.com/alexott/clojure-examples/tree/master/compojure-simple/,
but I have no description of it yet (I planned to do this, but had no
time).

This is very simple multi-module example - cpj-core defines compojure
routes + implementation of functions, while cpj-war is used to generate
.war file. (cpj-standalone uses jetty to run webapp without deploying to
existing container)


--
With best wishes, Alex Ott, MBA
http://alexott.blogspot.com/ http://alexott.net
http://alexott-ru.blogspot.com/

Adrian Cuthbertson

unread,
Jun 22, 2010, 12:37:17 AM6/22/10
to clo...@googlegroups.com
> You can use Compojure to build webapp in more convenient way.

I should elaborate on my previous post. It was intended not to
recommend the clojure way of building web apps (there's plenty of info
regarding that - compojure, ring, clout, hiccup, conjure, etc), but
rather as a specific, detailed example of integration from java to
clojure and how to extend java classes and implement java interfaces
in clojure.

A "canonical" example of this, (as Todd originally requested) is how
one implements the servet interface in clojure and uses it in a
container like tomcat. That is presented in my example.

A further important detail (for me at least), is how one can (from
java) dynamically load clojure namespaces and call clojure functions
in a way that allows one to extend existing java systems easily. That
is also shown in the example (see the ns-sym/require logic). This is
very useful for dynamically loading alternative implementations of
functions from different namespaces, giving the ability to deploy
dependency injection, factory mechanisms and other such strategies.

An example of using this ns loading technique for creating TimerTasks,
callable from java (or clojure), implemented in clojure is shown in
http://groups.google.co.za/group/clojure/browse_thread/thread/a35e45935af55a3/7ba78a7223c1f802?hl=en&lnk=gst&q=Timertask#7ba78a7223c1f802

The 1.2 master branch also introduces deftype and reify which (over
and above defprotocol and defrecord) add greatly to the tools for
modular, extensible java/clojure interop.

-Rgds, Adrian.

Todd

unread,
Jun 23, 2010, 11:11:03 PM6/23/10
to clo...@googlegroups.com, Adrian Cuthbertson, ale...@gmail.com
Adrian/Alex -
Thank you for both of your responses.

As I'm new to clojure (and lisp in general), it'll take me a bit to
puzzle through your code and links. However, I'll use it as an example
to study the language. Thanks again!

-Todd

Reply all
Reply to author
Forward
0 new messages