Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Resources don't work in uberjar

1,452 views
Skip to first unread message

Dan Harbin

unread,
Jan 26, 2015, 9:24:28 PM1/26/15
to clo...@googlegroups.com
I've created a sample project at Github[1] to demonstrate the problem I'm facing with accessing a resource file when using an uberjar.  Could someone point out what I'm doing wrong here?  Thanks!

[1]: https://github.com/RasterBurn/halp

### Given this code: ###############

```clojure
(ns halp.core
  (:require [clojure.java.io :as io])
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (-> "hi.txt"
      io/resource
      io/file
      (io/copy *out*)))
```

### It runs well under leiningen #######

```
➜  halp  lein run
.
─────────▄──────────────▄
────────▌▒█───────────▄▀▒▌
────────▌▒▒▀▄───────▄▀▒▒▒▐
───────▐▄▀▒▒▀▀▀▀▄▄▄▀▒▒▒▒▒▐
─────▄▄▀▒▒▒▒▒▒▒▒▒▒▒█▒▒▄█▒▐
───▄▀▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▀██▀▒▌
──▐▒▒▒▄▄▄▒▒▒▒▒▒▒▒▒▒▒▒▒▀▄▒▒▌
──▌▒▒▐▄█▀▒▒▒▒▄▀█▄▒▒▒▒▒▒▒█▒▐
─▐▒▒▒▒▒▒▒▒▒▒▒▌██▀▒▒▒▒▒▒▒▒▀▄▌
─▌▒▀▄██▄▒▒▒▒▒▒▒▒▒▒▒░░░░▒▒▒▒▌
─▌▀▐▄█▄█▌▄▒▀▒▒▒▒▒▒░░░░░░▒▒▒▐
▐▒▀▐▀▐▀▒▒▄▄▒▄▒▒▒▒▒░░░░░░▒▒▒▒▌
▐▒▒▒▀▀▄▄▒▒▒▄▒▒▒▒▒▒░░░░░░▒▒▒▐
─▌▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒▒▒░░░░▒▒▒▒▌
─▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▐
──▀▄▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▄▒▒▒▒▌
────▀▄▒▒▒▒▒▒▒▒▒▒▄▄▄▀▒▒▒▒▄▀
───▐▀▒▀▄▄▄▄▄▄▀▀▀▒▒▒▒▒▄▄▀
──▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▀▀
```

### But not so much as an uberjar #########

```
➜  halp  java -jar target/uberjar/halp-0.1.0-SNAPSHOT-standalone.jar
Exception in thread "main" java.lang.IllegalArgumentException: Not a file: jar:file:/home/vagrant/code/halp/target/uberjar/halp-0.1.0-SNAPSHOT-standalone.jar!/hi.txt
        at clojure.java.io$fn__8588.invoke(io.clj:63)
        at clojure.java.io$fn__8572$G__8556__8577.invoke(io.clj:35)
        at clojure.java.io$file.invoke(io.clj:414)
        at halp.core$_main.doInvoke(core.clj:11)
        at clojure.lang.RestFn.invoke(RestFn.java:397)
        at clojure.lang.AFn.applyToHelper(AFn.java:152)
        at clojure.lang.RestFn.applyTo(RestFn.java:132)
        at halp.core.main(Unknown Source)
```

Jeroen van Dijk

unread,
Jan 27, 2015, 3:24:43 AM1/27/15
to clo...@googlegroups.com
Hi Dan,

Not sure if there are better options, but I know `slurp` does work on resources in jars. You could (ab)use `spit` to do the copying.

HTH,
Jeroen

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Benjamin VanRyseghem

unread,
Jan 27, 2015, 7:28:59 AM1/27/15
to clo...@googlegroups.com, clo...@googlegroups.com
I face something similar.

The issue was that inside a jar file, a resource is not a java.io.File anymore.

I could turn around using an InputStream (I was in the case I wanted to serve a resource via http-kit)

If you want more info, I can point you to the code where I use it

Hope it helps,
Ben




--

Dan Harbin

unread,
Jan 27, 2015, 8:00:46 AM1/27/15
to clo...@googlegroups.com
Ben, I would appreciate it if you'd show me the sample code.  Thanks for your help, everyone!

