Fork me on GitHub
#interop
<
2022-03-02
>
Crispin04:03:11

Is there a way to create a class with specific methods available via reflection at runtime?

Crispin04:03:38

I dont have an interface. I just need a class with two methods (that will be invoked via reflection)

Crispin04:03:08

using proxy generates a class and instance, but the methods on that class are not direct

Crispin04:03:01

instead it has __initClojureFnMappings, __updateClojureFnMappings and __getClojureFnMappings

Crispin04:03:28

I can generate a clean class with (ns ... (:genclass))

Crispin04:03:46

but I seem have issues with working with that inside the repl

Crispin04:03:09

is there any other way to do what ns is doing, but used like reify and proxy?

Crispin05:03:05

so I got it working with ns and compiled into an uberjar. But I can't seem to get a reflectable class instance in a jacked in repl.

Crispin05:03:29

just end up getting ClassNotFoundException on an import, or IllegalArgumentException: Unable to resolve classname: Resolver on attempting direct instantiation

Crispin05:03:31

I assume evaluating namespaces and forms in the repl does not compile classes and class load them?

seancorfield05:03:16

@retrogradeorbit If you have an ns with (:gen-class) you need to explicitly compile that namespace.

seancorfield05:03:22

How are you building the uberjar?

seancorfield05:03:10

(mostly, the uberjar-building process will compile that namespace if you have the build steps configured properly)

Crispin05:03:12

with leiningen

seancorfield05:03:21

So lein uberjar?

Crispin05:03:29

yep and that works

seancorfield05:03:21

And you have :aot specified in project.clj with your ns that has the (:gen-class)? Or :aot :all? (I haven't used lein for years so I'm a bit rusty -- we switched to deps.edn four years ago at work).

Crispin05:03:49

yep :aot :all

seancorfield05:03:00

OK, so what do you mean by "jacked in repl"?

Crispin05:03:19

so fire up a cider repl. jack in (launches lein repl).

Crispin05:03:39

then evaluate the calling namespace

seancorfield05:03:41

Ah, I always start a REPL manually at the command-line and then connect my editor to it.

Crispin05:03:45

or the genclass namespace

seancorfield05:03:19

You'll need to explicitly compile that namespace and you'll need classes on your classpath for it to be picked up.

Crispin05:03:25

ah ok... so if I lein repl from the command line it may have access to the gen-classed class?

Crispin05:03:51

can I compile that namespace from the repl?

seancorfield05:03:52

(compile the.ns) in a REPL would work, regardless of how you start it

Crispin05:03:08

and that loads the class too?

Crispin05:03:12

or then I import it?

seancorfield05:03:18

But I can't remember how to tell lein about the classes folder that needs to be on the classpath.

seancorfield05:03:48

It's been too long since I used Leiningen to remember how to make this work with it.

seancorfield05:03:01

(I don't use nREPL either, just a Socket REPL)

seancorfield05:03:24

lein has a :prep-tasks option, as I recall, so you can get it to compile code prior to starting a REPL automatically. With that and having classes on the classpath, you should be fine. But you'll have to read the Leiningen docs for details.

seancorfield05:03:53

OK, so I did lein new app crispin and then updated project.clj to look like this:

(defproject crispin "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url ""}
  :dependencies [[org.clojure/clojure "1.10.1"]]
  :main ^:skip-aot crispin.core
  :prep-tasks ["compile"]
  :aot [crispin.core]
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})
and now when I start lein repl it compiles crispin.core (which has (:gen-class)) and I can (import crispin.core) in that REPL.

Crispin05:03:29

nice! thankyou!

seancorfield05:03:23

Seems that lein generates classes into target/default/classes by default and that seems to be already on the classpath. So much "magic" happening in Leiningen 🙂

Crispin05:03:18

ok that worked! with the :prep-task and the :aot, the jack-in now works 🎉