Java -> Clojure -> Java

clojure, java

Solution

Try this:

String script = "(do                                    "+
                "  (import '(a.problem.domain Domain))  "+
                "  (fn [s]                " +
                "   (.passBackMsg (Domain.) s)               "+
                "))                                         ";

IFn fn = (IFn)RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script));

fn.invoke("hello");

UPDATED: Below sample code works fine:

package hello_clj;

import clojure.lang.RT;
import clojure.lang.IFn;

public class Main {

    public String passBackMsg(String s) {
        return s;
    }

    public static void main(String[] args) {
        String script = "(do (import 'hello_clj.Main) (fn [s] " + 
                        "(.passBackMsg (Main.) s) ))";

        IFn fn = (IFn)RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script));
        System.out.print(fn.invoke("Hello"));
    }

}

Problem

I am trying to use Clojure as a scripting language from a host Java program. The idea being that the end user will be able to write Clojure scripting code that will call a domain-specific Java API. At runtime, the host Java program will evaluate the end-user's Clojure script (which will in turn call the domain APIs). So I started with a dead-simple prototype to explore the terrain. domain ``` package a.problem.domain; public class Domain { public Domain() { } public String defaultMsg() { return "default"; } public String passBackMsg(String s) { return s; } } ``` Host Java program (end user's Clojure script hard-coded for simplicity) ``` String script = "(do "+ " (import '(a.problem.domain Domain)) "+ " (.defaultMsg (Domain.)) "+ ") "; System.out.println(RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script))); ``` (code snippet taken from here) So far so good. However I couldn't find a way to invoke the second method (the one that requires an argument). Instead I resorted to dynamically generating the Clojure script at runtime and replacing a placeholder with a literal that represents the result of calling the domain method passBackMsg. Obviously, this is unsatisfactory and doesn't go very far (what if I want to pass a java.sql.Connection to my Clojure script ?). So, how do I invoke the passBackMsg method from the host Java program? When I try the following: ``` String script = "(ns foo) "+ "(import '(a.problem.domain Domain)) "+ "(defn numberToString [s] ( "+ " (.passBackMsg (Domain.) s) "+ ")) "; RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script)); // line-A System.out.println(RT.var("foo", "numberToString").invoke(33)); // line-B ``` I get: ``` java.lang.IllegalStateException: Can't change/establish root binding of: *ns* with set ``` ... on line-A. When I try without the ns and with: ``` RT.var("user", "numberToString").invoke(33) ``` ("user" being a wild guess as I don't see a var method without a namespace argument) I get an: ``` java.lang.IllegalStateException: Attempting to call unbound fn: #'user/numberToString" ``` ... on line-B.

Original source