4

I am a fledgling Clojure programmer experienced wielding straight-up-the-middle Java in eclipse. I am trying to get my Java program to call the simple "hello" function created from the http://dev.clojure.org/display/doc/Getting+Started+with+Eclipse+and+Counterclockwise article. The hello function works fine from a Clojure REPL launched through the counterclockwise plug-in. Problems arise when I try to execute the hello function from a Java class.

Googling around reveals that there are basically 2 ways to do this: clojure.lang.RT can load the Clojure source and execute it as a script, or directly when the Clojure source was compiled into a JAR.

The clojure.lang.RT variant is working without problem, but I am completely at a loss as to how to get the direct invocation variant working. In the Java file, the compiler cannot resolve "myproject.core".

The Clojure source is core.clj and is as follows and works like a champ through the REPL:

(ns myproject.core
  (:gen-class
    :name myproject.core
    :methods [#^{:static true} [hello [String] String]]))

(defn -main
  "I don't do a whole lot."
  [& args]
  (println "Hello, World!"))

(defn hello [who] (str "Hello " who " !"))

The Java source, however, won't compile:

import java.io.*;
import clojure.lang.*;
public class HelloJava {
    public static void main( String[] args ) {
        loadResourceVariant();
        directVariant();
    }

    public static void directVariant() {
        myproject.core.hello( "Bob" );
    }

    public static void loadResourceVariant() {
        try {
            RT.loadResourceScript( "myproject/core.clj" );
            // Get a reference to the hello function.
            Var hello = RT.var( "myproject.core", "hello" );
            // Invoke the hello function
            Object result = hello.invoke( "Robert" );
            System.out.println( result );
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }
}

The compiler error is...

myproject.core cannot be resolved to a type HelloJava.java  /myproject/src  line 11 Java Problem

How do I configure my counterclockwise project to put the .class representation of core.clj into the classes directory so it can be directly referenced from Java?

This has to be possible without going full-on Maven and the like. No?

Laurel
  • 5,965
  • 14
  • 31
  • 57
Bob Kuhar
  • 10,838
  • 11
  • 62
  • 115
  • From what I see, you are lacking a dash in front of your `hello` function. You need it in order to make it callable from Java. I suggest just creating another wrapper function which invokes your original `hello` function like this: `(defn -hello [who] (hello who))` and then invoking _this_ function from Java. What it will do is invoke your original `hello` function which is not visible from Java. Also be sure to place compiled jar on you path. – JovanToroman Jun 21 '20 at 08:28

2 Answers2

0

The way I am calling clojure from java is by making an api namespace, usually separated from the implementing namespace:

(ns test.api
  (:use [test.impl :only [f1 f2]])
  (:gen-class
    :methods [#^{:static true} [exportedF1 [String] int]
              #^{:static true} [exportedF2 [String String] void]]))

(defn -exportedF1 [^String query ^String file] (f1 query file))
(defn -exportedF2 [^String query] (f2 query))

then I make sure to AOT that namespace, and uberjar it.

now just include that jar in your java classpath, and you'r ready to go.

Shlomi
  • 4,708
  • 1
  • 23
  • 32
  • That would work, but really doesn't fit my workflow. I mean, you have to complete your clojure work, JAR it up, import it into the Java, and continue on with your work. The workflow I am trying to get to is to be changing the Clojure and the Java simultaneously and have the projects "link" just like regular Java projects can. – Bob Kuhar May 29 '13 at 20:23
  • yes, what i did was set up the two projects on eclipse, then in the java project i added the clojure jar to the build path, directly from the clojure project's target directory. Another option: in a different project of mine, i simply used lein as the java built tool too, so i had both java and clojure on the same project (even scala!), and i could change either java, scala or clojure and it would compile and run just fine! check it out here http://bit.ly/12kzshw . Not saying its perfect, but its an option, hope this helps – Shlomi May 30 '13 at 21:39
0

I think that your question was answered by user clartaq here: Calling clojure from java.

To summarize - follow these steps if you want to export a Clojure function to Java:

  1. Create a project using a fully qualified name; e.g., lein new com.example.stuff instead of just of lein new stuff.
  2. Write a "wrapper function"; e.g. -foo if your function is called foo.
  3. Extend the (ns ...) form at the top of your file to export your function
  4. Edit your project.clj file to trigger AOT compilation
  5. Build an uberjar file.

Check my GitHub page if you want to see how I applied this approach.

Jorge D
  • 41
  • 7