跳到主要内容

JEP 223: New Version-String Scheme

Summary

Define a version-string scheme that easily distinguishes major, minor, and security-update releases, and apply it to the JDK.

Goals

  • Be easily understandable by humans, and easily parsable by programs.

  • Align with current industry practices, in particular Semantic Versioning.

  • Be adoptable by existing packaging systems and platform-deployment mechanisms including RPM, dpkg, IPS, and the Java Network Launching Protocol (JNLP).

  • Eliminate the current practice of encoding two types of information in one element of the version string, i.e., the minor release number and the security level, which is difficult to decipher and results in skipping many version numbers.

  • Provide a simple API for version-string parsing, validation, and comparison.

Non-Goals

  • Change the version-string format used by any release prior to this JEP's targeted release.

Motivation

Which release contains all of the most recent security fixes: JDK 7 Update 55, or JDK 7 Update 60?

It looks like JDK 7 Update 60 is five releases later than Update 55, so therefore it must include more security fixes, right?

That conclusion is, sadly, incorrect: These two releases both contain exactly the same security fixes. To understand this answer, you first need to understand the current numbering scheme for JDK Update releases. Minor releases containing changes beyond security fixes are multiples of 20. Security releases based on the previous minor release are odd numbers incremented by five, or by six if necessary in order to keep the update number odd. To understand whether a minor release is actually more secure than an earlier release ultimately requires looking at the release notes or the source code.

What's the difference between releases named "JDK 7 Update 60", "1.7.0_60", and "JDK 7u60"?

These are just different names for the same release. These differences make it difficult to identify and verify equivalent releases. A simple pointwise comparison of sequences of parsed tokens does not suffice; instead, a fairly sophisticated algorithm is needed. The use of the lower-case 'u' is not an industry standard and is not language-neutral.

It's long past time for a simpler, more intuitive versioning scheme.

Description

Version numbers

A version number, $VNUM, is a non-empty sequence of elements separated by period characters (U+002E). An element is either zero, or an unsigned integer numeral without leading zeros. The final element in a version number must not be zero. The format is:

[1-9][0-9]*((\.0)*\.[1-9][0-9]*)*

The sequence may be of arbitrary length but the first three elements are assigned specific meanings, as follows:

$MAJOR.$MINOR.$SECURITY
  • $MAJOR --- The major version number, incremented for a major release that contains significant new features as specified in a new edition of the Java SE Platform Specification, e.g., JSR 337 for Java SE 8. Features may be removed in a major release, given advance notice at least one major release ahead of time, and incompatible changes may be made when justified. The $MAJOR version number of JDK 8 is 8; the $MAJOR version number of JDK 9 is 9. When $MAJOR is incremented, all subsequent elements are removed.

  • $MINOR --- The minor version number, incremented for a minor update release that may contain compatible bug fixes, revisions to standard APIs mandated by a Maintenance Release of the relevant Platform Specification, and implementation features outside the scope of that Specification such as new JDK-specific APIs, additional service providers, new garbage collectors, and ports to new hardware architectures.

  • $SECURITY --- The security level, incremented for a security-update release that contains critical fixes including those necessary to improve security. $SECURITY is not reset to zero when $MINOR is incremented. A higher value of $SECURITY for a given $MAJOR value, therefore, always indicates a more secure release, regardless of the value of $MINOR.

The fourth and later elements of a version number are free for use by downstream consumers of the JDK code base. Such a consumer may, e.g., use the fourth element to identify patch releases which contain a small number of critical non-security fixes in addition to the security fixes in the corresponding security release.

The version number does not include trailing zero elements; i.e., $SECURITY is omitted if it has the value zero, and $MINOR is omitted if both $MINOR and $SECURITY have the value zero.

The sequence of numerals in a version number is compared to another such sequence in numerical, pointwise fashion; e.g., 9.9.1 is less than 9.10.3. If one sequence is shorter than another then the missing elements of the shorter sequence are considered to be less than the corresponding elements of the longer sequence; e.g., 9.1.2 is less than 9.1.2.1.

Version strings

A version string, $VSTR, consists of a version number $VNUM, as described above, optionally followed by pre-release and build information, in one of the following formats:

$VNUM(-$PRE)?\+$BUILD(-$OPT)?
$VNUM-$PRE(-$OPT)?
$VNUM(+-$OPT)?

