Virtualenv for python is a pretty neat and useful piece of code. The ability to create multiple environments, exclusive to each other, and manage their packages/dependencies is rather useful. I’d been thinking about an equivalent for Java for a while, and realised that there are a few features that slightly overlap with those provided by Maven.
For example, to the best of my knowledge, Java doesn’t have a specialised package manager for packages (in the sense of gem for Ruby, luarocks for Lua, pip/easy_install for Python, etc) which was a slightly stumbling block.
My implementation consisted of two parts:
- Package manager – This was based on Maven, but doesn’t use any of the Maven APIs or classes. The reasons for this is that Maven Central was the most canonical repository for packages and package data, which is exactly what I wanted when I was resolving dependencies.
- Classpath management – This was was implemented in shell scripts for Linux (and to some extent Windows, but this is still a work in progress), for the reason that I wanted to parse commands and arguments from commands like ‘java’ and ‘javac’.
We tackle the package manager first.
By some distance, this was the easier part of the project. Jenever parses package names via group name/artifact Id through install like so:
The convention it uses is Maven’s groupId.artifactId:version for a specific version, or groupId.artifactId which parses the package repository data and retrieves the most recent version. This will recursively resolve and download the package’s dependencies, then download the package itself. Obviously this will fail if given incorrect input like a package name that doesn’t exist.
It’s all well and good being able to add all of these freshly downloaded jars to our $CLASSPATH, and
java Foo will run exactly as you imagine. However, should you decide to stick another classpath declaration in your command (say if you have some classes/jars that aren’t available through Jenever) then most likely the entire thing will fail to compile.
Why? Because given the prescence of -cp or -classpath, $CLASSPATH is not appended to this path, merely discarded.
This makes sense, as it could end up introducing conflicts or other such problems (as you’re able to see what you’re passing on the commandline, but I doubt people are able to remember their classpath). So we’d want to introduce a mechanism for doing exactly this: appending the new classpath to the already declared path on the commandline.
java -cp some:paths:here Foo to transparently become
java -cp some:paths:here:$CLASSPATH
In Unix shell (my implementation is in Bash), this is accomplished by aliasing java and javac (for example) to commands that process the arguments and sets them to what we want, which is essentially replacing the argument to -cp/classpath with the original argument but with the environment variable appended. In Windows batch files this is slightly different, but the implementation is largely the same apart from cmd-specific implemtations.
Where does this all tie in with Jenever? By using two commands (
exitjen), we control what is in our classpath by adding all of the jars in the directory location corresponding to the current environment choice, and by realiasing java commands to use this new parameter, we have a close approximation of a virtualenv-alike.
Interest, mainly. Most of my projects are small enough that I don’t really need to fire up an entire Maven installation if I want to test something that only requires a few libs, like Google’s guava libraries or Junit. In addition, I’ve recently been using my netbook for development, and it’s not particularly happy with a full bells-and-whistles IDE such as Eclipse, so I’ve been playing around with using this, vim, and a browser – the compiler tells me what I need to download to fix it.
Source is available here and requires Maven to build, though there will be prebuilt jars/scripts packaged up on the Downloads page.