CONTENTS | PREV


JavaTM Product Versioning Specification

February 10, 1998


Evolution in open distributed systems needs to be managed carefully because correct operation depends on a number of dependencies between packages. Changes within a distributed system can have a significant impact on many groups of individuals, including users, support organizations, web administrators, and developers. Packages within a distributed system need to operate correctly with only partial knowledge about the state of the whole system. The difficulty level increases because the packages of the system must be able to evolve at different rates. Evolution in such a system is made possible by explicitly managing the dependencies between the packages using techniques of object oriented design to govern how individual packages evolve. The Java language defines packages that are a natural for the consistent unit of update, packages that expose only public interfaces and that consume only public interfaces of other classes.

Introduction

In any system, support must be provided for the system to evolve over time. Most existing systems have conventions and mechanisms that specify how change is accommodated. These systems have been based on the model where software programs are installed on a computer. Typically, developers specify what versions of other packages are required and the installation process verifies and configures the system.

In open distributed systems, however, the static assumptions of existing systems do not work. Evolution is more difficult because it is not possible to control how or when packages change. Correct operation depends on a greater number of dependencies between packages. There is a real need for an updated set of conventions and mechanisms which specify how packages of a system should evolve so that the goal of an open reliable scalable distributed system can be achieved.

This document specifies the following:

Requirements

Changes within a distributed system have a significant impact on the following groups:

Each group has different requirements on network-deployed products that evolve over time.

End Users

End users need to feel confident that Java-based products will become increasingly reliable and compatible over time. Their reluctance to upgrade must be addressed by building confidence in the "Write Once, Run Anywhere" philosophy. With Java, it should no longer be the norm that users will complain "If I upgrade, it will break something" or "I won't be able to read or write data that others can use."

Product Support Organizations

Product support organizations rely on being able to correctly and easily identify the product that is being used, the environment in which it is being used, and the integrity of the product packaging.

Webmasters and Administrators

Webmasters, administrators, and service providers need a reliable and supportable way in which to deploy applications for their clients via the web or network filesystems.

Product Developers

Product developers need to know how to write and deploy applications and libraries that satisfy the requirements of users, administrators, and support personnel. They must be able to make products and packages that can do the following:

Problems of Evolution in Distributed Systems

Problems can occur in open distributed systems when packages evolve and are frequently updated. If the specified behavior inherent in the use of public interfaces is not maintained, the system can fail in unexpected ways. Open systems are comprised of many packages from different companies and organizations. These organizations operate asynchronously, introducing and upgrading their products according to their own schedules. Distribution of upgraded products takes time and adoption is not universal.

In Java, the components of local and distributed systems rely on the public interfaces and contracts for the behavior of other packages. Those packages will themselves evolve over time. In order for a package to operate correctly, packages that it depends upon must continue to provide the expected behavior even if those packages have been updated.

Only partial consistency is possible in distributed systems, since it is impossible to have knowledge of the entire state of the system. Each process and each package of the system has its own partial view of the current state of the system, accumulated incrementally by requesting information from other parts of the distributed system. Each piece of information, whether from an applet that was started, a class that was loaded, a remote method invoked, or a web page retrieved, must be treated carefully so that it can be used consistently with the rest of that partial view.

Several types of errors can result from inconsistencies in the classes that are loaded: class verification errors, classes compute incorrectly but without recognizable errors, or user requested functions that exhibit arbitrary failures.

These problems can occur in the following typical scenarios:

These problems cannot be prevented or solved directly because they arise out of inconsistencies between dynamically loaded packages that are not under the control of a single system administrator and so cannot be addressed by current configuration management techniques.

Design for Evolution

