How to pass in a parameter to a static method in Clojure?

584 Views Asked by At

I have the following method:

package com.streambright.http.handlers;

import org.rapidoid.http.Req;
import org.rapidoid.u.U;

import java.util.Map;

public class EchoHandler {

    public static Map<String, String> handleEcho(Req req) {
        return U.map(req.headers());
    }
}

When I use it from have it is perfectly fine:

On.get("/echo").managed(false).json(EchoHandler::handleEcho);

However when I try to do the same in Clojure it fails with wrong number of arguments.

Handler:

(ns s.echo-http-handler
  (:import
    [org.rapidoid.http  Req ]
    [org.rapidoid.u     U   ] )
  (:gen-class
    :methods [^:static [handler [org.rapidoid.http.Req] java.util.Map]]))

(defn -handler
  [Req req]
  (U/map (.headers req)))

When I start the server:

(ns s.http
  (:require
    [s.echo-http-handler  :as echo    ] )
  (:import
    [org.rapidoid.config  Conf  ]
    [org.rapidoid.setup   On    ] ) )

    (defn start
      []
      (set-http-params!)
      (.json (.managed (On/get "/echo") false) echo/-handler))

It starts up and throws the following error:

clojure.lang.ArityException: Wrong number of args (0) passed to: echo-http-handler/-handler

How do I pass in the req to the function in Clojure?

2

There are 2 best solutions below

5
Carcigenicate On BEST ANSWER

Assuming the problem is with how it's handeling Clojure functions, the following should work:

(ns your-namespace
  (:import [org.rapidoid.http Req ReqHandler]))

; Helper to create an object that implements ReqHandler
(defn makeReqHandler [f]
  ; Basically "new ReqHandler {...}"
  (reify ReqHandler
    (execute [this, ^Req req] (f req))

This function takes a Clojure function, and uses it to create an object that implements ReqHandler. I think the problem was it was assuming (for some reason) that you wanted the 0-arity overload of .json, when you really wanted the overload that passes an argument to the callback. This helper should prevent that confusion.

Then use as:

(let [handler (makeReqHandler echo/-handler)]
  (.json (.managed (On/get "/echo") false) handler)))

Although that could also be written as:

(->  (On/get "/echo")
     (.managed false)
     (.json handler))

Or even with the handler inlined to show that it's still possible using a wrapper function to use terse anonymous function syntax:

(->  (On/get "/echo")
     (.managed false)
     (.json (makeReqHandler #(U/map (.headers %)))))

Which is arguably a lot more readable. Because this is always implicitly the first argument when using Java interop, doto and -> are great at getting rid of nesting.

0
Ivan Grishaev On

I used to play with rapidoid a bit. Your mistake here is you are trying to pass just a Clojure function as a handler. Instead, it should be an instance of ReqHandler Java class. So you need to create an instance of anonymous class that extends it.

Wrong:

(defn -handler
  [Req req]
  (U/map (.headers req)))

Correct:

(reify ReqHandler
 (execute [this, ^Req req]
   ;; here, access req's fields to write the body or headers
   ))