Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Clojure and Java Dependencies

James Conroy-Finn edited this page Nov 20, 2016 · 6 revisions

Selenium WebDriver Dependencies

The clj-webdriver API is resilient to changes in the implementation of Selenium WebDriver itself. More often than not, new releases of Selenium WebDriver do not require any edits to clj-webdriver's code. For this reason, as of the 0.7.x releases, clj-webdriver no longer ships with Selenium WebDriver dependencies specified, so that consumers of the library can simply specify the version of Selenium WebDriver they need for the browser versions they support without needing to exclude ones clj-webdriver specifies.

Dependency Resolution Problems

I've kept this section here due to how many issues are opened for clj-webdriver due to dependency conflicts.

The biggest problem with automated transitive dependency management is understanding its behavior when two different artifacts both depend on a third artifact. If they both depend on the exact same version, there's no problem; if they depend on different versions, which one "wins"?

The answer is: you should check.

You can use mvn dependency:tree in Maven projects or lein deps :tree in Clojure projects to see exactly how the final dependency resolution works itself out. For clj-webdriver, you should be certain that the versions of Selenium-WebDriver's JAR's that you want are the ones that "win" by looking at the output of those commands.

A nice one-liner I use to help focus on dependencies I'm investigating pipes lein deps :tree to egrep like this:

lein deps :tree | egrep --color "$|selenium-java"

This will show you all the output of lein deps :tree but also colorize, in this case, selenium-java so that it's easier to see in the (often ludicrously verbose) output of this command.

If you discover that some JAR you depend on is "winning" the dependency resolution and you need to specify different versions of the JAR's it depends on, then you can use exclusions to prevent its transitive dependencies from being respected, and then specify the JAR's that you need explicitly.

A good example is the [com.codeborne/phantomjsdriver "1.2.1"] artifact for PhantomJS. This JAR specifies dependencies on some core Selenium-WebDriver JAR's, but in your own project you're likely using more recent versions. To prevent the PhantomJS JAR from controlling what core Selenium-WebDriver JAR's your project pulls in, in your project.clj you can include it in your dependencies like this:

:dependencies [[com.codeborne/phantomjsdriver "1.2.1"
                                              :exclusions [org.seleniumhq.selenium/selenium-java
                                                           org.seleniumhq.selenium/selenium-server
                                                           org.seleniumhq.selenium/selenium-remote-driver]]
               [org.seleniumhq.selenium/selenium-java "2.47.1"]]

This excludes the PhantomJS dependencies on Selenium-WebDriver and explicitly pulls in version 2.47.1 for use in your code.

You might ask: won't that break the library that needed the older versions? It might. If it does, you're in deeper trouble, and will likely need to port the functionality you need manually. However, frequently you're able to specify newer versions of JAR's that other JAR's also depend on without breaking them, because the underlying code they rely on is still present in the newer JAR's. Libraries that honor semantic versioning tend to behave well within respective version ranges, but since errors for missing classes can show up at runtime in Clojure applications, if you use exclusions you should test your code thoroughly.