how to get the Class generated by gen-class in clojure - clojure

I am using some java lib which require the access the class generated by gen-class
(ns cljfx.test
(:import some.java.Lib))
(gen-class :name Main)
(defn -main [& arg]
(Lib/method-require-class-arg (classOf Main)))
the prototype of method-require-class-arg is
public static void method-require-class-arg(Class someClass) {
// ...
}
How to write the (classOf Main) part in the first snippet?

Your gen-class example is incorrect. If you use
(gen-class :name cljfx.test.Main)
then just cljfx.test.Main will return the Class object.

You can use Class/forName method.
Ex: (Class/forName "java.lang.String")
In your case it should be (Class/forName "Main") and you need to make sure you enable aot in your project.clj

Related

Problems calling a clojure function that takes a map as parameter from java

I'm using maven with several modules, one in java, another in clojure. I'm calling a clojure function from java and want to pass in a HashMap as a parameter and return a HashMap.
(I ran lein uberjar and lein pom on the clojure project to make it work with maven. I can get things to work for clojure function with simple types e.g. String, so the maven setup does work.)
I am getting the following error when I run some java unit tests calling the java code:
java.lang.ClassCastException: class clojure.lang.LazySeq cannot be cast to class java.util.Map (clojure.lang.LazySeq is in unnamed module o
f loader 'app'; java.util.Map is in module java.base of loader 'bootstrap')
How can I get this to work? Is this the proper way to call clojure methods from java?
What about if the HashMap had a POJO object as a value rather than a String?
My java code:
import interop.Core;
public class BillingCalc {
static Map<String, String> nonEmptyItems(Map<String, String> items) {
return Core.non_empty_seats(new HashMap<String, String>());
}
}
My clojure code:
(ns interop.core
(:gen-class
:name interop.Core
:methods [^{:static true} [apply_vat_to_netto [java.math.BigDecimal java.math.BigDecimal] java.math.BigDecimal]
^{:static true} [non_empty_seats [java.util.Map] java.util.Map]]) )
(defn -filter-empty-seats
"filter out those with empty seats"
[seats]
(filter (fn [[_ v]] (pos? (:booked-items v))) seats))
(defn -non_empty_seats
[java-seats]
(-filter-empty-seats (into {} java-seats)))
I guess that your error is caused by this definition in :gen-class:
[non_empty_seats [java.util.Map] java.util.Map]]
From docs for :gen-class:
:methods [ [name [param-types] return-type], ...]
The expected type of returned value is java.util.Map, but filter in -filter-empty-seats returns instance of clojure.lang.LazySeq. You should rewrite -non_empty_seats like this:
(defn -non_empty_seats
[java-seats]
(into {} (-filter-empty-seats java-seats)))

Problem overwritting java method from clojure

I am desperetly trying to create a class that with a "toString" method from clojure
According to clojure docs the following should work:
(ns override-test.simpleClass
(:gen-class
:name simpleClass
:methods [[^{Override {}} toString [] String]]
:state state
:init init
:constructors {[String] []}))
(defn -init
[name_]
[[] (atom name_)])
(defn -toString [this]
(deref (.state this)))
However evaluating
(simpleClass. "test")
Throws
CompilerException java.lang.ClassFormatError: Duplicate method name "toString" with signature "()Ljava.lang.String;" in class file simpleClass, compiling:(override_test/simpleClass.clj:19:3)
Any incites of what i might be doing wrong ?
As Biped Phill mentioned the problem was that toString seems to be already implemented by virtue of the automatic subclassing mechanism of gen-class. Probably Ljava.lang. String is treated as a (?) superclass and toString is added automatically, so i just had to remove it from :methods which is for not inherited methods and it worked like a charm.

with-redefs not replacing valid Compojure route function call in clojure.test