Benjamin VanRyseghem

unread,
Jan 27, 2015, 9:12:23 AM1/27/15
to clo...@googlegroups.com, clo...@googlegroups.com

Here I used to do something like `io/as-file path-to-my-file`
but it failed because it’s not resolved as a file anymore when it’s jarred

Good luck,
Ben

David James

unread,
Jan 27, 2015, 11:13:19 AM1/27/15
to clo...@googlegroups.com
You may find value in reading this:

> Methods in the classes Class and ClassLoader provide a location-independent way to locate resources.

Marshall Bockrath-Vandegrift

unread,
Jan 27, 2015, 3:52:00 PM1/27/15
to clo...@googlegroups.com
On Monday, January 26, 2015 at 9:24:28 PM UTC-5, Dan Harbin wrote:

      io/file


Just delete that line.  The `io/resource` function returns a URL which all the Clojure IO functions can handle just fine-as is.  When running in development the URL happens to be a `file://` URL, and thus something `io/file` can handle.  Once the resource is in a JAR that is no longer the case, and hence exceptions.  Just don't require a file when any URL will do and you'll be fine.

-Marshall

Dan Harbin

unread,
Jan 28, 2015, 10:57:25 AM1/28/15
to clo...@googlegroups.com
Marshall,

If I remove io/file, I get:

Exception in thread "main" java.lang.IllegalArgumentException: No method in multimethod 'do-copy' for dispatch value: [java.net.URL java.io.OutputStreamWriter]
        at clojure.lang.MultiFn.getFn(MultiFn.java:160)
        at clojure.lang.MultiFn.invoke(MultiFn.java:236)
        at clojure.java.io$copy.doInvoke(io.clj:396)
        at clojure.lang.RestFn.invoke(RestFn.java:425)
        at halp.core$_main.doInvoke(core.clj:10)
        at clojure.lang.RestFn.invoke(RestFn.java:397)
        at clojure.lang.AFn.applyToHelper(AFn.java:152)
        at clojure.lang.RestFn.applyTo(RestFn.java:132)
        at halp.core.main(Unknown Source)


It doesn't look like io/copy works with a URL.

Jeroen van Dijk

unread,
Jan 28, 2015, 11:36:19 AM1/28/15
to clo...@googlegroups.com
I'm not sure anymore what your goal is exactly, but here is what I meant to be complete:
(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (->> "hi.txt"
      io/resource
      slurp
      (spit "out.txt")))

--

Dan Harbin

unread,
Jan 28, 2015, 11:46:11 AM1/28/15
to clo...@googlegroups.com
Here's my goal:

I have a thread that listens on a channel for tuples of [file-path, file-contents] and writes files to a zip file using clojure.java.io/copy.

It's been really nice to be able to specify file-contents as anything clojure.java.io/copy can consume, such as a byte[] or a String, and it will "just work".  Well, except when I'm trying to add something from my resources like: [file-path, (io/file (io/resource "myfile.txt"))] while in an uberjar.

I'm able to workaround using the suggestion to use io/input-stream instead of io/file, but now I have to deal with closing the stream myself which is neatly handled for me when io/copy is given a file.

Dan Harbin

unread,
Jan 28, 2015, 11:52:12 AM1/28/15
to clo...@googlegroups.com
It seems my problem is related to this line in io.clj:


    (if (= "file" (.getProtocol u))
      (as-file (escaped-utf8-urlstring->str
                (.replace (.getFile u) \/ File/separatorChar)))
      (throw (IllegalArgumentException. (str "Not a file: " u)))))

When running from the repl, .getProtocol returns "file", but when running from an uberjar, .getProtocol returns "jar".

Dan Harbin

unread,
Jan 28, 2015, 12:13:58 PM1/28/15
to clo...@googlegroups.com
After perusing stackoverflow, it's evident to me that the "proper" way to access a file within a jar is to use getResourceAsStream (as was mentioned in the thread above, thank you), and I suppose clojure's io/input-stream does the trick as  well.  Thanks, all!


Reply all
Reply to author
Forward
0 new messages