Review of Java ClassPath API

Jaroslav Tulach <[email protected]> Thu, 15 May 2003 15:01:19 +0200
Newsgroups gmane.comp.java.netbeans.modules.projects.devel
Message-ID <[email protected]>
Java Classpath API
==================

The classpath API was introduced in 3.4 with the goal to be extended in 
upcomming projects release. The goal was to allow continuous transition 
from the old and deprecated usage of FileSystems for classpath purposes 
in 3.x releases that will continue to work in NetBeans 4.x.

There is no doubt that the current 4.x version of classpath API serves 
that purpose and continues to support 3.x usage, but the new stuff 
introduced in 4.x seems to complicate the API violate some rules for 
good API design.


Deep inheritance hierarchy
--------------------------

The heart of the API used to be ClassPath interface, that was to main 
entry point for users of the API that allowed queries about classpath 
elements, resources and classloaders. It was specifically designed to 
allow future extensibility, but in spite of that a new class PathContent 
that extends ClassPath was introduced that contains the new 4.x 
functionality that was originally planned to be added into ClassPath.

This confuses the reader as there is no main entry point, makes writing 
of other APIs complicated (shall I use ClassPath as argument or 
PathContent?) and incorrectly misuses inheritance hierarchy that is an 
implementation mechanism to express API which leads to deep inheritance 
tree. For example FileSetPathContent has four superclasses and people 
are yet encouraged to subclass it.

The recommendation is to merge ClassPath and PathContent functionality 
and keep only ClassPath.


Mixing API and SPI I.
---------------------

In order to support structured paths a new interface PathResource has 
been introduced that duplicates and enhances functionality already 
available in ClassPath.Entry. Moreover this class mixes API with SPI 
which makes it look even more strange.

The only valid usecases it was possible to find (because the classpath 
requirements do not contain anything about structured classpaths) are 
part of the javadoc documentation and are build around usage of methods 
PathContent.getAllResources(), PathContent.getResources () and 
PathResource.getEnvironment(). But the class PathResource contains other 
methods like isLeaf(), getContents(), etc. It is belived that these 
methods are SPI (at least there is no usecase for them from API point of 
view), they are only used internally to correctly implement 
PathContent.getAllResources (). As mixing API and SPI can only cause 
troubles, a refactoring of this part of API is suggested.

First of all, the API part of the PathResource shall be moved to 
ClassPath.Entry (methods isValid(), getEnvironment, getFile, maybe the 
change listener belong there). The structured functionality shall be 
handled internally and by a separate API, so users of the 
ClassPath.getResources() and ClassPath.getAllResources () are not scared 
by it. Possible solution is to have factory method ClassPath.Entry 
createStructuredEntry (ClassPath structure) or instead of the ClassPath 
argument create completely new interface.


Mixing API and SPI II.
----------------------

The 3.x version of ClassPath API forbid anyone except the API itself to 
provide own versions of ClassPath or ClassPath.Entry objects. This is 
not possible anymore, as there will be different implementations of 
ClassPath provided by different modules, but the way how this is allowed 
is very dangerous and shall be replaced by a different design.

History have taught us that mixing API and SPI is very dangerous and 
that is why new APIs (Registry, Looks), tries to separate it. The main 
reason is that it is desirable to add new methods into API classes, but 
it is (nearly) forbiden to do so in SPI interfaces (read more). This 
suggestion is violated by ClassPath and its subclasses. It would be much 
better for future development of this API to stop using subclassing as a 
way to write SPI.

The suggested change is to follow the Registry API design and keep 
ClassPath and ClassPath.Entry final and under control of the module and 
provide interfaces and factory methods to create new instances. All the 
SPI functionality of the ClassPath shall be extracted to an interface, 
say ClassPathImpl. Plus there shall be a factory method ClassPath 
Factory.createClassPath (ClassPathImpl impl) that could be used by 
modules trying to provide their own factory. Also ProxyPathContent shall 
be replaced by an interface ProxyImpl extends ClassPathImpl and factory 
method to create ClassPath from it. Other SPI classes shall be changed 
in such a way as well.