Search code examples
javanode.jsclojurenode-java

How can I export a Java class (.jar) from Clojure?


I am relatively novice at Clojure and Java. I have an existing Clojure project someone else wrote that I am trying to embed in NodeJS using node-java.

Clojure

The project defines a namespace that provides certain public functions, like so:

(ns my.namespace
  (:require ...etc...))

(defn dosomething ...)
(defn dosomethingelse ...)

I have built the project with leiningen (lein jar and lein uberjar).

Questions

The #import() docs on node-java say I need to import a java class like so:

const java = require('java');
var Test = java.import('Test');
  1. How can I access these functions (presumably as Java class static methods?)
  2. Am I approaching this all wrong? =)

Update

Thanks to Magos (answer below) I made a little more progress. It turns out I can use (:gen-class :name my.name) in the (ns ...) scope to tell it to generate a class. If I add a profile to the project.clj like so:

...
:profiles {
  ...
  :uberjar {:aot :all}
}
...

It will compile and I can now see the class in Node. I still haven't figured out how to export the methods, though. Working on that part now.


Solution

  • Note: I arrived at this through a combination of Magos's answer and clartaq's comment to the question.

    Here are simple instructions for how to do it. Let's assume you have this (simple) clojure code:

    (ns my.namespace
      "a trivial library"
      (:require [something.else :as other]))
    
    (defn excite
      "make things more exciting"
      [mystr]
      (print-str mystr "!"))
    

    Use these steps to expose the excite method.

    1. Create an exposed version of the method with the same signature by prefixing it with -. It should simply call the function you wish to expose.

      (defn -excite [mystr] (excite mystr))
      
    2. Declare in (ns ...) that you want to generate a class and export methods.

      (ns my.namespace
        "a trivial library"
        (:require [something.else :as other])
        (:gen-class
          :name my.classname
          :methods [
            ;   metadata      mtd.name signature  returns
            #^{:static true} [excite   [String]   void]
          ]))
      

      Note that you may optionally remove the #^{:static true} if you do not wish to provide this as a static method.

    3. In your project.clj (assuming you are using leiningen), add ahead-of-time compilation instructions to your :profiles entry:

      :profiles {:uberjar {:aot :all}}
      
    4. Compile your uberjar:

      lein uberjar
      

    The resulting .jar file will have a class my.classname with a static method excite.