In a previous post, I laid out my thinking on how to approach the package renaming problem which the Jakarta EE community now faces. Regardless of whether the community chooses big bang or incremental, there are still existing artifacts in the world using the Java EE package names that the community will need to use together with the new Jakarta EE package names.
Tools are always important to take the drudgery away from developers. So I have put together a tool prototype which can be used to transform binaries such as individual class files and complete JARs and WARs to rename uses of the Java EE package names to their new Jakarta EE package names.
The tools is rule driven which is nice since the Jakarta EE community still needs to define the actual package renames for Jakarta EE 9. The rules also allow the users to control which class files in a JAR/WAR are transformed. Different users may want different rules depending upon their specific needs. And the tool can be used for any package renaming challenge, not just the specific Jakarta EE package renames.
The tools provides an API allowing it to be embedded in a runtime to dynamically transform class files during the class loader definition process. The API also supports transforming JAR files. A CLI is also provided to allow use from the command line. Ultimately, the tool can be packaged as Gradle and Maven plugins to incorporate in a broader tool chain.
Given that the tool is prototype, and there is much work to be done in the Jakarta EE community regarding the package renames, I have started a list of TODOs in the project' issues for known work items.
Please try out the tool and let me know what you think. I am hoping that tooling such as this will ease the community cost of dealing with the package renames in Jakarta EE.
PS. Package renaming in source code is also something the community will need to deal with. But most IDEs are pretty good at this sort of thing, so I think there is probably sufficient tooling in existence for handling the package renames in source code.
Thursday, October 17, 2019
Friday, May 17, 2019
I am an Incrementalist: Jakarta EE and package renaming
Eclipse Jakarta EE has been placed in the position that it
may not evolve the enterprise APIs under their existing package names. That is,
the package names starting with java or javax. See Update on Jakarta EE Rights to Java Trademarks
for the background on how we arrived at this state.
So this means that after Jakarta EE 8 (which is API
identical to Java EE 8 from which it descends), whenever an API in Jakarta EE
is to be updated for a new specification version, the package names used by the
API must be renamed away from java or javax. (Note: some other things will
also need to be renamed such as system property names, property file names, and
XML schema namespaces if those things start with java or javax. For
example, the property file META-INF/services/javax.persistence.PersistenceProvider.)
But this also means that if an API does not need to be changed, then it is free
to remain in its current package names. Only a change to the signature of a
package, that is, adding or removing types in the package or adding or removing
members in the existing types in the package, will require a name change to the
package.
There has been much discussion on the Jakarta EE mail lists
and in blogs about what to do given the above constraint and David Blevins has kindly
summed up the two main choices being discussed by the Jakarta EE Specification
Committee: https://www.eclipse.org/lists/jakartaee-platform-dev/msg00029.html.
In a nutshell, the two main choices are (1) “Big Bang” and
(2) Incremental. Big Bang says: Let’s rename all the packages in all the
Jakarta EE specifications all at once for the Jakarta EE release after Jakarta
EE 8. Incremental says: Let’s rename packages only when necessary such as when,
in the normal course of specification innovation, a Jakarta EE specification
project wants to update its API.
I would like to argue that Jakarta EE should chose the
Incremental option.
Big Bang has no technical value and large, up-front
community costs.
The names of the packages are of little technical value in
and of themselves. They just need to be unique and descriptive to programmers.
In source code, developers almost never see the package names. They are generally
in import statements at the top of the source file and most IDEs kindly
collapse the view of the import statements so they are not “in the way” of the
developer. So, a developer will generally not really know or care if the
Jakarta EE API being used in the source code is a mix of package names starting
with java or javax, unchanged since Jakarta EE 8, and updated API with
package names starting with jakarta. That is, there is little mental cost to
such a mixture. The Jakarta EE 8 API are already spread across many, many
package names and developers can easily deal with this. That some will start
with java or javax and some with jakarta is largely irrelevant to a
developer. The developer mostly works with type and member names which are not
subject to the package rename problem.
But once source code is compiled into class files, packaged
into artifacts, and distributed to repositories, the package names are baked in
to the artifacts and play an important role in interoperation between artifacts:
binary compatibility. Modern Java applications generally include many 3rd
party open source artifacts from public repositories such as Maven Central and
there are many such artifacts in Maven Central which use the current package
names. If Jakarta EE 9 were to rename all packages, then the corpus of existing
artifacts is no longer usable in Jakarta EE 9 and later. At least not without
some technical “magic” in builds, deployments, and/or runtimes to attempt to
rename package references on-the-fly. Such magic may be incomplete and will
break jar signatures and will complicate builds and tool chains. It will not be
transparent.
Jakarta EE must minimize the inflection point/blast radius on
the Java community caused by the undesired constraint to rename packages if
they are changed. The larger the inflection point, the more reason you give to
developers to consider alternatives to Jakarta EE and to Java in general. The
Incremental approach minimizes the inflection point providing an evolutionary
approach to the package naming changes rather than the revolutionary approach
of the Big Bang.
Some Jakarta EE specification may never be updated. They
have long been stable in the Java EE world and will likely remain so in Jakarta
EE. So why rename their packages? The Big Bang proposal even recognizes this by
indicating that some specification will be “frozen” in their current package
names. But, of course, there is the possibility that one day, Jakarta EE will
want to update a frozen specification. And then the package names will need to
be changed. The Incremental approach takes this approach to all Jakarta EE
specifications. Only rename packages when absolutely necessary to minimize the
impact on the Java community.
Renaming packages incrementally, as needed, does not reduce
the freedom of action for Jakarta EE to innovate. It is just a necessary part
of the first innovation of a Jakarta EE specification.
A Big Bang approach does not remove the need to run existing
applications on earlier platform versions.
It increases the burden on customers since they must update all parts of
their application for the complete package renaming when the need to access a
new innovation in a single updated Jakarta EE specification when none of the
other Jakarta EE specifications they use have any new innovations. Just package
renames for no technical reason. It also
puts a large burden on all application server vendors. Rather than having to
update parts of their implementations to support the package name changes of a
Jakarta EE specification when the specification is updated for some new
innovation, they must spend a lot of resources to support both old and new
packages name for the implementations of all Jakarta EE specifications.
There are some arguments in favor of a Big Bang approach. It
“gets the job done” once and for all and for new specifications and
implementations the old java or javax package names will fade from
collective memories. In addition, the requirement to use a certified Java SE implementation
licensed by Oracle to claim compliance with Eclipse Jakarta EE evaporates once there
are no longer any java or javax package names in a Jakarta EE specification.
However, these arguments do not seem sufficient motivation to disrupt the
ability of all existing applications to run on a future Jakarta EE 9 platform.
In general, lazy evaluation is a good strategy in
programming. Don’t do a thing until the thing needs to be done. We should apply
that strategy in Jakarta EE to package renaming and take the Incremental approach.
Finally, I am reminded of Æsop’s fable, The Tortoise & the Hare. “The
race is not always to the swift.”
Friday, April 04, 2014
Java 8, bnd and references to compile-time constants
Java 8 was recently released and I wanted to test it out with the OSGi build. I installed JDK 8 on my Mac, pointed JAVA_HOME at it and started a clean build of OSGi. After the build completed, I ran the Core Compliance Tests to verify the build. Unfortunately several of the tests were now failing.
In order to diagnose the issue, I compared it to the same build built with JDK 7. The JDK 7 build passed all the tests running under JDK 7 or JDK 8. The JDK 8 build failed the same tests running under JDK 7 or JDK 8. So the issue was in the building with JDK 8 not the running under JDK 8. The failure was that one of the test bundles was failing to resolve. This was caused by an import for a package that was not exported by any bundle. When building with JDK 8, bnd added a package to the test bundle's Import-Package statement that is not present in the Import-Package statement when building with JDK 7.
Digging further into the test bundle, the only reference to that package was to a static final String constant. During compilation, javac must copy the referenced string into the compiled class since the final String is a compile-time constant and thus the referenced field is not referenced at runtime.
So in the example code:
But when compiling with JDK 8, javac adds a constant pool entry for the class holding the referenced constant even though this class it not reference at runtime by the compiled class. So for the example above, the Referencer class file now has a constant pool entry for the Constant class.
During the building of a bundle, bnd analyzes the class files in the bundle to find class references to generate the necessary Import-Package statement. So the presence of this new constant pool entry for the constant holding class caused bnd to add the package of that class to the Import-Package statement. bnd assumed that all class entries in the constant pool were runtime references.
An email conversation with Alex Buckley, JLS spec lead, confirmed that this new behavior for javac in JDK 8 is intentional. javac is now adding compile-time dependencies to the constant pool to support compile-time dependency analysis using class files.
This means that bnd's assumption that all class entries in the constant pool were runtime references is no longer valid for classes compiled by JDK 8. So Peter Kriens is making fixes to bnd for the 2.3 release to do deeper analysis so only runtime references to classes will result in their packages being added to the Import-Package statement. Compile-time only dependencies wont result in their packages being added to the Import-Package statement. So stay tuned for bnd 2.3 if you plan on using JDK 8 to build your bundles.
This also means that anyone doing bytecode analysis for runtime dependencies of class files needs to be aware that the constant pool can now also contain compile-time only dependencies.
In order to diagnose the issue, I compared it to the same build built with JDK 7. The JDK 7 build passed all the tests running under JDK 7 or JDK 8. The JDK 8 build failed the same tests running under JDK 7 or JDK 8. So the issue was in the building with JDK 8 not the running under JDK 8. The failure was that one of the test bundles was failing to resolve. This was caused by an import for a package that was not exported by any bundle. When building with JDK 8, bnd added a package to the test bundle's Import-Package statement that is not present in the Import-Package statement when building with JDK 7.
Digging further into the test bundle, the only reference to that package was to a static final String constant. During compilation, javac must copy the referenced string into the compiled class since the final String is a compile-time constant and thus the referenced field is not referenced at runtime.
So in the example code:
public class Referencer {the class file for Referencer has its own copy of the string "Hello, World!" and does not access Constant at runtime to obtain the string to print it out. In fact, the Referencer class file compiled by JDK 7 has no references to Constant.
public static void main(String[] args) {
System.out.println(Constant.hello);
}
}
class Constant {
static final String hello = "Hello, World!";
}
But when compiling with JDK 8, javac adds a constant pool entry for the class holding the referenced constant even though this class it not reference at runtime by the compiled class. So for the example above, the Referencer class file now has a constant pool entry for the Constant class.
During the building of a bundle, bnd analyzes the class files in the bundle to find class references to generate the necessary Import-Package statement. So the presence of this new constant pool entry for the constant holding class caused bnd to add the package of that class to the Import-Package statement. bnd assumed that all class entries in the constant pool were runtime references.
An email conversation with Alex Buckley, JLS spec lead, confirmed that this new behavior for javac in JDK 8 is intentional. javac is now adding compile-time dependencies to the constant pool to support compile-time dependency analysis using class files.
This means that bnd's assumption that all class entries in the constant pool were runtime references is no longer valid for classes compiled by JDK 8. So Peter Kriens is making fixes to bnd for the 2.3 release to do deeper analysis so only runtime references to classes will result in their packages being added to the Import-Package statement. Compile-time only dependencies wont result in their packages being added to the Import-Package statement. So stay tuned for bnd 2.3 if you plan on using JDK 8 to build your bundles.
This also means that anyone doing bytecode analysis for runtime dependencies of class files needs to be aware that the constant pool can now also contain compile-time only dependencies.
Posted by