# The Classpath is a Lie

**URL:** https://clojureverse.org/t/the-classpath-is-a-lie/8093
**Category:** Blogs
**Created:** [August 25, 2021, 2:35pm UTC](https://clojureverse.org/t/the-classpath-is-a-lie/8093 "2021-08-25T14:35:50Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![plexus](https://clojureverse.org/user_avatar/clojureverse.org/plexus/32/6_2.png) [@plexus](https://clojureverse.org/u/plexus)
#### Post date: [August 25, 2021, 2:35pm UTC](https://clojureverse.org/t/the-classpath-is-a-lie/8093/1 "2021-08-25T14:35:50Z")

</div>

Three years ago I wrote here about some frustrations and questions I had when trying to deal with the classpath and class loaders.

> [@Dynamically adding to the classpath in a post Java 9 world. Help?](https://v2.clojureverse.org/t/dynamically-adding-to-the-classpath-in-a-post-java-9-world-help/2520):
>
> I’ve been trying to figure out how to dynamically add an entry to the classpath, in a way that works under Java 8 and Java 9, that works when running under Leiningen or through clj. I’ve gone down the rabbit hole trying to understand classloaders and the Java 9 changes, and I’m still utterly confused and without a working solution. I’ve compared Pomegranate 1.0.0, Pomegranate 0.4.0, Dynapath, and the experimentel add-lib from tools.deps.alpha, and it seems the only thing that actually works in …

Back in May @Daniel_Szmulewicz blogged about this topic, finally answering a question I’ve had since then, where do all those extra `DynamicClassLoader` instances come from?

> **[Once Upon a Class](https://danielsz.github.io/blog/2021-05-12T13_24.html)**
>
> Every time you press ENTER at the REPL, a new class is born. In addition, it comes with a new class loader instance exclusively dedicated to loading it. That’s Clojure behind the scenes doing its runtime magic. On the other hand, in a nREPL client...

Recently I’ve had reason once again to dive (too) deep into this topic. The result is a blog post of my own. It’s pretty heavy and technical, but I thought it was important to share some of this stuff for the next person who is trying to understand these ClassLoaders and how they relate to Clojure. I still feel like a noob with this stuff, but I’m a lot less confused than I was three years ago.

If you manage to make it through the Java snippets and technical details there’s a link at the end with some practical REPL helpers which you may find useful.

> **[The Classpath is a Lie](https://lambdaisland.com/blog/2021-08-25-classpath-is-a-lie)**
>
> by Arne Brasseur A key concept when working with Clojure is “the classpath”, a concept which we inherit from Clojure’s host language Java. It’s a sequence of paths that Clojure...

---

<div class="post-metadata">

### Author: ![didibus](https://clojureverse.org/user_avatar/clojureverse.org/didibus/32/951_2.png) [@didibus](https://clojureverse.org/u/didibus)
#### Post date: [August 26, 2021, 2:28am UTC](https://clojureverse.org/t/the-classpath-is-a-lie/8093/2 "2021-08-26T02:28:39Z")

</div>

Loved it, really informative.

> Sadly we can’t change the system classloader. Its classpath may as well be set in stone

Can’t you use -Xbootclasspath to change it 🕶?

> removing something that is part of the classpath the application started with is impossible

Seems it be possible to tombstone it though, like keep a list of classes for which in your custom getResource will pretend nothing was found and won’t call the parent methods. So getResource.coupd check in a tombstone-classes and if it finds it there, return nothing (or throw) or whatever it’s supposed to do when something is missing.

---

<div class="post-metadata">

### Author: ![plexus](https://clojureverse.org/user_avatar/clojureverse.org/plexus/32/6_2.png) [@plexus](https://clojureverse.org/u/plexus)
#### Post date: [August 26, 2021, 5:27am UTC](https://clojureverse.org/t/the-classpath-is-a-lie/8093/3 "2021-08-26T05:27:59Z")

</div>

> [@didibus](#):
>
> Seems it be possible to tombstone it though

You’re right, I did consider that. It makes the logic a bit more complicated but worth a try.

The other thing that occured to me is that you could actually replace the application classloader. Sadly neither AppClassLoader nor its parent BuiltinClassLoader have public constructors. Using a URLClassLoader probably mostly works. Or have the DynamicClassLoader sit directly above the PlatformClassloader and have it handle the full classpath.

The question is always how this stuff interacts with other tools and libraries. On the Clojure side there are others who have come up with clever classloader hacks, and their assumptions about the state of the world may interfere with our own. Similarly on the Java side there’s a lot of variation on how people run their JVM. We did have one or two reports of people who had to disable the classpath hacking in Kaocha because it caused issues.

But I guess we can just try and see how far we get 🙂

---

<div class="post-metadata">

### Author: ![mdiin](https://clojureverse.org/user_avatar/clojureverse.org/mdiin/32/3599_2.png) [@mdiin](https://clojureverse.org/u/mdiin)
#### Post date: [August 26, 2021, 1:51pm UTC](https://clojureverse.org/t/the-classpath-is-a-lie/8093/4 "2021-08-26T13:51:12Z")

</div>

I am not all the way through your article yet, but so far it’s both enlightening and entertaining, thanks! 😃

---

<div class="post-metadata">

### Author: ![ray-mcdermott](https://clojureverse.org/user_avatar/clojureverse.org/ray-mcdermott/32/251_2.png) [@ray-mcdermott](https://clojureverse.org/u/ray-mcdermott)
#### Post date: [August 30, 2021, 8:37am UTC](https://clojureverse.org/t/the-classpath-is-a-lie/8093/5 "2021-08-30T08:37:04Z")

</div>

When I wanted to use add-lib in a shared PREPL, I asked Alex and ended up with something like this

```auto
(defn shared-prepl-server
  "Create a PREPL socket-server and return the port on which it is available"
  [opts]
  (let [socket-opts (merge {:port 0
                               :name "repl-node"
                               :accept 'clojure.core.server/io-prepl}
                              opts)
        current-thread (Thread/currentThread)
        cl (.getContextClassLoader current-thread)
        _ (.setContextClassLoader current-thread (DynamicClassLoader. cl))
        socket (start-server socket-opts)]
    {:socket socket
     :port (.getLocalPort ^ServerSocket socket)}))

```

I have been using `https://github.com/just-sultanov/clj-deps` cos the tech was broken when I moved to Java 11 in the core library. Now that I see from your excellent blog post that it’s working again I’ll swap it back.

Great work as ever Arne!

---

<div class="post-metadata">

### Author: ![ray-mcdermott](https://clojureverse.org/user_avatar/clojureverse.org/ray-mcdermott/32/251_2.png) [@ray-mcdermott](https://clojureverse.org/u/ray-mcdermott)
#### Post date: [August 30, 2021, 8:41am UTC](https://clojureverse.org/t/the-classpath-is-a-lie/8093/6 "2021-08-30T08:41:42Z")

</div>

I am just going to add that I the repl-repl never needed to change `:local/root` so that’s definitely a new trick 🙂

---

<div class="post-metadata">

### Author: ![system](https://clojureverse.org/uploads/default/original/2X/5/51079bf9e4b7d9466242c06cf1e43b9f8bd6da14.png) [@system](https://clojureverse.org/u/system)
#### Post date: [February 28, 2022, 8:42pm UTC](https://clojureverse.org/t/the-classpath-is-a-lie/8093/7 "2022-02-28T20:42:08Z")

</div>

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.
