Play Framework is reborn like a phoenix from the ashes.... and gets rid of Akka - JVM Weekly vol. 60
And it was supposed to be regular already - but I forgot myself about All Saints' Day, which is Bank Holiday in Poland. Fortunately, today again I have a lot of interesting stuff for you!
1. Because it's been a long time since we had JEPs.... well it's new JEPs!
JEP draft: Ahead of Time Compilation for the Java Virtual Machine
Well, the initial glimpses of the Leyden project have emerged, even though the JEP isn't currently tied to it - perhaps this is simply due to it still being in the Draft stage. Nonetheless, it's intriguing enough for me to bring it up at this early point.
The JEP draft: Ahead of Time Compilation for the Java Virtual Machine aims to enhance the JVM by enabling it to load applications and libraries pre-compiled Ahead of Time. The objective remains consistent with some other recent changes in JVM - to improve application start-up and execution performance. The developers plan to address the performance issues stemming from the current dynamic execution model in three phases: pre-interpretation (Tier 0), C1 compilation (Tier 3), and finally C2 (Tier 4), with stages 1 and 2 handling specific cases of C1 compilation. Ideally, the entire process could be executed immediately with a C2 compiler, but in reality, this would lead to a lengthy 'boot' of the optimized version. The Graal compiler serves as a fitting example. Prior to Java 17, it was a viable substitute for C2, but its operation adversely impacted the application as it needed to be compiled before it could function effectively. Moreover, the de-optimization process that takes place when C2-compiled code encounters an incorrect optimization assumption is expensive, as the C1 profiling variant must be recompiled and then discarded after being recompiled by C2.
The draft is still very much a work in progress (it doesn't even have a complete description), but I will be keeping an eye on the topic and will certainly return to it.
JEP 461: Stream Gatherers (Preview)
What occurs when Viktor Klang, the previous Tech Lead at Akka, suggests modifications to the Stream API? It appears that we are receiving a proposal for the new development direction of arguably the most widely adopted new feature in the language over the past ten years.
Although the current Stream API provides a comprehensive range of processor operations (like .map
, .filter
), there was a demand to expand it with operations such as fixedWindow(2)
or scan((sum, next) -> sum + next)
. Described JEP proposed by Viktor is a result of recurring requests for adding such extra operations to the Stream API in Java 8. These requests couldn't be accommodated as their application was too specific to be included in the main Stream API. Currently, there's no way for someone to develop their own processor operations, in a way similar to how the Stream::collect
interface allow you to wrote collectors. Consequently, it can sometimes be unexpectedly challenging to achieve certain effects.
The new Stream::gather
interface is designed for creating its own intermediate operation, which can handle various types. Viktor has conducted a comprehensive classification of different use cases of the new API in his work, Gathering the streams. He describes various types of stream operations, such as indirect operations, incremental operations, stateful operations, stateless operations, and more. All of these are well explained in the original document mentioned earlier. The new interface, named Gatherer, is reminiscent of the well-known Collector in its design, which it was based on. To give you a better idea, an example of a gatherer is the fold method.
The key methods of the new interface are:
/** @param <T> the element type
* @param <A> the (mutable) intermediate accumulation type
* @param <R> the (probably immutable) final accumulation type
*/
interface Gatherer<T,A,R> {
Supplier<A> initializer();
Gatherer.Integrator<A,T,R> integrator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
(...)
}
Gatherer is engineered to offer the ability to compose and reuse the individual components that are created with this interface. For instance, the execution of a popular map on a standard abstraction is demonstrated in proposal:
public static <T, R> Gatherer<T, ?, R> map(Function<? super T, ? extends R> mapper) {
return Gatherer.of(
(unused, element, downstream) -> // integrator
downstream.push(mapper.apply(element))
);
}
If you're curious, an excellent beginner's video has, as always, found its way to the Inside Java channel.
JEP 462: Structured Concurrency (Second Preview)
Here we see a recurrence of the excitement - Structured Concurrency was suggested in JEP 428 and introduced in JDK 19 as a trial API. The feature underwent a "second round" in the trial phase in JDK 20 as JEP 437, with minor enhancements stemming from the introduction of Scope Values, and proceeded to the Preview stage in JDK 21. JDK 22 and JEP 462, on the other hand, will not bring any modifications, but the API will continue to be in Preview - the community is merely getting acquainted with Virtual Threads, hence it might not be prudent to hasten the stabilization of additional control structures for them too swiftly.
JEP 463: Implicitly Declared Classes and Instance Main Methods (Second Preview)
JEP 463 presents a revised version of the idea proposed in JEP 445. Its goal is to make it easier for new Java programmers to write 'hello world'. The intention is to steer Java towards a path where beginners can begin with straightforward single class programs. They won't need to grasp the more intricate functions designed for bigger projects right from the start. Thus, it should be feasible to simplify the declaration of programs. This will enable students to naturally advance to more complex aspects of Java as they hone their skills.
This time, however, there are quite a few modifications. The initial concept of unnamed classes has been reevaluated. Rather than incorporating support for unnamed classes, if a class isn't explicitly defined, the system will assign it a name and it will operate as a regular top-level class.
The previous version introduced a complex set of rules for methods that were identified as the main
of such an unnamed class. Now, choosing the default method has been simplified to a two-step process. This process primarily considers if a potential main
method has a String[]
parameter, eliminating any confusion caused by the limitation that a class cannot declare both a static and an instantiated method with the same name and parameters as per the original proposal.
2. Big news in the Kotlin ecosystem - K2 in Beta and a stable Multiplatform
I have always argued that the 1.x.20 versions of Kotlin have sometimes been more interesting than the stable ones. The latest update, Kotlin version 1.9.20 confirms this, introducing significant advances in two of the language's major projects - the K2 compiler has entered the Beta phase for all platforms, and Kotlin Multiplatform has reached stable status. So let's break down this release to see what's inside.
The K2 compiler beta is now available for JVM, Native, JS, and Wasm platforms, indicating its readiness for broader testing. The next significant milestone for Kotlin is the release of version 2.0.0, which will feature the fully stable K2. JetBrains has announced an increase in the number of test releases leading up to Kotlin 2.0, including several Beta and Release Candidates (RC) versions, to promptly address any potential issues. The current goal is to ensure binary compatibility and prevent 'poisoning' of K2-compiled binaries (a scenario where code compiled from one version of the compiler causes problems when used with binaries compiled by a different version), thereby allowing the use of derived artifacts in production environments. JetBrains asserts that it has thoroughly tested the compiler, and encourages users to contribute to its further stabilization by testing the K2 compiler in their own projects.
This update brings several notable enhancements, including significantly better support for cross-platform projects, a default custom memory allocator in Kotlin/Native, and improvements to the garbage collector's performance in Kotlin/Native. Furthermore, Kotlin/Wasm now supports new targets, the latest Wasm GC, and the WASI API in its standard library.
Classically, you will also get all the news in the video version.
But there is more. It has been announced that Kotlin Multiplatform is now stable and ready for production use. As a reminder: while the technology allows developers to share code between different platforms, it is particularly focused on mobile developers. Kotlin Multiplatform (KMP) blurs the boundaries between cross-platform and native development, allowing developers freedom in the amount of code they share and integration into any project - Android, iOS, but also server (although here the amount of synergy is lesser).
The sheer growth of the Kotlin Multiplatform ecosystem clearly indicates that it has gained significant traction:
Google has been increasingly providing support to KMP users with a variety of new experimental versions of the Jetpack libraries. Of particular interest is the new Compose Multiplatform 1.5.10. This version is now compatible with the stable Kotlin 1.9.20, enabling full sharing of logic and UI components. It is now stable for Android and desktop (JVM), and offers alpha support for iOS and experimental support for the web (Wasm). This update brings enhanced Material 3 components for shared code, fixes for iOS text fields, native scrolling physics with a rubber band effect, interoperability with UIKit through crossfade animation, and improved compilation speed with compiler caching support. Furthermore, initial support for the K2 compiler has been introduced, and rendering performance on iOS has been improved.
3. Play Framework is reborn like a Phoenix from the ashes
We recently celebrated Halloween, so announcements of this nature shouldn't be shocking - the deceased are making a comeback. I'm aware that we had a sneak peek of this some time ago, but I must confess that this is one of the most unexpected comebacks of the year for me. I'm still in disbelief. When I first joined the industry, Play was a major contender for the title of "Spring's main rival", and I even developed a few production services using it. I recall how I enthusiastically embraced everything back then, including diving into combinations of Groovy/Clojure and Java EE...
Regrettably, the company behind Play, initially known as Typesafe and now Lightbend, gradually lost interest in the solution. Initially, they transitioned it to Lagoma (does anyone still recall that one?) and subsequently to Akka Serverless. In 2021, Play was finally returned to the community, with Matthias Kurz and Greg Methvin assuming responsibility. Now, akin to a phoenix rising from the ashes, after nearly four years, a 2.9 release and a 3.0 release have emerged concurrently. Why two at once? There's an intriguing tale behind that as well.
Play 3.0 introduces a significant update in the platform's development - the shift from Akka to Apache Pekko. Apache Pekko is a branch of Akka 2.6.x that has already experienced some evolution and brought in some unique ideas. Play 3.0 employs Pekko and its HTTP components, indicating a further direction in the modernisation of the framework's infrastructure. However, for applications that are heavily integrated with Akka, this change might necessitate some migration efforts. To aid users in this transition, the Pekko team has published a comprehensive migration guide that highlights the potential challenges. Due to that, Play Framework team has also launched Play 2.9, which maintains the use of Akka and Akka HTTP (this explains why there were two releases simultaneously).
The driving force behind this technological shift was not so much the technology itself, but the licensing dispute that ensued after Lightbend decided to alter it for Akka. Previously, under the Apache 2.0 license, Akka was freely modifiable and usable. However, Lightbend's updated license introduced a commercial license for certain Akka features, transitioning the source code to Business Source License (BSL) 1.1. This situation mirrors that of Terraforma, which has also chosen to adopt BSL.
And some people didn't like it very much....
Note: NSFW. Additionally, no thoughts from the video are the official editorial line 😄
The shift in licensing elicited a significant reaction, particularly from those who had created their Play applications utilizing the complete Akka feature set. These developers were confronted with a decision: they could either reconstruct their applications to accommodate the restrictions of the Akka version available as open source, purchase a commercial license for the full feature set, or seek out alternative solutions.
If you're interested in the specifics, Play has released an official stance on Akka's license alteration, which is available here How Play Deals with Akka's License Change.
But let's get back to the new releases themselves. The Play 2.9.0 and Play 3.0 releases are especially significant as they mark the end of nearly two years of community-led development since Lightbend Inc. passed the project to the community. The updates are centered around support for updated programming languages. The new Play(s?) brings compatibility with Scala 3, but only for versions 3.3.1 and later, highlighting the need for migration steps while continuing to support Scala 2.13. Support for Java 8 has also been dropped, and a minimum of Java 11 is now necessary for operation.
Also, Play is discontinuing support for outdated versions like Scala 2.12, sbt 0.13
, and Java 8, adjusting to its dependencies that no longer back these editions. Significant library updates include Akka HTTP 10.2, an upgrade to Guice 6.0.0 and Jackson 2.14. Play has enhanced its modular components such as sbt-web
and sbt-js-engine
, and transitioned to Jakarta Persistence API to support updated versions of Hibernate and EclipseLink. Some new features related to the operation of the framework itself have also been introduced. Full release notes can be found here.
Do I believe there's still room for the Play Framework in the market? I'm torn between two perspectives. On one side, there's a plethora of projects that continue to utilize it, as evidenced by the active community on OpenCollective. Within the Scala community, there doesn't appear to have been any significant competition either. Even if there was, Play's reputation seems to hold strong. However, four years is a considerable amount of time. There's been a new wave of solutions in the Java world, like Micronaut and Quarkus, that have effectively dominated the market for those seeking an alternative to Spring. Therefore, I suspect that if Play continues to be actively developed, it will likely solidify its position within the Scala community. I'm rooting for it though - I have many fond memories of it.