The things we make available as APIs are "cast in stone". We can't change them; we have to live with them for a long, long time. So it is a good idea to show as little as possible in APIs. However, sometimes we are faced with a situation when some pieces, while of no interest to majority of users, would be beneficial to a small group of consumers.
Moreover, often such special functionality would reveal too much of implementation details thus locking us into the current implementation. One solution is usage of "x-friends" attribute in OSGi manifest, but it requires explicit list of consumers.
So, how do we make some additional functionality available to those special customers without locking is into the current implementation?
Well, there is no magic wand. But there is a compromise: we provide those extra pieces, but we reserve the right to change them - sometimes.
Just like "Application Programming Interfaces" (API) we'll have "Service Provider Interfaces" (SPI). The difference would be that SPIs can be modified or removed when minor version of the bundle changes (for instance, 3.3 to 3.4). Such breaking changes should only be considered as a last resort but they are allowed.
Consumers of SPIs would be expected to specify smaller version range in the dependencies (for instance, [3.3, 3.4) as opposing to [3.3, 4.0) ). It would be a good idea for SPI consumers to "register" with SPI providers so that they can be contacted before changes in SPI take place. I think that list of e-mails of users can be added to Javadocs of the SPIs. (If there are too many users to list - consider making it an API.)
The SPI classes / interfaces / methods will have "@spi" tag added to Javadocs. The text of the "@spi" tag might specify a way to register with SPI provider.
SPI classes and interfaces need to be specified in OSGi manifests just like API classes and interfaces. Same rules for backward compatibility will apply with the exception that backward compatibility can be broken when minor version is incremented. SPI classes and interfaces can be placed in a package having ".spi" in the name to underscore the differences.
To recap this in a short form:
- SPIs are like APIs but for a small subset of customers, probably exposing implementation details
- Marked by "@spi" tag in Javadoc
- Can be broken in a minor release
- Consumers are advised to register with suppliers to be notified on upcoming changes
I don't think the defining characteristic of SPI is that it can change. We have quite a lot of API in the platform that I would call SPI, and for most of it we have been able to attain full contract compatibility, or at least binary compatibility, across all releases. The defining characteristic of SPI is that it's special API for a very particular, advanced client, and not intended for general client use. It may impose specific restrictions that go beyond the general API usage contract of the platform.
However, I still agree it's useful to be able to flag SPI, for a variety of reasons:
- To clearly distinguish SPI from client API, so that the client API is simplified and the "advanced" functionality it not readily visible.
- To emphasize that the API is very special and should not be implemented/used lightly. There may be additional constraints made upon the API client that are unusually restrictive.
- To allow API tools or simple searches to scan for SPI usage.
Here is what I recommend:
- Add a javadoc tag (@spi may be ok, or perhaps something like @eclipse.spi to ensure we don't overload with others who might have defined such a tag).
- Use this @spi tag as a place where unusual restrictions could be imposed. One such restriction could be a limit on the compatibility promise, although this wouldn't necessarily be the case. For example, the Equinox framework adapter could specify that it maintains compatibility only across service releases. Again, this is quite unusual - for most SPI binary compatibility would be maintained.
- Recommend that SPI be placed in separate packages from internals or normal client API. The naming convention would be "org.eclipse.<project>.spi.*" or "org.eclipse.<component>.spi.*" in the case of the platform project.
- Release documentation that describes what SPI is, how it should be treated by clients, how to specify SPI, etc.
Some examples of existing SPI in the platform:
- org.eclipse.core.filesystem.provider package, for implementors of file systems
- LockListener class in org.eclipse.core.jobs
- org.eclipse.core.resources.team package in resources bundle
- Synchronizer class in SWT
- WorkspaceLock in org.eclipse.core.resources. This is an example of an SPI that made an unusual restriction: "This method is for internal use by the platform-related plug-ins. Clients should not call this method." Because of this, we were able to break contract compatibility because we had placed restrictions on the clients.
- Framework adaptor packages in org.eclipse.osgi
--John arthorne.ca.ibm.com 13:36, 9 October 2007 (EDT)