-
-
Save witek/e15bf7340ab2fd4ae46a81315071986c to your computer and use it in GitHub Desktop.
Clojure Plugin implemented in Java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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