where:

  • $PRE, matching ([a-zA-Z0-9]+) --- A pre-release identifier. Typically ea, for an early-access release that's under active development and potentially unstable, or internal, for an internal developer build.

    When comparing two version strings, a string with a pre-release identifier is always less than one with an equal $VNUM but no such identifier. Pre-release identifiers are compared numerically when they consist only of digits, and lexicographically otherwise. Numeric identifiers are considered to be less than non-numeric identifiers.

  • $BUILD, matching (0|[1-9][0-9]*) --- The build number, incremented for each promoted build. $BUILD is reset to one when any portion of $VNUM is incremented.

    When comparing two version strings with equal $VNUM and $PRE components, a string without a $BUILD component is always less than one with a $BUILD component; otherwise, $BUILD numbers are compared numerically.

  • $OPT, matching ([-a-zA-Z0-9\.]+) --- Additional build information, if desired. In the case of an internal build this will often contain the date and time of the build.

    When comparing two version strings the value of $OPT, if present, may or may not be significant depending on the chosen comparison method.

A version number 10-ea matches $VNUM = "10" and $PRE = "ea". The version number 10+-ea matches $VNUM = "10" and $OPT = "ea".

The following table compares potential version strings for JDK 9, using the existing and proposed formats:

Existing                Proposed
Release Type long short long short
------------ -------------------- --------------------
Early Access 1.9.0-ea-b19 9-ea 9-ea+19 9-ea
Major 1.9.0-b100 9 9+100 9
Security #1 1.9.0_5-b20 9u5 9.0.1+20 9.0.1
Security #2 1.9.0_11-b12 9u11 9.0.2+12 9.0.2
Minor #1 1.9.0_20-b62 9u20 9.1.2+62 9.1.2
Security #3 1.9.0_25-b15 9u25 9.1.3+15 9.1.3
Security #4 1.9.0_31-b08 9u31 9.1.4+8 9.1.4
Minor #2 1.9.0_40-b45 9u40 9.2.4+45 9.2.4

For reference, this table shows version strings in the new format as they would have been used, hypothetically, for some JDK 7 update and security releases:

Actual               Hypothetical
Release Type long short long short
------------ -------------------- -------------------
Security 2013/04 1.7.0_21-b11 7u21 7.4.10+11 7.4.10
Security 2013/06 1.7.0_25-b15 7u25 7.4.11+15 7.4.11
Minor 2013/09 1.7.0_40-b43 7u40 7.5.11+43 7.5.11
Security 2013/10 1.7.0_45-b18 7u45 7.5.12+18 7.5.12
Security 2014/01 1.7.0_51-b13 7u51 7.5.13+13 7.5.13
Security 2014/04 1.7.0_55-b13 7u55 7.5.14+13 7.5.14
Minor 2014/05 1.7.0_60-b19 7u60 7.6.14+19 7.6.14
Security 2014/07 1.7.0_65-b20 7u65 7.6.15+20 7.6.15

Dropping the initial 1 element from version numbers

This proposal drops the initial 1 element from JDK version numbers. That is, it suggests that the first release of JDK 9 will have the version number 9.0.0 rather than 1.9.0.0.

After nearly twenty years it's clear that the second element of the current version-number scheme is the JDK's de facto $MAJOR version number. We increment that element when we add significant new features, and also when we make incompatible changes.

We could start treating the initial element of the current scheme as the $MAJOR version number, but then JDK 9 would have the version number 2.0.0 even though everyone already refers to it as "JDK 9". This would help no one.

If we retain the initial 1 then JDK version numbers will continue to violate the principles of Semantic Versioning and developers new to Java will continue to be confused about the difference between, e.g., 1.9 and 9.

There is some risk in dropping the initial 1. There are many ways to compare version numbers; some will work correctly, while some will not.

  • Existing code that compares version numbers by parsing their elements and comparing them numerically will continue to work, since nine is greater than one; i.e., 9.0.0 will be considered to be later than 1.8.0.

  • Existing code that skips the initial element when it has the value 1 will also continue to work, since in the new scheme the initial element will never have that value.

  • Existing code that assumes the initial element to have the value 1, however, and therefore always skips to the second element when comparing version numbers, will not work correctly; e.g., such code will consider 9.0.1 to precede 1.8.0.

