clojureclojure-java-interop

How to add a Java instance as context when using clojure as a scripting language?


I found the amazing question How can I use clojure as scripting language for a Java program? which helped tremendously, but I can't figure out how to get an existing Java instance into Clojure. The use case is something really similar to AutoCad's AutoLisp. I want to let users manipulate an application with scripting so that they are free to do more without my help or input. I want to have a class that does some work

public class Testing {
    public void work() {
        // ....
    }
}

and then add it to Clojure

public class Main {
    public static void main() {
        Testing t = new Testing()
        IFn eval = Clojure.var("clojure.core", "eval");
        System.out.println(eval.invoke(Clojure.read("(import Testing)")));
        // How do i get "t" into clojure?
        System.out.println(eval.invoke(Clojure.read("(.work t)")));
    }
}

However I can't figure out how. I don't seem to be able to invoke def with arguments from java. I have been fiddling with this and with documentation for a while and can't seem to figure it out.


Solution

  • import clojure.java.api.Clojure;
    import clojure.lang.Var;
    import clojure.lang.RT;
    import clojure.lang.Compiler;
    
    
    public class Main {
        public static void main(String[] _argv) {
            // Using String instead of Testing just to avoid having to
            // deal with multiple files during compilation.
            String s = "Hello there";
    
            // Needed to allow creating new namespaces.
            // If you ever get stuck with some functionality not working, check out
            // Compiler.load - there are other bindings in there which, I guess, might be important.
            // So you can either copy all the bindings here or simply use Compiler.load instead of
            // Compiler.eval for script pieces that don't require bindRoot.
            Var.pushThreadBindings(RT.mapUniqueKeys(RT.CURRENT_NS, RT.CURRENT_NS.deref()));
            try {
                Compiler.eval(Clojure.read("(ns user)"));
                // def returns the var itself.
                ((Var) Compiler.eval(Clojure.read("(def s)"))).bindRoot(s);
    
                Compiler.eval(Clojure.read("(println s \"in\" (ns-name *ns*)))"));
            } finally {
                Var.popThreadBindings();
            }
        }
    }