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.