Anecdotal evidence suggests that existing code in the third category is not very common, but we would welcome data to the contrary.

API

A simple Java API to parse, validate, and compare version strings will be defined (8072379, 8144062):

package java.lang;

import java.util.Optional;

public class Runtime {

public static Version version();

public static class Version
implements Comparable<Version>
{

public static Version parse(String);

public int major();
public int minor();
public int security();

public List<Integer> version();
public Optional<String> pre();
public Optional<Integer> build();
public Optional<String> optional();

public int compareTo(Version o);
public int compareToIgnoreOpt(Version o);

public boolean equals(Object o);
public boolean equalsIgnoreOpt(Object o);

public String toString();
public int hashCode();
}
}

An equivalent C API will be defined, most likely in terms of a revised jvm_version_info struct.

All code in the JDK that inspects and compares JDK version strings will be updated to use these APIs. Developers whose libraries or applications inspect and compare JDK version strings will be encouraged to use these APIs.

System properties

The values returned by the following system properties are modified by this JEP. The general syntax is as follows:

Name                            Syntax
------------------------------ --------------
java.version $VNUM(\-$PRE)?
java.runtime.version $VSTR
java.vm.version $VSTR
java.specification.version $VNUM
java.vm.specification.version $VNUM

The system property java.class.version is not affected.

The following table shows the existing and proposed values for different release types:

System Property                   Existing      Proposed
------------------------------- ------------ --------
Early Access
java.version 1.9.0-ea 9-ea
java.runtime.version 1.9.0-ea-b73 9-ea+73
java.vm.version 1.9.0-ea-b73 9-ea+73
java.specification.version 1.9 9
java.vm.specification.version 1.9 9

Major (GA)
java.version 1.9.0 9
java.runtime.version 1.9.0-b100 9+100
java.vm.version 1.9.0-b100 9+100
java.specification.version 1.9 9
java.vm.specification.version 1.9 9

Minor #1 (GA)
java.version 1.9.0_20 9.1.2
java.runtime.version 1.9.0_20-b62 9.1.2+62
java.vm.version 1.9.0_20-b62 9.1.2+62
java.specification.version 1.9 9
java.vm.specification.version 1.9 9

Security #1 (GA)
java.version 1.9.0_5 9.0.1
java.runtime.version 1.9.0_5-b20 9.0.1+20
java.vm.version 1.9.0_5-b20 9.0.1+20
java.specification.version 1.9 9
java.vm.specification.version 1.9 9

Note that all code which has historically detected . in any of these system properties as part of version identification will need to be examined and potentially modified. For example, System.getProperty("java.version").indexof('.') will return -1 for major releases.

Launcher

In the OpenJDK java launcher implementation, system properties are used when reporting version information, e.g. java -version, java -fullversion, and java -showversion.

The launcher output continues to depend on the system properties as follows:

$ java -version
openjdk version \"${java.version}\"
${java.runtime.name} (build ${java.runtime.version})
${java.vm.name} (build ${java.vm.version}, ${java.vm.info})

$ java -showversion < ... >
openjdk version \"${java.version}\"
${java.runtime.name} (build ${java.runtime.version})
${java.vm.name} (build ${java.vm.version}, ${java.vm.info})
[ ... ]

$ java -fullversion
openjdk full version \"${java.runtime.version}\"

Implementation details may be found in the source.

@since JavaDoc tag

The value for the @since JavaDoc tag will continue to be aligned with the system property java.specification.version; hence, new JDK 9 APIs will be indicated by @since 9.

Mercurial changeset tags

Mercurial tags are used to identify a promotion's changesets. Tools such as Code Tool's jcheck, which is used to validate all changesets pushed to JDK release forests, will be enhanced to support tags using the new version scheme.

The general syntax for Mercurial tags is jdk\-$VNUM\+$BUILD. The following table shows the proposed values for different release types:

Release Type      Proposed
---------------- -----------
Major (GA) jdk-9+100
Minor #1 (GA) jdk-9.1.2+27
Security #1 (GA) jdk-9.0.1+3

Some tools may need to support both existing and proposed tag formats.

Testing

Changing the syntax and semantics of version strings will require extensive testing of all component areas. Existing tests that are independent of the JDK version string should continue to pass.