9

In Java there's a simple way to get path of a running jar file:

MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath()

But in Clojure we do not have class name, only namespace and functions. Same thing applies to the uncompiled scripts/REPL.

So my questions are:

  1. How can we find a path to the running jar file?
  2. How can we find a path to uncompiled source files?
Community
  • 1
  • 1
ffriend
  • 27,562
  • 13
  • 91
  • 132
  • If you want to find this path in order to locate resources (such as images, etc), you can package them inside the JAR instead. Java has an API to access resource files which are packaged inside a JAR. – Alex D Mar 15 '12 at 13:58
  • @AlexD: resources such as images are not the only possible usage. I may need to run separate command line programs packed with my JAR/scripts, or I may need it to register my program in the system. Also some frameworks rely on the real paths in filesystem, not URIs in JAR. – ffriend Mar 15 '12 at 17:02

6 Answers6

8

By default the name of your class is the name of your AOT-compiled namespace (that's what gen-class is for), so you can simply use the namespace's class.

(ns foo.core
  (:gen-class))

(defn this-jar
  "utility function to get the name of jar in which this function is invoked"
  [& [ns]]
  ;; The .toURI step is vital to avoid problems with special characters,
  ;; including spaces and pluses.
  ;; Source: https://stackoverflow.com/q/320542/7012#comment18478290_320595
  (-> (or ns (class *ns*))
      .getProtectionDomain .getCodeSource .getLocation .toURI .getPath))

(defn -main [& _]
  (println (this-jar foo.core)))

Result of running:

$ java -cp foo-0.1.0-SNAPSHOT-standalone.jar foo.core
/home/rlevy/prj/foo/target/foo-0.1.0-SNAPSHOT-standalone.jar
Avi Flax
  • 50,872
  • 9
  • 47
  • 64
rplevy
  • 5,393
  • 3
  • 32
  • 31
2

The idea of classpath is to hide where classes come from. You may have classes with the same name loaded from different classloaders, you may have the same class in multiple jars and rely on classpath ordering to choose the correct one.

Why do you want to know? If it's for any other reason than debug/logging purposes you are on dangerous ground and should tread carefully.

In fact it's perfectly reasonable for classes to have no jar file. This can happen in java for any runtime generated classes (think proxies).

In clojure a simple example would be as shown in the repl session below... You'll see @mikera's suggestion works fine for clojure.lang.Atom which is a built in class. But when you use a deftype to create your own type, clojure generates a class and it has no location...

user> (prn (-> clojure.lang.Atom 
               (.getProtectionDomain) 
               (.getCodeSource) 
               (.getLocation)))
#<URL file:/workspace/clj-scratch/lib/clojure-1.3.0.jar>
nil
user> (deftype Foo [])
user.Foo
user> (prn (-> (Foo.)
               (.getClass)
               (.getProtectionDomain)
               (.getCodeSource)
               (.getLocation)))
nil
nil
user> 
sw1nn
  • 7,278
  • 1
  • 26
  • 36
  • I understand all of this, but I'm not restricting possible ways to Java's class loaders - Clojure may have its own method to locate running files. For example, when some function throws an exception, Clojure runtime prints stacktrace with names of all corresponding files. So I believe there's some (maybe not very strict) way to get this information from within a script. – ffriend Mar 15 '12 at 17:22
  • The only way to get classes into the JVM is via classloaders. clojure can't provide some other way, because the JVM prevents it for security reasons. The stack traces in clojure are JVM stacktraces. The on-the-fly generation of classes is why clojure stack traces are so difficult to interpret. e.g. evaluating a form in the REPL causes a class to be generated with no underlying source - hence in this case you get a stack element with no file information... – sw1nn Mar 15 '12 at 17:36
  • `.clj` files are not classes and thus are tracked by Clojure runtime itself. The main point is that either JVM or Clojure runtime always know where they load code from. – ffriend Mar 15 '12 at 18:01
  • clojure loads resources (i.e. .clj files) using ClassLoader#getResource() / ClassLoader#getSystemResource() and friends so again it's all about the classpath.... – sw1nn Mar 26 '12 at 18:22
1
(defn this-jar
  "utility function to get the name of jar in which this function is invoked"
  [& [ns]]
  (-> (or ns (class *ns*))
      .getProtectionDomain .getCodeSource .getLocation .toURI .getPath))

Note that it's crucial to call .toURI to avoid problems with paths that have spaces as described in the equivalent Java question: How to get the path of a running JAR file?.

Hemaolle
  • 1,950
  • 18
  • 21
1

I haven't tried this, but it seems like all you need is a class instance. So for example can you not do this:

(-> (new Object) (.getClass) (.getProtectionDomain) (.getCodeSource) (.getLocation) (.getPath))
Kevin
  • 24,871
  • 19
  • 102
  • 158
1

You could try getting the path from a class defined by Clojure itself, e.g.:

(-> clojure.lang.Atom (.getProtectionDomain) (.getCodeSource) (.getLocation))

=> file:/some/path/to/clojure-1.3.0.jar

I believe this is technically the running jar file if you are running Clojure scripts or coding at the REPL.

mikera
  • 105,238
  • 25
  • 256
  • 415
  • Technically running program resides in memory :) I need path to _my_ jar/sources. User may put `clojure.jar` separately from my code and link it via `classpath`, so my program still will not know where it is located. – ffriend Mar 14 '12 at 03:29
0

find source files in a jar: tools.namespace/clojure-sources-in-jar

number23_cn
  • 4,611
  • 26
  • 31
  • I'm interested in how to find path to JAR file or to uncompiled .clj file, e.g. to get path to a running code, and not how to find .clj file in particular JAR archive. – ffriend Jun 14 '12 at 20:45
  • find source files in a dir? use tools.namespace/find-clojure-sources-in-dir function. – number23_cn Jun 15 '12 at 00:47
  • I need to find path to _running_ code. I don't know the directory and even structure of the project, I only know current point in the program, and I want to know where the code (source or compiled) lies. E.g. I want to find another file, lying in the same directory as file with my function. But my function may be in .clj file, compiled .class or inside of .jar, that is added to classpath separately from other jars via -cp option. I don't know how user of my function will add it to the project. He even may rename my .clj file, but my function should still find correct location. – ffriend Jun 15 '12 at 01:11
  • the function will compile to class, I can get path if AOT-compiled. – number23_cn Jun 15 '12 at 01:54
  • What will be the name of this class (note that you should get it in code, not just on file system)? What if it won't be AOT-compiled? I'm interested in Clojure-based way, something that can be safely used in Clojure code and always yield correct result. – ffriend Jun 15 '12 at 02:05
  • if AOT-compiled, classes path or a jar file path, I cannot get clojure source file on file system. if you use `lein uberjar` package up all files into a standalone jar, you can get the jar file path, and get all source files in this jar. – number23_cn Jun 15 '12 at 02:19
  • Once again: that's not me who will define project structure. The user of my code may put everything into single uberjar, separate jars, folder with .class files or even .clj files. I **don't know** how he will do it, and I **can't rely** on any particular way. I only know that some resources always stay at the same relative path to the file with my code. Thus I need to find path to my code, that **is being executed**, regardless of how user packed his project. – ffriend Jun 15 '12 at 02:34