Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to locate resources using ClassLoader methods, because jar does not have directories #59

Open
ALai57 opened this issue Jun 21, 2021 · 0 comments

Comments

@ALai57
Copy link

ALai57 commented Jun 21, 2021

Context: I'm using rules_clojure to build a Clojure jar that includes some resources that my application depends on. For example, I'm including files from the folder resources/migrations/ where I plan to add new database migrations (e.g. resources/migrations/foo.sql and resources/migrations/bar.sql).

My goal: I would like to have my clojure application automatically detect the files inside the resources/migrations/ folder in the jar so I can run migrations. I would like to rely on existing ClassLoader methods such as getResource and getResources. Both of these methods are used in common Clojure migrations libraries, ragtime and migratus. At the moment, neither library can run migrations because they cannot locate a directory within the jar to find migrations.

Problem: When I build the jar, I see resources/migrations/foo and resources/migrations/bar inside the jar. However, the directories resources/ and resources/migrations/ are missing from the jar. This means the ClassLoader methods for interacting with the jar filesystem are unable to work. Several common Clojure database migration libraries (ragtime and migratus) both depend on these paths for locating relevant resources inside a jar See System Resources

Proposed change to the rules_clojure library: In the scripts/library.clj file, gather all objects that will be inserted into the jar, decompose each into all relevant subdirectories and add all subdirectories to the jar. For example, the file resources/migrations/foo.sql, would decompose into ["resources/" "resources/migrations/"], both of which would be added to the jar.

Proposed change in In scripts/library.clj, after put-next-entry!:

;; Add directories into the jar file. Without directories, we lose the ability
;; to identify system resources and navigate the jar file like a Filesystem
;; using ClassLoader methods.
;; https://docs.oracle.com/javase/8/docs/technotes/guides/lang/resources.html
(defn path-parts
  [path]
  (str/split path #"/"))

(defn all-subdirs
  ([parts]
   (all-subdirs parts #{}))
  ([parts acc]
   (if parts
     (let [path (str (str/join "/" parts) "/")]
       (all-subdirs (butlast parts) (conj acc path)))
     acc)))

(def dirs
  (->> (concat (map ns-path sources)
               (map (partial resource-path build-location) data))
       (mapcat (comp all-subdirs path-parts))
       set))

(with-open [jar-os (-> compile-jar FileOutputStream. BufferedOutputStream. JarOutputStream.)]
  (put-next-entry! jar-os JarFile/MANIFEST_NAME)
  (.write manifest jar-os)
  (.closeEntry jar-os)
  (doseq [file sources]
    (put-next-entry! jar-os (ns-path file))
    (io/copy file jar-os)
    (.closeEntry jar-os))
  (doseq [file data]
    (put-next-entry! jar-os (resource-path build-location file))
    (io/copy file jar-os)
    (.closeEntry jar-os))
  (doseq [dir dirs]
    (put-next-entry! jar-os dir)))

Would you be open to a PR making the changes outlined above?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant