Many things in Eclipse require or uses a classpath, i.e. java projects, junit and server launches, hibernate console etc. For these classpath containers are handy to use instead of forcing users to individually pick the (possible long) list of jars needed to get their work done. A classpath container allow plugin providers to automatically create a group of jar's which can be added and removed in one operation - simple and efficient.
Unfortunately I've seen over the years that many plug-in's create what I call navel-gazing classpath containers which creates a bigger problem than it solves.
Let's take the latest example from JBoss Developer Studio we had to fix, the Drools plugin classpath container:
This screenshot shows the default classpath container provided by earlier versions of Drools plugins. Notice how all the jar's points to a fixed internal location (.cp\lib) inside the Drools plugin, that is what I call a navel-gazing class path container.
The Good
First, lets see why so many plugins seem to do it this way. There is (only) one good
reason for having navel-gazing classpath containers.
That is to reduces the setup time since no separate configuration is needed to define where the runtime/set-of-jars are located.
With a navel-gazing class path container the Drools plugin can out-of-the-box provide a running
Drools project without any knowledge and any influence by other plugins/projects. That is a good thing, right ? Well...
The Bad
Why do I not like this navel-gazing ? Don't I want users to get started as easily as possible without having to think about details like which dependencies the project has ?
Sure, I want users to get started as easily as possibly with their work but any developer should actually care about or at least have some awareness about which dependencies a project have - and if we use navel-gazing classpath containers they don't have to be aware.
Furthermore when it come to newbies it is my experience that they have a tendency to think that projects created by wizards in their IDE's is the best way of creating projects - if the IDE does it, then it must be correct and should not be questioned.
Therefore it is important that the project your plug-in creates is actually something that is useful when a project evolves over time, updates it dependencies and possibly gets developed together with a team. (that is at least the goal for a good IDE)
Unfortunately navel-gazing classpath providers is doing the exact opposite.
It is not something you should use on a team because all the jars point to jars inside the plugin and thus it is most likely dependent on which version of the plugin the user is running. If a team does not keep the versions of plugin in sync the team risc to be running against different jars resulting in possible compile errors and in worst case subtle runtime differences.
Another common issue with navel-gazing classpath containers is that they only support one possible runtime. Making it impossible to have projects in your workspace that uses different version of the jars, i.e. having a old version of your project using Drools 4 with Java 5 and a new version of your project Drools 5 using Java 6 would require multiple Eclipse installations.
Imagine if Eclipse only supported projects that worked with the Java version you were running Eclipse it self with ? Not very useful.
Even worse is that if a user updates his plug-in via an updatesite he will (probably unwillingly) change the runtime dependencies of his projects because the jar's inside the plug-in are now updated.
Other Examples
Another example of a navel-gazing classpath container is the Ant or JUnit classpath container provided by eclipse. These also links to classpath's inside Eclipse causing the same kind of issues as described above. Though since Ant and JUnit are very stable and mature frameworks it is not so bad but in principle it is wrong and your projects should not use these classpath containers lightly.
Solutions
Ok, so navel-gazing is bad; what should plug-ins do instead ?
Let's look at a good classpath container everyone should know: JRE classpath container provided by Eclipse Java Development Tooling (JDT).
The screenshot shows what the classpath container provides when you
have created a Java Project. Notice how the jars are from some external location.
These locations are defined in preferences under Installed JRE's
.
These locations all have names allowing a team to just sync up which names (which actually are id's) to use for it's dependencies, it also allow to use multiple runtimes within the same Eclipse instance and it points to a well defined location which is under the user control i.e. not dependent on any updates to your eclipse plugins.
This simple notion of having explicit defined locations for your runtimes makes a big difference and this is what any decent plugin should do if it wishes to provide a framework specific classpath container.
And that is also what the Drools team did in the end and the result is a non-navel-gazing classpath container:
Alternatives
Of course there are other solutions to this problem, namely not to use plugin provided runtimes provided by plugins at all and instead use something like Ivy and Maven for full dependency management.
If you want to use full dependency management inside Eclipse take a look at m2eclipse which actually provides a classpath container which jars are calculated based on your Maven dependencies. There is also IvyIDE that does the same for Ivy.
Want to hear more
John Graham and I are doing a talk at EclipseCon 2009 called "Max and John's excellent plugin adventure - The Highlight Reel" where we will present and discuss anti-patterns like the above.
Do you have any favorite Eclipse anti-patterns ?