Call Clojure from Java, they said, it'll be easy, they said

918 views Asked by At

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?

2

There are 2 answers

2
Shlomi On

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.

0
Jorge D On

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.