The key to dealing with these problems and meeting the requirements stated above is the careful design of the packages and packaging of the system so that they may be updated, distributed, and loaded in consistent units. Typical to mass produced products is the notion of the field replaceable unit. It is the smallest unit of a product that can be identified with a specification, a supplier, can be distributed and redistributed, and can be replaced if faulty. This same model is used for software distribution, products have a name, a version number, adhere to one or more specifications, are distributed on the network or CD-ROM and its problems can be reported to support organizations. These packages are the smallest unit that can be distributed, used, validated and replaced or upgraded when necessary. Packages can be assembled with other packages and each package can still be identified, verified, and distributed.

The Java language-based package mechanism fits well with the idea of a replaceable unit. Java packages expose only public interfaces and use only the public interfaces of other packages. The JavaTM Language Specification defines the approaches for compatible evolution of packages.

Java Language Specification on Backwards Compatibility

The Java Language Specification lays the groundwork for developing packages that can be expected to evolve gracefully over time. It defines how classes can change and still be backward compatible with other classes previously compiled and distributed. Essential to robust evolution is the stability of the public, protected, and package interfaces and behavior as the implementations evolve. It defines "compatible" changes as "those changes that do not change existing interfaces or behavior." Thus, if a class defines a method, and the method had a particular behavior, that same contract must be supported by the all later evolutions of the class. Detailed rules are given in Chapter 13 of the JavaTM Language Specification. One additional incompatible change has been added: it is incompatible to add methods to a public interface.

Although incompatible changes are not permitted, new or similar functionality can always be added in new or existing interfaces or classes.

By choosing the Java package as the unit of update, the package and private methods of the classes can change, thereby allowing flexibility in the implementation of the package while the public and protected classes and methods maintain the external interfaces and semantics.

Object Serialization Specification on Backwards Compatibility

Robust persistent storage and robust communication between the components is important to distributed systems. Components must be able to maintain persistent storage as they evolve, being able to evolve classes and yet have them read data previously written to storage. Components in a distributed system evolve at different rates and must still be able to communicate reliably.

Adhering to the compatibility requirements of object serialization allows newer and older versions to communicate in a predictable and consistent way. The details are in Chapter 5 of the JavaTM Object Serialization Specification.

Package Version Specification

There are several categories of artifacts that need to be identified including specifications, implementation, the Java Virtual Machine and Java Runtime Environment.

Specification Versioning

Open systems are based on the idea that a specification may have multiple implementations. Specifications evolve under the auspices of an organization or company. It is highly undesirable if a specification has multiple incompatible versions. Each version of a specification or implementation must evolve only into a single subsequent version. The philosophy of requiring specifications to be backward compatible allows specifications to be identified as supersets of the previous specification. Since there is a single sequence of version specifications they can meaningfully be identified by version numbers with specific semantics that imply the ordering. Specification version numbers use a Dewey decimal notation consisting of numbers separated by periods.

A specification is identified by the:

Virtual Machine Versioning

An implementation of the Java Virtual Machine should be identify both the specification and the implementation. These properties should be added to those already available using java.lang.System.getProperties.

java.vm.specification.version i.e. 1.2
java.vm.specification.vendor i.e. Sun Microsystems Inc.
java.vm.specification.name i.e. JavaTM Virtual Machine Specification
java.vm.version i.e. Solaris 5.5 Native 1.0 build32
java.vm.vendor i.e. Sun Microsystems Inc.
java.vm.name i.e. Solaris 5.x JVM

These properties are accessed using the method java.lang.System.getProperty and each returns a string.

Version Identification of the Java Runtime

The requirement to identify the JavaTM Runtime is already partially met via the properties specified by the JavaTM Language Specification, §20.18.7 usingjava.lang.System.getProperties.

java.version i.e. Solaris 1.2
java.vendor i.e. Sun Microsystems Inc.

Currently these identify the implementation of the JavaTM runtime and the core classes that are available. These properties do not identify the JavaTM Language Specification version that this JDKTM implements.

The following additional properties are needed to identify the version of the JavaTM Runtime Environment specification to which this implementation adheres:

java.specification.version i.e. 1.1
java.specification.name i.e. JavaTM Language Specification
java.specification.vendor i.e. Sun Microsystems Inc.

