How to load program resources in Clojure

clojure, interop, java, markdown, resources

Solution

It's the directory structure.

Continuing with the scripting engine example in the OP, a Clojure equivalent would be:

(ns com.domain.example
  (:gen-class)
  (:import (java.io InputStreamReader))
  (:import (javax.script ScriptEngineManager ScriptEngine)))

(defn load-resource
  [name]
  (let [rsc-name (str "com/domain/resources/" name)
        thr (Thread/currentThread)
        ldr (.getContextClassLoader thr)]
    (.getResourceAsStream ldr rsc-name)))

(defn markdown-to-html
  [mkdn]
  (let [manager (new ScriptEngineManager)
        engine (.getEngineByName manager "js")
        is (InputStreamReader. (load-resource "showdown.js"))
        _ (.eval engine is)
        cnv-arg (str "new Showdown.converter().makeHtml(\"" mkdn "\")")]
    (.eval engine cnv-arg)))

(defn -main
  []
  (println (markdown-to-html "plain, *emphasis*, **strong**")))

Note that the path to the resources is `com/domain/resources` for this code as opposed to `com/domain/scriptingtest/resources` in the Java version. In the clojure version, the source file, `example.clj` is in `com/domain`. In the Java version, the source file, `Example.java` is in the `com/domain/scriptingtest` package.

When setting up a project in my IDE, NetBeans, the Java project wizard asks for an enclosing package for the source. The Clojure plugin, enclojure, asks for a namespace, not a package. I had never noted that difference before. Hence the "off-by-one" error in the directory structure expected.

Problem

How do you load program resources such as icons, strings, graphical elements, scripts, and so on in a Clojure program? I am using a project layout similar to that in many Java projects where there is a "resources" directory hanging off of a "source" directory. A jar file is created from the source and includes the resources, but I can't seem to get the resources loaded as I would in Java. The first thing I tried was something like ``` (ClassLoader/getSystemResource "resources/myscript.js") ``` But could never find the resource. You can do something similar with ``` ... (let [cls (.getClass net.mydomain.somenamespace) strm (.getResourceAsStream cls name) ] ... ``` where name is the `name` of the resource to load, but the stream is `nil`. You can try using the context class loader with something like ``` ... (let [thr (Thread/currentThread) ldr (.getContextClassLoader thr) strem (.getResourceAsStream ldr name)] ... ``` But `strem` is always nil. In frustration, I've tried placing the resource files in just about every directory in the program. They get copied into the jar correctly, but I still can't seem to load them. I've looked at the language sources for the `load` function and the run-time library, but am not "getting" it. Any help would be appreciated. EDIT: Here's a more concrete example. In Java, if you wanted to convert MarkDown to HTML, you might use the `showdown.js` script and write something like: ``` package scriptingtest; import java.io.InputStreamReader; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; public class Example { private Object converter; public String transformMarkDown(String markdownString) { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("js"); try { engine.eval(new InputStreamReader(getClass().getResourceAsStream( "resources/showdown.js"))); converter = engine.eval("new Showdown.converter()"); } catch (Exception e) { return "Failed to create converter"; } try { return ((Invocable) engine).invokeMethod(converter, "makeHtml", markdownString).toString(); } catch (Exception e) { return "Conversion failed"; } } public static void main(String[] args) { System.out.println(new Example().transformMarkDown("plain, *emphasis*, **strong**")); } } ``` when I create the project, it all gets compiled and packed into a jar. When run, the program outputs `<p>plain, <em>emphasis</em>, <strong>strong</strong></p>` A literal translation to Clojure seems pretty straightforward, but I run into trouble trying to create the `InputStreamReader` -- I can't seem to write the code needed to find the script file in the jar. Edit: Added "markdown" tag since the post gives two complete examples of approaches to processing markdown.

Original source