I want to test my Compojure endpoints and see that the required method is called. However, with-redefs-fn is not replacing the method.
Test method-
(ns project-name.handler-test
(:require [clojure.test :refer :all]
[project-name.handler :as handler :refer [retrieve-records test-handler]]
[ring.mock.request :as mock]))
(deftest fn-handler-routes
(with-redefs-fn {#'handler/retrieve-records (fn [arg] :test)}
(let [response {:status 200 :body :test}]
(let [mock-handler (test-handler)]
(is (= response (mock-handler (mock/request :get "/records/name"))))))))
I am guessing routes are static, and thus the method fires and returns actual data instead of calling the redefined function. test-handler is a failing attempt to try to circumvent -- it fires okay, but does not use with-redefs and returns repo data.
Pertinent Source Code-
(defn retrieve-records [arg] (repo/get-records arg))
(defroutes routes
(GET "/" [] (resource-response "index.html" {:root "public"}))
(context "/records" []
(GET "/name" [] (retrieve-records :lastname)))
(resources "/"))
(defn test-handler []
(-> #'routes wrap-reload))
Related Issues
I suspect a similar underlying issue as this SO question about Midje but I am using clojure.test and not midje.
This question is different from a very similar SO question in that the Compojure route is legitimate and I want to return something in the :body, not just test the status (already tried that answer).
This question is different from an integration test question in that it is a handler unit test, though probably the same underlying issue.
Any help would be greatly appreciated.

Clojure: Attaching annotations to AOT-compiled methods

I have a module for the BaseX Java interface which I'm writing in Clojure. The interface provides a number of annotations which can be used to determine how methods are called and optimized; however, I'm having trouble getting these to actually attach to the generated class:
(ns net.dyfis.svnkit_wrapper.SvnWrapper
(:import (org.basex.query QueryModule
QueryModule$Requires
QueryModule$Permission
QueryModule$Deterministic))
(:gen-class
:main false
:extends org.basex.query.QueryModule
:methods [
^{:static true}
[^{QueryModule$Requires QueryModule$Permission/NONE,
Deprecated {}}
cat [java.lang.String] java.lang.String]
^{:static true}
[^{QueryModule$Deterministic {},
QueryModule$Requires QueryModule$Permission/NONE}
catRev [java.lang.String int] java.lang.String]]))
However, only the Deprecated annotation gets attached -- the QueryModule$Requires and QueryModule$Deterministic annotations are silently discarded:
>>> cat
public static java.lang.String net.dyfis.svnkit_wrapper.SvnWrapper.cat(java.lang.String)
>>> cat.getAnnotations()
array(java.lang.annotation.Annotation,[#java.lang.Deprecated()])
This is happening with Clojure 1.4.0-beta6, whereas support for annotations in AOT-compiled methods is supposed to be present from Clojure 1.2. As such, this is presumably a usage error -- but what should I be doing differently?
Except for classes in java.lang (like Deprecated), all classnames must be fully qualified in gen-class declarations. So, your code should be:
^{org.basex.query.QueryModule$Deterministic {},
org.basex.query.QueryModule$Requires org.basex.query.QueryModule$Permission/NONE}
Note that the same restriction does not apply to annotation metadata on/in deftype, defprotocol, or defrecord forms.
Moving the gen-class definition out of the ns declaration allowed the imports to apply:
(ns net.dyfis.svnkit_wrapper.SvnWrapper
(:import (org.basex.query QueryModule
QueryModule$Requires
QueryModule$Permission
QueryModule$Deterministic)))
(gen-class
:name com.indeed.svnkit_wrapper.SvnWrapper
:main false
:extends org.basex.query.QueryModule
:methods [
^{:static true}
[^{QueryModule$Requires QueryModule$Permission/NONE,
Deprecated {}}
cat [java.lang.String] java.lang.String]
^{:static true}
[^{QueryModule$Deterministic {},
QueryModule$Requires QueryModule$Permission/NONE}
catRev [java.lang.String long] java.lang.String]
])

Declare array as return type in gen-class method declaration in Clojure

How to declare an array in method declaration in gen-class?
(ns foo.bar
(:gen-class
:methods [[parseString [String Object] Object]]))
That works fine. But the return type is really an array. How I can declare that so Java can understand it?
Try
(ns foo.bar
(:gen-class
:methods [[parseString [String Object] "[Ljava.lang.Object;"]]))
I needed a
static Number[][] method(int, Number[][]);
signature, in a similar way:
(:gen-class
:methods [#^{:static true} [method [int "[[Ljava.lang.Number;"] "[[Ljava.lang.Number;"]])
seemed to work.