Review comments for these guidelines can be created, viewed and responded to at SharedComponentVersioningGuidelinesReview
This document describes our guidelines around shared components in Derby, where a shared component is a collection of one or more packages that is used across multiple subsystems within Derby. Very loosely, I would define a "subsystem" as the code contained in one of the top-level source directories, e.g. engine, client, drda, tools, etc. As of this writing, the only shared component like this is the proposed common code component (see DERBY-289). Other components may be defined in the future as needed (for example, perhaps the DRDA networking code should be separated out as an independent shared component).
The guidelines for shared components in Derby are based on the Jakarta Runtime Versioning Guidelines. There are some areas that these guidelines do not cover, however, which we will address here:
Mixed Version Support
Two applications should be able to use one version of the Derby network client driver and a different version of the Derby embedded driver within the same Java VM without having to resort to creating specialized classloaders. Older versions of Derby jars will get their expected behavior in a mixed version environment regardless of jar ordering. Newer versions of Derby jars will get new behavior if the newer jar files are loaded first, and older behavior if the older jar files are loaded first. This is enabled by supporting forward compatibility as described below.
Guidelines for Forward Compatibility
A consumer built against version X.Y of a shared component should work with version X.Y' of a module where Y' < Y. This is called forward compatibility.
If the consumer uses new functionality added in version X.Y of the module, it should degrade to X.Y' level functionality. In the rare cases where this is not achievable, the consumer should throw an exception explaining that it requires version X.Y or greater of the shared component (rather than an obtuse Java exception like NoSuchMethodException
).
Forward compatibility must be guaranteed between patch revisions (from X.Y.Z to X.Y.Z') and minor revisions (from X.Y to X.Y'). We should strive for forward compatibility between major releases, but it is not a guarantee.
The consumer decides it can work with a given version of a shared component not by looking up its version number and then extrapolating what features are available, but instead by calling a static method to see if the the feature or features are available in the version of the shared component that has been loaded at runtime.
The details of how this is accomplished will be documented in the package-level javadoc for the org.apache.derby.common
package.
Deprecation Guidelines
A method or an interface may be deprecated. This is done using the @deprecated tag in the method or interface Javadoc. A method or interface must be available for 2 major releases after it is deprecated. For example, if it is deprecated in version 8.2, it can be removed in version 10.0 or greater. An exception to this rule may occur if it becomes clear that there is still heavy use of this interface.
Location and Distribution of Shared Components
All shared components should comprise of one or more packages under the package org.apache.derby.common
(stored in the source tree under java/common/org/apache/derby/common
).
Although it would be conceptually nice to have a separate JAR file for each shared component, it is a priority for us to keep a very simple user experience. For this reason, the classes of a shared components will be merged into the appropriate subset of the existing jar files.
Documenting Shared Components
We will provide a top-level package.html
javadoc under java/org/apache/derby/common
that describes the shared components guidelines (once they are approved) and lists the available components. For each shared component we will provide:
- Name of component
- Description of component (its role or intent)
- Packages contained within the component
- The name of the Info class that provides the hasFeature() method
User Visible Impact and Restrictions
The intent is that the shared component infrastructure and code will have minimal visible impact to users.
There may be some unavoidable impact to users who are running in an application environment where multiple instances of the Derby jars can be loaded by the same classloader. In particular, it is possible that one version of the common code may "shadow" another version such that changes/bugfixes can be hidden in unexpected ways. Note that these problems already exist where users are running with two different versions of the engine or two different versions of the network client within the same classloader context. Adding common code also makes it possible for these problems to occur for users who are running different versions of the network client and embedded client within the same classloader context.
In these mixed version environments, users can avoid shadowing issues by (a) ensuring that the right version is loaded first or (b) using multiple classloaders so that no one classloader has a mixed version situation.
To help alleviate the confusion and debugging time for these users, we will be providing documentation describing the potential problems for these kinds of environments and how they can be solved, and we will output runtime information at startup to show exactly what versions of each jar file a user is running with and log a warning if the versions are not the same.
We will strive to minimize the impact in mixed version environments using mechanisms such as support for forward-compatibility and compatibility tests.
There should be no visible impact in user environments where there are no mixed versions.
Testing Impact
In the first new release after common components are introduced, we will want to test for compatibility between patch and minor releases in the following ways:
- Build common component unit tests against the new release and test against jar files from old releases (forward compatibility tests)
- Build common component unit tests from old releases and test against jar files from the new release (backward compatibility tests)
Any incompatibilities introduced between patch and minor releases should be considered a regression. Any incompatibilities between major releases should be documented.
Introducing New Shared Components and Shared Packages
When a developer wishes to introduce a new shared component or a new package within an existing shared component, this should be put up for a vote requiring at a minimum lazy consensus, with consensus approval required for large changes to the structure of Derby. This is to help ensure that what we are sharing makes sense and does not accidentally paint us into some kind of architectural corner. Adding new classes or methods to an existing shared package does not need to be put up for a vote but should be reviewed using the standard patch review mechanism.