These properties are accessed using the method java.lang.System.getProperty and return their values as strings.

Package Versioning

Each JavaTM package consists of class files and optional resource files. The information needed to identify the contents of the package is stored with the package contents.

This specification applies to all packages, regardless of whether they are developed as a core package distributed with a JavaTM Runtime, a standard extension, an applet or application package.

Unlike version numbers for specifications, version information for implementations cannot be used to identify the package as being backward compatible with earlier versions. Package version numbers are present to identify differences between the specification and the implementation, i.e. bugs. New versions of implementations are specifically produced to remove (undesirable or incorrect) behavior and thus are intended not to be backward compatible. Therefore, package version strings can have any unique value and can only be compared for equality. For a complete explanation of this rationale, see Rationale for Limiting Implementation Version Numbers to Identity.

The following attribute names are defined for a package. The value of each attribute is a string:

Package-Title Title of the package
Package-Version Version number
Package-Vendor Vendors company or organization
Specification-Title Title of the specification
Specification-Version Version number
Specification-Vendor Vendors company or organization

These attributes are stored in the manifest and retrieved by programs using the java.lang.Package API described below.

JAR Manifest Format

The current manifest format is extended to allow the specification of the attributes for package versioning information. A manifest entry should be created for each JavaTM package. The name of the entry will be the directory within the archive that contains the package's class and resource files. For example:

Manifest-version: 1.0
Name: java/util/
Specification-Title: "Java Utility Classes" 
Specification-Version: "1.2" 
Specification-Vendor: "Sun Microsystems Inc.". 
Package-Title: "java.util"
Package-Version: "build57" 
Package-Vendor: "SunMicrosystems. Inc."

To insert these attributes in the manifest, create a prototype manifest file and use JarTool's -m switch to merge the attributes into the manifest when the manifest is built. JarTool will be extended to browse and set the versioning attributes in the manifest.

How Users Know What is Running

When bugs occur, users must be able to report the identities of the packages in use. The application, applet, or browser must be able to expose the available information to the user, either on demand or when an error occurs. Available APIs can report the following information:

Rationale for Limiting Implementation Version Numbers to Identity

Implementations evolve independently over time to fix bugs, to improve performance, or to add new functions that are called for by more recent revisions of the specifications. Packages implement specifications and must identify which version of each specification they implement. Interactions occur between packages through their public and protected interfaces and classes only. The public API and behavior must remain stable over time, so changes can be allowed in the implementation of one package without affecting the behavior of another package.

If the classes of a package always faithfully implemented the specification, it would be sufficient just to identify the specification. Since in the real world this rarely happens, packages need to identify themselves so that bugs can be reported against the packages that may have contributed to the problem.

There is a significant tendency to try to attach some significance to version identifiers of implementations. If the purpose is to allow the tracking of bugs then a unique number is sufficient. It is also sufficient for a client package to workaround a bug in a particular version of a vendors package since that version can be tested for and the bug avoided.

However, many additional problems can occur when one package attempts to work around bugs in other packages. They need to identify behavior that is not part of the specification and may try to use behavior that is only part of one implementation. Such implementation specific behavior cannot be relied upon to be in any particular version other than the one(s) seen and tested by the developer.

A bug first appears in some version of a vendors package and may or may not continue to be a problem in subsequent versions. If the client of the buggy package uses a workaround based on version numbers, it could correctly work around the bug in the specific version. Now, if the buggy package was fixed, how would the client package know whether the bug was fixed or not? If it assumed that higher versions still contained the bug, it would still try to work around the bug. The workaround itself might not work correctly with the non-buggy package. This could cause a cascade of bugs caused by fixing a bug. Only the developer, through testing with a new version, can determine whether or not the workaround for a bug is still necessary or whether it will cause problems with the correctly behaving package. The developer only knows that the bug exists in a particular individual versions.


CONTENTS | PREV


Copyright © 1998 Sun Microsystems, Inc. All Rights Reserved.