Fork me on GitHub
#beginners
<
2023-12-09
>
Jonathan Bennett13:12:43

How do I tell my uberjar where/how to find the resources directory if I'm using lein uberjar to create it?

phill13:12:28

Do you mean, you can unzip the uberjar and spot the contents of your resources directory somewhere inside it, but you wonder how your program can find them? Or do you mean you can unzip the uberjar and you don't see the contents of your resources directory anywhere among the contents of the uberjar?

Jonathan Bennett13:12:42

I've done a bit of searching since asking. I have a folder of game data called resources I need to access. When I do lein run or lein repl, my code can reference files inside the directory contents, but I don't know how to do that in the uberjar.

phill13:12:00

The key is that from the point of view of a running program, resources must not be treated as a directory. Its contents can be accessed via Java's getResourceAsStream or Clojure's resource function (https://clojure.github.io/clojure/clojure.java.io-api.html#clojure.java.io/resource).

phill13:12:39

The most charitable view of this quirk of Java's design is that your running program (edit: running from a jar, I mean) can't access "src" either.

Jonathan Bennett13:12:33

So if I've got ~9000 files in there, am I going to need to call (io/resource "filename") for every one?

phill13:12:11

Yup. If that's not pleasant then resources isn't a good place for such stuff OR you could store a "zip" as the resource, open the "zip" once, and cruise around it using java.util.zip.

Jonathan Bennett14:12:43

So where should it be if it isn't in resources? Just trying to figure out how this work in Java. I'd like to do this something like how this project does it: https://github.com/MegaMek/megamek/tree/master

Jonathan Bennett14:12:23

But it's a huge project, I can't figure out where to begin looking to find how they load their resources.

Bob B15:12:03

to the initial question, lein uses :resource-paths with a vector containing zero or more relative paths to resource directories

seancorfield18:12:10

When you run an uberjar, the classpath is just that JAR file so you can treat it as a file and use java.util.jar.* classes and functions to walk through the entries in that JAR file looking for the files you want. For example, at work we have a migration uberjar that has SQL migrations in .sql files in a resources folder in the source project. We use a "unique" pattern for the paths to those SQL so we can regex-match them when we walk the entries in the JAR file, and that gives us a list of relative paths (from the classpath root) which we can then pass to io/resource when we need to read them.

seancorfield18:12:07

(let [cp (System/getProperty "java.class.path")
        jar (java.io.File. cp)]
    (if (and jar (.exists jar))
      (let [jis (java.util.jar.JarInputStream. (java.io.FileInputStream. jar))
            files (loop [entry (.getNextJarEntry jis) matches #{}]
                    (if-not entry
                      (sort matches)
                      (recur (.getNextJarEntry jis)
                             (if-let [match (re-find matching-pattern (.getName entry))]
                               (conj matches match)
                               matches))))]
        (do-stuff-to files))
      (throw (ex-info "Unable to find JAR file" {:cp cp}))))

🆒 1
Kishor Pandey13:12:05

is there any example of loading jar from local machine in project.clj file ?