'# dynapath [![Build Status](https://secure.travis-ci.org/tobias/dynapath.png?branch=master)](https://travis-ci.org/tobias/dynapath)

dynapath provides a protocol and util functions for class loaders that
make their effective classpaths readable and/or modifiable.

## Rationale 

Clojure uses a `clojure.lang.DynamicClassLoader` by default (an
extension of `java.net.URLClassLoader`), which provides `.getURLs` for
reading the effective classpath and `.addURL` for modifying it. It's
common for projects that need to read or modify the effective
classpath to assume that a `URLClassLoader` is always available. But
in some environments, the available class loader may not be a
`URLClassLoader`, and may not be readable or modifiable.

Some projects (notably `pomegranate`) handle this by providing a
protocol that can be implemented for other class loaders that may
provide similar functionality.

dynapath provides a protocol that is based on an extraction of
pomegranate's protocol, and is intended to be a standard way for
accessing or modifying the effective classpath. Using dynapath in your
library instead of assuming a class loader or implementing your own
protocol provides the following benefits:

* Your library can work with any modifiable/readable class loader
  without any changes
* Any project that has already implemented `DynamicClasspath` for
  whatever esoteric class loader they are using will not need any
  other changes to use your library as well

## Usage

Add it as a dependency:

For a Leiningen/Boot project:

    [org.tcrawley/dynapath "1.1.0"]

For a maven project:

    <dependency>
      <groupId>org.tcrawley</groupId>
      <artifactId>dynapath</artifactId>
      <version>1.1.0</version>
    </dependency>

If you need to access or modify the effective classpath:

    (require '[dynapath.util :as dp])
    
    ;; returns a seq of the urls for the classloader. Takes any classloader
    ;; (whether it implements DynamicClasspath or not) and does the right thing
    (dp/classpath-urls a-classloader)
      
    ;; returns a seq of all the urls available from the classloader and its 
    ;; parentage chain
    (dp/all-classpath-urls a-classloader)
    
    ;; adds a url to the given classloader if it is addable
    (dp/add-classpath-url a-classloader a-url)

Loading the `dynapath.defaults` namespace will automatically implement 
`classpath-urls` and `add-classpath-url` for `clojure.lang.DynamicClassLoader` and
`classpath-urls` for `java.net.URLClassLoader`.

If you need to implement `DynamicClasspath`:

    (require '[dynapath.dynamic-classpath :as dc])
    
    (extend-type AReadableButNotModifiableClassLoader
      dc/DynamicClasspath
      (can-read? [_] true)
      (can-add? [_] false)
      (classpath-urls [cl] (seq ...)))

    (extend AReadableAndModifiableClassLoader
      dc/DynamicClasspath
      (assoc dc/base-readable-addable-classpath ;; implements can-read? and can-add?
             :classpath-urls (fn [cl] ...)
             :add-classpath-url (fn [cl url] ...)))

## Note on URLClassLoader

Prior versions of dynapath implemented `add-classpath-url` for
`java.net.URLClassLoader`. Doing so required reflective access to its
protected `addURL` method, which would result in a warning printed to
stdout under Java 9. To prevent that, that implementation has been
removed, and libraries that were relying on that behavior should
instead ensure they have a modifiable classloader as high in the
classloader tree as they can control. For example,
[boot implements its own classloader](https://github.com/boot-clj/boot/commit/a046a497a8bb7f3d1e7aa8d4db4a81c51beaef7d)
to do this.

## Who's using it?

* [bultitude](https://github.com/Raynes/bultitude)
* [immutant](https://github.com/immutant/immutant)
* [ritz](https://github.com/pallet/ritz)
* [tair-repl](https://github.com/xumingming/tair-repl)
* [pomegranate](https://github.com/cemerick/pomegranate)
* [boot](http://boot-clj.com/)
* [metabase](https://github.com/metabase/metabase)

Are you using it? If so, add yourself to this list and send me a PR.

## License

Copyright © 2012-2017 Tobias Crawley

Distributed under the Eclipse Public License.
