Thursday, May 27, 2010

JDT, Manifest Classpath, Classpath Containers and Helios

In the interest of public service, thought I’d communicate a behavior change in Eclipse Java Developer Tools (JDT) coming in Helios (aka Eclipse 3.6). In Galileo (aka Eclipse 3.5), JDT started resolving manifest classpath in libraries added to project’s build path. This worked whether the library was added to project’s build path directly or via a classpath container, such as the user library facility provided by JDT or one implemented by a third party. In Helios, this behavior was changed to exclude classpath containers from manifest classpath resolution.

This change in behavior has potential to affect users who have started relying on this facility in their projects. A workspace with projects that built fine in Galileo may not build in Helios. The cause may not be obvious as the only thing that the user will notice is build errors complaining of missing types. The user will need to figure out where those types were coming from originally and take steps to make sure those libraries are referenced directly. The exact way to do that is dependent on the implementation of the classpath container in question. For instance, if the container is based on JDT user library feature, then the definition of the user library will need to be adjusted in preferences. Alternatively, users can set a system property in their eclipse.ini file to revert to Galileo behavior. Adding the following line to the end of the file should to the trick:

-DresolveReferencedLibrariesForContainers=true

This change can also affect third parties shipping plugins on top of JDT that implement classpath containers. If the classpath container was implemented to rely on manifest classpath resolution, it will need to be updated to work properly on Helios. Fortunately, JDT provides an API to make this process less painful…

JavaCore.getReferencedClasspathEntries( [IClasspathEntry], [IJavaProject] )

The above API call will do manifest classpath resolution on the library referenced by the provided classpath entry. Pass in null for the second parameter. The result is an array of classpath entries that can be added to the original set in classpath container initialization. Several things to watch out for…

  1. Make sure not add the same library twice. This is fairly easy to do, especially if your container implementation gives user some control over the contents. The last time I ran into this, JDT threw an exception on container initialization.
  2. Check returned entries for existence before adding them to your container. The getReferencedClasspathEntries method does not check for existence, but if your container adds a reference to a non-existing file, JDT will put an error in the problems view and nothing will build. Could be an ugly surprise for your users as a code fix in the container implementation will be required to resolve this.
  3. The getReferencedClasspathEntries method is new for Helios, which means that your container implementation will no longer be compatible with Galileo. If you need to support Galileo and Helios from the same code base, you will need to implement your own manifest classpath resolution.

References

  1. Bug 305037 - missing story for attributes of referenced JARs in classpath containers
  2. Bug 313965 - Breaking change in classpath container API
  3. Bug 313890 - Migration guide to 3.6 for containers with MANIFEST-referred entries

Olivier Thomann has asked me to include the following clarifying statement:

“Galileo behavior was wrong as a container should control exactly what is inside the container. So Helios fixed this issue.”

I have no disagreement with that statement. My purpose in writing this blog is purely to document a difference in behavior from Galileo to Helios that can appear to many as a regression. I do think this change could have been handled better, but that’s besides the point at this stage in Helios release and not the reason that I wrote this post.