Skip to content

Instantly share code, notes, and snippets.

@witek
Created January 14, 2022 11:53
Show Gist options
  • Save witek/e15bf7340ab2fd4ae46a81315071986c to your computer and use it in GitHub Desktop.
Save witek/e15bf7340ab2fd4ae46a81315071986c to your computer and use it in GitHub Desktop.
Clojure Plugin implemented in Java
package ilarkesto.clojure;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import clojure.java.api.Clojure;
import clojure.lang.IFn;
import ilarkesto.core.base.RuntimeTracker;
import ilarkesto.core.logging.Log;
public class ClojurePlugin {
private static final Log log = Log.get(ClojurePlugin.class);
private URLClassLoader classLoader;
public ClojurePlugin(String cljSrcPath)
throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, SecurityException {
URL url = new File(cljSrcPath).toURI().toURL();
classLoader = new URLClassLoader(new URL[] { url }, ClojurePlugin.class.getClassLoader());
Thread.currentThread().setContextClassLoader(classLoader);
}
public IFn var(String namespace, String fn) {
Thread.currentThread().setContextClassLoader(classLoader);
try {
return Clojure.var(namespace, fn);
} catch (Throwable ex) {
throw new RuntimeException("var for " + namespace + "/" + fn + " failed", ex);
}
}
public Object read(String s) {
Thread.currentThread().setContextClassLoader(classLoader);
try {
return Clojure.read(s);
} catch (Throwable ex) {
throw new RuntimeException("read failed", ex);
}
}
public void require(String namespace, boolean failOnError) {
try {
var("clojure.core", "require").invoke(read(namespace));
} catch (Throwable ex) {
RuntimeException newEx = new RuntimeException("reload for " + namespace + " failed", ex);
if (failOnError) {
throw newEx;
} else {
log.error(newEx);
}
}
}
// public void reload(String namespace) {
// try {
// var("clojure.core", "require").invoke(read(namespace), read(":reload-all"));
// } catch (Throwable ex) {
// throw new RuntimeException("reload for " + namespace + " failed", ex);
// }
// }
public void refresh(String namespace, boolean failOnError) {
RuntimeTracker rt = new RuntimeTracker();
Thread.currentThread().setContextClassLoader(classLoader);
require(namespace, failOnError);
Object ret = var("clojure.core", "load-string")
.invoke("(require 'clojure.tools.namespace.repl) (clojure.tools.namespace.repl/refresh)");
if (ret instanceof Exception) {
RuntimeException newEx = new RuntimeException("Refreshing namespace failed: " + namespace, (Exception) ret);
if (failOnError) {
throw newEx;
} else {
log.error(newEx);
}
}
log.debug("Runtime:", namespace, " [refresh] ", rt.getRuntimeFormated());
}
public void refresh(String namespace) {
refresh(namespace, true);
}
public Object invoke(String namespace, String fnName, Object... args) {
Thread.currentThread().setContextClassLoader(classLoader);
refresh(namespace);
RuntimeTracker rt = new RuntimeTracker();
IFn fn = var(namespace, fnName);
try {
switch (args.length) {
case 0:
return fn.invoke();
case 1:
return fn.invoke(args[0]);
case 2:
return fn.invoke(args[0], args[1]);
case 3:
return fn.invoke(args[0], args[1], args[2]);
}
throw new IllegalArgumentException("Too may args: " + args.length);
} catch (Throwable ex) {
throw new RuntimeException("invoke for " + namespace + "/" + fnName + " failed", ex);
} finally {
log.debug("Runtime:", namespace + "/" + fnName, " [invocation] ", rt.getRuntimeFormated());
}
}
public Object eval(String clojureCode) {
try {
return var("clojure.core", "load-string").invoke(clojureCode);
} catch (Throwable ex) {
throw new RuntimeException("eval failed", ex);
}
}
public String evalAndPrStr(String clojureCode) {
try {
return (String) var("clojure.core", "pr-str").invoke(eval(clojureCode));
} catch (Throwable ex) {
throw new RuntimeException("eval failed", ex);
}
}
public String pprint(Object o) {
try {
var("clojure.core", "require").invoke(read("clojure.pprint"));
} catch (Throwable ex) {
throw new RuntimeException("require for clojure.pprint failed", ex);
}
IFn pprint = (IFn) eval("(defn pprint [o] (with-out-str (clojure.pprint/pprint o)))");
try {
return (String) pprint.invoke(o);
} catch (Throwable ex) {
throw new RuntimeException("pprint failed", ex);
}
}
public String evalAndPprint(String clojureCode) {
try {
return pprint(eval(clojureCode));
} catch (Throwable ex) {
throw new RuntimeException("eval failed", ex);
}
}
public Object startNrepl(int port) {
require("nrepl.server", true);
IFn start = var("nrepl.server", "start-server");
Object atom = start.invoke(Clojure.read(":port"), Clojure.read(Integer.toString(port)));
log.info("nREPL server started on port", port);
return atom;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment