How to genclass a servlet

mistakes — cgrand, 4 June 2008 @ 15 h 45 min
This is an old post, refers to Clojure.org for details on how gen-class works now.

Although generating a servlet class is a canonical example of using Clojure’s genclass I had never do this — I’ve used a handmade ClojureServlet which predates genclass.
Today, as I was starting a new project, I decided to get rid of this legacy ClojureServlet and to generate a brand new one.

Right at the end of genclass.clj there’s the following example:

(clojure/gen-and-save-class "/Users/rich/dev/clojure/gen/" 
 'org.clojure.ClojureServlet 
 :extends javax.servlet.http.HttpServlet)

I changed the path, created the directory structure (gen-and-save-class doesn’t create the org/clojure/ directories) and ran this code (NB: servlet-api.jar must be on the classpath). Et voilà, org/clojure/ClojureServlet.class!

Now to the Clojure code, I created org/clojure/ClojureServlet.clj to define the methods and launched Tomcat to test the servlet. It crashed in the init method.
In org/clojure/ClojureServlet.clj I have:

(in-ns 'org.clojure.ClojureServlet)
(clojure/refer 'clojure)

(defn init [this config]
  ;censored
)

Cause: init does not call super.init(ServletConfig) as GenericServlet mandates it. Hopefully GenericServlet provides an init() which is easier to override, so I changed the code to:

(in-ns 'org.clojure.ClojureServlet)
(clojure/refer 'clojure)

(defn init-void [this]
  ;censored, again
)

which override only the init method taking no arguments. The bug was fixed.

To override a specific signature, define a var named methodName-firstArgClassName-secondClassName... (e.g. init-ServletConfig) or methodName-void if no arguments.

Multimethods aren’t magic (nor mutable)

mistakes — cgrand, 28 May 2008 @ 17 h 55 min

What does this code display?

(defmulti foo first)
(def print-foo (comp println foo))
(defmethod foo :a [x]
  "it's a 'a'!")
(print-foo [:a])

Nothing, it raises an error: it doesn’t know what to do with the dispatch value :a. The value of foo is captured when comp is called.

The thing to keep in mind is that defmethod mutates the var, not the multimethod itself — which is of course immutable. The solution is to pass the var instead of the multimethod to comp:

(defmulti foo first)
(def print-foo (comp println #'foo))
(defmethod foo :a [x]
  "it's a 'a'!")
(print-foo [:a]) ; prints "it's a 'a'!"

Update or maybe it’s a code smell that you should use a macro instead of a function.

Update 2 Clojure’s father (Rich Hickey) says : the correct thing to do is use the var.

Jumping to Javadocs from the REPL

utilities — cgrand, @ 17 h 29 min
(defn open-url [url]
  (let [htmlpane (new javax.swing.JEditorPane url)]
    (.setEditable htmlpane false)
    (.addHyperlinkListener htmlpane
      (proxy [javax.swing.event.HyperlinkListener] []
        (hyperlinkUpdate [#^javax.swing.event.HyperlinkEvent e]
          (when (= (.getEventType e) (. javax.swing.event.HyperlinkEvent$EventType ACTIVATED))
            (if (instance? javax.swing.text.html.HTMLFrameHyperlinkEvent e)
              (.. htmlpane getDocument (processHTMLFrameHyperlinkEvent e))
              (try
                (.setPage htmlpane (.getURL e))
                (catch Throwable t
                               (.printStackTrace t))))))))
    (doto (new javax.swing.JFrame)
      (setContentPane (new javax.swing.JScrollPane htmlpane))
      (setBounds 32 32 700 900)
      (show))))

(defn javadoc [c]
  (let [url (str "http://java.sun.com/javase/6/docs/api/"
              (.. c getName (replace \. \/) (replace \$ \.)) ".html")]
    (open-url url)))

; usage:
; (javadoc Throwable) opens a window displaying Throwable's javadoc 
; hint: (javadoc (class some-object))
« Previous Page
(c) 2024 Clojure and me | powered by WordPress with Barecity