tag:blogger.com,1999:blog-87457458867227149572024-03-18T17:00:01.859-07:00Andriy Redko {devmind}Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.comBlogger112125tag:blogger.com,1999:blog-8745745886722714957.post-33134440822801567532023-12-26T11:41:00.000-08:002023-12-26T11:41:10.172-08:00When concurrency bites (yet again!): class initialization deadlocks<p>Concurrent and parallel programming on JVM platform has never been easy: yes, it is significantly safer and simpler than in most programming languages (thanks to outstanding <a href="https://docs.oracle.com/en/java/javase/21/core/concurrency.html#GUID-59C16A2D-57CE-4C83-9D6F-91A48E01E3C6">concurrency support</a> by Java language, standard library and JVM) but still, surprises pop up from time to time.</p>
<p>In today's post we are going to learn how a seemingly innocent implementation may intermittently deadlock during the class initialization under some circumstances. To begin, here is the <code>Options</code> class we are going to work with along the way.</p>
<pre class="brush:java">
package com.example;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public final class Options {
public final static Options EMPTY = new Builder().build();
private final Map<String, String> options;
public Options(final Map<String, String> options) {
this.options = new HashMap<>(Objects.requireNonNull(options));
}
@Override
public String toString() {
return "Options=" + options.toString();
}
public static class Builder {
public static final Options EMPTY = new Builder().build();
private final Map<String, String> options = new HashMap<>();
public Builder option(final String name, final String value) {
this.options.put(name, value);
return this;
}
public Options build() {
return new Options(options);
}
}
}
</pre>
<p>The snippet above implements a variation of the <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder pattern</a> (in this case, for <code>Options</code> class). Although the sample is somewhat made up, the similarities to the existing implementations aren't (for example, please check <a href="https://github.com/opensearch-project/OpenSearch/issues/10065">Parts of Rest High-Level Client not thread-safe</a> out). At a glance, it seems to be no-brainer, the code compiles and runs perfectly fine (the console output serves as a proof in this case).</p>
<pre class="brush:java">
public static void main(String[] args) {
System.out.println("New instance: " + new Options.Builder().build());
System.out.println("EMPTY Options instance: " + Options.EMPTY);
System.out.println("EMPTY Options.Builder instance: " + Options.Builder.EMPTY);
}
</pre>
<p>Once executed, we should see a few lines printed out.</p>
<pre>
New instance: Options={}
EMPTY Options instance: Options={}
EMPTY Options.Builder instance: Options={}
</pre>
<p>The attentive reviewer may spot something fishy about this implementation, specifically related to <code>EMPTY</code> static fields: there is an explicit bidirectional (or better to say, circular) dependency between <code>Options</code> and <code>Options.Builder</code> classes. But JVM is able to handle that, non issue, right? Well, yes and no, and to understand why, let us take a look at the variation of the initialization sequence that is triggered concurrently:</p>
<pre class="brush:java">
public static void main(String[] args) {
try (ExecutorService executor = Executors.newFixedThreadPool(2)) {
executor.submit(() -> System.out.println("New instance: " + new Options.Builder().build()));
executor.submit(() -> System.out.println("EMPTY Options instance: " + Options.EMPTY));
}
}
</pre>
<p>Surprisingly (or not?), the execution of this code intermittently hangs the JVM. If we look into the thread dump, the reason becomes very clear (thanks to <a href="https://bugs.openjdk.org/browse/JDK-8288064">JDK-8288064: Class initialization locking</a>, the output has been redacted a bit to highlight the clues).</p>
<pre>
"pool-1-thread-1" #29 [8432] prio=5 os_prio=0 cpu=0.00ms elapsed=499.54s allocated=7800B defined_classes=1 tid=0x000002609f22b680 nid=8432 waiting on condition [0x000000be184fe000]
java.lang.Thread.State: RUNNABLE
at com.example.Options$Builder.build(Options.java:35)
- waiting on the Class initialization monitor for com.example.Options
at com.example.Options$Builder.<clinit>(Options.java:26)
...
"pool-1-thread-2" #30 [19688] prio=5 os_prio=0 cpu=0.00ms elapsed=499.54s allocated=5184B defined_classes=1 tid=0x000002609f233e40 nid=19688 waiting on condition [0x000000be185fe000]
java.lang.Thread.State: RUNNABLE
at com.example.Options.<clinit>(Options.java:9)
- waiting on the Class initialization monitor for com.example.Options$Builder
...
</pre>
<p>The <code>Options</code> and <code>Options.Builder</code> classes deadlock during initialization (and indeed, <code>Options</code> needs <code>Options.Builder</code> to initialize <code>EMPTY</code> static field however <code>Options.Builder</code> needs <code>Options</code> to initialize own <code>EMPTY</code> static field). The <a href="https://docs.oracle.com/javase/specs/jls/se21/html/index.html">JLS</a> (<a href="https://docs.oracle.com/javase/specs/jls/se21/html/index.html">Java Language Specification</a>) is very clear on that (but how many of us have read the specification anyway?):</p>
<blockquote>Because the Java programming language is multithreaded, initialization of a class or interface requires careful synchronization, since some other thread may be trying to initialize the same class or interface at the same time. There is also the possibility that initialization of a class or interface may be requested recursively as part of the initialization of that class or interface; for example, a variable initializer in class A might invoke a method of an unrelated class B, which might in turn invoke a method of class A. The implementation of the Java Virtual Machine is responsible for taking care of synchronization and recursive initialization by using the following procedure. - <a href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-12.html#jls-12.4.2">12.4.2. Detailed Initialization Procedure</a>
</blockquote>
<p>This is by no means a new issue that could be solved by restructuring the code, it has been known for years (<a href="https://bugs.openjdk.org/browse/JDK-4891511">JDK-4891511: Deadlock in class initialization specification, JLS 2nd ed. 12.4.2</a>) but it still bites and not easy to troubleshoot (<a href="https://bugs.openjdk.org/browse/JDK-8059913">JDK-8059913: Deadlock finder is unable to find deadlocks caused by <clinit></a>). Luckily, JVM diagnostics is getting better and upcoming <a href="https://openjdk.org/projects/jdk/22/">JDK-22</a> release will bring yet another improvement as part of <a href="https://bugs.openjdk.org/browse/JDK-8316229">JDK-8316229: Enhance class initialization logging</a>, it should definitely help to debug apparent class initialization deadlocks (when JVM has <code>class+init</code> debug logging enabled using <code>-Xlog:class+init=debug</code> command line option).</p>
<pre>
...
[0.089s][debug][class,init] Thread "pool-1-thread-1" is initializing com.example.Options$Builder
[0.089s][debug][class,init] Thread "pool-1-thread-2" is initializing com.example.Options
[0.089s][info ][class,init] 511 Initializing 'com/example/Options$Builder' (0x000002b9dd001000) by thread "pool-1-thread-1"
[0.089s][info ][class,init] 512 Initializing 'com/example/Options' (0x000002b9dd001218) by thread "pool-1-thread-2"
[0.089s][debug][class,init] Thread "pool-1-thread-1" recursively initializing com.example.Options$Builder
[0.089s][debug][class,init] Thread "pool-1-thread-2" waiting for initialization of com.example.Options$Builder by thread "pool-1-thread-1"
[0.089s][debug][class,init] Thread "pool-1-thread-1" waiting for initialization of com.example.Options by thread "pool-1-thread-2"
...
</pre>
<p>Undoubtedly, JVM is very sophisticated piece of technology and each release (which is happening every six months these days) brings more features, bugfixes and improvements. Despite getting smarter, JVM still requires developers to be aware of its limitations and think about the code they write (would co-pilots and LLMs help with that is yet to be seen).</p>
<p>I πΊπ¦ stand πΊπ¦ with πΊπ¦ Ukraine.</p>
Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-52391873750962223492023-10-05T13:18:00.001-07:002023-10-05T13:18:28.720-07:00JDK-21: green threads are officially back!<p>The <a href="https://jdk.java.net/21/">JDK-21</a> is there, bringing virtual threads (back) into JVM as a generally available feature (if you are old enough like myself, you might have remembered that in <a href="https://web.archive.org/web/20040211225937/http://java.sun.com/developer/technicalArticles/Programming/linux/">Java 2 releases prior to 1.3</a> the JVM used its own threads library, known as green threads, to implement threads in the Java platform). This is big, but what else is coming?</p>
<ul>
<li><p><a href="https://openjdk.org/jeps/431">JEP-431: Sequenced Collections</a>: introduces new interfaces to represent collections with a defined encounter order. Each such collection has a well-defined first element, second element, and so forth, up to the last element. It also provides uniform APIs for accessing its first and last elements, and for processing its elements in reverse order.</p>
<p>The following new interfaces have been introduced (and retrofitted into the existing collections type hierarchy), potentially a breaking change for some library implementors:
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/SequencedCollection.html">java.util.SequencedCollection</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/SequencedMap.html">java.util.SequencedMap</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/SequencedSet.html">java.util.SequencedSet</a></li>
</ul>
</p></li>
<li><p><a href="https://openjdk.org/jeps/439">JEP-439: Generational ZGC</a>: improves application performance by extending the Z Garbage Collector (<a href="https://openjdk.org/jeps/377">ZGC</a>) to maintain separate <a href="https://en.wikipedia.org/wiki/Tracing_garbage_collection#Generational_GC_%28ephemeral_GC%29">generations</a> for young and old objects. This will allow <a href="https://openjdk.org/jeps/377">ZGC</a> to collect young objects β which tend to die young β more frequently.</p><p>
By default, the <code>-XX:+UseZGC</code> command-line option selects non-generational ZGC, but to select the Generational ZGC, additional command line option <code>-XX:+ZGenerational</code> is required:
<pre>
$ java -XX:+UseZGC -XX:+ZGenerational ...
</pre>
</p></li>
<li><p><a href="https://openjdk.org/jeps/440">JEP-440: Record Patterns</a>: enhances the Java programming language with record patterns to deconstruct record values. Record patterns and type patterns can be nested to enable a powerful, declarative, and composable form of data navigation and processing. This is certainly a huge step towards having a powerful, feature-rich pattern matching capabilities in the language:</p>
<pre class="brush:java">
interface Host {}
record TcpHost(String name, int port) implements Host {}
record HttpHost(String scheme, String name, int port) implements Host {}
</pre>
<p>The are several places the records could be deconstructed, <code>instanceof</code> check being one of those:</p>
<pre class="brush:java">
final Host host = new HttpHost("https", "localhost", 8080);
if (host instanceof HttpHost(var scheme, var name, var port)) {
...
} else if (host instanceof TcpHost(var name, var port)) {
...
}
</pre>
</li>
<li><p><a href="https://openjdk.org/jeps/441">JEP-441: Pattern Matching for switch</a>: enhances the Java programming language with pattern matching for <code>switch</code> expressions and statements. Extending pattern matching to switch allows an expression to be tested against a number of patterns, each with a specific action, so that complex data-oriented queries can be expressed concisely and safely.</p>
<p>Considering the example with the records deconstruction from above, we could use record patterns in <code>switch</code> expressions too:</p>
<pre class="brush:java">
var hostname = switch(host) {
case HttpHost(var scheme, var name, var port) -> name;
case TcpHost(var name, var port) -> name;
default -> throw new IllegalArgumentException("Unknown host");
};
</pre>
<p>But the <code>switch</code> patterns are much more powerful, with guards to pattern case labels, <code>null</code> labels, etc.</p>
<pre class="brush:java">
final Object obj = ... ;
var o = switch (obj) {
case null -> ... ;
case String s -> ... ;
case String[] a when a.length == 0 -> ... ;
case String[] a -> ... ;
default -> ... ;
}
</pre>
</li>
<li><p><a href="https://openjdk.org/jeps/444">JEP-444: Virtual Threads</a>: introduces virtual threads to the Java Platform. Virtual threads are lightweight threads that dramatically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications. The virtual threads and executors could be used along the traditional ones, following the same familiar API:</p>
<pre class="brush:java">
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
...
});
}
</pre>
<p>Some of the quirks of the virtual threads we have discussed previously <a href="https://aredko.blogspot.com/2023/01/project-loom-in-jdk-19-benefits-but.html">here</a> and <a href="https://aredko.blogspot.com/2023/06/javas-securitymanager-in-age-of-virtual.html">here</a>, but there is one more: you could use them in parallel streams, but should you? The answer is a bit complicated, so referring you to <a href="https://www.javaspecialists.eu/archive/Issue311-Virtual-Threads-and-Parallel-Streams.html">Virtual Threads and Parallel Streams</a> article if you are looking for clarity.</p>
<p>The JDK tooling (like <code>jcmd</code> and <code>jfr</code>) has been updated to include the information about virtual threads where applicable.</p>
<blockquote cite="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html#GUID-265B55B7-F330-46E2-BCF0-9FFD8A5A15B3">The <code>jcmd</code> thread dump lists virtual threads that are blocked in network I/O operations and virtual threads that are created by the ExecutorService interface. It does not include object addresses, locks, JNI statistics, heap statistics, and other information that appears in traditional thread dumps (as per <a href="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html#GUID-265B55B7-F330-46E2-BCF0-9FFD8A5A15B3">Viewing Virtual Threads in jcmd Thread Dumps</a>). </blockquote>
<blockquote cite="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html#GUID-F24A6627-4599-4FEF-B17F-DF35EBD34F7B">Java Flight Recorder (JFR) can emit these events related to virtual threads (as per <a href="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html#GUID-F24A6627-4599-4FEF-B17F-DF35EBD34F7">Java Flight Recorder Events for Virtual Threads</a>):
<ul>
<li><code>jdk.VirtualThreadStart</code> and <code>jdk.VirtualThreadEnd</code> (disabled by default)</li>
<li><code>jdk.VirtualThreadPinned</code> (enabled by default with a threshold of 20 ms)</li>
<li><code>jdk.VirtualThreadSubmitFailed</code> (enabled by default)</li>
</ul>
</blockquote>
<p>It is worth noting that Oracle <a href="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html#GUID-DC4306FC-D6C1-4BCC-AECE-48C32C1A8DAA">has published a comprehesive guide</a> on virtual threads as par of <a href="https://jdk.java.net/21/">JDK-21</a> documentation update.</p>
</li>
<li><p><a href="https://openjdk.org/jeps/449">JEP-449: Deprecate the Windows 32-bit x86 Port for Removal</a>: deprecates the Windows 32-bit x86 port, with the intent to remove it in a future release.</p></li>
<li><p><a href="https://openjdk.org/jeps/451">JEP-451: Prepare to Disallow the Dynamic Loading of Agents</a>: issues warnings when agents are loaded dynamically into a running JVM. These warnings aim to prepare users for a future release which disallows the dynamic loading of agents by default in order to improve integrity by default. Serviceability tools that load agents at startup will not cause warnings to be issued in any release.</p>
<p>Running with <code>-XX:+EnableDynamicAgentLoading</code> on the command line serves as an explicit "opt-in"
that allows agent code to be loaded into a running VM and thus suppresses the warning. Running
with <code>-XX:-EnableDynamicAgentLoading</code> disallows agent code from being loaded into a running VM and
can be used to test possible future behavior.</p>
<p>In addition, the system property <code>jdk.instrument.traceUsage</code> can be used to trace uses of the
<code>java.lang.instrument</code> API. Running with <code>-Djdk.instrument.traceUsage</code> or <code>-Djdk.instrument.traceUsage=true</code>
causes usages of the API to print a trace message and stack trace. This can be used to identify agents
that are dynamically loaded instead of being started on the command line with <code>-javaagent</code>.</p>
</li>
<li><p><a href="https://openjdk.org/jeps/452">JEP-452: Key Encapsulation Mechanism API</a>: introduces an API for key encapsulation mechanisms (KEMs), an encryption technique for securing symmetric keys using public key cryptography. The new APIs are centered around <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/javax/crypto/KEM.html">javax.crypto.KEM</a> and <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/javax/crypto/KEMSpi.html">javax.crypto.KEMSpi</a> abstractions.</p></li>
<li><p><a href="https://openjdk.org/jeps/430">JEP-430: String Templates (Preview)</a>: enhances the Java programming language with string templates. String templates complement Java's existing string literals and text blocks by coupling literal text with embedded expressions and template processors to produce specialized results. This <a href="https://openjdk.org/jeps/12">is a preview language feature and API</a>.</p></li>
<li><p><a href="https://openjdk.org/jeps/453">JEP-453: Structured Concurrency (Preview)</a>: simplifies concurrent programming by introducing an API for structured concurrency. Structured concurrency treats groups of related tasks running in different threads as a single unit of work, thereby streamlining error handling and cancellation, improving reliability, and enhancing observability. This <a href="https://openjdk.org/jeps/12">is a preview language feature and API</a>.</p></li>
<li><p><a href="https://openjdk.org/jeps/443">JEP-443: Unnamed Patterns and Variables (Preview)</a>: enhances the Java language with unnamed patterns, which match a record component without stating the component's name or type, and unnamed variables, which can be initialized but not used. Both are denoted by an underscore character, <code>_</code>. This <a href="https://openjdk.org/jeps/12">is a preview language feature</a>.</p></li>
<li><p><a href="https://openjdk.org/jeps/445">JEP-445: Unnamed Classes and Instance Main Methods (Preview)</a>: evolves the Java language so that students can write their first programs without needing to understand language features designed for large programs. Far from using a separate dialect of Java, students can write streamlined declarations for single-class programs and then seamlessly expand their programs to use more advanced features as their skills grow. This <a href="https://openjdk.org/jeps/12">is a preview language feature</a>.</p></li>
<li><p><a href="https://openjdk.org/jeps/446">JEP-446: Scoped Values (Preview)</a>: introduces scoped values, values that may be safely and efficiently shared to methods without using method parameters. They are preferred to thread-local variables, especially when using large numbers of virtual threads. This <a href="https://openjdk.org/jeps/12">is a preview language API</a>.</p><p>In effect, a scoped value is an implicit method parameter. It is "as if" every method in a sequence of calls has an additional, invisible, parameter. None of the methods declare this parameter and only the methods that have access to the scoped value object can access its value (the data). Scoped values make it possible to pass data securely from a caller to a faraway callee through a sequence of intermediate methods that do not declare a parameter for the data and have no access to the data.</p></li>
<li><p><a href="https://openjdk.org/jeps/442">JEP-442: Foreign Function & Memory API (3rd Preview)</a>: introduces an API by which Java programs can interoperate with code and data outside of the Java runtime. By efficiently invoking foreign functions (i.e., code outside the JVM), and by safely accessing foreign memory (i.e., memory not managed by the JVM), the API enables Java programs to call native libraries and process native data without the brittleness and danger of JNI. This <a href="https://openjdk.org/jeps/12">is a preview language API</a>.</p></li>
<li><p><a href="https://openjdk.org/jeps/448">JEP-448: Vector API (6th Incubator)</a>: introduces an API to express vector computations that reliably compile at runtime to optimal vector instructions on supported CPU architectures, thus achieving performance superior to equivalent scalar computations.</p></li>
</ul>
<p>Those JEPs are the themes of <a href="https://jdk.java.net/21/">JDK-21</a> but what other features are coming? There are quite a few to unpack to be fair.</p>
<ul>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8296344">JDK-8296344: Remove Dependency on G1 for Writing the CDS Archive Heap</a>: in previous JDKs, the dumping of the <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">CDS</a> archive heap has complex interactions with G1, however in <a href="https://jdk.java.net/21/">JDK-21</a> those have been removed. That also makes it possible to dump the archive heap with non-G1 collectors.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8298048">JDK-8298048: Combine CDS Archive Heap into a Single Block</a>: combines all objects in the <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">CDS</a> archive heap into a single, contiguous block. </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8296263">JDK-8296263: Uniform APIs for Using Archived Heap Regions</a>: replaces two different mechanisms that existed for using archived heap regions stored in a <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">CDS</a> archive with unified one to be used for all GC policies.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8298966">JDK-8298966: Deprecate JMX Subject Delegation and the method JMXConnector.getMBeanServerConnection(Subject) for removal</a>: deprecates the Java Management Extension (JMX) Subject Delegation feature for removal in a future release. The feature is enabled by the method <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.management/javax/management/remote/JMXConnector.html#getMBeanServerConnection%28javax.security.auth.Subject%29">getMBeanServerConnection(javax.security.auth.Subject)</a> of the <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.management/javax/management/remote/JMXConnector.html">JMXConnector</a> class which will be deprecated for removal.
</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8311073">JDK-8311073: Note if implicit annotation processing is being used</a>: by default, the long-standing policy is that annotation processing is implicitly enabled. As that policy may change in the future, the users should get some notice they are relying on implicit processing. </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8303530">JDK-8303530: Redefine JAXP Configuration File</a>: provides the way to specify the properties file to be used by JAXP factories during the initialization outside of the JDK by specifying <code>jdk.xml.config.file</code> system property. </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8306703">JDK-8306703: JFR: Summary views</a>: provides means to visualize aggregated event data from the shell. There are 70 predefined views, such as <strong>hot-methods</strong>, <strong>gc-pauses</strong>, <strong>pinned-threads</strong>, <strong>allocation-by-site</strong>, <strong>gc</strong>, <strong>memory-leaks-by-class</strong>, and more. A list of available views can be found through using <code>jcmd <pid> JFR.view</code> or <code>jfr view <recoding></code> commands, for example:</p>
<pre>
jcmd <pid> JFR.view allocation-by-site
jcmd <pid> JFR.view hot-methods
jcmd <pid> JFR.view
jcmd <pid> JFR.view maxage=1h maxsize=2000MB gc-pauses
jcmd <pid> JFR.view cell-height=3 truncate=beginning width=80 system-processes
jfr view --cell-height 2 FreeMemory <recoding>
jfr view --verbose gc <recoding>
</pre>
</li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8267140">JDK-8267140: Support closing the HttpClient by making it auto-closable</a>: the <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html">HttpClient</a> now implements <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/AutoCloseable.html">AutoCloseable</a> and has been enriched with following new methods:
</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html#close%28%29">void close()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html#shutdown%28%29">void shutdown()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html#shutdownNow%28%29">void shutdownNow()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html#awaitTermination%28java.time.Duration%29">boolean awaitTermination(Duration duration)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html#isTerminated%28%29">boolean isTerminated()</a></p></li>
</ul>
</li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8306560">JDK-8306560: Add TOOLING.jsh load file</a>: adds TOOLING.jsh load file to have better and built-in access for tools shipping with the JDK and their command-line interface. This is truly a gem since it is now possible to run tools provided by the Java Development Kit (JDK) in a <a href="https://docs.oracle.com/en/java/javase/21/jshell/introduction-jshell.html">JShell</a> session (see please <a href="https://sormuras.github.io/blog/2023-03-09-jshell-tooling">Running JDK Tools within a JShell Session</a> for background).</p>
<pre>
$ jshell TOOLING
| Welcome to JShell -- Version 21
| For an introduction type: /help intro
jshell> interface Empty {}
jshell> javap(Empty.class)
</pre>
<p>The list of available tools could be fetched using <code>tools()</code> method.</p>
<pre>
jshell> tools()
jar
javac
javadoc
javap
jdeps
jlink
jmod
jpackage
</pre>
</li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8015831">JDK-8015831: Add lint check for calling overridable methods from a constructor</a>: this one was certainly on my wish list for a long time, the <code>javac</code> may issue a warning (new lint warning category <code>this-escape</code>) when detects the calling of the overridable methods in constructors (please check <a href="https://marxsoftware.blogspot.com/2023/03/jdk-21-b15-new-xlint-warnings.html">OpenJDK 21 Compiler Warning on Constructor Calling Overridable Methods</a> for more details and excellent examples).</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8307466">JDK-8307466: java.time.Instant calculation bug in until and between methods</a>: fixes the implementation of <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/Instant.html#until%28java.time.temporal.Temporal,java.time.temporal.TemporalUnit%29">java.time.Instant.until(Temporal endExclusive, TemporalUnit unit)</a> in some cases did not correctly borrow or carry from the nanos to the seconds when computing using <code>ChronoUnit.MILLIS</code> or <code>ChronoUnit.MICROS</code>.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8027682">JDK-8027682: javac wrongly accepts semicolons in package and import decls</a>: disallow extra semicolons between <code>import</code> statements, for example</p>
<pre class="brush:java">
import java.util.Map;;;;
import java.util.Set;
</pre>
<p>will now generate a compiler error (but for backward compatibility, when compiling source versions prior to <code>21</code>, a warning is generated instead of an error).</p>
</li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8026369">JDK-8026369: javac potentially ambiguous overload warning needs an improved scheme</a>: prior to <a href="https://jdk.java.net/21/">JDK-21</a>, the <code>javac</code> compiler was omitting some potentially ambiguous overload warnings enabled by the <code>-Xlint:overloads</code> option.
</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8305092">JDK-8305092: Improve Thread.sleep(millis, nanos) for sub-millisecond granularity</a>: as it says, the <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#sleep%28long,int%29">Thread.sleep(long millis, int nanos)</a> is now able to perform sub-millisecond sleep.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8302659">JDK-8302659: Modernize Windows native code for NetworkInterface</a>: the change may impact the implementations that lookup of network interfaces by calling <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/NetworkInterface.html#getByName%28java.lang.String%29">NetworkInterace.getByName(String name)</a> method however the display name returned by <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/NetworkInterface.html#getDisplayName%28%29">NetworkInterface.getDisplayName()</a> has not been changed.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8300869">JDK-8300869: Make use of the Double.toString(double) algorithm in java.util.Formatter</a>: replaces <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Formatter.html">java.util.Formatter</a> algorithm to format <code>double</code> and <code>float</code> with a the one used by <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Double.html#toString%28double%29">Double.toString(double d)</a>.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8205129">JDK-8205129: Remove java.lang.Compiler</a>: the <code>java.lang.Compiler</code> class was deprecated for removal long time ago and it has been removed in <a href="https://jdk.java.net/21/">JDK-21</a>.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8297295">JDK-8297295: Remove ThreadGroup.allowThreadSuspension</a>: the method <code>ThreadGroup.allowThreadSuspension</code> was deprecated for removal since <a href="https://jdk.java.net/14/">JDK-14</a> and its time has come.</p></li>
</ul>
<p>The <a href="https://jdk.java.net/21/">JDK-21</a> changeset looks already impressive but ... we are not done yet, let us walk through the standard library changes.</p>
<ul>
<li><p>New interfaces <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/constant/ModuleDesc.html">java.lang.constant.ModuleDesc</a> and <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/constant/PackageDesc.html">java.lang.constant.PackageDesc</a> for describing <code>Module</code> and <code>Package</code> constants respectively.</p></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/21/docs/api/jdk.management/com/sun/management/ThreadMXBean.html">com.sun.management.ThreadMXBean</a> interface got one new default method:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/jdk.management/com/sun/management/ThreadMXBean.html#getTotalThreadAllocatedBytes%28%29">default long getTotalThreadAllocatedBytes()</a></p></li>
</ul>
</li>
<li><p>The class <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/DelayQueue.html">java.util.concurrent.DelayQueue</a> overrides:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/DelayQueue.html#remove%28%29">java.util.concurrent.Delayed remove()</a></p></li>
</ul>
</li>
<li><p>As part of <a href="https://bugs.openjdk.org/browse/JDK-8301226">JDK-8301226</a>, the <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Math.html">java.lang.Math</a> got new static method family:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Math.html#clamp%28double,double,double%29">static double clamp(double value, double min, double max)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Math.html#clamp%28float,float,float%29">static float clamp(float value, float min, float max)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Math.html#clamp%28long,int,int%29">static int clamp(long value, int min, int max)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Math.html#clamp%28long,long,long%29">static long clamp(long value, long min, long max)</a></p></li>
</ul>
</li>
<li><p>Under the same <a href="https://bugs.openjdk.org/browse/JDK-8301226">JDK-8301226</a> issue, the <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StrictMath.html">java.lang.StrictMath</a> has those new methods too: </p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StrictMath.html#clamp%28double,double,double%29">static double clamp(double value, double min, double max)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StrictMath.html#clamp%28float,float,float%29">static float clamp(float value, float min, float max)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StrictMath.html#clamp%28long,int,int%29">static int clamp(long value, int min, int max)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StrictMath.html#clamp%28long,long,long%29">static long clamp(long value, long min, long max)</a></p></li>
</ul>
</li>
<li><p>In scope of <a href="https://bugs.openjdk.org/browse/JDK-8302323">JDK-8302323</a>, the rare addition of the new methods to <a href="https://bugs.openjdk.org/browse/JDK-8302323">java.lang.StringBuilder</a> class has happened:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringBuilder.html#repeat%28int,int%29">StringBuilder repeat(int codePoint, int count)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringBuilder.html#repeat%28java.lang.CharSequence,int%29">StringBuilder repeat(CharSequence cs, int count)</a></p></li>
</ul>
</li>
<li><p>And similarly, still under <a href="https://bugs.openjdk.org/browse/JDK-8302323">JDK-8302323</a>, to <a href="https://bugs.openjdk.org/browse/JDK-8302323">java.lang.StringBuffer</a> class:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringBuffer.html#repeat%28int,int%29">StringBuffer repeat(int codePoint, int count)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringBuffer.html#repeat%28java.lang.CharSequence,int%29">StringBuffer repeat(CharSequence cs, int count)</a></p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Locale.html">java.util.Locale</a> was also refreshed with:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Locale.html#availableLocales%28%29">static Stream<Locale> availableLocales()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Locale.html#caseFoldLanguageTag%28java.lang.String%29">static String caseFoldLanguageTag(String languageTag)</a></p></li>
</ul>
</li>
<li><p>The <a href="https://bugs.openjdk.org/browse/JDK-8302590">JDK-8302590</a> and <a href="https://bugs.openjdk.org/browse/JDK-8305486">JDK-8305486</a> bring improvements to <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html">java.lang.String</a> class:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#indexOf%28int,int,int%29">int indexOf(int ch, int beginIndex, int endIndex)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#indexOf%28java.lang.String,int,int%29">int indexOf(String str, int beginIndex, int endIndex)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#splitWithDelimiters%28java.lang.String,int%29">String[] splitWithDelimiters(String regex, int limit)</a></p></li>
</ul>
</li>
<li><p>The <a href="https://bugs.openjdk.org/browse/JDK-8305486">JDK-8305486</a> also touched the <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Pattern.html">java.util.regex.Patter</a>n class:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Pattern.html#splitWithDelimiters%28java.lang.CharSequence,int%29">String[] splitWithDelimiters(CharSequence input, int limit)</a></p></li>
</ul>
</li>
<li><p>Probably the most love in <a href="https://jdk.java.net/21/">JDK-21</a> was directed to emojis, with <a href="https://bugs.openjdk.org/browse/JDK-8303018">JDK-8303018</a> adding new emoji properties to <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html">java.lang.Character</a>:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#isEmoji%28int%29">static boolean isEmoji(int codePoint)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#isEmojiComponent%28int%29">static boolean isEmojiComponent(int codePoint)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#isEmojiModifier%28int%29">static boolean isEmojiModifier(int codePoint)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#isEmojiModifierBase%28int%29">static boolean isEmojiModifierBase(int codePoint)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#isEmojiPresentation%28int%29">static boolean isEmojiPresentation(int codePoint)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#isExtendedPictographic%28int%29">static boolean isExtendedPictographic(int codePoint)</a></p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Pattern.html">java.util.regex.Pattern</a> got some of the emoji share as well in scope of <a href="https://bugs.openjdk.org/browse/JDK-8305107">JDK-8305107</a>: one can match characters that have emoji-related properties with the new <code>\p{IsXXX}</code> constructs, for example:</p>
<pre>
Pattern.compile("\\p{IsEmoji}").matcher("π").matches()
</pre>
</li>
</ul>
<p>From the security perspective, <a href="https://jdk.java.net/21/">JDK-21</a> is pretty packed with enhancements. Some of them we have highlighted above but a few more deserve special mentions (if you need a comprehensive look, please check out <a href="https://seanjmullan.org/blog/2023/09/22/jdk21">JDK 21 Security Enhancements</a> article):</p>
<ul>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8298127">JDK-8298127: HSS/LMS Signature Verification</a>: implements support for Leighton-Micali Signatures (LMS) as described in <a href="https://datatracker.ietf.org/doc/html/rfc8554">RFC-8554</a>. LMS is an approved software signing algorithm for CNSA 2.0, with SHA-256/192 parameters recommended.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8288050">JDK-8288050: Add support of SHA-512/224 and SHA-512/256 to the PBKDF2 and PBES2 impls in SunJCE provider</a>: the SunJCE provider is enhanced with additional PBES2 Cipher and Mac algorithms, such as those using SHA-512/224 and SHA-512/256 message digests.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8301553">JDK-8301553: Support Password-Based Cryptography in SunPKCS11</a>: supports an initial set of Password-Based Cryptography algorithms in the SunPKCS11 security provider. </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8305091">JDK-8305091: Change ChaCha20 cipher init behavior to match AES-GCM</a>: allows ChaCha20 decrypt-mode Cipher objects to reuse the key and nonce, conforming to the same rules that AES-GCM does. </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8179502">JDK-8179502: Enhance OCSP, CRL and Certificate Fetch Timeouts</a>: delivers an enhanced syntax for properties related to certificate, CRL, and OCSP connect and read timeouts. The new system properties <code>com.sun.security.ocsp.readtimeout</code>, <code>com.sun.security.cert.timeout</code> and <code>com.sun.security.cert.readtimeout</code> have been introduced to control this behavior.</p></li>
</ul>
<p>From all perspectives, <a href="https://jdk.java.net/21/">JDK-21</a> looks like the release worth migrating to (it is supposed to be LTS), despite the fact there are <a href="https://adoptium.net/blog/2023/09/temurin21-delay/">unforeseen delays announced</a> by some vendors.</p>
<p>I πΊπ¦ stand πΊπ¦ with πΊπ¦ Ukraine.</p>
Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-31133891220472807332023-06-27T18:20:00.002-07:002023-06-27T18:22:31.108-07:00Java's SecurityManager in the age of virtual threads<p>The <a href="https://openjdk.org/projects/jdk/21/">JDK-21</a> is just around the corner, bringing the virtual threads (<a href="https://openjdk.org/jeps/444">JEP 444: Virtual Threads</a>) to the mainstream, at least this is the plan so far. For vast majority of the projects out there the virtual threads open up a huge number of opportunities but for some - add new headaches.</p>
<p>More specifically, let us talk about niche projects that still rely on <a href="https://docs.oracle.com/javase/tutorial/essential/environment/security.html">SecurityManager</a>. The <a href="https://docs.oracle.com/javase/tutorial/essential/environment/security.html">SecurityManager</a> has been deprecated for removal in JDK-17 (<a href="https://openjdk.org/jeps/411">JEP 411: Deprecate the Security Manager for Removal</a>) and disallowed in JDK-18 (<a href="https://bugs.openjdk.org/browse/JDK-8270380">JDK-8270380</a>). Its usage is discouraged but it is still there and is used in production systems. </p>
<p>Since <a href="https://jdk.java.net/21/">JDK-21 early builds</a> are available to everyone (as of this moment, the latest build is <strong>21-ea+28-2377</strong>), let us find out what is happening when <a href="https://docs.oracle.com/javase/tutorial/essential/environment/security.html">SecurityManager</a> meets virtual threads. The code snippet below would serve as an example of the application that installs the <a href="https://docs.oracle.com/javase/tutorial/essential/environment/security.html">SecurityManager</a> and tries to fetch, well, the content of <a href="https://openjdk.org/jeps/444">JEP 444</a>, for sake of doing network calls. In the first attempt, we are going to use standard thread pool (operating system threads).</p>
<pre class="brush:java">
public class Starter {
public static void main(String[] args) throws Exception {
System.setSecurityManager(new SecurityManager());
try (var executor = Executors.newSingleThreadExecutor()) {
var future = executor.submit(() -> fetch("https://openjdk.org/jeps/444"));
System.out.println(future.get());
}
}
private static String fetch(String url) throws MalformedURLException, IOException, URISyntaxException {
try (var in = new URI(url).toURL().openStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}
}
</pre>
<p>If we run this example using just <code>java</code> (thanks to <a href="https://openjdk.org/jeps/330">JEP 330: Launch Single-File Source-Code Programs</a>), it is going to fail with the <code>java.security.AccessControlException</code>: </p>
<pre>
$ java -Djava.security.manager=allow src/main/java/com/example/Starter.java
...
Exception in thread "main" java.util.concurrent.ExecutionException: java.security.AccessControlException: access denied ("java.net.SocketPermission" "openjdk.org:443" "connect,resolve")
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
at com.example.Starter.main(Starter.java:15)
Suppressed: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "modifyThread")
at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:488)
at java.base/java.security.AccessController.checkPermission(AccessController.java:1071)
at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:411)
at java.base/java.util.concurrent.ThreadPoolExecutor.checkShutdownAccess(ThreadPoolExecutor.java:764)
at java.base/java.util.concurrent.ThreadPoolExecutor.shutdown(ThreadPoolExecutor.java:1394)
at java.base/java.util.concurrent.Executors$DelegatedExecutorService.shutdown(Executors.java:759)
at java.base/java.util.concurrent.Executors$AutoShutdownDelegatedExecutorService.shutdown(Executors.java:846)
at java.base/java.util.concurrent.ExecutorService.close(ExecutorService.java:413)
at com.example.Starter.main(Starter.java:13)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:484)
at jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:208)
at jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:135)
Caused by: java.security.AccessControlException: access denied ("java.net.SocketPermission" "openjdk.org:443" "connect,resolve")
at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:488)
at java.base/java.security.AccessController.checkPermission(AccessController.java:1071)
at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:411)
at java.base/java.lang.SecurityManager.checkConnect(SecurityManager.java:905)
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:619)
at java.base/sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264)
at java.base/sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:377)
at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:193)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1237)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1123)
at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:179)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1675)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1599)
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:223)
at java.base/java.net.URL.openStream(URL.java:1325)
at com.example.Starter.fetch(Starter.java:24)
at com.example.Starter.lambda$main$0(Starter.java:14)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
</pre>
<p>That is kind of expected, we need to craft the policy to allow the connection over socket to <strong>openjdk.org</strong> host, the minimal one we need is below, stored in <strong>src/main/resources/security.policy</strong>:</p>
<pre>
grant {
permission java.lang.RuntimePermission "modifyThread";
permission java.net.SocketPermission "openjdk.org:443", "connect,resolve";
};
</pre>
<p>Now, if we rerun this example with this policy, it should print out the content of the JEP into the console (in HTML format):</p>
<pre>
$ java -Djava.security.manager=allow -Djava.security.policy=src/main/resources/security.policy src/main/java/com/example/Starter.java
...
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
...
</pre>
<p>Awesome, so let just switch over to virtual threads! It should just work, right?</p>
<pre class="brush:java">
public class Starter {
public static void main(String[] args) throws Exception {
System.setSecurityManager(new SecurityManager());
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var future = executor.submit(() -> fetch("https://openjdk.org/jeps/444"));
System.out.println(future.get());
}
}
private static String fetch(String url) throws MalformedURLException, IOException, URISyntaxException {
try (var in = new URI(url).toURL().openStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}
}
</pre>
<p>Or should it?</p>
<pre>
$ java -Djava.security.manager=allow -Djava.security.policy=src/main/resources/security.policy src/main/java/com/example/Starter.java
...
Exception in thread "main" java.util.concurrent.ExecutionException: java.security.AccessControlException: access denied ("java.net.SocketPermission" "openjdk.org:443" "connect,resolve")
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
at com.example.Starter.main(Starter.java:15)
Caused by: java.security.AccessControlException: access denied ("java.net.SocketPermission" "openjdk.org:443" "connect,resolve")
at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:488)
at java.base/java.security.AccessController.checkPermission(AccessController.java:1071)
at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:411)
at java.base/java.lang.SecurityManager.checkConnect(SecurityManager.java:905)
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:619)
at java.base/sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:264)
at java.base/sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:377)
at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:193)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1237)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1123)
at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:179)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1675)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1599)
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:223)
at java.base/java.net.URL.openStream(URL.java:1325)
at com.example.Starter.fetch(Starter.java:20)
at com.example.Starter.lambda$main$0(Starter.java:14)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.lang.VirtualThread.run(VirtualThread.java:311)
</pre>
<p>If you are surprised, so was I. But we really shouldn't be if we read <a href="https://openjdk.org/jeps/444">JEP 444: Virtual Threads</a> carefully enough. It says clearly:</p>
<blockquote><ul><li>Virtual threads have no permissions when running with a SecurityManager set.</li></ul></blockquote>
<p>Why is that? Back in the days, the rumors were being spread that <a href="https://openjdk.org/projects/loom/">Project Loom</a> was one of the reasons to kick <a href="https://docs.oracle.com/javase/tutorial/essential/environment/security.html">SecurityManager</a> out, the two didn't play well together. True or not, here we are.</p>
<p>For better or worse, the <a href="https://docs.oracle.com/javase/tutorial/essential/environment/security.html">SecurityManager</a> has transitioned from being the source of annoying deprecation warnings to rather a serious obstacle on the route of adopting recent JDK features. The time of making hard decisions is approaching very fast.</p>
<p>I πΊπ¦ stand πΊπ¦ with πΊπ¦ Ukraine.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-29708354445555488762023-03-22T07:21:00.000-07:002023-03-22T07:21:17.397-07:00JDK-20: the most boring JDK release yet?<p>Still hot off the press, <a href="https://jdk.java.net/20/">JDK-20</a> is out! These are terrific news, but what about exciting new features? Although a number of JEPs made it into the release, all of them are either preview or incubation features: </p>
<ul>
<li><p><a href="https://openjdk.org/jeps/429">JEP-429: Scoped Values (Incubator)</a>: introduces scoped values, which enable the sharing of immutable data within and across threads. They are preferred to thread-local variables, especially when using large numbers of virtual threads.</p></li>
<li><p><a href="https://openjdk.org/jeps/432">JEP-432: Record Patterns (Second Preview)</a>: enhances the Java programming language with record patterns to deconstruct record values. Record patterns and type patterns can be nested to enable a powerful, declarative, and composable form of data navigation and processing.</p></li>
<li><p><a href="https://openjdk.org/jeps/433">JEP-433: Pattern Matching for switch (Fourth Preview)</a>: enhances the Java programming language with pattern matching for switch expressions and statements. Extending pattern matching to switch allows an expression to be tested against a number of patterns, each with a specific action, so that complex data-oriented queries can be expressed concisely and safely.</p> <p>If you keen to learn more, please check out <a href="https://dev.java/learn/pattern-matching/">Using Pattern Matching</a> publication, a pretty comprehensive overview of this feature.</p></li>
<li><p><a href="https://openjdk.org/jeps/434">JEP-434: Foreign Function & Memory API (Second Preview)</a>: introduces an API by which Java programs can interoperate with code and data outside of the Java runtime. By efficiently invoking foreign functions (i.e., code outside the JVM), and by safely accessing foreign memory (i.e., memory not managed by the JVM), the API enables Java programs to call native libraries and process native data without the brittleness and danger of JNI.</p></li>
<li><p><a href="https://openjdk.org/jeps/436">JEP-436: Virtual Threads (Second Preview)</a>: introduces virtual threads to the Java Platform. Virtual threads are lightweight threads that dramatically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications. </p></li>
<li><p><a href="https://openjdk.org/jeps/437">JEP-437: Structured Concurrency (Second Incubator)</a>: simplifies multithreaded programming by introducing an API for structured concurrency. Structured concurrency treats multiple tasks running in different threads as a single unit of work, thereby streamlining error handling and cancellation, improving reliability, and enhancing observability.</p></li>
<li><p><a href="https://openjdk.org/jeps/438">JEP-438: Vector API (Fifth Incubator)</a>: introduces an API to express vector computations that reliably compile at runtime to optimal vector instructions on supported CPU architectures, thus achieving performance superior to equivalent scalar computations.</p></li>
</ul>
<p>The pessimists may say this is the most boring release of JDK yet, but the optimist would argue that this is the calm before the storm (yes, I am talking about the next LTS release later this year, <a href="https://jdk.java.net/21/">JDK-21</a>). Nonetheless, there are quite a few notable changes to look at.</p>
<ul>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8283093">JDK-8283093: JMX connections should default to using an ObjectInputFilter</a>: enables RMI object filtering for JMX by default, by giving a default value to the existing <code>management.properties</code> setting, <code>com.sun.management.jmxremote.serial.filter.pattern</code>. The intent is to limit possible misuse of a JMX connection.</p></li>
<li><p>The ability to stop, suspend, or resume a thread with the corresponding <code>Thread.stop()</code>, <code>Thread.suspend()</code> or <code>Thread.resume()</code> methods have been eliminated. Those methods have been degraded to throw a <code>java.lang.UnsupportedOperationException</code> exception as per <a href="https://bugs.openjdk.org/browse/JDK-8249627">JDK-8249627: Degrade Thread.suspend and Thread.resume</a> and <a href="https://bugs.openjdk.org/browse/JDK-8289610">JDK-8289610: Degrade Thread.stop</a>.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8293590">JDK-8293590: Some syntax checks performed by URL.openConnection() could be performed earlier, at URL construction</a>: previously, some of the parsing and validation actions performed by the <code>URLStreamHander</code> implementations were delayed until
<code>URLStreamHandler::openConnection</code> or <code>URLConnection::connect</code> calls. Starting from <a href="https://jdk.java.net/20/">JDK-20</a>, some of these checks are performed earlier, at construction time, when <code>URLStreamHandler::parseURL</code> is called. A new JDK system property <code>jdk.net.url.delayParsing</code> can be specified on the command line to revert to the previous behavior.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8292698">JDK-8292698: Improve performance of DataInputStream</a></p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8297609">JDK-8297609: Add application/wasm MIME type for wasm file extension</a>: adds <code>application/wasm</code> as a MIME type for WebAssembly mapping in the default shipped with the JDK <code>content-types.properties</code> file (for the reference, please check <a href="https://www.iana.org/assignments/media-types/application/wasm">https://www.iana.org/assignments/media-types/application/wasm</a>). </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8295803%20">JDK-8295803: Console should be usable in jshell and other environments</a></p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8301863">JDK-8301863: ObjectInputFilter example incorrectly calls rejectUndecidedClass</a></p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8254711">JDK-8254711: Add java.security.Provider.getService JFR Event</a>: adds an event each time a JCE cryptographic service for a specific algorithm is requested and/or used.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8291753">JDK-8291753: Add JFR event for GC CPU Time</a> and <a href="https://bugs.openjdk.org/browse/JDK-8292373">JDK-8292373: Add JFR event for GC CPU Time (other GCs)</a>: adds a JFR event which includes same information through GCTraceCPUTime.</p></li>
</ul>
<p>The standard library was the one benefited the most in <a href="https://jdk.java.net/20/">JDK-20</a> release, let us take a closer look at what has changed:</p>
<ul>
<li>
<p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/io/BufferedInputStream.html">java.io.BufferedInputStream</a> got a specialized implementation for <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/io/InputStream.html#transferTo%28java.io.OutputStream%29">long transferTo(OutputStream out) throws IOException</a></p>
</li>
<li>
<p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/io/PushbackInputStream.html">java.io.PushbackInputStream</a> got a specialized implementation for <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/io/InputStream.html#transferTo%28java.io.OutputStream%29">long transferTo(OutputStream out) throws IOException</a></p>
</li>
<li>
<p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/io/SequenceInputStream.html">java.io.SequenceInputStream</a> got a specialized implementation for <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/io/InputStream.html#transferTo%28java.io.OutputStream%29">long transferTo(OutputStream out) throws IOException</a></p>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Float.html">java.lang.Float</a> got two new conversion methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Float.html#float16ToFloat%28short%29">static float float16ToFloat(short floatBinary16)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Float.html#floatToFloat16%28float%29">static short floatToFloat16(float f)</a></p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/package-summary.html">java.lang.reflect</a> package was enhanced with:</p>
<ul>
<li><p>A new <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/AccessFlag.html">enum java.lang.reflect.AccessFla</a>g</p></li>
<li><p>A new <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/ClassFileFormatVersion.html">enum java.lang.reflect.ClassFileFormatVersion</a></p></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/Field.html">java.lang.reflect.Field</a> got a new method:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/Field.html#accessFlags%28%29">Set<AccessFlag> accessFlags()</a>
</p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/Member.html">java.lang.reflect.Member</a> got a new method:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/Member.html#accessFlags%28%29">Set<AccessFlag> accessFlags()</a>
</p></li>
</ul></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/Parameter.html">java.lang.reflect.Parameter</a> got a new method:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/reflect/Parameter.html#accessFlags%28%29">Set<AccessFlag> accessFlags()</a>
</p></li>
</ul></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Class.html">java.lang.Class</a> class got a new method as well:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Class.html#accessFlags%28%29">Set<AccessFlag> accessFlags()</a></p></li>
</ul>
</li>
<li><p>A new method was added to <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/module/ModuleDescriptor.html">java.lang.module.ModuleDescriptor</a> class:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/module/ModuleDescriptor.html#accessFlags%28%29">Set<AccessFlag> accessFlags()</a></p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/net/URL.html">java.net.URL</a> was enhanced with a factory method:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/net/URL.html#of%28java.net.URI,java.net.URLStreamHandler%29">static URL of(URI uri, URLStreamHandler handler) throws MalformedURLException</a></p></li>
</ul>
</li>
<li><p>A new methods found their way into <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/nio/file/spi/FileSystemProvider.html">java.nio.file.spi.FileSystemProvide</a>r:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/nio/file/spi/FileSystemProvider.html#readAttributesIfExists%28java.nio.file.Path,java.lang.Class,java.nio.file.LinkOption...%29"><A extends BasicFileAttributes> A readAttributesIfExists(Path path, Class<A> type, LinkOption... options)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/nio/file/spi/FileSystemProvider.html#exists%28java.nio.file.Path,java.nio.file.LinkOption...%29">boolean exists(Path path, LinkOption... options)</a></p></li>
</ul></li>
<li><p>As part of <a href="https://bugs.openjdk.org/browse/JDK-8297648">JDK-8297648: j.u.concurrent updates for JDK 20</a>, couple of classes where enriched with new methods:</p>
<ul>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/ForkJoinPool.html">java.util.concurrent.ForkJoinPool</a> has gotten:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/ForkJoinPool.html#externalSubmit%28java.util.concurrent.ForkJoinTask%29"><T> ForkJoinTask<T> externalSubmit(ForkJoinTask<T> task)</a></p></li>
</ul>
</li>
</ul>
<ul>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/ForkJoinWorkerThread.html">java.util.concurrent.ForkJoinWorkerThread</a> has gotten:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/ForkJoinWorkerThread.html#getQueuedTaskCount%28%29">int getQueuedTaskCount()</a></p></li>
</ul>
</li>
</ul>
</li>
<li><p>A long standing bug <a href="https://bugs.openjdk.org/browse/JDK-8178355">JDK-8178355: IdentityHashMap uses identity-based comparison for values everywhere except remove(K,V) and replace(K,V,V) Export</a> has been finally fixed by providing overrides in <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/IdentityHashMap.html">java.util.IdentityHashMap</a> for:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/IdentityHashMap.html#remove%28java.lang.Object,java.lang.Object%29">boolean remove(Object key, Object value)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/IdentityHashMap.html#replace%28K,V,V%29">boolean replace(K key, V oldValue, V newValue)</a></p></li>
</ul>
</li>
<li><p>A couple of changes touched <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/javax/net/ssl/SSLParameters.html">javax.net.ssl.SSLParameters</a> class:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/javax/net/ssl/SSLParameters.html#getNamedGroups%28%29">String[] getNamedGroups()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/javax/net/ssl/SSLParameters.html#setNamedGroups%28java.lang.String%5B%5D%29">void setNamedGroups(String[] namedGroups)</a></p></li>
</ul>
</li>
<li><p>The improvements made inside <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/package-summary.html">java.util.regex</a> package came as a pleasant surprise, those are related to named groups (<a href="https://bugs.openjdk.org/browse/JDK-8292872">JDK-8292872: MatchResult should provide values of named-capturing groups</a>):</p>
<ul>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/Matcher.html">java.util.regex.Matcher</a> class got new methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/Matcher.html#hasMatch%28%29">boolean hasMatch()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/Matcher.html#namedGroups%28%29">Map<String, Integer> namedGroups()</a></p></li>
</ul>
</li>
<li><p>The interface <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/MatchResult.html">java.util.regex.MatchResult</a> also got new methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/MatchResult.html#end%28java.lang.String%29">default int end(String name)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/MatchResult.html#group%28java.lang.String%29">default String group(String name)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/MatchResult.html#hasMatch%28%29">default boolean hasMatch()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/MatchResult.html#namedGroups%28%29">default Map<String, Integer> namedGroups()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/regex/MatchResult.html#start%28java.lang.String%29">default int start(String name</a>)</p></li>
</ul>
</li>
</ul>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/zip/ZipInputStream.html">java.util.zip.ZipInputStream</a> introduces overrides for methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/zip/ZipInputStream.html#read%28%29">int read() throws IOException</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/zip/ZipInputStream.html#readAllBytes%28%29">byte[] readAllBytes() throws IOException</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/zip/ZipInputStream.html#readNBytes%28int%29">byte[] readNBytes(int len) throws IOException</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/zip/ZipInputStream.html#readNBytes%28byte%5B%5D,int,int%29">int readNBytes(byte[] b, int off, int len) throws IOException</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/zip/ZipInputStream.html#skipNBytes%28long%29">void skipNBytes(long n) throws IOException</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/zip/ZipInputStream.html#transferTo%28java.io.OutputStream%29">long transferTo(OutputStream out) throws IOException</a></p></li>
</ul>
</li>
<li><p>A couple of new public constructors were introduced into the <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/security/InvalidParameterException.html">java.security.InvalidParameterException</a> class:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/security/InvalidParameterException.html#%3Cinit%3E%28java.lang.String,java.lang.Throwable%29">InvalidParameterException(String msg, Throwable cause)</a></li>
<li><a href="file:///C:/Users/retab/Development/jdk-api-diff/target/jdk-api-diff.html#java.security.InvalidParameterException">InvalidParameterException(Throwable cause)</a></a></li>
</ul>
</li>
</ul>
<p>The garbage collectors have got a considerable chunk of improvements (especially G1), covered by <a href="https://tschatzl.github.io/2023/03/14/jdk20-g1-parallel-gc-changes.html">JDK 20 G1/Parallel/Serial GC changes</a> in great details. To highlight just a few:</p>
<ul>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8210708">JDK-8210708: Use single mark bitmap in G1</a>: G1 native memory footprint has been reduced significantly by removing one of the mark bitmaps spanning the entire Java heap. Please check the brilliant <a href="https://tschatzl.github.io/2022/08/04/concurrent-marking.html">Concurrent Marking in G1</a> article for deep technical dive.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8292654">JDK-8292654: G1 remembered set memory footprint regression after JDK-8286115</a>: fixes the previously introduced regression that caused significantly increased memory usage in some benchmarks.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8256265">JDK-8256265: G1: Improve parallelism in regions that failed evacuation</a></p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8137022">JDK-8137022: Concurrent refinement thread adjustment and (de-)activation suboptimal</a></p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8297247">JDK-8297247: Add GarbageCollectorMXBean for Remark and Cleanup pause time in G1</a>: adds Remark and Cleanup time to a G1 JMX bean.</p></li>
</ul>
<p>From the security perspective, it worth mentioning these changes:</p>
<ul>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8256660">JDK-8256660: Disable DTLS 1.0 by default</a>: disables DTLS 1.0 by default by adding "DTLSv1.0" to the <code>jdk.tls.disabledAlgorithms</code> security property in the <code>java.security</code> configuration file.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8290368">JDK-8290368: Introduce LDAP and RMI protocol-specific object factory filters to JNDI implementation</a>: introduces LDAP-specific factories filter (defined by <code>jdk.jndi.ldap.object.factoriesFilter property</code>) and RMI-specific factories filter (defined by <code>jdk.jndi.rmi.object.factoriesFilter</code> property). The new factory filters are consulted in tandem with the <code>jdk.jndi.object.factoriesFilter </code> global factories filter to determine if a specific object factory is permitted to instantiate objects for the given protocol.</p></li>
</ul>
<p>This releases proves one more time that boring is not always bad. If you want to learn more about these (and other) features in <a href="https://jdk.java.net/20/">JDK-20</a>, I would highly recommend going over <a href="https://nipafx.dev/java-20-guide/">Java 20 π₯±</a> guide.</p>
<p>I πΊπ¦ stand πΊπ¦ with πΊπ¦ Ukraine.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-43235619785451765642023-01-03T13:55:00.000-08:002023-01-03T13:55:25.167-08:00Project Loom in JDK-19: the benefits but with quirks <p>A few months have passed already since <a href="https://jdk.java.net/19/">JDK-19</a> release, which we <a href="https://aredko.blogspot.com/2022/10/jdk-19-revolution-is-looming.html">talked about previously</a> in details. More and more developers are switching to <a href="https://jdk.java.net/19/">JDK-19</a>, turning their heads towards <a href="https://openjdk.org/projects/loom/">Project Loom</a> and starting to play with virtual threads and structured concurrency (despite the incubation / preview status of these features). And it certainly makes sense, sooner or later, the JVM and API changes will be finalized, marking the era of the <a href="https://openjdk.org/projects/loom/">Project Loom</a> production readiness.</p>
<p>In today's post, we are going to cover some not so obvious quirks (by-products of the <a href="https://openjdk.org/projects/loom/">Project Loom</a> implementation) you should be aware of (or may run into) while switching to <a href="https://jdk.java.net/19/">JDK-19</a> in the green-field or, more importantly, brown-field projects. Those may manifest even if you are not planning to use <a href="https://openjdk.org/projects/loom/">Project Loom</a> just yet.</p>
<p>Let us kick it off with API changes. The code snippet below uses old fashioned <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html">java.lang.Thread</a> class to spawn some work aside. The computation uses the instance of the <code>Builder</code> inner class, the implementation of the <code>Builder::build()</code> method is left off since it is not really important.</p>
<pre class="brush:java">
public class Starter {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread() {
public void run() {
final Builder builder = new Builder();
builder.build();
}
};
thread.start();
thread.join();
}
private static class Builder {
public void build() {
// implementation details
}
}
}
</pre>
<p>The code compiles and runs just fine on any modern JDK, predating <a href="https://jdk.java.net/19/">JDK-19</a>. On <a href="https://jdk.java.net/19/">JDK-19</a> however, it fails to compile, with somewhat cryptic error.</p>
<pre>
Unresolved compilation problems:
Cannot instantiate the type Thread.Builder
The method build() is undefined for the type Thread.Builder
</pre>
<p>The rare example of how existing code may clash with API changes: as part of the <a href="https://openjdk.org/projects/loom/">Project Loom</a>, the <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html">java.lang.Thread</a> got a new <code>public sealed interface Builder</code>, which is being rightly picked by the compiler (instead of our <code>Builder</code> class) inside <code>Thread</code>'s the subclass. The fix is easy (but may not look pretty), just use the qualified class name:</p>
<pre class="brush:java">
public class Starter {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread() {
public void run() {
final Starter.Builder builder = new Starter.Builder();
builder.build();
}
};
thread.start();
thread.join();
}
private static class Builder {
public void build() {
// implementation details
}
}
}
</pre>
<p>Please notice that nonetheless JDK's <b>preview features were not enabled</b>, the preview APIs are still visible and taken into the consideration by the compiler. The <a href="https://bugs.openjdk.org/browse/JDK-8287968">issue</a> has been reported (<a href="https://bugs.openjdk.org/browse/JDK-8287968">JDK-8287968</a>) and the possible <a href="https://bugs.openjdk.org/browse/JDK-8288416">incompatibilities</a> have been documented (<a href="https://bugs.openjdk.org/browse/JDK-828841">JDK-8288416</a>).</p>
<p>The next quirk we are going to look at is also related to <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html">java.lang.Thread</a> but this time we would be using thread pools (executors) from the standard library. Let us assume we need an executor instance which tracks the moment when the new thread is started. One of the options to accomplish that is to use custom <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadFactory.html">java.util.concurrent.ThreadFactory</a> and override <code>Thread::start()</code> method.</p>
<pre class="brush:java">
public class Starter {
public static void main(String[] args) throws Exception {
final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r) {
@Override
public void start() {
System.out.println("Thread has started!");
super.start();
}
};
}
});
executor.submit(() -> {}).get(1, TimeUnit.SECONDS);
executor.shutdown();
}
}
</pre>
<p>On JDKs prior to <a href="https://jdk.java.net/19/">JDK-19</a>, the expected message will be printed out in the console.</p>
<pre>
Thread has started!
</pre>
<p>But not in <a href="https://jdk.java.net/19/">JDK-19</a>: in scope of the <a href="https://openjdk.org/projects/loom/">Project Loom</a> implementation, the thread pools and executors (notably <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html">ForkJoinPool</a> and <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html">ThreadPoolExecutor</a>) do not call <code>Thread::start()</code> method anymore. It <b>does not matter if the preview features are enabled or not</b>, and sadly, there is no workaround to simulate the desired behavior (the alternative <code>Thread::start(ThreadContainer)</code> replacement is not accessible). The <a href="https://bugs.openjdk.org/browse/JDK-8292027">issue</a> has been reported and is still open as of today (<a href="https://bugs.openjdk.org/browse/JDK-8292027">JDK-8292027</a>).</p>
<p>Great, so far we have seen some quirks caused by <a href="https://openjdk.org/projects/loom/">Project Loom</a> irrespective of the fact it is used or not. Moving on, let us quickly summarize the constraints you may run into when using <a href="https://openjdk.org/projects/loom/">Project Loom</a> (by enabling JDKs preview features) and virtual threads.</p>
<ul>
<li>be aware of the limitations using <code>synchronized</code> blocks or methods in scope of virtual threads</li>
<li>be aware of the limitations using native methods or foreign functions in scope of virtual threads</li>
<li>be aware of the limitations some APIs (like file system) in the JDK have when called in scope to virtual threads</li>
</ul>
<p>Two JEPs, the <a href="https://openjdk.org/jeps/425">JEP-425: Virtual Threads (Preview)</a> and <a href="https://openjdk.org/jeps/436">JEP-436: Virtual Threads (Second Preview)</a> offer quite a comprehensive overview with respect to the virtual thread implementation and limitations in <a href="https://jdk.java.net/19/">JDK-19</a> and upcoming <a href="https://jdk.java.net/20/">JDK-20</a>, worth of your time reading them. Another good source I would recommend is <a href="https://blogs.oracle.com/javamagazine/post/java-loom-virtual-threads-platform-threads">Coming to Java 19: Virtual threads and platform threads</a> published by <a href="https://blogs.oracle.com/javamagazine/">Java Magazine</a> last May.</p>
<p>
It is fair to say that <a href="https://openjdk.org/projects/loom/">Project Loom</a> is evolving really fast, and this is the kind of the feature JVM really screamed for. Yes, it has some limitations now, but there are high chances that in the future most of them will be lifted or worked through.</p>
<p>I πΊπ¦ stand πΊπ¦ with πΊπ¦ Ukraine.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-73272993540910101332022-10-27T18:11:00.001-07:002022-10-27T18:11:25.849-07:00JDK 19: revolution is looming!<p>The <a href="https://jdk.java.net/19/">JDK-19</a> landed last month and may look like yet another routine release except it is not: <a href="https://openjdk.org/projects/loom/">Project Loom</a> has finally delivered first bits! Alhough still in half-incubation / half-preview, it is a really big deal to the future of <a href="https://openjdk.org">OpenJDK</a> platform. Interestingly enough, a vast majority of the <a href="https://jdk.java.net/19/">JDK-19</a> JEPs are either preview or incubating features. So, what <a href="https://jdk.java.net/19/">JDK-19</a> has to offer?</p>
<ul>
<li><p><a href="https://openjdk.org/jeps/425">JEP-425: Virtual Threads (Preview)</a>: introduces virtual threads to the Java Platform. Virtual threads are lightweight threads that dramatically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications.</p></li>
<li><p><a href="https://openjdk.org/jeps/428">JEP-428: Structured Concurrency (Incubator)</a>: simplifies multithreaded programming by introducing an API for structured concurrency. Structured concurrency treats multiple tasks running in different threads as a single unit of work, thereby streamlining error handling and cancellation, improving reliability, and enhancing observability.</p></li>
<li><p><a href="https://openjdk.org/jeps/405">JEP-405: Record Patterns (Preview)</a>: enhances the Java programming language with record patterns to deconstruct record values. Record patterns and type patterns can be nested to enable a powerful, declarative, and composable form of data navigation and processing. </p></li>
<li><p><a href="https://openjdk.org/jeps/422">JEP-422: Linux/RISC-V Port</a>: ports the JDK to <a href="https://en.wikipedia.org/wiki/RISC-V">RISC-V</a> hardware.</p></li>
<li><p><a href="https://openjdk.org/jeps/424">JEP-424: Foreign Function & Memory API (Preview)</a>: introduces an API by which Java programs can interoperate with code and data outside of the Java runtime. By efficiently invoking foreign functions (i.e., code outside the JVM), and by safely accessing foreign memory (i.e., memory not managed by the JVM), the API enables Java programs to call native libraries and process native data without the brittleness and danger of JNI. The JEP comes with a whole new <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/foreign/package-summary.html">java.lang.foreign</a> package.</p></li>
<li><p><a href="https://openjdk.org/jeps/426">JEP-426: Vector API (Fourth Incubator)</a>: introduces an API to express vector computations that reliably compile at runtime to optimal vector instructions on supported CPU architectures, thus achieving performance superior to equivalent scalar computations.</p></li>
<li><p><a href="https://openjdk.org/jeps/427">JEP-427: Pattern Matching for switch (Third Preview</a>): enhances the Java programming language with pattern matching for switch expressions and statements. Extending pattern matching to switch allows an expression to be tested against a number of patterns, each with a specific action, so that complex data-oriented queries can be expressed concisely and safely.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8283620">JDK-8283620: New System Properties for `System.out` and `System.err`</a>: two new system properties, <code>stdout.encoding</code> and <code>stderr.encoding</code>, have been introduced. The value of these system properties is the encoding used by the <code>System.out</code> and <code>System.err</code> streams respectively (the default values of these system properties depend on the platform and fallback to the <code>native.encoding</code> property when the console streams are not available). </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8212136">JDK-8212136: Remove finalizer implementation in SSLSocketImpl</a>: the underlying native resource releases are now done by the Socket implementation (the TLS <code>close_notify</code> messages will no longer be emitted if SSLSocket is not explicitly closed).</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8255495">JDK-8255495: Support CDS Archived Heap for uncompressed oops</a>: adds support for uncompressed oops for <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">CDS</a> archived heap (for now, only G1 GC is supported, <a href="https://wiki.openjdk.org/display/zgc/Main">ZGC</a> as a future target).</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8261455">JDK-8261455: Automatically generate the CDS archive if necessary</a>: allows the <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">CDS</a> archive to be automatically generated if necessary (e.g., if the specified archive doesn't exist, or if it's out of date because the JDK version was updated).
</p><pre class="brush:java">
java -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=app.jsa -cp app.jar AppMain
</pre>
<p>In the previous JDKs, the <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">CDS</a> archive must be explicitly created before usage (with <code>-Xshare:dump</code> or <code>-XX:ArchiveClassesAtExit</code>).
</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8278067">JDK-8278067: Make HttpURLConnection default keep alive timeout configurable</a>: two new system properties have been added, <code>http.keepAlive.time.server</code> and <code>http.keepAlive.time.proxy</code>, which control the keep alive behavior of <a href="https://download.java.net/java/early_access/loom/docs/api/java.base/java/net/HttpURLConnection.html">HttpURLConnection</a> in the case where the server does not specify a keep alive time (by means of <code>Keep-Alive</code> header). </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8268081">JDK-8268081: Update Unicode Data Files to 14.0.0</a>: introduces support of the Unicode Standard version 14.0.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8281181">JDK-8281181: Do not use CPU Shares to compute active processor count</a>: addresses an incorrect interpretation of the Linux cgroups parameter <code>cpu.shares</code> which might cause the JVM inside a container to use fewer CPUs than available.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8274788">JDK-8274788: Support archived heap objects in ParallelGC</a>: improves startup time since the module graph could be loaded from the archive.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8280396">JDK-8280396: G1: Full gc mark stack draining should prefer to make work available to other threads</a> and <a href="https://bugs.openjdk.org/browse/JDK-8280705">JDK-8280705: Parallel: Full gc mark stack draining should prefer to make work available to other threads</a>: allow other threads to steal and distribute work.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8176706">JDK-8176706: Additional Date-Time Formats</a>: introduces additional date/time formats in the <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/DateTimeFormatter.html">java.time.format.DateTimeFormatter</a> and <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/DateTimeFormatterBuilder.html">java.time.format.DateTimeFormatterBuilder</a> classes. The new methods have been added to support that:</p></li>
<ul>
<li><p>To <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/DateTimeFormatter.html">java.time.format.DateTimeFormatter</a>:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/DateTimeFormatter.html#ofLocalizedPattern%28java.lang.String%29">static java.time.format.DateTimeFormatter ofLocalizedPattern(java.lang.String requestedTemplate)</a></p></li>
</ul>
</li>
<li><p>To <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/DateTimeFormatterBuilder.html">java.time.format.DateTimeFormatterBuilder</a>:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/DateTimeFormatterBuilder.html#appendLocalized%28java.lang.String%29">java.time.format.DateTimeFormatterBuilder appendLocalized(java.lang.String requestedTemplate)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/format/DateTimeFormatterBuilder.html#getLocalizedDateTimePattern%28java.lang.String,java.time.chrono.Chronology,java.util.Locale%29">static java.lang.String getLocalizedDateTimePattern(java.lang.String requestedTemplate, java.time.chrono.Chronology chrono, java.util.Locale locale)</a></p></li>
</ul>
</li>
</ul>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8285445">JDK-8285660: New System Property to Disable Windows Alternate Data Stream Support in java.io.File</a>: a new system property <code>jdk.io.File.enableADS</code> has been added to control the access to NTFS Alternate Data Streams (<a href="https://owasp.org/www-community/attacks/Windows_alternate_data_stream">ADS</a>) on Windows (which is enabled by default).</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8280357">JDK-8280357: User's Home Directory set to $HOME if Invalid</a>: on Linux and macOS, the system property <code>user.home</code> is set to the home directory provided by the operating system but if the directory name provided is empty or only a single character, the value of the environment variable <code>HOME</code> is going to be used instead.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-4511638">JDK-4511638: Double.toString(double) and Float.toString(float) may Return Slightly Different Results</a>: the specification of these methods has been tightened comparing to earlier releases and the new <a href="https://jdk.java.net/19/">JDK-19</a> implementation fully adheres to it. As a consequence, some returned strings are now shorter than when using earlier releases, and for inputs at the extremes of the subnormal ranges near zero, <b>might look differently</b>.</p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8186958">JDK-8186958: New Methods to Create Preallocated HashMaps and HashSets</a>: introduces new static factory methods to allow creation of <code>HashMap</code> and related instances that are preallocated to accommodate an expected number of mappings or elements.
</p>
<ul>
<li><p>Class <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/WeakHashMap.html">java.util.WeakHashMap<K, V></a></p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/WeakHashMap.html#newWeakHashMap%28int%29">static <K, V> java.util.WeakHashMap<K, V> newWeakHashMap(int numElements)</a></p></li>
</ul>
</li>
<li><p>Class <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/HashMap.html">java.util.HashMap<K, V></a></p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/HashMap.html#newHashMap%28int%29">static <K, V> java.util.HashMap<K, V> newHashMap(int numElements)</a></p></li>
</ul>
</li>
<li><p>Class <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/HashSet.html">java.util.HashSet<E></a></p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/HashSet.html#newHashSet%28int%29">static <E> java.util.HashSet<E> newHashSet(int numElements)</a></p></li>
</ul>
</li>
<li><p>Class <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/LinkedHashMap.html">java.util.LinkedHashMap<K, V></a></p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/LinkedHashMap.html#newLinkedHashMap%28int%29">static <K, V> java.util.LinkedHashMap<K, V> newLinkedHashMap(int numElements)</a></p></li>
</ul>
</li>
<li><p>Class <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/LinkedHashSet.html">java.util.LinkedHashSet<E></a></p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/LinkedHashSet.html#newLinkedHashSet%28int%29">static <E> java.util.LinkedHashSet<E> newLinkedHashSet(int numElements)</a></p></li>
</ul>
</li>
</ul> </li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8274148">JDK-8274148: JShell Highlights Deprecated Elements, Variables, and Keywords</a>: <a href="https://docs.oracle.com/javase/9/jshell/introduction-jshell.htm">JShell</a> now marks deprecated elements and highlights variables and keywords in the console. </p></li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8271585">JDK-8271585: JFR: Scrub recording data</a>: adds programmatic and command-line support to scrub a recording file (to scrub a recording file means to remove sensitive or uninteresting data to reduce the file size).</p>
<pre class="brush:java">jfr scrub [filters] [recording-file] [output-file]</pre><p>Please check out this short but insightful <a href="https://inside.java/2022/10/03/sip068/">JFR Scrub - Sip of Java</a> blog post to learn more about this <a href="https://en.wikipedia.org/wiki/JDK_Flight_Recorder">JFR</a> new command.</p></li>
<li><p>From the standard library point of view, a large chunk of changes went into the classes under <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/package-summary.html">java.util.concurrent</a> package so let us start from those:</p></li>
<ul>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/Future.html">java.util.concurrent.Future<T></a> (and its implementations like <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/CompletableFuture.html">java.util.concurrent.CompletableFuture<T></a>, <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/FutureTask.html">java.util.concurrent.FutureTask<T></a>, <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ForkJoinTask.html">java.util.concurrent.ForkJoinTask<T></a>) got three new methods and one enumeration: </p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/Future.html#exceptionNow%28%29">java.lang.Throwable exceptionNow()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/Future.html#resultNow%28%29">T resultNow()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/Future.html#state%28%29">java.util.concurrent.Future$State state()</a></p></li>
<li><p>enum <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/Future.State.html">java.util.concurrent.Future$State</a></p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ExecutorService.html">java.util.concurrent.ExecutorService</a> now implements <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/AutoCloseable.html">java.lang.AutoCloseable</a> interface</p></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ForkJoinPool.html">java.util.concurrent.ForkJoinPool</a> also implements <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/AutoCloseable.html">java.lang.AutoCloseable</a> interface and has two more methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ForkJoinPool.html#lazySubmit%28java.util.concurrent.ForkJoinTask%29"><T> java.util.concurrent.ForkJoinTask<T> lazySubmit(java.util.concurrent.ForkJoinTask<T> task)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ForkJoinPool.html#setParallelism%28int%29">int setParallelism(int size)</a></p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ForkJoinTask.html">java.util.concurrent.ForkJoinTask<T></a> additionally was enriched with:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ForkJoinTask.html#adaptInterruptible%28java.util.concurrent.Callable%29">static java.util.concurrent.ForkJoinTask<T> adaptInterruptible(java.util.concurrent.Callable<? extends T> callable)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ForkJoinTask.html#quietlyJoin%28long,java.util.concurrent.TimeUnit%29">boolean quietlyJoin(long timeout, java.util.concurrent.TimeUnit unit)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ForkJoinTask.html#quietlyJoinUninterruptibly%28long,java.util.concurrent.TimeUnit%29">boolean quietlyJoinUninterruptibly(long timeout, java.util.concurrent.TimeUnit unit)</a></p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/Executors.html">java.util.concurrent.Executors</a> factory became even more capable with preview APIs:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/Executors.html#newThreadPerTaskExecutor%28java.util.concurrent.ThreadFactory%29">static java.util.concurrent.ExecutorService newThreadPerTaskExecutor(java.util.concurrent.ThreadFactory threadFactory)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/Executors.html#newVirtualThreadPerTaskExecutor%28%29">static java.util.concurrent.ExecutorService newVirtualThreadPerTaskExecutor()</a></p></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.html">java.lang.Thread</a> comes at the end with whole bunch of additions (some also in preview):</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.html#isVirtual%28%29">boolean isVirtual()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.html#join%28java.time.Duration%29">boolean join(java.time.Duration duration)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.html#threadId%28%29">long threadId()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.html#ofPlatform%28%29">static java.lang.Thread$Builder$OfPlatform ofPlatform()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.html#ofVirtual%28%29">static java.lang.Thread$Builder$OfVirtual ofVirtual()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.html#sleep%28java.time.Duration%29">static void sleep(java.time.Duration duration)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.html#startVirtualThread%28java.lang.Runnable%29">static java.lang.Thread startVirtualThread(java.lang.Runnable task)</a></p></li>
<li><p>interface <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.Builder.OfVirtual.html">Thread.Builder.OfVirtual</a></p></li>
<li><p>interface <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.Builder.OfPlatform.html">Thread.Builder.OfPlatform</a></p></li>
<li><p>interface <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Thread.Builder.html">Thread.Builder</a></p></li>
</ul>
</li>
</ul>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Long.html">java.lang.Long</a> has introduced a new methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Long.html#compress%28long,long%29">static long compress(long i, long mask)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Long.html#expand%28long,long%29">static long expand(long i, long mask)</a></p></li>
</ul></li>
<li><p>Similarly, the <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Integer.html">java.lang.Integer</a> has introduced this pair of methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Integer.html#compress%28int,int%29">static int compress(int i, int mask)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Integer.html#expand%28int,int%29">static int expand(int i, int mask)</a></p></li>
</ul></li>
<li><p>The class <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/math/BigInteger.html">java.math.BigInteger</a> got parallel multiplication support:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/math/BigInteger.html#parallelMultiply%28java.math.BigInteger%29">java.math.BigInteger parallelMultiply(java.math.BigInteger val)</a></p></li>
</ul></li>
<li><p>The class <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/Locale.html">java.util.Locale</a> was added a number of factory methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/Locale.html#of%28java.lang.String,java.lang.String,java.lang.String%29">static java.util.Locale of(java.lang.String language, java.lang.String country, java.lang.String variant)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/Locale.html#of%28java.lang.String,java.lang.String%29">static java.util.Locale of(java.lang.String language, java.lang.String country)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/Locale.html#of%28java.lang.String%29">static java.util.Locale of(java.lang.String language)</a></p></li>
</ul></li>
<li><p>The quite useful <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/Objects.html">java.util.Objects</a> utility class was not forgotten:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/Objects.html#toIdentityString%28java.lang.Object%29">static java.lang.String toIdentityString(java.lang.Object o)</a></p></li>
</ul></li>
<li><p>The new factory method made it to <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/Random.html">java.util.Random</a> class:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/Random.html#from%28java.util.random.RandomGenerator%29">static java.util.Random from(java.util.random.RandomGenerator generator)</a></p></li>
</ul></li>
<li><p>The interface <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/chrono/Chronology.html">java.time.chrono.Chronology</a> was enriched with new method:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/time/chrono/Chronology.html#isIsoBased%28%29">boolean isIsoBased()</a></p></li>
</ul></li>
<li><p>Minor but useful addition to <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/text/DecimalFormatSymbols.html">java.text.DecimalFormatSymbols</a> class:</p><ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/text/DecimalFormatSymbols.html#getLocale%28%29">Locale getLocale()</a></p></li>
</ul></li>
<li><p>And <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/MethodHandles.html">java.lang.invoke.MethodHandles</a> wraps it up with tons of new preview APIs:</p><ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/MethodHandles.html#collectCoordinates%28java.lang.invoke.VarHandle,int,java.lang.invoke.MethodHandle%29">static java.lang.invoke.VarHandle collectCoordinates(java.lang.invoke.VarHandle target, int pos, java.lang.invoke.MethodHandle filter)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/MethodHandles.html#dropCoordinates%28java.lang.invoke.VarHandle,int,java.lang.Class...%29">static java.lang.invoke.VarHandle dropCoordinates(java.lang.invoke.VarHandle target, int pos, java.lang.Class<?>... valueTypes)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/MethodHandles.html#filterCoordinates%28java.lang.invoke.VarHandle,int,java.lang.invoke.MethodHandle...%29">static java.lang.invoke.VarHandle filterCoordinates(java.lang.invoke.VarHandle target, int pos, java.lang.invoke.MethodHandle... filters)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/MethodHandles.html#filterValue%28java.lang.invoke.VarHandle,java.lang.invoke.MethodHandle,java.lang.invoke.MethodHandle%29">static java.lang.invoke.VarHandle filterValue(java.lang.invoke.VarHandle target, java.lang.invoke.MethodHandle filterToTarget, java.lang.invoke.MethodHandle filterFromTarget)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/MethodHandles.html#insertCoordinates%28java.lang.invoke.VarHandle,int,java.lang.Object...%29">static java.lang.invoke.VarHandle insertCoordinates(java.lang.invoke.VarHandle target, int pos, java.lang.Object... values)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/MethodHandles.html#memorySegmentViewVarHandle%28java.lang.foreign.ValueLayout%29">static java.lang.invoke.VarHandle memorySegmentViewVarHandle(java.lang.foreign.ValueLayout layout)</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/invoke/MethodHandles.html#permuteCoordinates%28java.lang.invoke.VarHandle,java.util.List,int...%29">static java.lang.invoke.VarHandle permuteCoordinates(java.lang.invoke.VarHandle target, java.util.List<Class<?>> newCoordinates, int... reorder)</a></p></li>
</ul></li>
</ul>
<p>From the security perspective, a few notable changes to mention (but feel free to go over much more detailed overview in the <a href="https://seanjmullan.org/blog/2022/09/22/jdk19">JDK 19 Security Enhancements</a> post by <a href="https://seanjmullan.org/">Sean Mullan</a>):</p>
<ul>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8281561">JDK-8281561: Disable http DIGEST mechanism with MD5 and SHA-1 by default</a>: the MD5 and SHA-1 message digest algorithms have been disabled by default for HTTP Digest authentication, they can re-enabled on an opt-in basis by setting a new system property <code>http.auth.digest.reEnabledAlgorithms</code>.</p>
</li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8280494">JDK-8280494: (D)TLS signature schemes</a>: introduces new APIs to customize the signature schemes for individual TLS or DTLS connections, for fine control of the security properties. The <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/javax/net/ssl/SSLParameters.html">javax.net.ssl.SSLParameters</a> was extended in these regards with two new methods:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/javax/net/ssl/SSLParameters.html#getSignatureSchemes%28%29">java.lang.String[] getSignatureSchemes()</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/javax/net/ssl/SSLParameters.html#setSignatureSchemes%28java.lang.String%5B%5D%29">void setSignatureSchemes(java.lang.String[] signatureSchemes)</a></p></li>
</ul>
</li>
<li><p><a href="https://bugs.openjdk.org/browse/JDK-8279842">JDK-8279842: HTTPS Channel Binding support for Java GSS/Kerberos</a>: adds the support for TLS channel binding tokens for Negotiate/Kerberos authentication over HTTPS through <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/javax/net/ssl/HttpsURLConnection.html">HttpsURLConnection</a>.
</p></li>
</ul>
<p>If you are one of the early adopters, please note that <a href="https://jdk.java.net/19/">JDK-19</a> introduced a native memory usage regression very late in the release process so that the fix could not be integrated any more (<a href="https://bugs.openjdk.org/browse/JDK-8292654">JDK-8292654</a>). The regression has already been addressed in <a href="https://jdk.java.net/19/">JDK 19.0.1</a> and later.</p>
<p>With that, hope you are as excited as I am about <a href="https://jdk.java.net/19/">JDK-19</a> release, the peaceful revolution is looming! And the <a href="https://jdk.java.net/20/">JDK-20</a> early access builds are already there, looking very promising!</p>
<p>I πΊπ¦ stand πΊπ¦ with πΊπ¦ Ukraine.</p>
Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-20604263217732271622022-08-30T16:23:00.002-07:002022-08-30T16:24:23.020-07:00Quick, somewhat naΓ―ve but still useful: benchmarking HTTP services from the command line<p>Quite often, while developing HTTP services, you may find yourself looking for a quick and easy way to throw some load at them. The tools like <a href="https://gatling.io/">Gatling</a> and <a href="https://jmeter.apache.org/">JMeter</a> are the golden standard in the open-source world, but developing meaningful load test suites in both of them may take some. It is very likely you are going to end up with one of these but during the development, using more approachable tooling gives the invaluable feedback much faster.<p>
<p>The command line <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> load testing tools is what we are going to talk about. There are a <a href="https://github.com/denji/awesome-http-benchmark">lot of them</a>, but we will focus on just a few: <a href="https://httpd.apache.org/docs/2.4/programs/ab.html">ab</a>, <a href="https://github.com/tsenart/vegeta">vegeta</a>, <a href="https://github.com/wg/wrk">wrk</a>, <a href="https://github.com/giltene/wrk2">wrk2</a>, and <a href="https://github.com/lnx-search/rewrk">rewrk</a>. And since <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> is getting <a href="https://aredko.blogspot.com/2022/01/so-you-want-to-expose-your-jax-rs.html">more and more traction</a>, the tools that support both will be highlighted and awarded with bonus points. The sample HTTP service we are going to run tests against is exposing only single <code>GET /services/catalog</code> endpoint over <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.1</a>, <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> and <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> over clear text.</p>
<p>Let us kick off with <a href="https://httpd.apache.org/docs/2.4/programs/ab.html">ab</a>, the <a href="https://httpd.apache.org/">Apache HTTP server</a> benchmarking tool: one of the oldest HTTP load testing tools out there. It is available on most Linux distributions and supports only <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.x</a> (in fact, it does not implement <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.x</a> fully). The standard set of parameters like desired number of requests, concurrency and timeout is supported.</p>
<pre>
$> ab -n 1000 -c 10 -s 1 -k http://localhost:19091/services/catalog
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 100 requests
...
Completed 1000 requests
Finished 1000 requests
Server Software:
Server Hostname: localhost
Server Port: 19091
Document Path: /services/catalog
Document Length: 41 bytes
Concurrency Level: 10
Time taken for tests: 51.031 seconds
Complete requests: 1000
Failed requests: 0
Keep-Alive requests: 0
Total transferred: 146000 bytes
HTML transferred: 41000 bytes
Requests per second: 19.60 [#/sec] (mean)
Time per request: 510.310 [ms] (mean)
Time per request: 51.031 [ms] (mean, across all concurrent requests)
Transfer rate: 2.79 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 5
Processing: 3 498 289.3 497 1004
Waiting: 2 498 289.2 496 1003
Total: 3 499 289.3 497 1004
Percentage of the requests served within a certain time (ms)
50% 497
66% 645
75% 744
80% 803
90% 914
95% 955
98% 979
99% 994
100% 1004 (longest request)
</pre>
<p>The report is pretty comprehensive but if your service is talking over <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> or is using <a href="https://en.wikipedia.org/wiki/HTTPS">HTTPS</a> with self-signed certificates (not unusual in development), you are out of luck.</p>
<p>Let us move on to a bit more advanced tools, or to be precise - a family of tools, inspired by <a href="https://github.com/wg/wrk">wrk</a>: a modern <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> benchmarking tool. There are no official binary releases of <a href="https://github.com/wg/wrk">wrk</a> available so you are better off building the bits from the sources yourself.</p>
<pre>
$> wrk -c 50 -d 10 -t 5 --latency --timeout 5s https://localhost:19093/services/catalog
Running 10s test @ https://localhost:19093/services/catalog
5 threads and 50 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 490.83ms 295.99ms 1.04s 57.06%
Req/Sec 20.79 11.78 60.00 81.29%
Latency Distribution
50% 474.64ms
75% 747.03ms
90% 903.44ms
99% 999.52ms
978 requests in 10.02s, 165.23KB read
Requests/sec: 97.62
Transfer/sec: 16.49KB
</pre>
<p>Capability-wise, it is very close to <a href="https://httpd.apache.org/docs/2.4/programs/ab.html">ab</a> with slightly better <a href="https://en.wikipedia.org/wiki/HTTPS">HTTPS</a> support. The report is as minimal as it could get, however the distinguishing feature of the <a href="https://github.com/wg/wrk">wrk</a> is the ability to use <a href="https://luajit.org/">LuaJIT scripting</a> to perform <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> request generation. No <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> support though.</p>
<p><a href="https://github.com/giltene/wrk2">wrk2</a> is an improved version of <a href="https://github.com/wg/wrk">wrk</a> (and is based mostly on its codebase) that was modified to produce a constant throughput load and accurate latency details. Unsurprisingly, you have to build this one from the sources as well (and, the binary name is kept as <code>wrk</code>).</p>
<pre>
$> wrk -c 50 -d 10 -t 5 -L -R 100 --timeout 5s https://localhost:19093/services/catalog
Running 10s test @ https://localhost:19093/services/catalog
5 threads and 50 connections
Thread calibration: mean lat.: 821.804ms, rate sampling interval: 2693ms
Thread calibration: mean lat.: 1077.276ms, rate sampling interval: 3698ms
Thread calibration: mean lat.: 993.376ms, rate sampling interval: 3282ms
Thread Stats Avg Stdev Max +/- Stdev
Latency 933.61ms 565.76ms 3.35s 70.42%
Req/Sec -nan -nan 0.00 0.00%
Latency Distribution (HdrHistogram - Recorded Latency)
50.000% 865.79ms
75.000% 1.22s
90.000% 1.69s
99.000% 2.61s
99.900% 3.35s
99.990% 3.35s
99.999% 3.35s
100.000% 3.35s
Detailed Percentile spectrum:
Value Percentile TotalCount 1/(1-Percentile)
28.143 0.000000 1 1.00
278.015 0.100000 36 1.11
426.751 0.200000 71 1.25
....
2969.599 0.996875 354 320.00
3348.479 0.997266 355 365.71
3348.479 1.000000 355 inf
#[Mean = 933.606, StdDeviation = 565.759]
#[Max = 3346.432, Total count = 355]
#[Buckets = 27, SubBuckets = 2048]
----------------------------------------------------------
893 requests in 10.05s, 150.87KB read
Requests/sec: 88.85
Transfer/sec: 15.01KB
</pre>
<p>Besides these noticeable enhancements, the feature set of <a href="https://github.com/giltene/wrk2">wrk2</a> is largely matching the <a href="https://github.com/wg/wrk">wrk</a>'s one, so <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> is out of the picture. But do not give up just yet, we are not done.</p>
<p>The most recent addition to <a href="https://github.com/wg/wrk">wrk</a>'s family is <a href="https://github.com/lnx-search/rewrk">rewrk</a>, a more modern <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> framework benchmark utility, which could be thought of as <a href="https://github.com/wg/wrk">wrk</a> rewritten in beloved <a href="https://www.rust-lang.org/">Rust</a> with <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> support backed in.</p>
<pre>
$> rewrk -c 50 -d 10s -t 5 --http2 --pct --host https://localhost:19093/services/catalog
Beginning round 1...
Benchmarking 50 connections @ https://localhost:19093/services/catalog for 10 second(s)
Latencies:
Avg Stdev Min Max
494.62ms 281.78ms 5.56ms 1038.28ms
Requests:
Total: 972 Req/Sec: 97.26
Transfer:
Total: 192.20 KB Transfer Rate: 19.23 KB/Sec
+ --------------- + --------------- +
| Percentile | Avg Latency |
+ --------------- + --------------- +
| 99.9% | 1038.28ms |
| 99% | 1006.26ms |
| 95% | 975.33ms |
| 90% | 942.94ms |
| 75% | 859.73ms |
| 50% | 737.45ms |
+ --------------- + --------------- +
</pre>
<p>As you may have noticed, the report is very similar to the one produced by <a href="https://github.com/wg/wrk">wrk</a>. In my opinion, this is a tool which has a perfect balance of features, simplicity and insights, at least while you are in the middle of development.</p>
<p>The last one we are going to look at is <a href="https://github.com/tsenart/vegeta">vegeta</a>, the <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> load testing tool and library, written in <a href="https://go.dev/">Go</a>. It supports not only <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a>, but <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> over clear text and has powerful reporting built-in. It heavily uses pipes for composing different steps together, for example: </p>
<pre>
$> echo "GET https://localhost:19093/services/catalog" |
vegeta attack -http2 -timeout 5s -workers 10 -insecure -duration 10s |
vegeta report
Requests [total, rate, throughput] 500, 50.10, 45.96
Duration [total, attack, wait] 10.88s, 9.98s, 899.836ms
Latencies [min, mean, 50, 90, 95, 99, max] 14.244ms, 529.886ms, 537.689ms, 918.294ms, 962.448ms, 1.007s, 1.068s
Bytes In [total, mean] 20500, 41.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:500
Error Set:
</pre>
<p>You could also ask for histograms (with customizable reporting buckets), not to mention beautiful plots with <code>plot</code> command:</p>
<pre>
$> echo "GET http://localhost:19092/services/catalog" |
vegeta attack -h2c -timeout 5s -workers 10 -insecure -duration 10s |
vegeta report -type="hist[0,200ms,400ms,600ms,800ms]"
Bucket # % Histogram
[0s, 200ms] 86 17.20% ############
[200ms, 400ms] 102 20.40% ###############
[400ms, 600ms] 98 19.60% ##############
[600ms, 800ms] 113 22.60% ################
[800ms, +Inf] 101 20.20% ###############
</pre>
<p>Out of all above, <a href="https://github.com/tsenart/vegeta">vegeta</a> is clearly the most powerful tool in terms of capabilities and reporting. It has all the chances to become the one-stop <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> benchmarking harness even for production.</p>
<p>So we walked through a good number of tools, which one is yours? My advice would be: for <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.x</a>, use <a href="https://httpd.apache.org/docs/2.4/programs/ab.html">ab</a>; for basic <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> look at <a href="https://github.com/lnx-search/rewrk">rewrk</a>; and if none of these fit, turn to <a href="https://github.com/tsenart/vegeta">vegeta</a>. Once your demands and sophistication of the load tests grow, consider <a href="https://gatling.io/">Gatling</a> or <a href="https://jmeter.apache.org/">JMeter</a>.</p>
<p>With that, Happy <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a> Load Testing!</p>
Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-54266473489305409532022-05-28T12:13:00.003-07:002022-05-28T12:13:46.643-07:00The lost art of learning ... (a new programming language)<p>I remember the time, 20 years ago, when as a junior software developer I was driven by constant, non-stoppable desire to learn new things: new features, techniques, frameworks, architectures, styles, practices, ... and surely, new programming languages. This curiosity, hunger to learn, drove me to consume as many books as possible and to hunt around the clock for the scanty online publications available at that time.</p>
<p>Year after year, the accumulated knowledge backed by invaluable experience led me to settle on proven and battle-tested (in production) choices, which have worked, work these days and I am sure will work in the future. For me, the foundation was set in Java (later <a href="https://www.scala-lang.org/">Scala</a> as well) and JVM platform in general. This is where my expertise lays in, but don't get me wrong, I still try to read a lot, balancing on the edge between books and enormous amount of the blog posts, articles, talks and podcast. How many programming languages I was able to master for the last, let say, 10 years? If we count the JVM ecosystem only, <a href="https://www.scala-lang.org/">Scala</a> and <a href="https://groovy-lang.org/">Groovy</a> would make the list, but otherwise - none, zero, 0. The pace of learning on this front has slowed for me, significantly ... </p>
<p>Why is that? Did I get burned out? <b>Nope (thanks God)</b>. Fed up by development? <b>Nope, hopefully never</b>. Opionated? <b>Yes, certainly</b>. Older? <b>Definitely</b>. Less curious? <b>Not really</b>. To be fair, there are so many things going on in the JVM ecosystem, so many projects are being developed by the community, it has become increasingly difficult to find the time for something else! And frankly, why bother if you could do Java for the next 20 years without any fear of it disappearing or becoming irrelevant.</p>
<p>JVM is amazing platform but its fundamental principles heavily impact certain design aspects of any programming language built on top of it. It is good and bad (everything is a trade-off), but with the time, you as a developer get used to it, questioning if anything else makes sense. As many others, I ended up in the bubble which has to be blown up. How? I decided to learn <a href="https://www.rust-lang.org/">Rust</a>, and go way beyond the basics.</p>
<p>But no worries, I do not intend to sell you <a href="https://www.rust-lang.org/">Rust</a> but rather share an alarming discovery: the lost art of learning a new programming language. It all started as usual, crunching through the blogs, people's and companies' experiences and finally getting the book, <a href="https://www.amazon.com/Programming-Rust-Fast-Systems-Development/dp/1492052590/ref=sr_1_1?keywords=rust+programming&qid=1653747433&sprefix=rust+pro%2Caps%2C85&sr=8-1">Programming Rust: Fast, Safe Systems Development</a> (really good one but the official <a href="https://doc.rust-lang.org/book/">The Rust Programming Language</a> is equally awesome). A few months (!) later, I was done reading, feeling ready for delivering cool, production-ready <a href="https://www.rust-lang.org/">Rust</a> applications, or so I thought ... The truth - I was far, very far from it ... and those are the things I did wrong.</p>
<ul>
<li>
<p><a href="https://www.rust-lang.org/">Rust</a> is concise (at least, to my taste), modern and powerful, but it is not a simple language. If some language aspects are not clear, do not skip over but try to understand them. If book is not doing sufficiently good job at it, look around till you get them, otherwise you risk to miss even more. I did not do that (overly confident that nothing could be harder than learning <a href="https://www.manning.com/books/functional-programming-in-scala">functional Scala</a>) and at some point I was somewhat lost.
</p>
</li>
<li>
<p>In the same vein, <a href="https://www.rust-lang.org/">Rust</a> has unique perspective on many things, including concurrency and memory management, and investing the time to understand what those are is a must (the Java/JVM background is not very helpful here). Essentially, you have to "unlearn" in order to grasp certain new concepts, this is not easy and confuses the brain.</p>
</li>
<li>
<p>Stay very focused and methodical, try to read at least few pages <b>**every day**</b>. Yes, we are all super busy, always distracted, have families and started to travel again, ... yada yada yada. Also, <a href="https://www.amazon.com/Programming-Rust-Fast-Systems-Development/dp/1492052590/ref=sr_1_1?keywords=rust+programming&qid=1653747433&sprefix=rust+pro%2Caps%2C85&sr=8-1">Programming Rust: Fast, Safe Systems Development</a> is not a slim book: ~800 pages! Irregular, long breaks in reading were killing my progress.</p>
</li>
<li>
<p><b>Practice, practice and practice.</b> If you have some pet projects in mind, this is great, if not - just come up with anything ad-hoc but try to use the things you have just read about. Learning the "theory" is great, but mastering the programming language is all about using it every day to solve problems. I screwed there epically by following "bed time story" style ... </p>
</li>
<li>
<p>Learn the coding practices and patterns by looking into the language's ecosystem, in this regard a curated list of <a href="https://github.com/rust-unofficial/awesome-rust">Rust code and resources</a> is super useful.
</p>
</li>
<li><p>Subscribe to newsletters to stay up to date and be aware what is going on in the core development and in the community. In case of <a href="https://www.rust-lang.org/">Rust</a>, the <a href="https://this-week-in-rust.org/">This Week in Rust</a> turned out to be invaluable.
</p></li>
<li><p>Watch talks and presentations, luckily most of the conferences do publish all their videos online these days. I stumbled upon the official <a href="https://www.youtube.com/c/RustVideos">Rust YouTube channel</a> and since then follow it closely.</p></li>
</ul>
<p>Learning new programming language is rewarding experience, in particular when it happens to be <a href="https://www.rust-lang.org/">Rust</a>, a <a href="https://insights.stackoverflow.com/survey/2021#most-loved-dreaded-and-wanted-language-love-dread"> most beloved programming language</a> for the last six years. But not only because of that, <a href="https://www.rust-lang.org/">Rust</a> is perfect fit for a system programming, something Java/JVM is inherently not suited very well (yes, it is better now with <a href="https://openjdk.java.net/jeps/330">JEP 330: Launch Single-File Source-Code Programs</a>, <a href="https://www.jbang.dev/">jbang</a> and <a href="https://www.graalvm.org/22.0/reference-manual/native-image/">GraalVM native images</a> but still far from being perfect for a task). From the other side, it is quite challenging when the programming language you are learning is not part of your day-to-day job.</p>
<p>As of this moment, I am not sure if <a href="https://www.rust-lang.org/">Rust</a> ever becomes my primary programming language but one thing I do know: I think every passionate software developer should strive to learn at least one new programming language every 1-2 years (we have so many good ones). It is amazing mental exercice and I used to follow this advice back in the days but lost the sight of it along the journey. I really should have not.</p>
<p>Happy learning and peace to everyone!</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com2tag:blogger.com,1999:blog-8745745886722714957.post-78360098595724514362022-03-25T12:46:00.000-07:002022-03-25T12:46:55.235-07:00The show must go on: JDK-18 is here!<p>It feels like <a href="https://aredko.blogspot.com/2021/09/chasing-javas-release-train-jdk-17-next.html">JDK-17 release</a> just landed not so long ago, but there is a new one already out: please welcome <a href="https://jdk.java.net/18/">JDK-18</a>! Although it is not an LTS release, there are quite a few interesting features to talk about (the preview and incubator ones are not included). </p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/400">JEP-400: UTF-8 by Default</a>: specifies <a href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a> as the default charset of the standard Java APIs. With this change, APIs that depend upon the default charset will behave consistently across all implementations, operating systems, locales, and configurations. In <a href="https://jdk.java.net/17/">JDK-17</a> and earlier, the default charset is determined when the Java runtime starts and this behavior could be restored in <a href="https://jdk.java.net/18/">JDK-18</a> using <b>file.encoding</b> system property by setting it to "<b>COMPAT</b>": <code>-Dfile.encoding=COMPAT</code> (the other supported value of this property is "<b>UTF-8</b>").</p></li>
<li><p><a href="https://openjdk.java.net/jeps/416">JEP-416: Reimplement Core Reflection with Method Handles</a>: reimplements <code>java.lang.reflect.Method</code>, <code>Constructor</code>, and <code>Field</code> on top of <code>java.lang.invoke</code> method handles. Making method handles the underlying mechanism for reflection will reduce the maintenance and development cost of both the <code>java.lang.reflect</code> and <code>java.lang.invoke</code> APIs. To mitigate the compatibility risks, there is possibility to enable the old implementation via <code>-Djdk.reflect.useDirectMethodHandle=false</code> system property.</p> </li>
<li><p><a href="https://openjdk.java.net/jeps/418">JEP-418: Internet-Address Resolution SPI</a>: defines a service-provider interface (SPI) for host name and address resolution, so that <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/net/InetAddress.html">java.net.InetAddress</a> can make use of resolvers other than the platform's built-in resolver. The new classes <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/net/spi/InetAddressResolver.html">java.net.spi.InetAddressResolver</a> and <a href="https://download.java.net/java/early_access/jdk18/docs/api/java.base/java/net/spi/InetAddressResolverProvider.html">java.net.spi.InetAddressResolverProvider</a> have been added into the standard library.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/421">JEP-421: Deprecate Finalization for Removal</a>: deprecates finalization for removal in a future release. Finalization remains enabled by default for now, but can be disabled (via the new command-line option <code>--finalization=disabled</code>) to facilitate early testing. In a future release it will be disabled by default, and in a later release it will be removed. Maintainers of libraries and applications that rely upon finalization should consider migrating to other resource management techniques such as the <a href="https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html">try-with-resources</a> statement and <a href="https://aredko.blogspot.com/2021/11/the-final-days-of-finalizers-in-java.html">cleaners</a>.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/408">JEP-408: Simple Web Server</a>: provides a command-line tool to start a minimal web server that serves static files only. No CGI or servlet-like functionality is available. This tool will be useful for prototyping, ad-hoc coding, and testing purposes, particularly in educational contexts. The following (new!) command line tool starts the Simple Web Server:
</p>
<pre class="brush:java">
$ jwebserver
</pre>
<p>Alternatively, the components of the Simple Web Server (i.e., server, handler, and filter) could be accessed through the API and integrated into existing applications, if needed. The following new packages contain Simple Web Server SPI and implementation:
</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/jdk.httpserver/com/sun/net/httpserver/package-summary.html">com.sun.net.httpserver</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/jdk.httpserver/com/sun/net/httpserver/spi/package-summary.html">com.sun.net.httpserver.spi</a></li>
</ul>
<p>The quick example of the Simple Web Server API in action is below:</p>
<pre class="brush:java">
var handler = new HttpHandler() {
public void handle(HttpExchange exchange) throws IOException {
final String response = "OK";
exchange.sendResponseHeaders(200, response.length());
try (OutputStream os = exchange.getResponseBody()) {
os.write(response.getBytes());
}
}
};
final HttpServer server = HttpServer.create(new InetSocketAddress(9091), 0);
server.createContext("/", handler);
server.start();
</pre>
</li>
<li><p><a href="https://openjdk.java.net/jeps/413">JEP-413: Code Snippets in Java API Documentation</a>: introduces an <code>@snippet</code> tag for JavaDoc's Standard Doclet, to simplify the inclusion of example source code in API documentation. This is probably one of the most interesting features of the <a href="https://jdk.java.net/18/">JDK-18</a>, let us take a look at it more closely by illustrating the usage of the Simple Web Server APIs usage.</p>
<pre class="brush:java">
/**
* The following code snippet shows how to use {@code HttpServer}:
* {@snippet :
* final HttpServer server = HttpServer.create(new InetSocketAddress(9091), 0);
* server.createContext("/", handler);
* server.start();
* }
*/
</pre>
<p>In this case, the snippet is embedded however it could also be externalized, for example:</p>
<pre class="brush:java">
/**
* The following code snippet shows how to use {@code HttpServer}:
* {@snippet file="SimpleWebServerStarter.java" region="server"}
*/
</pre>
<p>And here is the relevant part of the <b>SimpleWebServerStarter.java</b> content:</p>
<pre class="brush:java">
var handler = ...;
// @start region="server"
final HttpServer server = HttpServer.create(new InetSocketAddress(9091), 0);
server.createContext("/", handler);
server.start();
// @end
</pre>
<p>In my opinion, the code snippets should significantly improve the quality and usefulness of Java documentation, hopefully we are going to see them getting traction. If you want to dig deeper, <a href="https://www.blogger.com/profile/10790950138196529391">Dustin Marx</a> has published an <a href="https://marxsoftware.blogspot.com/2021/09/jdk-18-code-snippets-in-java-api.html">excellent post</a> on the subject with more details.</p>
</li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8253119">JDK-8253119: Remove the legacy PlainSocketImpl and PlainDatagramSocketImpl implementation</a>. With that, the respective replacement delivered by <a href="https://openjdk.java.net/jeps/353">JEP-353</a> and <a href="https://openjdk.java.net/jeps/373">JEP-373</a> becomes the only implementation available going forward.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8256425">JDK-8256425: Obsolete Biased Locking</a>: finally wraps up <a href="https://openjdk.java.net/jeps/374">JEP-374</a> which was supposed to be delivered in <a href="https://jdk.java.net/15/">JDK-15</a> but was <a href="https://bugs.openjdk.java.net/browse/JDK-8256253">postponed</a> due to community feedback.</p></li>
<li><p>Support for string deduplication, used to be available in <a href="https://openjdk.java.net/jeps/192">G1</a> only, was finally rolled into other garbage collectors: <a href="https://bugs.openjdk.java.net/browse/JDK-8272609">SerialGC</a>, <a href="https://bugs.openjdk.java.net/browse/JDK-8267185">ParallelGC</a> and <a href="https://bugs.openjdk.java.net/browse/JDK-8267186">ZGC</a>.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8275056">JDK-8275056: Virtualize G1CardSet containers over heap region</a>: allows arbitrary combinations of the card table size with any heap region size.</p></li>
<li><p>C2 compiler was enhanced with <a href="https://bugs.openjdk.java.net/browse/JDK-8259609">JDK-8259609: C2: optimize long range checks in long counted loops</a>, <a href="https://bugs.openjdk.java.net/browse/JDK-8276116">JDK-8276116: C2: optimize long range checks in int counted loops</a> and <a href="https://bugs.openjdk.java.net/browse/JDK-8276162">JDK-8276162: C2: Optimise unsigned comparison pattern</a>.</p></li>
<li><p>Notably, C1 compiler got <a href="https://bugs.openjdk.java.net/browse/JDK-8274983">JDK-8274983: C1: optimizes the invocation of private interface methods</a> and <a href="https://bugs.openjdk.java.net/browse/JDK-8265518">JDK-8265518: C1: Intrinsic support for Preconditions.checkIndex</a></p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8273278">JDK-8273278: Support XSLT on GraalVM Native Image--deterministic bytecode generation in XSLT</a>: fixes quite annoying issue when <code>native-image</code> can not match the hash of the runtime-generated bytecode to the pre-defined classes.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8189591">JDK-8189591: No way to locally suppress doclint warnings</a>: it becomes now possible to use <code>@SuppressWarnings</code> annotation to suppress messages from <a href="https://docs.oracle.com/en/java/javase/18/docs/specs/man/javadoc.html#doclint">DocLint</a> about issues in documentation comments, when it is not possible or practical to fix the issues that were found.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8266936">JDK-8266936: Add a finalization JFR event</a>: introduces a new <a href="https://bell-sw.com/announcements/2020/06/24/Java-Flight-Recorder-a-gem-hidden-in-OpenJDK/">JDK Flight Recorder</a> event, <code>jdk.FinalizerStatistics</code>, to help identify classes that still use finalizers (at runtime).
</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8274335">JDK-8274335: Expand serial warning to check for bad overloads of serial-related methods and ineffectual fields</a>: expand <code>javac</code>'s <code>-Xlint:serial</code> from checking the declaration of <code>serialVersionUID</code> fields to checking the declarations of all serialization-related fields and methods.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8277861">JDK-8277861: Terminally deprecate Thread.stop</a>: terminally deprecates <code>Thread.stop</code> method so it can be degraded and removed in the future release.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8080659">JDK-8133686: HttpURLConnection's getHeaderFields method returns field values in reverse order</a>: the multiple header values for a given field-name are now returned in the order in which they were added.
</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8251329">JDK-8251329: Files.walkFileTree walks infinitely if zip has dir named "." inside</a>: the ZIP file system provider has been changed to reject existing ZIP files that contain entries with "." or "..".</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8231640">JDK-8231640: Canonical property storage</a>: introduces a new system property, <code>java.properties.date</code>, to allow applications to control the default date comment written out by the <code>java.util.Properties::store</code> methods.</p></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/nio/charset/Charset.html">java.nio.charset.Charset</a> class got a new method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/nio/charset/Charset.html#forName%28java.lang.String,java.nio.charset.Charset%29">static Charset forName(String charsetName, Charset fallback)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/time/Duration.html">java.time.Duration</a> got some love as well:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/time/Duration.html#isPositive%28%29">boolean isPositive()</a></li>
</ul>
</li>
<li><p>A new method went into <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/io/PrintStream.html">java.io.PrintStream</a> class:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/io/PrintStream.html#charset%28%29">Charset charset()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/io/FileInputStream.html">java.io.FileInputStream</a> got a specialized implementation of (<a href="https://bugs.openjdk.java.net/browse/JDK-8272297">JDK-8272297</a>):</p>
<ul><li>
<a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/io/InputStream.html#transferTo%28java.io.OutputStream%29">long transferTo(java.io.OutputStream) throws IOException</a> (overridden)</li></ul></li>
<li><p>One new method found its way into <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/lang/model/element/ExecutableElement.html">javax.lang.model.element.ExecutableElement</a> interface:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/lang/model/element/ExecutableElement.html#getEnclosingElement%28%29">Element getEnclosingElement()</a></li>
</ul>
</li>
<li><p>And a few methods were added to <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/lang/model/util/Elements.html">javax.lang.model.util.Elements</a> interface:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/lang/model/util/Elements.html#getOutermostTypeElement%28javax.lang.model.element.Element%29">TypeElement getOutermostTypeElement(Element e)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/lang/model/util/Elements.html#getFileObjectOf%28javax.lang.model.element.Element%29">JavaFileObject getFileObjectOf(Element e)</a></li>
</ul></li>
<li><p>A very useful additions to <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/lang/model/SourceVersion.html">javax.lang.model.SourceVersion</a> enumeration to support mappings to runtime version:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/lang/model/SourceVersion.html#runtimeVersion%28%29">Runtime.Version runtimeVersion()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/lang/model/SourceVersion.html#valueOf%28java.lang.Runtime.Version%29">static SourceVersion valueOf(Runtime.Version rv)</a></li>
</ul></li>
<li><p>The annotation processing got some improvements in the scope of <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/annotation/processing/Messager.html">javax.annotation.processing.Messager</a> interface:</p><ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/annotation/processing/Messager.html#printError%28java.lang.CharSequence%29">void printError(CharSequence msg)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/annotation/processing/Messager.html#printError%28java.lang.CharSequence,javax.lang.model.element.Element%29">void printError(CharSequence msg, Element e)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/annotation/processing/Messager.html#printWarning%28java.lang.CharSequence%29">void printWarning(CharSequence msg)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/annotation/processing/Messager.html#printWarning%28java.lang.CharSequence,javax.lang.model.element.Element%29">void printWarning(CharSequence msg, Element e)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/annotation/processing/Messager.html#printNote%28java.lang.CharSequence%29">void printNote(CharSequence msg)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.compiler/javax/annotation/processing/Messager.html#printNote%28java.lang.CharSequence,javax.lang.model.element.Element%29">void printNote(CharSequence msg, Element e)</a></li>
</ul></li>
<li><p>The largest set of changes went into <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html">java.lang.Math</a> utility class:</p><ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#ceilDiv%28int,int%29">static int ceilDiv(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#ceilDiv%28long,int%29">static long ceilDiv(long x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#ceilDiv%28long,long%29">static long ceilDiv(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#ceilDivExact%28int,int%29">static int ceilDivExact(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#ceilDivExact%28long,long%29">static long ceilDivExact(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#ceilMod%28int,int%29">static int ceilMod(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#ceilMod%28long,int%29">static int ceilMod(long x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#ceilMod%28long,long%29">static long ceilMod(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#divideExact%28int,int%29">static int divideExact(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#divideExact%28long,long%29">static long divideExact(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#floorDivExact%28int,int%29">static int floorDivExact(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#floorDivExact%28long,long%29">static long floorDivExact(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Math.html#unsignedMultiplyHigh%28long,long%29">static long unsignedMultiplyHigh(long x, long y)</a></li>
</ul></li>
<li><p>And, similarly, to <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html">java.lang.StrictMath</a> utility class:</p><ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#ceilDiv%28int,int%29">static int ceilDiv(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#ceilDiv%28long,int%29">static long ceilDiv(long x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#ceilDiv%28long,long%29">static long ceilDiv(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#ceilDivExact%28int,int%29">static int ceilDivExact(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#ceilDivExact%28long,long%29">static long ceilDivExact(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#ceilMod%28int,int%29">static int ceilMod(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#ceilMod%28long,int%29">static int ceilMod(long x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#ceilMod%28long,long%29">static long ceilMod(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#divideExact%28int,int%29">static int divideExact(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#divideExact%28long,long%29">static long divideExact(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#floorDivExact%28int,int%29">static int floorDivExact(int x, int y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#floorDivExact%28long,long%29">static long floorDivExact(long x, long y)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/StrictMath.html#unsignedMultiplyHigh%28long,long%29">static long unsignedMultiplyHigh(long x, long y)</a></li>
</ul></li>
</ul>
<p>The <a href="https://jdk.java.net/18/">JDK-18</a> comes in with a large number of the <a href="https://seanjmullan.org/blog/2022/03/23/jdk18%09">security enhancements</a>, including further work on <a href="https://openjdk.java.net/jeps/411">phasing out SecurityManager</a> and its APIs:</p>
<ul>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8231107">JDK-8231107: Allow store password to be null when saving a PKCS12 KeyStore</a></p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8275252">JDK-8275252: Migrate cacerts from JKS to password-less PKCS12</a>: by password-less, it means the certificates are not encrypted and it contains no MacData for integrity check.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8255409">JDK-8255409: SunPKCS11 Provider Now Supports Some PKCS#11 v3.0 APIs</a></p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8270380">JDK-8270380: Change the default value of the java.security.manager system property to disallow</a>: quite an impactful change for the projects which still rely on <code>SecurityManager</code>.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8269039">JDK-8269039: Disabled SHA-1 Signed JARs</a></p></li>
<li><p>New methods added to <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/javax/security/auth/Subject.html">Subject</a> class as part of <a href="https://bugs.openjdk.java.net/browse/JDK-8267108">JDK-8267108: Alternate Subject.getSubject and doAs APIs that do not depend on Security Manager APIs</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/javax/security/auth/Subject.html#current%28%29">static Subject current()</a> (replacement for <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/javax/security/auth/Subject.html#getSubject%28java.security.AccessControlContext%29">static Subject getSubject(AccessControlContext acc)</a>)</li>
<li><a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/javax/security/auth/Subject.html#callAs%28javax.security.auth.Subject,java.util.concurrent.Callable%29">static <T> T callAs(Subject subject, Callable<T> action) throws CompletionException</a> (replacement for <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/javax/security/auth/Subject.html#doAs%28javax.security.auth.Subject,java.security.PrivilegedAction%29">static <T> T doAs(Subject subject, PrivilegedAction<T> action)</a> and <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/javax/security/auth/Subject.html#doAs%28javax.security.auth.Subject,java.security.PrivilegedExceptionAction%29">static <T> T doAs(Subject subject, PrivilegedExceptionAction<T> action) throws PrivilegedActionException</a>)</li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/security/KeyStore.html">KeyStore</a> got one new method, in scope of <a href="https://bugs.openjdk.java.net/browse/JDK-8225181">JDK-8225181</a>:</p>
<ul>
<li>
<a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/security/KeyStore.html#getAttributes%28java.lang.String%29">final Set<KeyStore.Entry.Attribute> getAttributes(String alias) throws KeyStoreException</a>
</li></ul>
</li>
<li><p>And <a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/security/KeyStoreSpi.html">KeyStoreSpi</a> as well:</p>
<ul>
<li>
<a href="https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/security/KeyStoreSpi.html#engineGetAttributes%28java.lang.String%29">Set<KeyStore.Entry.Attribute> engineGetAttributes(String alias)</a>
</li></ul>
</li>
</ul>
<p>And while we are enjoying the perils of <a href="https://jdk.java.net/18/">JDK-18</a>, the early access builds of <a href="https://jdk.java.net/19/">JDK-19</a> are already available. There are not much details of what is coming just yet, but may be <a href="https://openjdk.java.net/projects/loom/">Project Loom</a> would finally make its appearance in some form or another? Who knows ...</p>
<div class="separator" style="clear: both;"><a href="https://raw.githubusercontent.com/reta/reta.github.io/master/i-stand-with-ukraine-profile-picture-picjumbo-com.jpg" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="200" data-original-height="800" data-original-width="800" src="https://raw.githubusercontent.com/reta/reta.github.io/master/i-stand-with-ukraine-profile-picture-picjumbo-com.jpg"/></a></div>
<p>Peace to everyone!</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-19710092709221521192022-01-20T17:02:00.000-08:002022-01-20T17:02:12.529-08:00So you want to expose your JAX-RS services over HTTP/2 ...<p>Nonetheless <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> is about six years old (already!), and <a href="https://en.wikipedia.org/wiki/HTTP/3">HTTP/3</a> is around the corner, it looks like the majority of the web applications and systems are stuck in time, operating over <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.x</a> protocol. And we are not even talking about legacy systems, it is not difficult to stumble upon greenfield web applications that ignore the existence of the <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> in principle. A few years ago the excuses like "immature <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> support by container of my choice" might have been justified but these days all the major web containers (<a href="https://www.eclipse.org/jetty/">Jetty</a>, <a href="https://tomcat.apache.org/">Apache Tomcat</a>, <a href="https://netty.io/">Netty</a>, <a href="https://undertow.io/">Undertow</a>) offer a first class <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> support, so why not use it?</p>
<p>The today's post is all about exposing and consuming your <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS</a> services over <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> protocol using latest <a href="https://cxf.apache.org/download.html">3.5.0</a> release of the <a href="https://cxf.apache.org/">Apache CXF</a> framework, a compliant <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS 2.1</a> implementation. Although <a href="https://http2.github.io/faq/#does-http2-require-encryption">HTTP/2 does not require encryption</a>, it is absolutely necessary these days for deploying real-world production systems. With that being said, we are going to cover both options: <b>h2c</b> (<a href="https://http2.github.io/">HTTP/2</a> over clear text, useful for development) and regular <b>h2</b> (<a href="https://http2.github.io/">HTTP/2</a> over <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a>).</p>
<p>Our <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS</a> resource, <b>PeopleResource</b>, exposes only one <b>@GET</b> endpoint with hardcoded response specification (to keeps things simple here): </p>
<pre class="brush:java">
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.example.model.Person;
import reactor.core.publisher.Flux;
@Path("/people")
public class PeopleResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Flux<Person> getPeople() {
return Flux.just(new Person("a@b.com", "Tom", "Knocker"));
}
}
</pre>
<p>The usage of <a href="https://www.reactive-streams.org/">reactive types</a> (<a href="https://projectreactor.io/">Project Reactor</a> in this case) is intentional here since this is most likely what you are going to end up with (but to be fair, not a requirement).</p>
<pre class="brush:xml">
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.14</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-reactor</artifactId>
<version>3.5.0</version>
</dependency>
</pre>
<p>To be noted, other options like <a href="https://reactivex.io/RxJava/3.x/javadoc/">RxJava3</a> / <a href="https://reactivex.io/RxJava/2.x/javadoc/">RxJava2</a> are also <a href="https://cxf.apache.org/docs/jax-rs-rxjava.html">available out of the box</a>. The <b>Person</b> model is as straightforward as it gets: </p>
<pre class="brush:java">
public class Person {
private String email;
private String firstName;
private String lastName;
// Getters and setters here
}
</pre>
<p>To benefit from <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> support, you need to pick your web server/container (<a href="https://www.eclipse.org/jetty/">Jetty</a>, <a href="https://netty.io/">Netty</a>, or <a href="https://undertow.io/">Undertow</a>) and (optionally) include a couple of additional dependencies (which might be specific to server/container and/or JDK version you are using). The <a href="https://cxf.apache.org/docs/http2-support.html">official documentation</a> has it covered in great details, for demonstration purposes we are going to use <a href="https://www.eclipse.org/jetty/">Jetty</a> (<b>9.4.44.v20210927</b>) and run on <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a>, the latest LTS version of the <a href="https://openjdk.java.net/">OpenJDK</a>.</p>
<pre class="brush:xml">
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-server</artifactId>
<version>9.4.44.v20210927</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-server</artifactId>
<version>9.4.44.v20210927</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-java-server</artifactId>
<version>9.4.44.v20210927</version>
</dependency>
</pre>
<p><a href="https://cxf.apache.org/">Apache CXF</a> lets you package and run your services as standalone executable JARs (or <a href="https://www.graalvm.org/">GraalVM</a>'s <a href="https://cxf.apache.org/docs/graalvm-support.html">native images</a> in certain cases), no additional frameworks required besides the main class.</p>
<pre class="brush:java">
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.apache.cxf.jaxrs.utils.JAXRSServerFactoryCustomizationUtils;
import org.apache.cxf.transport.http.HttpServerEngineSupport;
import com.example.rest.PeopleResource;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
public class ServerStarter {
public static void main( final String[] args ) throws Exception {
final Bus bus = BusFactory.getDefaultBus();
bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
final JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
bean.setResourceClasses(PeopleResource.class);
bean.setResourceProvider(PeopleResource.class,
new SingletonResourceProvider(new PeopleResource()));
bean.setAddress("http://localhost:19091/services");
bean.setProvider(new JacksonJsonProvider());
bean.setBus(bus);
JAXRSServerFactoryCustomizationUtils.customize(bean);
Server server = bean.create();
server.start();
}
}
</pre>
<p>The key configuration here is <b>HttpServerEngineSupport.ENABLE_HTTP2</b> property which has to be set to <b>true</b> in order to notify the transport provider of your choice to turn <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> support on. Without <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a> configuration your <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS</a> resources become accessible over <b>h2c</b> (<a href="https://http2.github.io/">HTTP/2</a> over clear text), additionally to <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.1</a>. Let us give it a try right away (please make sure your have <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a> available by default).</p>
<pre>
$ mvn clean package
$ java -jar target/jaxrs-standalone-jetty-http2-0.0.1-SNAPSHOT-h2c.jar
[INFO] 2022-01-16 11:11:16.255 org.apache.cxf.endpoint.ServerImpl -[] Setting the server's publish address to be http://localhost:19091/services
[INFO] 2022-01-16 11:11:16.322 org.eclipse.jetty.util.log -[] Logging initialized @482ms to org.eclipse.jetty.util.log.Slf4jLog
[INFO] 2022-01-16 11:11:16.361 org.eclipse.jetty.server.Server -[] jetty-9.4.44.v20210927; built: 2021-09-27T23:02:44.612Z; git: 8da83308eeca865e495e53ef315a249d63ba9332; jvm 17+35-2724
[INFO] 2022-01-16 11:11:16.449 o.e.jetty.server.AbstractConnector -[] Started ServerConnector@3f4faf53{HTTP/1.1, (h2c, http/1.1)}{localhost:19091}
[INFO] 2022-01-16 11:11:16.449 org.eclipse.jetty.server.Server -[] Started @613ms
[WARN] 2022-01-16 11:11:16.451 o.e.j.server.handler.ContextHandler -[] Empty contextPath
[INFO] 2022-01-16 11:11:16.466 o.e.j.server.handler.ContextHandler -[] Started o.e.j.s.h.ContextHandler@495ee280{/,null,AVAILABLE}
...
</pre><p>It is as simple as that, if you don't believe it, <a href="https://www.eclipse.org/jetty/">Jetty</a> dumps quite handy message in the console regarding the supported protocols: <code>{HTTP/1.1, (h2c, http/1.1)}</code>. The Swiss army knife of the web developer, <a href="https://curl.se/">curl</a>, is the easiest way to verify things are working as expected.</p>
<pre>
$ curl http://localhost:19091/services/people --http2 -iv
...
* Connected to localhost (127.0.0.1) port 19091 (#0)
> GET /services/people HTTP/1.1
> Host: localhost:19091
> User-Agent: curl/7.71.1
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQCAAAAAAIAAAAA
...
* Mark bundle as not supporting multiuse
< HTTP/1.1 101 Switching Protocols
* Received 101
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< server: Jetty(9.4.44.v20210927)
< date: Sun, 16 Jan 2022 17:08:08 GMT
< content-type: application/json
< content-length: 60
...
HTTP/1.1 101 Switching Protocols
HTTP/2 200
server: Jetty(9.4.44.v20210927)
date: Sun, 16 Jan 2022 17:08:08 GMT
content-type: application/json
content-length: 60
[{"email":"a@b.com","firstName":"Tom","lastName":"Knocker"}]
</pre>
<p>There is something interesting happening here. Nonetheless we have asked for <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a>, the client connects over <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.1</a> first and only than switches the protocol (<code>HTTP/1.1 101 Switching Protocols</code>) to <a href="https://http2.github.io/">HTTP/2</a>. This is expected for <a href="https://http2.github.io/">HTTP/2</a> over clear text (<b>h2c</b>), however we could use <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> prior knowledge to skip over the protocol upgrade steps.</p>
<pre>
$ curl http://localhost:19091/services/people --http2-prior-knowledge -iv
...
* Connected to localhost (127.0.0.1) port 19091 (#0)
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x274df30)
> GET /services/people HTTP/2
> Host: localhost:19091
> user-agent: curl/7.71.1
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< server: Jetty(9.4.44.v20210927)
< date: Sun, 16 Jan 2022 17:06:40 GMT
< content-type: application/json
< content-length: 60
...
HTTP/2 200
server: Jetty(9.4.44.v20210927)
date: Sun, 16 Jan 2022 17:06:40 GMT
content-type: application/json
content-length: 60
[{"email":"a@b.com","firstName":"Tom","lastName":"Knocker"}]
</pre>
<p>Configuring <a href="https://http2.github.io/">HTTP/2</a> over <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a> requires just a bit more efforts to setup the certificates and key managers (we are using self-signed certificates issued to <code>localhost</code>, please check <a href="https://aredko.blogspot.com/2015/10/creating-sample-https-server-for-fun.html">Creating sample HTTPS server for fun and profit</a> if you are curious how to generate your own): </p>
<pre class="brush:java">
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.configuration.jsse.TLSParameterJaxBUtils;
import org.apache.cxf.configuration.jsse.TLSServerParameters;
import org.apache.cxf.configuration.security.KeyManagersType;
import org.apache.cxf.configuration.security.KeyStoreType;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.apache.cxf.jaxrs.utils.JAXRSServerFactoryCustomizationUtils;
import org.apache.cxf.transport.http.HttpServerEngineSupport;
import org.apache.cxf.transport.http_jetty.JettyHTTPServerEngineFactory;
import com.example.rest.PeopleResource;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
public class TlsServerStarter {
public static void main( final String[] args ) throws Exception {
final Bus bus = BusFactory.getDefaultBus();
bus.setProperty(HttpServerEngineSupport.ENABLE_HTTP2, true);
final KeyStoreType keystore = new KeyStoreType();
keystore.setType("JKS");
keystore.setPassword("strong-passw0rd-here");
keystore.setResource("certs/server.jks");
final KeyManagersType kmt = new KeyManagersType();
kmt.setKeyStore(keystore);
kmt.setKeyPassword("strong-passw0rd-here");
final TLSServerParameters parameters = new TLSServerParameters();
parameters.setKeyManagers(TLSParameterJaxBUtils.getKeyManagers(kmt));
final JettyHTTPServerEngineFactory factory = new JettyHTTPServerEngineFactory(bus);
factory.setTLSServerParametersForPort("localhost", 19091, parameters);
final JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
bean.setResourceClasses(PeopleResource.class);
bean.setResourceProvider(PeopleResource.class,
new SingletonResourceProvider(new PeopleResource()));
bean.setAddress("https://localhost:19091/services");
bean.setProvider(new JacksonJsonProvider());
bean.setBus(bus);
JAXRSServerFactoryCustomizationUtils.customize(bean);
Server server = bean.create();
server.start();
}
}
</pre><p>Now if we repeat the experiment, the results are going to be quite different.</p>
<pre>
$ mvn clean package
$ java -jar target/jaxrs-standalone-jetty-http2-0.0.1-SNAPSHOT-h2.jar
[INFO] 2022-01-17 19:06:37.481 org.apache.cxf.endpoint.ServerImpl -[] Setting the server's publish address to be https://localhost:19091/services
[INFO] 2022-01-17 19:06:37.536 org.eclipse.jetty.util.log -[] Logging initialized @724ms to org.eclipse.jetty.util.log.Slf4jLog
[INFO] 2022-01-17 19:06:37.576 org.eclipse.jetty.server.Server -[] jetty-9.4.44.v20210927; built: 2021-09-27T23:02:44.612Z; git: 8da83308eeca865e495e53ef315a249d63ba9332; jvm 17+35-2724
[INFO] 2022-01-17 19:06:37.749 o.e.jetty.server.AbstractConnector -[] Started ServerConnector@163370c2{ssl, (ssl, alpn, h2, http/1.1)}{localhost:19091}
[INFO] 2022-01-17 19:06:37.749 org.eclipse.jetty.server.Server -[] Started @937ms
[WARN] 2022-01-17 19:06:37.752 o.e.j.server.handler.ContextHandler -[] Empty contextPath
[INFO] 2022-01-17 19:06:37.772 o.e.j.server.handler.ContextHandler -[] Started o.e.j.s.h.ContextHandler@403f0a22{/,null,AVAILABLE}
...
</pre>
<p>The list of the supported protocols listed by <a href="https://www.eclipse.org/jetty/">Jetty</a> includes few newcomers: <code>{ssl, (ssl, alpn, h2, http/1.1)}</code>. The presence of <a href="https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation">ALPN</a> (<a href="https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation">Application-Layer Protocol Negotiation</a>) is very important as it allows the application layer to negotiate which protocol should be selected over a <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a> connection. Without further ado, let us see that in action.</p>
<pre>
$ curl https://localhost:19091/services/people --http2 -k
* Connected to localhost (127.0.0.1) port 19091 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
...
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=XX; ST=XX; L=XX; O=XX; CN=localhost
* start date: Jan 18 00:16:42 2022 GMT
* expire date: Nov 7 00:16:42 2024 GMT
* issuer: C=XX; ST=XX; L=XX; O=XX; CN=localhost
* SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
...
> GET /services/people HTTP/2
> Host: localhost:19091
> user-agent: curl/7.71.1
> accept: */*
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< server: Jetty(9.4.44.v20210927)
< date: Tue, 18 Jan 2022 00:19:20 GMT
< content-type: application/json
< content-length: 60
HTTP/2 200
server: Jetty(9.4.44.v20210927)
date: Tue, 18 Jan 2022 00:19:20 GMT
content-type: application/json
content-length: 60
[{"email":"a@b.com","firstName":"Tom","lastName":"Knocker"}]
</pre><p>As we can see, the client and server negotiated the protocols from the start and <a href="https://http2.github.io/">HTTP/2</a> has been picked, completely bypassing the <code>HTTP/1.1 101 Switching Protocols</code> dance we have seen before.</p><p>Hopefully things are looking exciting already, but to be fair, it is very likely that your are already hosting <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS</a> services inside applications powered by widely popular <a href="https://spring.io/projects/spring-boot">Spring Boot</a> framework. Wouldn't it be awesome to have <a href="https://http2.github.io/">HTTP/2</a> support right there? Absolutely, and in fact you don't need anything special from the <a href="https://cxf.apache.org/">Apache CXF</a> besides using the provided <a href="https://spring.io/projects/spring-boot">Spring Boot</a> starters.</p>
<pre class="brush:xml">
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
<version>3.5.0</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
</pre>
<p>The application configuration is minimal but still required (although <a href="https://issues.apache.org/jira/browse/CXF-8350">in future</a> it should be completely auto-configurable):</p>
<pre class="brush:java">
import org.apache.cxf.Bus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.utils.JAXRSServerFactoryCustomizationUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.example.rest.PeopleResource;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
@Configuration
public class AppConfig {
@Bean
public Server server(Bus bus, PeopleResource service) {
JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
bean.setBus(bus);
bean.setServiceBean(service);
bean.setProvider(new JacksonJsonProvider());
bean.setAddress("/");
JAXRSServerFactoryCustomizationUtils.customize(bean);
return bean.create();
}
}
</pre>
<p>Everything else, including <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a> configuration, is done through configuration properties, which are usually provided inside <b>application.yml</b> (or externalized altogether): </p>
<pre class="brush:java">
server:
port: 19091
http2:
enabled: true
---
spring:
config:
activate:
on-profile: h2
server:
ssl:
key-store: "classpath:certs/server.jks"
key-store-password: "strong-passw0rd-here"
key-password: "strong-passw0rd-here"
</pre><p>The <a href="https://http2.github.io/">HTTP/2</a> protocol is enabled by setting <a href="https://docs.spring.io/spring-boot/docs/2.6.x/reference/html/howto.html#howto.webserver.configure-http2">server.http2.enabled</a> configuration property to <code>true</code>, the <a href="https://cxf.apache.org/">Apache CXF</a> is not involved in any way, it is solely offered by <a href="https://spring.io/projects/spring-boot">Spring Boot</a>. The <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS/SSL</a> is activated by <a href="https://docs.spring.io/">Spring</a> profile <code>h2</code>, otherwise it runs <a href="https://http2.github.io/">HTTP/2</a> over clear text.</p>
<pre>
$ java -jar target/jaxrs-spring-boot-jetty-http2-0.0.1-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.2)
[INFO] 2022-01-19 20:08:55.645 o.h.validator.internal.util.Version -[] HV000001: Hibernate Validator 6.2.0.Final
[INFO] 2022-01-19 20:08:55.646 com.example.ServerStarter -[] No active profile set, falling back to default profiles: default
[INFO] 2022-01-19 20:08:56.777 org.eclipse.jetty.util.log -[] Logging initialized @2319ms to org.eclipse.jetty.util.log.Slf4jLog
[INFO] 2022-01-19 20:08:57.008 o.s.b.w.e.j.JettyServletWebServerFactory -[] Server initialized with port: 19091
[INFO] 2022-01-19 20:08:57.011 org.eclipse.jetty.server.Server -[] jetty-9.4.44.v20210927; built: 2021-09-27T23:02:44.612Z; git: 8da83308eeca865e495e53ef315a249d63ba9332; jvm 17+35-2724
[INFO] 2022-01-19 20:08:57.052 o.e.j.s.h.ContextHandler.application -[] Initializing Spring embedded WebApplicationContext
[INFO] 2022-01-19 20:08:57.052 o.s.b.w.s.c.ServletWebServerApplicationContext -[] Root WebApplicationContext: initialization completed in 1352 ms
[INFO] 2022-01-19 20:08:57.237 org.eclipse.jetty.server.session -[] DefaultSessionIdManager workerName=node0
[INFO] 2022-01-19 20:08:57.238 org.eclipse.jetty.server.session -[] No SessionScavenger set, using defaults
[INFO] 2022-01-19 20:08:57.238 org.eclipse.jetty.server.session -[] node0 Scavenging every 660000ms
[INFO] 2022-01-19 20:08:57.245 org.eclipse.jetty.server.Server -[] Started @2788ms
[INFO] 2022-01-19 20:08:57.422 org.apache.cxf.endpoint.ServerImpl -[] Setting the server's publish address to be /
[INFO] 2022-01-19 20:08:58.038 o.e.j.s.h.ContextHandler.application -[] Initializing Spring DispatcherServlet 'dispatcherServlet'
[INFO] 2022-01-19 20:08:58.038 o.s.web.servlet.DispatcherServlet -[] Initializing Servlet 'dispatcherServlet'
[INFO] 2022-01-19 20:08:58.038 o.s.web.servlet.DispatcherServlet -[] Completed initialization in 0 ms
[INFO] 2022-01-19 20:08:58.080 o.e.jetty.server.AbstractConnector -[] Started ServerConnector@ee86bcb{HTTP/1.1, (http/1.1, h2c)}{0.0.0.0:19091}
[INFO] 2022-01-19 20:08:58.081 o.s.b.w.e.jetty.JettyWebServer -[] Jetty started on port(s) 19091 (http/1.1, h2c) with context path '/'
[INFO] 2022-01-19 20:08:58.093 com.example.ServerStarter -[] Started ServerStarter in 2.939 seconds (JVM running for 3.636)
...
</pre>
<p>The already familiar list of protocols appears in the console: <code>{HTTP/1.1, (http/1.1, h2c)}</code>. To activate <a href="https://http2.github.io/">HTTP/2</a> over <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a> we could pass <b>--spring.profiles.active=h2</b> command line argument, for example:</p>
<pre>
$ java -jar target/jaxrs-spring-boot-jetty-http2-0.0.1-SNAPSHOT.jar --spring.profiles.active=h2
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.2)
[INFO] 2022-01-19 20:13:17.999 com.example.ServerStarter -[] The following profiles are active: h2
[INFO] 2022-01-19 20:13:17.999 o.h.validator.internal.util.Version -[] HV000001: Hibernate Validator 6.2.0.Final
[INFO] 2022-01-19 20:13:19.124 org.eclipse.jetty.util.log -[] Logging initialized @2277ms to org.eclipse.jetty.util.log.Slf4jLog
[INFO] 2022-01-19 20:13:19.368 o.s.b.w.e.j.JettyServletWebServerFactory -[] Server initialized with port: 19091
[INFO] 2022-01-19 20:13:19.398 org.eclipse.jetty.server.Server -[] jetty-9.4.44.v20210927; built: 2021-09-27T23:02:44.612Z; git: 8da83308eeca865e495e53ef315a249d63ba9332; jvm 17+35-2724
[INFO] 2022-01-19 20:13:19.433 o.e.j.s.h.ContextHandler.application -[] Initializing Spring embedded WebApplicationContext
[INFO] 2022-01-19 20:13:19.433 o.s.b.w.s.c.ServletWebServerApplicationContext -[] Root WebApplicationContext: initialization completed in 1380 ms
[INFO] 2022-01-19 20:13:19.618 org.eclipse.jetty.server.session -[] DefaultSessionIdManager workerName=node0
[INFO] 2022-01-19 20:13:19.618 org.eclipse.jetty.server.session -[] No SessionScavenger set, using defaults
[INFO] 2022-01-19 20:13:19.619 org.eclipse.jetty.server.session -[] node0 Scavenging every 660000ms [INFO] 2022-01-19 20:13:19.626 org.eclipse.jetty.server.Server -[] Started @2779ms
[INFO] 2022-01-19 20:13:19.823 org.apache.cxf.endpoint.ServerImpl -[] Setting the server's publish address to be /
[INFO] 2022-01-19 20:13:20.394 o.e.j.s.h.ContextHandler.application -[] Initializing Spring DispatcherServlet 'dispatcherServlet'
[INFO] 2022-01-19 20:13:20.394 o.s.web.servlet.DispatcherServlet -[] Initializing Servlet 'dispatcherServlet'
[INFO] 2022-01-19 20:13:20.395 o.s.web.servlet.DispatcherServlet -[] Completed initialization in 1 ms
[INFO] 2022-01-19 20:13:20.775 o.e.jetty.server.AbstractConnector -[] Started SslValidatingServerConnector@7e3181aa{SSL, (ssl, alpn, h2, http/1.1)}{0.0.0.0:19091}
[INFO] 2022-01-19 20:13:20.776 o.s.b.w.e.jetty.JettyWebServer -[] Jetty started on port(s) 19091 (ssl, alpn, h2, http/1.1) with context path '/'
[INFO] 2022-01-19 20:13:20.786 com.example.ServerStarter -[] Started ServerStarter in 3.285 seconds (JVM running for 3.939)
...
</pre>
<p>And we see <code>{SSL, (ssl, alpn, h2, http/1.1)}</code> this time around. If you would like to repeat the experiment with the <a href="https://curl.se/">curl</a> commands we executed before, feel free to do so, the observed results should be the same. It is worth mentioning that along with <a href="https://www.eclipse.org/jetty/">Jetty</a>, <a href="https://spring.io/projects/spring-boot">Spring Boot</a> bakes first-class support for <a href="https://tomcat.apache.org/">Apache Tomcat</a>, <a href="https://netty.io/">Netty</a> (<a href="https://projectreactor.io/docs/netty/release/reference/index.html">Reactor Netty</a> to be precise) and <a href="https://undertow.io/">Undertow</a>.</p>
<p>Huh, quite likely you are now convinced that <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> is pretty well supported these days and it is here for you to take advantage of. We have seen <a href="https://spring.io/projects/spring-boot">Spring Boot</a> and <a href="https://cxf.apache.org/">Apache CXF</a> in action, but <a href="https://quarkus.io">Quarkus</a>, <a href="https://micronaut.io">Micronaut</a>, <a href="https://helidon.io/">Helidon</a> (and many others) are on a par with <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> support, enjoy!</p>
<p>The complete project sources are available on <a href="https://github.com/reta/jaxrs-http2">Github</a>.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-54969183973959318692021-11-26T14:02:00.002-08:002021-11-26T14:02:39.691-08:00The final days of finalizers in Java<p>If you are developing in Java long enough, you are surely aware of the <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#finalize--">Object::finalize()</a> method and the concept of the finalizers in general.</p>
<pre class="brush:java">
protected void finalize() throws Throwable
</pre>
<blockquote>
<pre>
...
</pre>
Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup.
<pre>
...
</pre>
</blockquote>
<p>This reasonably good idea has gotten quite bad reputation over the years and definitely is one of the worst nightmares of the JVM developers. There are enough details and horror stories on the web related to finalizers (who did not implement it at least once?), but the end is near: <a href="https://openjdk.java.net/jeps/421">JEP-421: Deprecate Finalization for Removal</a>, proposed to become a part of the <a href="https://openjdk.java.net/projects/jdk/18/">JDK-18</a> release, kicks off the process to phase finalizers out.</p>
<p>In this rather short post we are going to talk about <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.html">java.lang.ref.Cleaner</a>, the mechanism alternative to the finalizers, which allows to perform the cleaning actions once the corresponding object instance becomes phantom reachable. It was introduced by <a href="https://bugs.openjdk.java.net/browse/JDK-8138696">JDK-8138696</a> and is available starting from <a href="https://openjdk.java.net/projects/jdk9/">JDK-9</a> and onwards.</p>
<p>The usage of <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.html">java.lang.ref.Cleaner</a> is pretty straightforward (although <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.html#create-java.util.concurrent.ThreadFactory-">advanced usage scenarios</a> are also possible):
<ul>
<li>obtain the instance of <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.html">java.lang.ref.Cleaner</a> using a suitable <code>Cleaner.create()</code> factory method</li>
<li>register a cleanup action (and access it by means of <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.Cleanable.html">Cleaner.Cleanable</a> instance if necessary)</li>
</ul>
</p>
<p>To illustrate it, let us consider a quick example. Assume we have been designing a <b>ResourceAccessor</b> class that accesses (or even allocates) some resources (possibly, natives ones, which are outside of the JVM control).</p>
<pre class="brush:java">
import java.lang.ref.Cleaner;
public class ResourceAccessor implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
private final Cleaner.Cleanable cleanable;
private final Resource resource;
public ResourceAccessor() {
this.resource = new Resource();
this.cleanable = cleaner.register(this, cleaner(resource));
}
@Override
public void close() throws Exception {
cleanable.clean();
}
private static Runnable cleaner(Resource resource) {
return () -> {
// Perform cleanup actions
resource.release();
};
}
}
</pre>
<p>The <b>ResourceAccessor</b> allocates the resource and registers the cleanup action on construction, keeping the reference to <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.Cleanable.html">Cleaner.Cleanable</a> instance. It also implements <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/AutoCloseable.html">AutoCloseable</a> interface and the <b>close()</b> method just delegates to the <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.Cleanable.html#clean--">Cleaner.Cleanable::clean</a>.</p>
<p>Please notice that cleanup action <b>should not hold any references</b> to the object instance registered for cleanup, otherwise that instance will not become phantom reachable and the cleaning action will not be invoked automatically. This is why we have wrapped the <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html">lamdba expression</a> behind the cleanup action inside the static method. Alternatively, usage of the standalone class or <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html">static nested class</a> that implements <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Runnable.html">Runnable</a> is also possible. Essentially, this is all we need!</p>
<p>Although <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.html">java.lang.ref.Cleaner</a>s provides better and safer alternative to the finalizers, you should not overuse them. The <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/AutoCloseable.html">AutoCloseable</a> and <a href="https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html">try-with-resources</a> idiom should be the preferred approach to manage resources in the majority of situations.</p>
<pre class="brush:java">
try (final ResourceAccessor resource = new ResourceAccessor()) {
// Safely use the resource
}
</pre>
<p>In these rare conditions when the lifespan of the resource is not well defined, the <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.html">java.lang.ref.Cleaner</a> is here to help. If you are curious how JDK is using <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.html">java.lang.ref.Cleaner</a> internally, please take a look at, for example, <a href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/Timer.java">java.util.Timer</a> or <a href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java">sun.nio.ch.NioSocketImpl</a> classes.</p>
Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-86811782818979066552021-09-30T14:05:00.000-07:002021-09-30T14:05:07.624-07:00Chasing Java's release train: JDK-17, the next big thing<p>Here we go, <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a>, the next LTS release, just became generally available. It is an important milestone for the <a href="https://openjdk.java.net/">OpenJDK</a> for years to come but sadly, <a href="https://openjdk.java.net/projects/loom/">Project Loom</a>, the most anticipated improvement of the JVM platform, was not able to make it, despite the extraordinary progress being made. Well, if you are still on <a href="https://www.oracle.com/java/technologies/javase/8all-relnotes.html">JDK-8</a>, like the <a href="https://www.jetbrains.com/lp/devecosystem-2021/java/">majority of us</a>, who cares, right?</p>
<p>Not really, for example <a href="https://spring.io/">Spring Framework</a> had made an announcement quite recently to support <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a> as a baseline. It is very likely <a href="https://camel.apache.org/blog/2021/09/eol-java8/">others</a> will follow, leaving <a href="https://www.oracle.com/java/technologies/javase/8all-relnotes.html">JDK-8</a> behind. So what is new in <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a>?</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/306">JEP-306: Restore Always-Strict Floating-Point Semantics</a>: makes floating-point operations consistently strict, rather than have both strict floating-point semantics (<code>strictfp</code>) and subtly different default floating-point semantics. This will restore the original floating-point semantics to the language and VM, matching the semantics before the introduction of strict and default floating-point modes in Java SE 1.2.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/382">JEP-382: New macOS Rendering Pipeline</a>: implements a Java 2D internal rendering pipeline for macOS using the <a href="https://developer.apple.com/metal/">Apple Metal API</a> as alternative to the existing pipeline, which uses the deprecated Apple OpenGL API.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/391">JEP-391: macOS/AArch64 Port</a>: ports the JDK to macOS/AArch64. Quick refresher, an AArch64 port for Linux was delivered in <a href="https://jdk.java.net/java-se-ri/9">JDK-9</a> (<a href="https://openjdk.java.net/jeps/237">JEP-237</a>) and for Windows (<a href="https://openjdk.java.net/jeps/388">JEP-388</a>) in <a href="https://openjdk.java.net/projects/jdk/16/">JDK-16</a>.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/403">JEP-403: Strongly Encapsulate JDK Internals</a>: strongly encapsulates all internal elements of the JDK, except for <a href="https://openjdk.java.net/jeps/260#Description">critical internal APIs</a> such as <code>sun.misc.Unsafe</code>. It will no longer be possible to relax the strong encapsulation of internal elements via a single command-line option, as was possible in <a href="https://jdk.java.net/java-se-ri/9">JDK-9</a> through <a href="https://openjdk.java.net/projects/jdk/16/">JDK-16</a>. This JEP is logical continuation of the <a href="https://openjdk.java.net/jeps/396">JEP-396</a>, and pushes things even further:</p>
<ul>
<li>The <code>sun.misc.Unsafe::defineAnonymousClass</code> method was removed. The replacements are <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClass%28byte%5B%5D,boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...%29">java.lang.invoke.MethodHandles.Lookup::defineHiddenClass</a> or <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClassWithClassData%28byte%5B%5D,java.lang.Object,boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...%29">java.lang.invoke.MethodHandles.Lookup::defineHiddenClassWithClassData</a>, see please <a href="https://bugs.openjdk.java.net/browse/JDK-8266760">JDK-8266760</a> for more details.</li>
<li>The mechanism of the relaxed strong encapsulation, controlled by command line option <code>--illegal-access</code> becomes obsolete. Any use of this option, whether with <code>permit</code>, <code>warn</code>, <code>debug</code>, or <code>deny</code>, will have no effect other than to issue a warning message.</li>
</ul>
</li>
<li><p><a href="https://openjdk.java.net/jeps/409">JEP-409: Sealed Classes</a>: enhances the Java programming language with <a href="https://cr.openjdk.java.net/~briangoetz/amber/datum.html">sealed classes and interfaces</a>. Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them. The JEP introduces new <code>sealed</code> / <code>non-sealed</code> modifiers and <code>permits</code> clause to the class or interface declaration.</p>
<pre class="brush:java">
public abstract sealed class Result permits Success<?>, Failure {
// ...
}
</pre>
<p>The classes specified by <code>permits</code> clause must be located near the superclass: either in the same module (if the superclass is in a named module) or in the same package (if the superclass is in the unnamed module). Every permitted subclass must directly extend the sealed class and must use a modifier to describe how it propagates the sealing initiated by its superclass: <code>final</code>, <code>sealed</code>, or <code>non-sealed</code>.
</p>
<pre class="brush:java">
public final class Failure extends Result {
public Throwable getCause() {
// ...
}
}
</pre>
<pre class="brush:java">
public final class Success<T> extends Result {
public T getResult() {
// ...
}
}
</pre>
<p>Interestingly, there is more compact way to declare sealed class hierarchy in the same source file. This way, the sealed class may omit the <code>permits</code> clause, the permitted subclasses are going to be inferred by the compiler.</p>
<pre class="brush:java">
public abstract sealed class Result {
public final class Success<T> extends Result {
public T getResult() {
// ...
}
}
public final class Failure extends Result {
public Throwable getCause() {
// ...
}
}
}
</pre>
<p>As for classes, an interface can be sealed by applying the <code>sealed</code> modifier. </p>
<pre class="brush:java">
public sealed interface Result permits Success<?>, Failure {
// ...
}
</pre>
<pre class="brush:java">
public final class Failure implements Result {
public Throwable getCause() {
// ...
}
}
</pre>
<pre class="brush:java">
public final class Success<T> implements Result {
public T getResult() {
// ...
}
}
</pre>
<p>Last but not least, sealed classes work well with <a href="https://docs.oracle.com/javase/specs/jls/se16/html/jls-8.html#jls-8.10">record classes</a>, which are implicitly <code>final</code>:</p>
<pre class="brush:java">
public record Failure(Throwable cause) implements Result {}
</pre>
<pre class="brush:java">
public record Success<T>(T result) implements Result {}
</pre>
<p>The benefits of sealed classes are two-fold. They allow to express <a href="https://en.wikipedia.org/wiki/Algebraic_data_type">algebraic data types</a> (ADTs) in Java as well as to enhance pattern matching for <code>switch</code> expressions in future JDKs.</p>
<p>The <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html">java.lang.Class</a> API has been enriched with additional methods to support sealed classes:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#getPermittedSubclasses%28%29">Class<?>[] getPermittedSubclasses()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#isSealed%28%29">boolean isSealed(</a>)</li>
</ul>
<p>One new method has been added to <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.compiler/javax/lang/model/element/TypeElement.html">TypeElement</a> class as well:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.compiler/javax/lang/model/element/TypeElement.html#getPermittedSubclasses%28%29">default List<? extends TypeMirror> getPermittedSubclasses(</a>)</li>
</ul>
<p>To be noted, sealed classes are not the compiler trick. The JVM recognizes sealed classes and interfaces at runtime, and prevents extension by unauthorized subclasses and subinterfaces.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/356">JEP-356: Enhanced Pseudo-Random Number Generators</a>: provides new interface types and implementations for pseudorandom number generators (PRNGs), including jumpable PRNGs and an additional class of splittable PRNG algorithms (LXM).</p>
<p>The standard library contains <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Random.html">Random</a> / <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/SecureRandom.html">SecureRandom</a>, <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ThreadLocalRandom.html">ThreadLocalRandom</a>, and <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/SplittableRandom.html">SplittableRandom</a> classes to deal with random number generation. All support pretty much the same set of methods however there are no relationships between them. That makes it difficult to abstract the random number generation logic. To address that, JEP provides a new interface, <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/random/RandomGenerator.html">RandomGenerator</a>, which supplies a uniform API for all existing and new PRNGs.</p>
<p>To learn more about the implementation details and new class hierarchy, I would highly recommend to check out excellent <a href="https://nipafx.dev/java-random-generator/">Better Random Number Generation in Java 17</a> article.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/415">JEP-415: Context-Specific Deserialization Filters</a>: allows applications to configure context-specific and dynamically-selected deserialization filters via a JVM-wide filter factory that is invoked to select a filter for each individual deserialization operation. The JEP is an improvement upon <a href="https://openjdk.java.net/jeps/290">JEP-290</a>, which introduced deserialization filters into JVM platform.</p>
<p>From the API standpoint, the <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/ObjectInputFilter.html">java.io.ObjectInputFilter</a> class had been extended to accommodate the required changes:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/ObjectInputFilter.html#allowFilter%28java.util.function.Predicate,java.io.ObjectInputFilter.Status%29">static ObjectInputFilter allowFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus)</a></li>
<li><a href="rejectFilter%28Predicate%3CClass%3C?%3E%3E%20predicate,%20ObjectInputFilter.Status%20otherStatus%29">static ObjectInputFilter rejectFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/ObjectInputFilter.html#merge%28java.io.ObjectInputFilter,java.io.ObjectInputFilter%29">static ObjectInputFilter merge(ObjectInputFilter filter, ObjectInputFilter anotherFilter)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/ObjectInputFilter.html#rejectUndecidedClass%28java.io.ObjectInputFilter%29">static ObjectInputFilter rejectUndecidedClass(ObjectInputFilter filter)</a></li>
</ul>
</li>
<li><p><a href="https://openjdk.java.net/jeps/398">JEP-398: Deprecate the Applet API for Removal</a>: deprecates the <a href="https://cr.openjdk.java.net/~iris/se/17/latestSpec/api/java.desktop/java/applet/package-summary.html">Applet API</a> for removal. It is essentially irrelevant since all web-browser vendors have either removed support for Java browser plug-ins or announced plans to do so.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/407">JEP-407: Remove RMI Activation</a>: removes the Remote Method Invocation (RMI) Activation mechanism, while preserving the rest of RMI. It was deprecated for removal <a href="https://openjdk.java.net/projects/jdk/15/">JDK-15</a> (<a href="https://openjdk.java.net/jeps/385">JEP-385</a>) and completely gone in <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a>.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/410">JEP-410: Remove the Experimental AOT and JIT Compiler</a>: removes the experimental Java-based ahead-of-time (AOT) and just-in-time (JIT) compiler. This compiler has seen little use since its introduction and the effort required to maintain it is significant. Retain the experimental Java-level JVM compiler interface (<a href="https://openjdk.java.net/jeps/243">JVMCI</a>) so that developers can continue to use externally-built versions of the compiler for JIT compilation (for example, <a href="https://www.graalvm.org/">GraalVM</a>).</p></li>
<li><p><a href="https://openjdk.java.net/jeps/411">JEP-411: Deprecate the Security Manager for Removal</a>: deprecate the <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/SecurityManager.html">Security Manager</a> for removal in a future release. The <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/SecurityManager.html">Security Manager</a> dates from Java 1.0. It has not been the primary means of securing client-side Java code for many years, and it has rarely been used to secure server-side code. To move Java forward, we intend to deprecate the <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/SecurityManager.html">Security Manager</a> for removal in concert with the legacy <a href="https://cr.openjdk.java.net/~iris/se/17/latestSpec/api/java.desktop/java/applet/package-summary.html">Applet API</a> (<a href="https://openjdk.java.net/jeps/398">JEP-398</a>).</p>
<p>This is probably the most debated and controversial change so far since <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/SecurityManager.html">Security Manager</a> is being used more widely than it was claimed. Apparently, the <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/SecurityManager.html">Security Manager</a> popped up as a real showstopper for <a href="https://openjdk.java.net/projects/loom/">Project Loom</a>, spawning <a href="https://inside.java/2021/04/23/security-and-sandboxing-post-securitymanager/">Security and Sandboxing Post SecurityManager</a> to justify the decision. </p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8251989">JDK-8251989: Hex formatting and parsing utility</a>. Introduces new utility class <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/HexFormat.html">java.util.HexFormat</a> that converts between bytes and chars and hex-encoded strings which may include additional formatting markup such as prefixes, suffixes, and delimiters. <a href="https://www.blogger.com/profile/10790950138196529391">Dustin Marx</a> has done a great job <a href="http://marxsoftware.blogspot.com/2020/12/jdk17-hex-formatting-parsing.html">illustrating this new API usage</a>.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8235139">JDK-8235139: Deprecate the socket impl factory mechanism</a>: proposes to deprecate, and eventually remove, the API points for statically configuring a system-wide factory for the socket types in the <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/package-summary.html">java.net</a> package.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8266369">JDK-8266369: Add a new Selector implementation for the Windows platform based on the "wepoll" library.</a> The main benefit is that the cost of selection operations are a function of the number of sockets that are ready for I/O rather than the number of channels registered with the <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/Selector.html">Selector</a>.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8267077">JDK-8267077: Unified Logging Supports Asynchronous Log Flushing</a>. To avoid undesirable delays in a thread using unified logging, it is now possible to request the unified logging system to operate in asynchronous mode by passing the command-line option <code>-Xlog:async</code>. In asynchronous logging mode, log sites enqueue all logging messages to a buffer and a dedicated thread is responsible for flushing them to the corresponding outputs. The intermediate buffer is bounded so in case of overflow, the enqueuing messages are discarded. The size of the intermediate buffer could be configured using the command-line option <code>-XX:AsyncLogBufferSize=<bytes></code>.</p></li>
<li><p>A whopping amount of improvements went into <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html">java.lang.invoke</a> package:</p>
<ul>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8263087">JDK-8263087: Add a MethodHandle combinator that switches over a set of MethodHandles</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8264288">JDK-8264288: Performance issue with MethodHandle.asCollector</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8255493">JDK-8255493: Support for pre-generated java.lang.invoke classes in CDS dynamic archive</a> (improves start-up time by <a href="https://wiki.openjdk.java.net/display/HotSpot/Support+for+pre-generated+java.lang.invoke+classes+in+CDS+archive">storing pre-generated LambdaForm handlers</a> in an application-specific CDS archive)</li>
</ul>
</li>
<li><p>A ton of goodies has been delivered from the standard library perspective. Let us start from <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ForkJoinPool.html">ForkJoinPool</a> class, which now provides dedicated implementation for:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/AbstractExecutorService.html#invokeAll%28java.util.Collection,long,java.util.concurrent.TimeUnit%29"><T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/AbstractExecutorService.html#invokeAny%28java.util.Collection%29"><T> T invokeAny(Collection<? extends Callable<T>> tasks)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/AbstractExecutorService.html#invokeAny%28java.util.Collection,long,java.util.concurrent.TimeUnit%29"><T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.Entry.html">java.util.Map.Entry</a> got quite useful utility method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.Entry.html#copyOf%28java.util.Map.Entry%29">static <K,V> Map.Entry<K,V> copyOf(Map.Entry<? extends K,? extends V> e)</a></li>
</ul></li>
<li><p>A new interface <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/InstantSource.html">java.time.InstantSource</a> was introduced, which provides access to the current instant and is implemented by <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Clock.html">java.time.Clock</a>.</p></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/DatagramSocket.html">java.net.DatagramSocket</a> has been enriched with better multicast group support:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/DatagramSocket.html#joinGroup%28java.net.SocketAddress,java.net.NetworkInterface%29">void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/DatagramSocket.html#leaveGroup%28java.net.SocketAddress,java.net.NetworkInterface%29">void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/MethodHandles.html">java.lang.invoke.MethodHandles</a> has one new method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/MethodHandles.html#tableSwitch%28java.lang.invoke.MethodHandle,java.lang.invoke.MethodHandle...%29">static MethodHandle tableSwitch(MethodHandle fallback, MethodHandle... targets)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/Console.html">java.io.Console</a> now returns the <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/charset/Charset.html">Charset</a> object used:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/Console.html#charset%28%29">Charset charset()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/FileInputStream.html">java.io.FileInputStream</a> introduced own implementations of:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/InputStream.html#readAllBytes%28%29">byte[] readAllBytes() throws IOException</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/InputStream.html#readAllBytes%28%29">byte[] readNBytes(int len) throws IOException</a></li>
</ul>
</li>
<li><p>Arguably, the largest amount of improvements went into <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Process.html">java.lang.Process</a> class:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Process.html#inputReader%28%29">BufferedReader inputReader()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Process.html#inputReader%28java.nio.charset.Charset%29">BufferedReader inputReader(Charset charset)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Process.html#errorReader%28%29">BufferedReader errorReader(</a>)</li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Process.html#errorReader%28java.nio.charset.Charset%29">BufferedReader errorReader(Charset charset)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Process.html#outputWriter%28%29">BufferedWriter outputWriter()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Process.html#outputWriter%28java.nio.charset.Charset%29">BufferedWriter outputWriter(Charset charset)</a></li>
</ul>
</li>
</ul>
<p>And what is happening on the <a href="https://seanjmullan.org/blog/2021/09/14/jdk17">security side</a> of things? Besides <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/SecurityManager.html">SecurityManager</a> deprecation (<a href="https://openjdk.java.net/jeps/411">JEP-411</a>), it is worth to mention:</p>
<ul>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8217633">JDK-8217633: Configurable extensions with system properties</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8196415">JDK-8196415: Disable SHA-1 Signed JARs</a></li>
<li>The default for security handshakes in <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a> is TLS 1.3 now, the previous versions of Java used TLS 1.2 (<a href="https://blogs.oracle.com/javamagazine/post/java-jdk-17-generally-available">more details here</a>)</li>
</ul>
<p><a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a> is an important release. First of all, it is going to become the de-facto choice for green field projects for the next couple of years. Second, the <a href="https://www.jrebel.com/blog/2021-java-technology-report">ones who are still on JDK-8</a> would migrate straight to <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a>, skipping <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a> entirely (there are hardly any reasons to migrate to <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a> first). Consequently, the third, migration from <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a> to <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a> requires comparatively low efforts (<a href="https://openjdk.java.net/jdk/17/jeps-since-jdk-11">in most cases</a>), it is logical to expect seeing the <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a> kicking out <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a>.</p>
<p>Are we done yet? Luckily, not at all, the JDK release train is accelerating, shifting from <a href="https://blogs.oracle.com/java/post/moving-the-jdk-to-a-two-year-lts-cadence">three years LTS releases to two years</a>. Hopefully, this strategic decision would speed up the pace of migration, specifically for enterprises, where many are stuck with older JDKs. </p>
<p>And let us not forget, the <a href="https://openjdk.java.net/projects/jdk/18/">JDK-18</a> is already in early access! It comes with the hope that <a href="https://openjdk.java.net/projects/loom/">Project Loom</a> is going to be included, in <a href="https://mail.openjdk.java.net/pipermail/porters-dev/2021-September/000688.html">some form or the other</a>.</p>
Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-57088739698836544352021-07-29T06:25:00.000-07:002021-07-29T06:25:15.861-07:00 Chasing Java's release train, from 8 to 16. Part 3: The avalanche of releases ...<p>Undoubtedly, <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a> was an import milestone but once the dust settled, another target appeared on the horizon, <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a>, the next LTS release. But between those, the avalanche of new releases and features was unleashed.</p>
<h3>JDK 12</h3>
<p><a href="https://openjdk.java.net/projects/jdk/12/">JDK-12</a> didn't have too many features packed into it nonetheless it includes considerable number of improvements, especially to G1 garbage collector.</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/334">JEP-334: JVM Constants API</a>: introduces an API to model nominal descriptions of key class-file and run-time artifacts, in particular constants that are loadable from the constant pool.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/230">JEP-230: Microbenchmark Suite</a>: adds a basic suite of microbenchmarks to the JDK source code, and make it easy for developers to run existing microbenchmarks and create new ones.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/189">JEP-189: Shenandoah: A Low-Pause-Time Garbage Collector (experimental)</a>: adds a new garbage collection (GC) algorithm named <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah</a> which reduces GC pause times by doing evacuation work concurrently with the running Java threads. Pause times with <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah</a> are independent of heap size, meaning you will have the same consistent pause times whether your heap is 200 MB or 200 GB. As we know, <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah</a> was improved in the latter releases and got <a href="https://bugs.openjdk.java.net/browse/JDK-8250784">backported to JDK-11</a>.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/344">JEP-344: Abortable Mixed Collections for G1</a>: makes G1 mixed collections abortable if they might exceed the pause target.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/189">JEP-346: Promptly Return Unused Committed Memory from G1</a>: enhances the G1 garbage collector to automatically return Java heap memory to the operating system when idle.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/341">JEP-341: Default CDS Archives</a>: enhances the JDK build process to generate a class data-sharing (CDS) archive, using the default class list, on 64-bit platforms. In scope of <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">CDS</a> efforts, it modified the JDK build to run <code>java -Xshare:dump</code> (<a href="https://aredko.blogspot.com/2021/05/chasing-javas-release-train-from-8-to.html">it needed manual execution</a>) after linking the image and keeps the resulting CDS archive in the <b>lib/server</b> directory, so that it is becomes a part of the resulting image.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8205516">JDK-8205516: JFR tool</a>: introduces a new JDK Flight Recorder command line tool, <a href="https://docs.oracle.com/en/java/javase/13/docs/specs/man/jfr.html">jfr</a> (which later was <a href="https://bugs.openjdk.java.net/browse/JDK-8228669">backported to JDK-11</a>).</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8188147">JDK-8188147: Compact Number Formatting support</a>: a new class <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/text/CompactNumberFormat.html">CompactNumberFormat</a> has been added to the standard library that formats a decimal number in its compact form. Please check out this <a href="https://marxsoftware.blogspot.com/2018/12/jdk12-compact-number-formatting.html">excellent blog post</a> to learn more.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8210838">JDK-8210838: Override javax.crypto.Cipher.toString()</a>: the <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/javax/crypto/Cipher.html">Cipher</a>'s <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/javax/crypto/Cipher.html#toString%28%29">toString()</a> method now returns a string containing the transformation, mode, and provider of the <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/javax/crypto/Cipher.html">Cipher</a>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/String.html">String</a> class had gotten a number of new methods:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/String.html#indent%28int%29">String indent(int n)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/String.html#transform%28java.util.function.Function%29"><R> R transform(Function<? super String, ? extends R> f)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/String.html#describeConstable%28%29">Optional<String> describeConstable()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/String.html#resolveConstantDesc%28java.lang.invoke.MethodHandles.Lookup%29">String resolveConstantDesc(MethodHandles.Lookup lookup)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Class.html">Class</a> class was also not left out:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Class.html#descriptorString%28%29">String descriptorString()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Class.html#componentType%28%29">Class<?> componentType()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Class.html#arrayType%28%29">Class<?> arrayType()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Class.html#describeConstable%28%29">Optional<String> describeConstable()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/invoke/VarHandle.html">VarHandle</a> minor improvements:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/invoke/VarHandle.html#toString%28%29">String toString()</a> (was overridden to return compact description)</li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/invoke/VarHandle.html#describeConstable%28%29">Optional<String> describeConstable()</a></li>
</ul>
</li>
<li><p>Very handy addition to <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/io/InputStream.html">InputStream</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/io/InputStream.html#skipNBytes%28long%29">void skipNBytes(long n) throws IOException</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/nio/file/Files.html">Files</a> utility class got just one new method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/nio/file/Files.html#mismatch%28java.nio.file.Path,java.nio.file.Path%29">static long mismatch(Path path, Path path2) throws IOException</a></li>
</ul>
</li>
<li><p>Several useful overloaded forms of the existing methods were added to (already monstrous) <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/CompletionStage.html">CompletionStage</a> class:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/CompletionStage.html#exceptionallyAsync%28java.util.function.Function%29">CompletionStage<T> exceptionallyAsync(Function<Throwable, ? extends T> fn</a>)</li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/CompletionStage.html#exceptionallyAsync%28java.util.function.Function,java.util.concurrent.Executor%29">CompletionStage<T> exceptionallyAsync(Function<Throwable, ? extends T> fn, Executor executor)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/CompletionStage.html#exceptionallyCompose%28java.util.function.Function%29">CompletionStage<T> exceptionallyCompose(Function<Throwable, ? extends CompletionStage<T>> fn)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/CompletionStage.html#exceptionallyComposeAsync%28java.util.function.Function%29">CompletionStage<T> exceptionallyComposeAsync(Function<Throwable, ? extends CompletionStage<T>> fn)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/concurrent/CompletionStage.html#exceptionallyCompose%28java.util.function.Function%29">CompletionStage<T> exceptionallyComposeAsync(Function<Throwable, ? extends CompletionStage<T>> fn, Executor executor</a>)</li>
</ul>
</li>
<li><p>Probably, the most practical (from the developer's standpoint) addition to the standard library was introduction of the <a href="https://nipafx.dev/java-12-teeing-collector/">teeing collector</a>, available via <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/stream/Collectors.html">Collectors</a> utility class:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/stream/Collectors.html#teeing%28java.util.stream.Collector,java.util.stream.Collector,java.util.function.BiFunction%29">static <T,R1,R2,R> Collector<T,?,R> teeing(Collector<? super T,?,R1> downstream1, Collector<? super T,?,R2> downstream2, BiFunction<? super R1,? super R2,R> merger)</a>: returns a <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/stream/Collector.html">Collector</a> that is a composite of two downstream collectors. Let us consider an overly simplified but illustrative example below when there is a need to find the shortest and longest (by length) string in the stream:
<pre class="brush:java">
final Stream<String> stream = Stream.of(
"Tom",
"Samantha",
"John"
);
var minMaxByLength = stream
.collect(
Collectors.teeing(
Collectors.minBy(Comparator.comparing(String::length)),
Collectors.maxBy(Comparator.comparing(String::length)),
(shortest, longest) -> new String[] {
shortest.orElse(""),
longest.orElse("")
}
)
);
</pre>
<p>The result of this code snippet is: </p>
<pre>
Shortest = Tom, Longest = Samantha
</pre>
By all means, this rather small change would significantly reduce the amount of the boilerplate code involved while dealing with Java <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/stream/Stream.html">Stream</a> APIs.
</li>
</ul>
</li>
<li><p>A whole new package, <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/constant/package-summary.html">java.lang.constant</a> was introduced in scope of the <a href="https://openjdk.java.net/jeps/334">JEP-334: JVM Constants API</a> implementation. Consequently, some classes got new methods as well.
</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Double.html">Double</a> got:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Double.html#describeConstable%28%29">Optional<Double> describeConstable()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Double.html#resolveConstantDesc%28java.lang.invoke.MethodHandles.Lookup%29">Double resolveConstantDesc(MethodHandles.Lookup lookup)</a></li>
</ul>
</li>
</ul>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Long.html">Long</a> got:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Long.html#describeConstable%28%29">Optional<Long> describeConstable()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Long.html#resolveConstantDesc%28java.lang.invoke.MethodHandles.Lookup%29">Long resolveConstantDesc(MethodHandles.Lookup lookup)</a></li>
</ul>
</li>
</ul>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Integer.html">Integer</a> got:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Integer.html#describeConstable%28%29">Optional<Integer> describeConstable()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Integer.html#resolveConstantDesc%28java.lang.invoke.MethodHandles.Lookup%29">Integer resolveConstantDesc(MethodHandles.Lookup lookup)</a></li>
</ul>
</li>
</ul>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Float.html">Float</a> got:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Float.html#describeConstable%28%29">Optional<Float> describeConstable()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Float.html#resolveConstantDesc%28java.lang.invoke.MethodHandles.Lookup%29">Float resolveConstantDesc(MethodHandles.Lookup lookup)</a></li>
</ul>
</li>
</ul>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Enum.html">Enum</a> got:</p>
<ul>
<li>new class <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Enum.EnumDesc.html">Enum.EnumDesc<E extends Enum<E>></a>, a nominal descriptor for an enumeration constant</li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/Enum.html#describeConstable%28%29">Optional<<Enum.EnumDesc<E>> describeConstable()</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Besides the changes we have talked about, <a href="https://openjdk.java.net/projects/jdk/12/">JDK-12</a> has delivered quite a number of the <a href="https://seanjmullan.org/blog/2019/03/19/jdk12">security enhancements</a>, notably:</p>
<ul>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8148188">JDK-8148188: Enhance the security libraries to record events of interest</a> (JFR)</li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8212605">JDK-8212605: Pure-Java implementation of AccessController.doPrivileged</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8212261">JDK-8212261: Add SSLSession accessors to HttpsURLConnection and SecureCacheResponse</a>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/net/SecureCacheResponse.html">java.net.SecureCacheResponse</a> was enriched with <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/net/SecureCacheResponse.html#getSSLSession%28%29">Optional<SSLSession> getSSLSession()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/javax/net/ssl/HttpsURLConnection.html">java.net.ssl.HttpsURLConnection</a> was enriched with <a href="https://docs.oracle.com/en/java/javase/12/docs/api/java.base/javax/net/ssl/HttpsURLConnection.html#getSSLSession%28%29">Optional<SSLSession> getSSLSession()</a></li>
</ul>
</li>
</ul>
<h3>JDK 13</h3>
<p>Just when the excitement after <a href="https://openjdk.java.net/projects/jdk/12/">JDK-12</a> release faded away, the <a href="https://openjdk.java.net/projects/jdk/13/">JDK-13</a> was ready to come along. By all means, it was a minor one from the features perspective.</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/351">JEP-351: ZGC: Uncommit Unused Memory</a>: enhances ZGC to return unused heap memory to the operating system. It continues the work started in <a href="https://aredko.blogspot.com/2021/05/chasing-javas-release-train-from-8-to.html">JDK-11</a>, still keeping the <a href="https://wiki.openjdk.java.net/display/zgc/Main">ZGC</a> under <b>experimental</b> label.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/350">JEP-350: Dynamic CDS Archives</a>: extends <a href="https://openjdk.java.net/jeps/310">application class-data sharing</a> to allow the dynamic archiving of classes at the end of Java application execution. The archived classes will include all loaded application classes and library classes that are not present in the default, base-layer <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">CDS</a> archive. A shared archive will be dynamically created when an application exits if the <code>-XX:ArchiveClassesAtExit</code> option is specified.</p>
<pre class="brush:java">
$ java -Xshare:dump -XX:ArchiveClassesAtExit=app.jsa -cp app.jar AppMain
</pre>
<p>Once the dynamic archive is ready, the application could be run using it:</p>
<pre class="brush:java">
$ java -XX:SharedArchiveFile=app.jsa -cp app.jar AppMain
</pre>
<p>Mostly every new JDK release brings improvements into <a href="https://openjdk.java.net/jeps/310">application class-data sharing</a>, promising substantial reduction of the application startup times.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/353">JEP-353: Reimplement the Legacy Socket API</a>: replaces the underlying implementation used by the <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/net/Socket.html">java.net.Socket</a> and <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/net/ServerSocket.html">java.net.ServerSocket</a> APIs with a simpler and more modern implementation that is easy to maintain and debug. The new implementation will be easy to adapt to work with user-mode threads, a.k.a. fibers, currently being explored in <a href="https://openjdk.java.net/projects/loom/">Project Loom</a>.</p><p>Interestingly, the old implementation was kept in the JDK and could be switched to using <code>-Djdk.net.usePlainSocketImpl=true</code> system property.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8220050">JDK-8220050: Deprecate -XX:-ThreadLocalHandshakes</a>: deprecates <code>-XX:-ThreadLocalHandshakes</code> so that thread-local handshakes will be guaranteed to be available.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8221431">JDK-8221431: Support for Unicode 12.1</a>: adds support of the latest <a href="https://unicode.org/versions/Unicode12.1.0/">Unicode Standard version 12.1</a>.
</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-5029431">JDK-5029431: Add absolute bulk put and get methods</a>: the buffer types in <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/package-summary.html">java.nio</a> package now define absolute bulk <code>get</code> and <code>put</code> methods to transfer contiguous sequences of data without regard to or effect on the buffer position.</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ByteBuffer.html">java.nio.ByteBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ByteBuffer.html#slice%28int,int%29">abstract ByteBuffer slice(int index, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ByteBuffer.html#get%28int,byte%5B%5D,int,int%29">ByteBuffer get(int index, byte[] dst, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ByteBuffer.html#get%28int,byte%5B%5D%29">ByteBuffer get(int index, byte[] dst)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ByteBuffer.html#put%28int,byte%5B%5D,int,int%29">ByteBuffer put(int index, byte[] src, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ByteBuffer.html#put%28int,byte%5B%5D%29">ByteBuffer put(int index, byte[] src)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/CharBuffer.html">java.nio.CharBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/CharBuffer.html#slice%28int,int%29">abstract CharBuffer slice(int index, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/CharBuffer.html#get%28int,char%5B%5D,int,int%29">CharBuffer get(int index, char[] dst, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/CharBuffer.html#get%28int,char%5B%5D%29">CharBuffer get(int index, char[] dst)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/CharBuffer.html#put%28int,char%5B%5D,int,int%29">CharBuffer put(int index, char[] src, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/CharBuffer.html#put%28int,char%5B%5D%29">CharBuffer put(int index, char[] src</a>)</li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/DoubleBuffer.html">java.nio.DoubleBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/DoubleBuffer.html#slice%28int,int%29">abstract DoubleBuffer slice(int index, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/DoubleBuffer.html#get%28int,double%5B%5D,int,int%29">DoubleBuffer get(int index, double[] dst, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/DoubleBuffer.html#get%28int,double%5B%5D%29">DoubleBuffer get(int index, double[] dst)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/DoubleBuffer.html#put%28int,double%5B%5D,int,int%29">DoubleBuffer put(int index, double[] src, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/DoubleBuffer.html#put%28int,double%5B%5D%29">DoubleBuffer put(int index, double[] src)</a></li>
</ul></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/FloatBuffer.html">java.nio.FloatBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/FloatBuffer.html#slice%28int,int%29">abstract FloatBuffer slice(int index, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/FloatBuffer.html#get%28int,float%5B%5D,int,int%29">FloatBuffer get(int index, float[] dst, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/FloatBuffer.html#get%28int,float%5B%5D%29">FloatBuffer get(int index, float[] dst)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/FloatBuffer.html#put%28int,float%5B%5D,int,int%29">FloatBuffer put(int index, float[] src, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/FloatBuffer.html#put%28int,float%5B%5D%29">FloatBuffer put(int index, float[] src)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/IntBuffer.html">java.nio.IntBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/IntBuffer.html#slice%28int,int%29">abstract IntBuffer slice(int index, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/IntBuffer.html#get%28int,int%5B%5D,int,int%29">IntBuffer get(int index, int[] dst, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/IntBuffer.html#get%28int,int%5B%5D%29">IntBuffer get(int index, int[] dst)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/IntBuffer.html#put%28int,int%5B%5D,int,int%29">IntBuffer put(int index, int[] src, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/IntBuffer.html#put%28int,int%5B%5D%29">IntBuffer put(int index, int[] src)</a></li>
</ul></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/LongBuffer.html">java.nio.LongBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/LongBuffer.html#slice%28int,int%29">abstract LongBuffer slice(int index, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/LongBuffer.html#get%28int,long%5B%5D,int,int%29">LongBuffer get(int index, long[] dst, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/LongBuffer.html#get%28int,long%5B%5D%29">LongBuffer get(int index, long[] dst)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/LongBuffer.html#put%28int,long%5B%5D,int,int%29">LongBuffer put(int index, long[] src, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/LongBuffer.html#put%28int,long%5B%5D%29">LongBuffer put(int index, long[] src)</a></li>
</ul></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ShortBuffer.html">java.nio.ShortBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ShortBuffer.html#slice%28int,int%29">abstract ShortBuffer slice(int index, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ShortBuffer.html#get%28int,short%5B%5D,int,int%29">ShortBuffer get(int index, short[] dst, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ShortBuffer.html#get%28int,short%5B%5D%29">ShortBuffer get(int index, short[] dst)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ShortBuffer.html#put%28int,short%5B%5D,int,int%29">ShortBuffer put(int index, short[] src, int offset, int length)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/ShortBuffer.html#put%28int,short%5B%5D%29">ShortBuffer put(int index, short[] src)</a></li>
</ul></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/Buffer.html">java.nio.Buffer</a> also had a new method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/Buffer.html#slice%28int,int%29">abstract Buffer slice(int index, int length)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/MappedByteBuffer.html">java.nio.MappedByteBuffer</a> got one improvement:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/MappedByteBuffer.html#force%28int,int%29">MappedByteBuffer force(int index,int length)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/String.html">String</a> class got a few new methods:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/String.html#stripIndent%28%29">String stripIndent()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/String.html#translateEscapes%28%29">String translateEscapes()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/String.html#formatted%28java.lang.Object...%29">String formatted(Object... args)</a></li>
</ul>
<p>But that is not it, the <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/String.html">String</a> also got a new <a href="https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/String.html#hashCode%28%29">hashCode()</a> optimization, see please <a href="https://bugs.openjdk.java.net/browse/JDK-8221836">JDK-8221836: Avoid recalculating String.hash when zero</a> for more details.</p>
</li>
<li><p>Last but not least, a large chunk of the improvements went into <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah GC</a> (not to mention numerous bug fixes):</p>
<ul>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8221766">JDK-8221766: Load-reference barriers for Shenandoah</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8222185">JDK-8222185: Shenandoah should report "committed" as capacity</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8222186">JDK-8222186: Shenandoah should not uncommit below minimum heap size</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8223759">JDK-8223759: Shenandoah should allow arbitrarily low initial heap size</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8223767">JDK-8223767: Shenandoah fails to build on Solaris x86_64</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8224584">JDK-8224584: Shenandoah: Eliminate forwarding pointer word</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8225048">JDK-8225048: Shenandoah x86_32 support</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8225229">JDK-8225229: Shenandoah: trim down default number of GC threads</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8225046">JDK-8225046: Shenandoah metrics logs refactoring</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8224584">JDK-8224584: Shenandoah: Eliminate forwarding pointer word</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8221750">JDK-8221750: Shenandoah: Enable ThreadLocalHandshake by default</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8221507">JDK-8221507: Implement JFR events for Shenandoah</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8217343">JDK-8217343: Shenandoah control thread should be able to run at critical priority</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8217016">JDK-8217016: Shenandoah: Streamline generation of CAS barriers</a></li>
</ul>
</li>
</ul>
<p>A fair amount of the <a href="https://seanjmullan.org/blog/2019/08/05/jdk13">security enhancements</a> in <a href="https://openjdk.java.net/projects/jdk/13/">JDK-13</a> is certainly worth checking out.</p>
<h3>JDK 14</h3>
<p>Going further, the <a href="https://openjdk.java.net/projects/jdk/14/">JDK-14</a> kept the steady pace of innovation and was bundled with quite a useful set of changes. Let us take a look at the most interesting ones.</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/345">JEP-345: NUMA-Aware Memory Allocation for G1</a>: improves G1 performance on large machines by implementing NUMA-aware memory allocation.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/363">JEP-363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector</a>: brings the end to the life of the <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html">Concurrent Mark Sweep</a> (<a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html">CMS</a>) garbage collector.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/352">JEP-352: Non-Volatile Mapped Byte Buffers</a>: adds new JDK-specific file mapping modes so that the <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/nio/channels/FileChannel.html">FileChannel</a> API can be used to create <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/nio/MappedByteBuffer.html">MappedByteBuffer</a> instances that refer to non-volatile memory.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/349">JEP-349: JFR Event Streaming</a>: exposes JDK Flight Recorder data for continuous monitoring.
</p></li>
<li><p><a href="https://openjdk.java.net/jeps/361">JEP-361: Switch Expressions</a>: extends <code>switch</code> so it can be used as either a statement or an expression, and so that both forms can use either traditional <code>case ... :</code> labels (with fall through) or new <code>case ... -></code> labels (with no fall through), with a further new statement for yielding a value from a <code>switch</code> expression. These changes will simplify everyday coding, and prepare the way for the use of pattern matching in switch. There are quite a few important considerations to keep in mind.</p>
<p>First, a new form of switch label, <code>"case L ->"</code>, was introduced. Second, the code to the right of a <code>"case L ->"</code> switch label is restricted to be an expression, a block, or a throw statement. The block is pretty much simplified version of the good old <code>switch</code> statement, please notice absence of explicit <code>break</code> and optional <code>default</code> case.</p>
<pre class="brush:java">
final Character command = (char)System.in.read();
switch (command) {
case 'A' -> {
// ...
}
case 'B', 'C', 'D' -> {
// ...
}
case 'E' -> throw new UnsupportedOperationException("Unsupported: " + command);
}
</pre>
<p>The expression form is a new and exceptionally useful form of <code>switch</code> statement, the example is below.</p>
<pre class="brush:java">
final Callable<?> callable = switch (command) {
case 'A' -> new CommandA();
case 'B', 'C', 'D' -> new CommandOthers();
default -> new CommandHelp();
};
</pre>
<p>Contrary to the <code>switch</code> statement, the cases of a <code>switch</code> expression must be exhaustive: for all possible values there must be a matching label, hence the presence of the <code>default</code> case (obviously throwing an exception is another valid option). In the case when the code blocks are needed, a new <code>yield</code> statement was introduced to yield a value for the enclosing expression, for example:</p>
<pre class="brush:java">
final Callable<?> callable = switch (command) {
case 'A' -> {
System.out.println("Executing command A");
yield new CommandA();
}
case 'B', 'C', 'D' -> {
System.out.println("Executing command B, C or D");
yield new CommandOthers();
}
default -> {
throw new UnsupportedOperationException("Unsupported: " + command);
}
};
</pre>
<p>Please note, rather than being a keyword, <code>yield</code> is a restricted identifier (much like <code>var</code>). The <code>break</code> statement and <code>yield</code> statement facilitate easy disambiguation between <code>switch</code> statements and <code>switch</code> expressions. For more details, please check out this excellent <a href="https://nipafx.dev/java-13-switch-expressions/">Definitive Guide To Switch Expressions</a> post.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/358">JEP-358: Helpful NullPointerExceptions</a>: improves the usability of <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/NullPointerException.html">NullPointerException</a>s generated by the JVM by describing precisely which variable was <code>null</code>. Undoubtedly, from the developer's standpoint, this feature alone deserves immediate switch to <a href="https://openjdk.java.net/projects/jdk/14/">JDK-14</a>. The following code snippet is going to convince you that it is worth it.</p>
<pre class="brush:java">
package com.example;
import java.util.Map;
public class NpeStarter {
public static void main(String[] args) {
Map.Entry<String, String> entry = null;
if (entry.getKey().equals("key")) {
// ...
}
}
}
</pre>
<p>What you get on pre-<a href="https://openjdk.java.net/projects/jdk/14/">JDK-14</a> releases is faceless <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/NullPointerException.html">NullPointerException</a>, where you have yet to figure out what exactly (<code>entry</code>? <code>entry.getKey()</code>?) is <code>null</code>.</p>
<pre>
Exception in thread "main" java.lang.NullPointerException
at com.example.NpeStarter.main(NpeStarter.java:8)
</pre>
<p>The arrival of <a href="https://openjdk.java.net/projects/jdk/14/">JDK-14</a> changes that completely:</p>
<pre>
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.Map$Entry.getKey()" because "entry" is null
at com.example.NpeStarter.main(NpeStarter.java:8)
</pre>
</li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8234863">JDK-8234863: Increase default value of MaxInlineLevel</a>: this small change went mostly unnoticeable but very likely has a beneficial impact on real world applications. It was also <a href="https://bugs.openjdk.java.net/browse/JDK-8256164">backported to JDK-11</a>.</p></li>
<li><p>New methods found the way into <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/PrintStream.html">PrintStream</a> class:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/PrintStream.html#write%28byte%5B%5D%29">void write(byte[] buf) throws IOException</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/PrintStream.html#writeBytes%28byte%5B%5D%29">void writeBytes(byte[] buf)</a></li>
</ul>
</li>
<li><p>The brand new <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/io/Serial.html">Serial </a> annotation had been introduced to indicates that an annotated field or method is part of the serialization mechanism defined by the <a href="https://docs.oracle.com/en/java/javase/14/docs/specs/serialization/index.html">Java Object Serialization Specification</a>. </p></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/NullPointerException.html">NullPointerException</a> class was enhanced to reflect the changes introduced by <a href="https://openjdk.java.net/jeps/358">JEP-358: Helpful NullPointerExceptions</a>:</p>
<ul><li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/NullPointerException.html#getMessage%28%29">
String getMessage()</a> (override)</li>
</ul>
</li>
<li><p>In scope of <a href="https://bugs.openjdk.java.net/browse/JDK-8225339">JDK-8225339: Optimize HashMap.keySet()/HashMap.values()/HashSet toArray() methods</a>
, the class as stable as <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/HashSet.html">java.util.HashSet</a>, got some neat (but not reflected in <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/HashSet.html">javadocs</a>) overrides as well.</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/AbstractCollection.html#toArray%28%29">Object[] toArray()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/AbstractCollection.html#toArray%28T%5B%5D%29"><T> T[] toArray(T[] a)</a></li>
</ul>
</li>
<li><p>Probably, <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/StrictMath.html">StrictMath</a> class had benefited the most in this release:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/StrictMath.html#incrementExact%28int%29">static int incrementExact(int a)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/StrictMath.html#incrementExact%28long%29">static long incrementExact(long a)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/StrictMath.html#decrementExact%28int%29">static int decrementExact(int a)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/StrictMath.html#decrementExact%28long%29">static long decrementExact(long a)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/StrictMath.html#negateExact%28int%29">static int negateExact(int a)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/StrictMath.html#negateExact%28long%29">static long negateExact(long a)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html">Parallel GC</a> got much better in <a href="https://openjdk.java.net/projects/jdk/14/">JDK-14</a>, a few notable improvements are mentioned below:</p>
<ul>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8213108">JDK-8213108: Improve work distribution during remembered set scan</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8220465">JDK-8220465: Use shadow regions for faster ParallelGC full GCs</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8204951">JDK-8204951: Investigate to use WorkGang for Parallel GC</a></li>
</ul>
</li>
</ul>
<p>The <a href="https://openjdk.java.net/projects/jdk/14/">JDK-14</a> was not without the <a href="https://seanjmullan.org/blog/2020/03/19/jdk14">security enhancements</a>, the most disruptive of those was probably the removal of the <code>java.security.acl</code> APIs (see please <a href="https://bugs.openjdk.java.net/browse/JDK-8191138">JDK-8191138</a>).</p>
<h3>JDK 15</h3>
<p>Arguably, the <a href="https://openjdk.java.net/projects/jdk/15/">JDK-15</a> was a major one for the reasons that many experimental and preview features had finally graduated to mainstream and became ready for production use. Let us start from those.</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/377">JEP-377: ZGC: A Scalable Low-Latency Garbage Collector (Production)</a>: changes the <a href="https://wiki.openjdk.java.net/display/zgc/Main">Z Garbage Collector</a> from an experimental feature into a product feature. <a href="https://wiki.openjdk.java.net/display/zgc/Main">ZGC</a> could enabled via the <code>-XX:+UseZGC</code> command line option.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/379">JEP-379: Shenandoah: A Low-Pause-Time Garbage Collector (Production)</a>: changes the <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah garbage collector</a> from an experimental feature into a product feature. <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah GC</a> could enabled via the <code>-XX:+UseShenandoahGC</code> command line option.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/339">JEP-339: Edwards-Curve Digital Signature Algorithm (EdDSA)</a>: implements cryptographic signatures using the Edwards-Curve Digital Signature Algorithm (EdDSA) as described by <a href="https://tools.ietf.org/html/rfc8032">RFC 8032</a>. A number of new classes had been added into <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/spec/package-summary.html">java.security.spec</a> and <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/package-summary.html">java.security.interfaces</a> packages:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECKey.html">java.security.interfaces.EdECKey</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECPrivateKey.html">java.security.interfaces.EdECPrivateKey</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECPublicKey.html">java.security.interfaces.EdECPublicKey</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/spec/EdDSAParameterSpec.html">java.security.spec.EdDSAParameterSpec</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/spec/EdECPoint.html">java.security.spec.EdECPoint</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/spec/EdECPrivateKeySpec.html">java.security.spec.EdECPrivateKeySpec</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/spec/EdECPublicKeySpec.html">java.security.spec.EdECPublicKeySpec</a></li>
</ul>
</li>
<li><p>J<a href="https://openjdk.java.net/jeps/378">EP-378: Text Blocks</a>: adds text blocks to the Java language. A text block is a multi-line string literal that avoids the need for most escape sequences, automatically formats the string in a predictable way, and gives the developer control over the format when desired. The text block is denoted with triple double quotes <code>"""</code> (fat delimiters).</p>
<pre class="brush:java">
var text = """
This is a multi-line
text block
""";
</pre>
<p>From language usability perspective, this long-awaited feature dramatically simplifies dealing with string literals for day-to-day developers, as well as makes code much more readable. There are tons of details and examples buried in the <a href="https://openjdk.java.net/jeps/378">JEP</a> (escaping, indentation, ...) so I would highly recommend reading it, not to forget <a href="https://nipafx.dev/java-13-text-blocks/">Definitive Guide To Text Blocks</a> post.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/373">JEP-373: Reimplement the Legacy DatagramSocket API</a>: replaces the underlying implementations of the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/net/DatagramSocket.html">java.net.DatagramSocket</a> and <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/net/MulticastSocket.html">java.net.MulticastSocket</a> APIs with simpler and more modern implementations that are easy to maintain and debug. The new implementations will be easy to adapt to work with virtual threads, currently being explored in <a href="https://openjdk.java.net/projects/loom/">Project Loom</a>. This is a follow-on to <a href="https://openjdk.java.net/jeps/353">JEP-353</a>, which already reimplemented the legacy <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/net/Socket.html">Socket AP</a>I and was delivered in <a href="https://openjdk.java.net/projects/jdk/13/">JDK-13</a>. <p>The old implementation was kept in the JDK and could be switched to using <code>-Djdk.net.usePlainDatagramSocketImpl=true</code> system property.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/371">JEP-371: Hidden Classes</a>: introduces hidden classes, which are classes that cannot be used directly by the bytecode of other classes. Hidden classes are intended for use by frameworks that generate classes at run time and use them indirectly, via reflection. A hidden class may be defined as a member of an <a href="https://openjdk.java.net/jeps/181">access control nest</a>, and may be unloaded independently of other classes.</p>
<p>A normal class is created by invoking <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/ClassLoader.html#defineClass%28java.lang.String,byte%5B%5D,int,int%29">ClassLoader::defineClass</a>, whereas a hidden class is created by invoking <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClass%28byte%5B%5D,boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...%29">Lookup::defineHiddenClass</a>. The <a href="https://openjdk.java.net/jeps/371">JEP</a> emphasizes that a hidden class is not anonymous. The only way for other classes to use a hidden class is indirectly, via its <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Class.html">Class</a> object. The hidden class cannot be used directly by bytecode instructions in other classes because it cannot be referenced nominally, that is, by name. Hidden class cannot enclose other classes and is unable to use itself as a field type, return type, or parameter type in its own field and method declarations.</p><p>A hidden class is not discoverable by the JVM during bytecode linkage, nor by programs making explicit use of class loaders.
</p>
<p>Methods of hidden classes are not shown in stack traces by default. They represent implementation details of language runtimes, and are never expected to be useful to developers diagnosing application issues. However, they can be included in stack traces via the options <code>-XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames</code>. For the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/StackWalker.html">StackWalker API</a>, stack frames for hidden classes should be included by a JVM implementation only if the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/StackWalker.Option.html#SHOW_HIDDEN_FRAMES">SHOW_HIDDEN_FRAMES</a> option is set.
</p><p>Last but not least, the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/invoke/LambdaMetafactory.html">LambdaMetaFactory</a> had been updated to use the new hidden classes <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html">Lookup</a> APIs, which include:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClass%28byte%5B%5D,boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...%29">MethodHandles.Lookup defineHiddenClass(byte[] bytes, boolean initialize, MethodHandles.Lookup.ClassOption... options) throws IllegalAccessException</a> (a hidden class can be created as a member of an existing nest by passing the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.ClassOption.html#NESTMATE">NESTMATE</a> option)</li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html#ensureInitialized%28java.lang.Class%29">Class<?> ensureInitialized(Class<?> targetClass) throws IllegalAccessException</a></li>
</ul>
</li>
<li><p><a href="https://openjdk.java.net/jeps/374">JEP-374: Disable and Deprecate Biased Locking</a>: disables biased locking by default, and deprecate all related command-line options.
</p></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Class.html">java.lang.Class</a> had further support for hidden classes:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Class.html#isHidden%28%29">boolean isHidden()</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/CharBuffer.html">CharBuffer</a> had one method added:</p><ul><li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/CharBuffer.html#isEmpty%28%29">boolean isEmpty()</a></li></ul></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/CharSequence.html">CharSequence</a> had a new method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/CharSequence.html#isEmpty%28%29">boolean isEmpty(</a>)</li>
</ul></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/NullPointerException.html">NullPointerException</a> got an implementation of:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Throwable.html#fillInStackTrace%28%29">Throwable fillInStackTrace()</a> (override)</li>
</ul></li>
<li><p>Two new methods were added to the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Math.html">java.lang.Math</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Math.html#absExact%28int%29">static int absExact(int a)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Math.html#absExact%28long%29">static long absExact(long a)</a></li>
</ul>
</li>
<li><p>And to the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/StrictMath.html">java.lang.StrictMath</a> consequently:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/StrictMath.html#absExact%28int%29">static int absExact(int a)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/StrictMath.html#absExact%28long%29">static long absExact(long a)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/AnnotatedType.html">java.lang.reflect.AnnotatedType</a> interface had three new methods:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/AnnotatedType.html#getAnnotations%28%29">Annotation[] getAnnotations()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/AnnotatedType.html#getAnnotation%28java.lang.Class%29"><T extends Annotation> T getAnnotation(Class<T> annotationClass)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/AnnotatedType.html#getDeclaredAnnotations%28%29">Annotation[] getDeclaredAnnotations()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/NoSuchElementException.html">java.util.NoSuchElementException</a> class got a couple of additional constructors</p></li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/TreeMap.html">java.util.TreeMap</a> got own implementation of:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/TreeMap.html#compute(K,java.util.function.BiFunction)">V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)</a> (override)</li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/TreeMap.html#computeIfAbsent(K,java.util.function.Function)">V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)</a> (override)</li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/TreeMap.html#computeIfPresent(K,java.util.function.BiFunction)">V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)</a> (override)</li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/TreeMap.html#merge%28K,V,java.util.function.BiFunction%29">V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)</a> (override)</li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Map.html#putIfAbsent%28K,V%29">V putIfAbsent(K key, V value)</a> (override)</li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/text/DecimalFormatSymbols.html">java.text.DecimalFormatSymbols</a> had new methods to support monetary grouping separator:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/text/DecimalFormatSymbols.html#getMonetaryGroupingSeparator%28%29">char getMonetaryGroupingSeparator()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/text/DecimalFormatSymbols.html#setMonetaryGroupingSeparator%28char%29">void setMonetaryGroupingSeparator(char monetaryGroupingSeparator)</a></li>
</ul>
</li>
<li><p>A number of enhancements to <a href="https://docs.oracle.com/en/java/javase/15/docs/api/index.html">java.nio</a> APIs.</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/ServerSocketChannel.html">java.nio.channels.ServerSocketChannel</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/ServerSocketChannel.html#open%28java.net.ProtocolFamily%29">static ServerSocketChannel open(ProtocolFamily family)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/SocketChannel.html">java.nio.channels.SocketChannel</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/SocketChannel.html#open%28java.net.ProtocolFamily%29">static SocketChannel open(ProtocolFamily family)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/spi/SelectorProvider.html">java.nio.channels.spi.SelectorProvider</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/spi/SelectorProvider.html#openSocketChannel%28java.net.ProtocolFamily%29">SocketChannel openSocketChannel(ProtocolFamily family) throws IOException</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/spi/SelectorProvider.html#openServerSocketChannel%28java.net.ProtocolFamily%29">ServerSocketChannel openServerSocketChannel(ProtocolFamily family) throws IOException</a></li>
</ul>
</li>
</ul>
</li>
<li><p>A number of gaps related to <a href="https://openjdk.java.net/jeps/334">JEP-334: JVM Constants API</a> were addressed:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Boolean.html">Boolean</a> got:</p><ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Boolean.html#describeConstable%28%29">Optional<DynamicConstantDesc<Boolean>> describeConstable()</a></li>
</ul></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Byte.html">Byte</a> got:</p><ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Byte.html#describeConstable%28%29">Optional<DynamicConstantDesc<Byte>> describeConstable()</a></li>
</ul></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Character.html">Character</a> got:</p><ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Character.html#describeConstable%28%29">Optional<DynamicConstantDesc<Character>> describeConstable()</a></li>
</ul></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Short.html">Short</a> got:</p><ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Short.html#describeConstable%28%29">Optional<DynamicConstantDesc<Short>> describeConstable()</a></li>
</ul></li>
</ul>
</li>
</ul>
<p>Besides <a href="https://openjdk.java.net/jeps/339">JEP-339</a> (EdDSA), there are quite a few <a href="https://seanjmullan.org/blog/2020/10/13/jdk15">security enhancements</a> in <a href="https://jdk.java.net/15">JDK-15</a>, the ones that deserve mentioning are:</p>
<ul>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8241325">JDK-8241325: Enhance SunJCE provider to support SHA-3 based Hmac algorithms</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8242145">JDK-8242145: New System Properties to configure the TLS signature schemes</a></li>
</ul>
<h3>JDK 16</h3>
<p>If <a href="https://openjdk.java.net/projects/jdk/15/">JDK-15</a> qualified as a major one then <a href="https://openjdk.java.net/projects/jdk/16/">JDK-16</a> could be easily stamped as huge, packed with new language features, tooling and GC improvements. Let us dig right in.</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/387">JEP-387: Elastic Metaspace</a>: returns unused HotSpot class-metadata (i.e., metaspace) memory to the operating system more promptly, reduce metaspace footprint, and simplify the metaspace code in order to reduce maintenance costs.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/376">JEP-376: ZGC: Concurrent Thread-Stack Processing</a>: moves <a href="https://wiki.openjdk.java.net/display/zgc/Main">ZGC</a> thread-stack processing from safepoints to a concurrent phase.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/386">JEP-386: Alpine Linux Port</a>: ports the JDK to Alpine Linux, and to other Linux distributions that use musl as their primary C library, on both the x64 and AArch64 architectures.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/388">JEP-388: Windows/AArch64 Port</a>: ports the JDK to Windows/AArch64.
</p></li>
<li><p><a href="https://openjdk.java.net/jeps/390">JEP-390: Warnings for Value-Based Classes</a>: designates the primitive wrapper classes as value-based and deprecate their constructors for removal, prompting new deprecation warnings. Provide warnings about improper attempts to synchronize on instances of any value-based classes in the Java Platform.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/396">JEP-396: Strongly encapsulate JDK internals by default</a>: strongly encapsulates all internal elements of the JDK by default, except for <a href="https://openjdk.java.net/jeps/260#Description">critical internal APIs</a> such as <code>sun.misc.Unsafe</code>. Allows end users to choose the relaxed strong encapsulation that has been the default since <a href="https://aredko.blogspot.com/2021/03/chasing-javas-release-train-from-8-to.html">JDK-9</a>. The impact of this <a href="https://openjdk.java.net/jeps/396">JEP</a> may vary and we have talked about it in <a href="https://aredko.blogspot.com/2021/01/jep-396-and-you-strong-encapsulation-of.html">JEP-396 and You</a> article.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/380">JEP-380 Unix-Domain Socket Channels</a>: adds Unix-domain (AF_UNIX) socket support to the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/SocketChannel.html">socket channel</a> and <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/ServerSocketChannel.html">server-socket channel</a> APIs in the <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/channels/package-summary.html">java.nio.channels</a> package. Extends the <a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/nio/channels/spi/SelectorProvider.html#inheritedChannel%28%29">inherited channel mechanism</a> to support Unix-domain socket channels and server socket channels.</p>
<p><a href="https://en.wikipedia.org/wiki/Unix_domain_socket">Unix-domain sockets</a> are used for inter-process communication (<a href="https://en.wikipedia.org/wiki/Inter-process_communication">IPC</a>) on the same host. They are similar to TCP/IP sockets in most respects but are more secure and more efficient than TCP/IP loopback connections. The supporting API changes in the standard library include:</p>
<ul>
<li>new <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/net/UnixDomainSocketAddress.html">java.net.UnixDomainSocketAddress</a> class</li>
<li>new <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/net/StandardProtocolFamily.html">java.net.StandardProtocolFamily</a> enumeration constant <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/net/StandardProtocolFamily.html#UNIX">UNIX</a></li>
</ul>
</li>
<li><p><a href="https://openjdk.java.net/jeps/395%20">JEP-395: Records</a>: enhances the Java programming language with <a href="https://cr.openjdk.java.net/~briangoetz/amber/datum.html">records</a>, which are classes that act as transparent carriers for immutable data. Records can be thought of as nominal tuples. Records are a new kind of class in the Java language, designed specifically to model plain immutable data aggregates.</p>
<pre class="brush:java">
record Person(String name, int age) {
Person {
if (age <= 0) {
throw new IllegalArgumentException("The age could not be negative");
}
}
}
</pre>
<p>Interestingly, records facilitated the relaxation of certain Java language constraints. Before <a href="https://openjdk.java.net/projects/jdk/16/">JDK-16</a>, an inner class was not allowed to declare a static member (unless the member is a constant variable). Starting from <a href="https://openjdk.java.net/projects/jdk/16/">JDK-16</a> it is allowed by the inner class to declare members that are either explicitly or implicitly static, in particular - a member of the type record class. For example:</p>
<pre class="brush:java">
public class Outer {
class Inner {
record Person(String name, int age) {
Person {
if (age <= 0) {
throw new IllegalArgumentException("The age could not be negative");
}
}
}
}
}
</pre>
<p>Local record classes are a particular case of nested record classes and could be used in place for complex transformations or/and for storing intermediate results. How the records support looks from the perspective of the API changes?</p>
<ul>
<li>new abstract class <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/Record.html">java.lang.Record</a> (the common superclass of all record classes)</li>
<li>new class <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/reflect/RecordComponent.html">java.lang.reflect.RecordComponent</a></li>
<li>new class <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/runtime/ObjectMethods.html">java.lang.runtime.ObjectMethods</a></li>
<li>new <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/annotation/ElementType.html">java.lang.annotation.ElementType</a> enumeration constant: <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/annotation/ElementType.html#RECORD_COMPONENT">RECORD_COMPONENT</a></li>
<li>new <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/Class.html">Class</a> methods:
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/Class.html#getRecordComponents%28%29">RecordComponent[] getRecordComponents()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/Class.html#isRecord%28%29">boolean isRecord()</a></li>
</ul>
</li>
</ul>
<p>The positive impact of this change on the Java programming language is tremendous. Looking into the future, the <a href="https://openjdk.java.net/jeps/405">pattern matching on records</a> is already scheduled for <a href="https://openjdk.java.net/projects/jdk/18/">JDK-18</a>.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/394">JEP-394: Pattern Matching for instanceof</a>: enhances the Java programming language with pattern matching for the <code>instanceof</code> operator. <a href="https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html">Pattern matching</a> allows common logic in a program, namely the conditional extraction of components from objects, to be expressed more concisely and safely.</p>
<pre class="brush:java">
final Number n = ...;
if (n instanceof Long l) {
// Use l as Long ...
} else if (n instanceof Integer i) {
// Use i as Integer ...
}
</pre>
<p>Or in extended form, when the <code>if</code> conditionals may rely on the matched pattern and narrow it further along:</p>
<pre class="brush:java">
final Long MIN_VALUE = 1000L;
if (n instanceof Long l && l >= MIN_VALUE) {
// Use l as Long ...
}
</pre>
<p>Together with records, this feature makes very strong case for having comprehensive <a href="https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html">pattern matching</a> support in Java, with <a href="https://openjdk.java.net/jeps/406">augmented <code>switch</code> statement</a> coming in <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a> (as preview feature).</p><p>For more insights, please check out <a href="https://nipafx.dev/java-pattern-matching/">Pattern Matching in Java</a>, <a href="https://nipafx.dev/java-type-pattern-matching/">Type Pattern Matching with instanceof</a> and <a href="https://benjiweber.co.uk/blog/2021/03/14/java-16-pattern-matching-fun/">Java 16 Pattern Matching Fun</a> articles.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/392">JEP-392:Packaging Tool</a>: provides the <code>jpackage</code> tool, for packaging self-contained Java applications. The supported platform-specific package formats are:</p>
<ul>
<li>Linux: <code>deb</code> and <code>rpm</code></li>
<li>macOS: <code>pkg</code> and <code>dmg</code></li>
<li>Windows: <code>msi</code> and <code>exe</code></li>
</ul>
<p>Under the hood, <code>jpackage</code> tool prepares an application image as input to the platform-specific packaging tool that it invokes in its final step. An application image contains both the files comprising the application as well as the JDK runtime image that will run it. By default, the <code>jpackage</code> tool invokes the <a href="https://openjdk.java.net/jeps/282">jlink</a> tool to create the runtime image.</p>
<p>In addition to the <a href="https://docs.oracle.com/en/java/javase/16/docs/specs/man/jpackage.html">command-line</a>, <code>jpackage</code> is accessible via the <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/spi/ToolProvider.html">ToolProvider API</a> under the name of <strong>"jpackage"</strong>.</p>
</li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8257602">JDK-8257602: Introduce JFR Event Throttling and new jdk.ObjectAllocationSample event (enabled by default)</a></p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8255616">JDK-8255616: Removal of experimental features AOT and Graal JIT</a></p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8247536">JDK-8247536: Support for pre-generated java.lang.invoke classes in CDS static archive</a></p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8254723">JDK-8254723: Add diagnostic command to write Linux perf map file</a>, very useful change for using JVM along with <code>perf record</code>, you just have to pass <code>-XX:+UnlockDiagnosticVMOptions -XX:+DumpPerfMapAtExit -XX:+PreserveFramePointer</code> command line flags at startup.</p></li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8244778">JDK-8244778: Archive full module graph in CDS</a></p></li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.net.http/module-summary.html">HTTP Client API</a> got some improvements:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.net.http/java/net/http/HttpRequest.BodyPublishers.html">HttpRequest.BodyPublishers</a> got a method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.net.http/java/net/http/HttpRequest.BodyPublishers.html#concat%28java.net.http.HttpRequest.BodyPublisher...%29">static HttpRequest.BodyPublisher concat(HttpRequest.BodyPublisher... publishers)</a></li>
</ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.net.http/java/net/http/HttpRequest.html">HttpRequest</a> got a method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.net.http/java/net/http/HttpRequest.html#newBuilder%28java.net.http.HttpRequest,java.util.function.BiPredicate%29">static HttpRequest.Builder newBuilder(HttpRequest request, BiPredicate<String,String> filter)</a></li>
</ul>
</li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/time/format/DateTimeFormatterBuilder.html">DateTimeFormatterBuilder</a> had gotten one new method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/time/format/DateTimeFormatterBuilder.html#appendDayPeriodText%28java.time.format.TextStyle%29">DateTimeFormatterBuilder appendDayPeriodText(TextStyle style)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.logging/java/util/logging/LogRecord.html">LogRecord</a> from <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.logging/java/util/logging/package-summary.html">java.util.logging</a> had some additions:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.logging/java/util/logging/LogRecord.html#getLongThreadID%28%29">long getLongThreadID()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.logging/java/util/logging/LogRecord.html#setLongThreadID%28long%29">LogRecord setLongThreadID(long longThreadID)</a></li>
</ul>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/package-summary.html">java.nio</a> had some APIs enhancements:</p>
<ul>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/ByteBuffer.html">java.nio.ByteBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/ByteBuffer.html#put%28int,java.nio.ByteBuffer,int,int%29">ByteBuffer put(int index, ByteBuffer src, int offset, int length)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/CharBuffer.html">java.nio.CharBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/CharBuffer.html#put%28int,java.nio.CharBuffer,int,int%29">CharBuffer put(int index, CharBuffer src, int offset, int length)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/DoubleBuffer.html">java.nio.DoubleBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/DoubleBuffer.html#put%28int,java.nio.DoubleBuffer,int,int%29">DoubleBuffer put(int index, DoubleBuffer src, int offset, int length)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/FloatBuffer.html">java.nio.FloatBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/FloatBuffer.html#put%28int,java.nio.FloatBuffer,int,int%29">FloatBuffer put(int index, FloatBuffer src, int offset, int length)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/IntBuffer.html">java.nio.IntBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/IntBuffer.html#put%28int,java.nio.IntBuffer,int,int%29">IntBuffer put(int index, IntBuffer src, int offset, int length)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/LongBuffer.html">java.nio.LongBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/LongBuffer.html#put%28int,java.nio.LongBuffer,int,int%29">LongBuffer put(int index, LongBuffer src, int offset, int length)</a></li>
</ul>
</li>
<li><p><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/ShortBuffer.html">java.nio.ShortBuffer</a></p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/ShortBuffer.html#put%28int,java.nio.ShortBuffer,int,int%29">ShortBuffer put(int index, ShortBuffer src, int offset, int length)</a></li>
</ul>
</li>
</ul>
<li><p>Multiple practical improvements went into <a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html">Stream</a> APIs</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html#toList%28%29">List<T> toList()</a>
<pre class="brush:java">
var numbers = Stream
.of(4.0, 16.0, 256.0)
.map(Math::sqrt)
.toList();
</pre>
</li>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html#mapMulti%28java.util.function.BiConsumer%29"><R> Stream<R> mapMulti(BiConsumer<? super T,? super Consumer<R>> mapper)</a>
<pre class="brush:java">
var numbers = Stream
.of(2.0, 8.0, 16.0)
.<Double>mapMulti((number, consumer) -> {
consumer.accept(number);
consumer.accept(Math.pow(number, 2));
})
.toList();
</pre>
<p>Would return a list of:</p>
<pre>
[2.0, 4.0, 8.0, 64.0, 16.0, 256.0]
</pre>
</li>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html#mapMultiToInt%28java.util.function.BiConsumer%29">IntStream mapMultiToInt(BiConsumer<? super T,? super IntConsumer> mapper)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html#mapMultiToLong%28java.util.function.BiConsumer%29">LongStream mapMultiToLong(BiConsumer<? super T,? super LongConsumer> mapper)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/stream/Stream.html#mapMultiToDouble%28java.util.function.BiConsumer%29">DoubleStream mapMultiToDouble(BiConsumer<? super T,? super DoubleConsumer> mapper)</a></li>
</ul>
</li>
</ul>
<p>Many <a href="https://seanjmullan.org/blog/2021/03/18/jdk16">security enhancements</a> were baked into <a href="https://openjdk.java.net/projects/jdk/16/">JDK-16</a>, just to mention a couple:</p>
<ul>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8172366">JDK-8172366: Support SHA-3 based signatures</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8254709">JDK-8254709: TLS support for the EdDSA signature algorithm</a></li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8242068">JDK-8242068: Signed JAR support for RSASSA-PSS and EdDSA</a></li>
</ul>
<h3>JDK 17: Mostly There</h3>
<p>Nonetheless the <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a> is not out yet, it has entered <a href="https://mail.openjdk.java.net/pipermail/jdk-dev/2021-July/005758.html">Rampdown Phase Two</a>, meaning its feature set is frozen and no further JEPs will be targeted
to this release. In the upcoming part we are going to cover it right when its release is announced. Stay tuned!</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-45526460554578290322021-05-29T11:33:00.003-07:002021-05-29T11:33:45.002-07:00 Chasing Java's release train, from 8 to 16. Part 2: The race to the next LTS release.<p>In the <a href="https://aredko.blogspot.com/2021/03/chasing-javas-release-train-from-8-to.html">first part</a> we thoroughly went through the massive amount of features delivered in scope of <a href="https://jdk.java.net/java-se-ri/9">JDK-9</a>. Nevertheless, this release was always considered as being transitional, with little or no adoption expected. It has a mission to kick off the race towards next LTS release, <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a>.</p>
<h3>JDK 10</h3>
<p><a href="https://openjdk.java.net/projects/jdk/10/">JDK-10</a>, the first release followed the six months cadence cycle, brought a number of new features into the language and JVM itself. Let us take a look at the most interesting ones from the developer's perspective.</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/286">JEP-286: Local-Variable Type Inference</a>: enhances the Java language to extend type inference to declarations of local variables with initializers. It allows the reserved type name <code>var</code> to be accepted in place of manifest types for local variable declarations with initializers, enhanced for-loop indexes, and index variables declared in traditional <code>for</code> loops:</p>
<pre class="brush:java">
var listOfMaps = new ArrayList<Map<String, String>>();
</pre>
<p>Or, enforcing immutability:</p>
<pre class="brush:java">
final var listOfMaps = new ArrayList<Map<String, String>>();
</pre>
<p>If used carefully, the local-variable type inference leads to more compact and concise code, drastically improving its readability, like for example within try-with-resources blocks or loops. </p>
<pre class="brush:java">
try (var out = new ByteArrayOutputStream()) {
out.write(...);
}
</pre>
<pre class="brush:java">
var listOfMaps = new ArrayList<Map<String, String>>();
for (var e: listOfMaps) {
// ...
}
</pre>
<p>The counterexample would be to strip valuable type information altogether by combining <code>var</code> and diamond operator <code><></code>, likely abusing the feature.</p>
<pre class="brush:java">
var listOfMaps = new ArrayList<>();
</pre>
<p>The fair amount of controversy and confusion around local-variable type inference resulted into publication of the <a href="http://cr.openjdk.java.net/~briangoetz/jep-286/lvti-faq.html">FAQ for JEP 286</a> which I would highly recommend to read over.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/316">JEP-316: Heap Allocation on Alternative Memory Devices</a>: enables the HotSpot VM to allocate the Java object heap on an alternative memory device, such as an NV-DIMM, specified by the user. The new flag <b>-XX:AllocateHeapAt=<path></b> has been added to support such memory device(s).</p></li>
<li><p><a href="https://openjdk.java.net/jeps/310">JEP-310: Application Class-Data Sharing</a>: to improve startup and footprint, extends the existing <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">Class-Data Sharing</a> ("CDS") feature to allow application classes to be placed in the shared archive. By and large, <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">Class-Data Sharing</a> is probably the least known feature of JDK (the story <a href="https://openjdk.java.net/groups/hotspot/docs/RuntimeOverview.html#Class%20Data%20Sharing|outline">goes back to JavaSE 5.0</a>!) which nonetheless is quite handy in many circumstances and could significantly reduce the application startup time (obviously it depends a lot on your application). In scope of this JEP, there are 3 steps to take:
<ol>
<li><p>Determine the classes to archive:</p>
<pre class="brush:java">
$ java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=app.lst -cp app.jar AppMain
</pre>
</li>
<li><p>Create the AppCDS archive:</p>
<pre class="brush:java">$ java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=app.lst -XX:SharedArchiveFile=app.jsa -cp app.jar
</pre>
<p>Please note that we do not launch the application but just provide the complete classpath instead.</p>
</li>
<li><p>Use the AppCDS archive:</p>
<pre class="brush:java">$ java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=app.jsa -cp app.jar AppMain
</pre>
</li>
</ol>
<p>One of the limitation of this JEP is that CDS cannot archive classes from user-defined modules (such as those specified in <b>--module-path</b>) but the good news are that CDS will get more improvements along <a href="https://openjdk.java.net/projects/jdk/12/">JDK-12</a> and <a href="https://openjdk.java.net/projects/jdk/13/">JDK-13</a> releases, stay tuned.
</p>
<li><p><a href="https://openjdk.java.net/jeps/319">JEP-319: Root Certificates</a>: provides a default set of root Certification Authority (CA) certificates in the JDK. Essentially, it means the <b>cacerts</b> keystore will be pre-populated with a set of root certificates issued by the CAs of Oracle's Java SE Root CA Program as such reducing the differences between OpenJDK and Oracle JDK builds. </p></li>
<li><p><a href="https://openjdk.java.net/jeps/322">JEP-322: Time-Based Release Versioning</a>: revises the version-string scheme of the Java SE Platform and the JDK, and related versioning information, for present and future time-based release models. The suggested version number format is:</p>
<pre>
[1-9][0-9]*((\.0)*\.[1-9][0-9]*)*
</pre><p>The version number might be possibly followed by pre-release, build, and other optional information. This new semantics of version numbers is certainly working well so far.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/307">JEP-307: Parallel Full GC for G1</a>: improves G1 worst-case latencies by making the full GC parallel.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/312">JEP-312: Thread-Local Handshakes</a>: introduces a way to execute a callback on threads without performing a global VM safepoint. Make it both possible and cheap to stop individual threads and not just all threads or none. The feature was controlled by new flag <b>-XX:ThreadLocalHandshakes</b> (default value <b>true</b>) which however was deprecated in <a href="https://bugs.openjdk.java.net/browse/JDK-8220050">JDK-13</a> since turning it off became not viable from the performance perspective.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/304">JEP-304: Garbage-Collector Interface</a>: improves the source code isolation of different garbage collectors by introducing a clean garbage collector (GC) interface. The motivation is very simple: make it much easier to implement new collectors and in fact a number of new GC collectors would greatly benefit from this change in later releases.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/313">JEP-313: Remove the Native-Header Generation Tool (javah)</a>: removes the <b>javah</b> tool from the JDK since it has been superseded by superior functionality in <b>javac</b> (<a href="https://bugs.openjdk.java.net/browse/JDK-7150368">JDK-7150368</a>).</p></li>
<li><p><a href="https://openjdk.java.net/jeps/317">JEP-317: Experimental Java-Based JIT Compiler</a>: enables the Java-based JIT compiler, <a href="https://github.com/oracle/graal">Graal</a>, to be used as an experimental JIT compiler on the Linux/x64 platform. Sadly, the story of <a href="https://github.com/oracle/graal">Graal</a> in OpenJDK <a href="http://openjdk.java.net/jeps/410">comes to the end</a> in <a href="https://openjdk.java.net/projects/jdk/17/">JDK-17</a>.</p></li>
<li><p>Multiple <a href="https://docs.oracle.com/javase/10/docs/api/java/util/stream/Collectors.html">Collectors</a> enhancements to deal with unmodifiable collections:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/stream/Collectors.html#toUnmodifiableList%28%29">static <T> Collector<T,?,List<T>> toUnmodifiableList()</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/stream/Collectors.html#toUnmodifiableSet%28%29">static <T> Collector<T,?,Set<T>> toUnmodifiableSet()</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/stream/Collectors.html#toUnmodifiableMap%28java.util.function.Function,java.util.function.Function%29">static <T,K,U> Collector<T,?,Map<K,U>> toUnmodifiableMap(Function, Function)</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/stream/Collectors.html#toUnmodifiableMap%28java.util.function.Function,java.util.function.Function,java.util.function.BinaryOperator%29">static <T,K,U> Collector<T,?,Map<K,U>> toUnmodifiableMap(Function, Function, BinaryOperator)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/util/List.html">List</a> got a new static method:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/List.html#copyOf%28java.util.Collection%29">static <E> List<E> copyOf(Collection<? extends E> coll)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/util/Set.html">Set</a> got a new static method:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/Set.html#copyOf%28java.util.Collection%29">static <E> Set<E> copyOf(Collection<? extends E> coll)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/util/Map.html">Map</a> got a new static method:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/Map.html#copyOf%28java.util.Map%29">static <K,V> Map<K,V> copyOf(Map<? extends K,? extends V> map)</a></li>
</ul>
</li>
<li><p>The family of <a href="https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html">Optional</a> classes (<a href="https://docs.oracle.com/javase/10/docs/api/java/util/OptionalDouble.html">OptionalDouble</a>, <a href="https://docs.oracle.com/javase/10/docs/api/java/util/OptionalInt.html">OptionalInt</a>, <a href="https://docs.oracle.com/javase/10/docs/api/java/util/OptionalLong.html">OptionalLong</a>) got a new method:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html#orElseThrow%28java.util.function.Supplier%29">T orElseThrow()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/util/jar/JarFile.html">JarFile</a>, along with <a href="https://docs.oracle.com/javase/10/docs/api/java/util/jar/JarEntry.html">JarEntry</a>, are finally capable to provide proper support of the <a href="https://openjdk.java.net/jeps/238">multi-release JARs</a>, introduced in <a href="https://aredko.blogspot.com/2021/03/chasing-javas-release-train-from-8-to.html">JDK-9</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/jar/JarFile.html#versionedStream%28%29">Stream<JarEntry> versionedStream()</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/jar/JarEntry.html#getRealName%28%29">String getRealName()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/nio/channels/Channels.html">Channels</a> class got two new overloads of the existing static methods:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/nio/channels/Channels.html#newReader%28java.nio.channels.ReadableByteChannel,java.nio.charset.Charset%29">static Reader newReader(ReadByteChannel, Charset)</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/nio/channels/Channels.html#newWriter%28java.nio.channels.WritableByteChannel,java.nio.charset.Charset%29">static Writer newWriter(WriteByteChannel, Charset)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/invoke/MethodType.html">MethodType</a> was enriched with:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/lang/invoke/MethodType.html#lastParameterType%28%29">Class<?> lastParameterType()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/io/Reader.html">Reader</a> got quite handy new method:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/io/Reader.html#transferTo%28java.io.Writer%29">long transferTo(Writer out) throws IOException</a></li>
</ul>
</li>
<li><p>A new method was introduced into <a href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileStore.html">FileStore</a> class:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/nio/file/FileStore.html#getBlockSize%28%29">long getBlockSize() throws IOException</a></li>
</ul>
</li>
<li><p>A number of new additions went into <a href="https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/StampedLock.html">StampedLock</a> class in a form of static methods:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/StampedLock.html#isLockStamp%28long%29">static boolean isLockStamp(long stamp)</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/StampedLock.html#isOptimisticReadStamp%28long%29">static boolean isOptimisticReadStamp(long stamp)</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/StampedLock.html#isReadLockStamp%28long%29">static boolean isReadLockStamp(long stamp)</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/StampedLock.html#isWriteLockStamp%28long%29">static boolean isWriteLockStamp(long stamp)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/Runtime.Version.html">Version</a> class was enhanced to reflect the <a href="https://openjdk.java.net/jeps/322">JEP-322: Time-Based Release Versioning</a> changes:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/lang/Runtime.Version.html#feature%28%29">int feature()</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/lang/Runtime.Version.html#interim%28%29">int interim()</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/lang/Runtime.Version.html#update%28%29">int update()</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/lang/Runtime.Version.html#patch%28%29">int patch()</a></li>
</ul>
</li>
<li><p>After many years, it became possible to get a PID of the running Java virtual machine using the APIs from standard library, thanks to the new method added to <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/management/RuntimeMXBean.html">RuntimeMXBean</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/lang/management/RuntimeMXBean.html#getPid%28%29">long getPid()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/management/ThreadMXBean.html">ThreadMXBean</a> was extended with new overloaded methods:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/lang/management/ThreadMXBean.html#dumpAllThreads%28boolean,boolean,int%29">ThreadInfo[] dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers, int maxDepth)</a></li>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/lang/management/ThreadMXBean.html#getThreadInfo%28long%5B%5D,boolean,boolean,int%29">ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, boolean lockedSynchronizers, int maxDepth)</a></li>
</ul>
</li>
<li><p>Minor by handy addition to <a href="https://docs.oracle.com/javase/10/docs/api/java/time/format/DateTimeFormatter.html">DateTimeFormatter</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/10/docs/api/java/time/format/DateTimeFormatter.html#localizedBy%28java.util.Locale%29">DateTimeFormatter localizedBy(Locale locale)</a></li>
</ul>
</li>
</ul>
<p>Undoubtedly, <a href="https://openjdk.java.net/projects/jdk/10/">JDK-10</a> release has quite moderate amount of features <a href="https://aredko.blogspot.com/2021/03/chasing-javas-release-train-from-8-to.html">comparing to JDK-9</a>, but every one of those was delivered much faster, thanks to the new release cycle.</p>
<h3>JDK 11</h3>
<p>The first <a href="https://www.oracle.com/java/technologies/java-se-support-roadmap.html">LTS</a> release of the JDK following the new schedule, <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a>, had seen the light in 2018, six month after <a href="https://openjdk.java.net/projects/jdk/10/">JDK-10</a> release. It finally brought a long awaited stability and established a new baseline in post <a href="https://jdk.java.net/java-se-ri/9">JDK-9</a> world. It also included a number of features.</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/327">JEP-327: Unicode 10</a>: upgrades existing platform APIs to support <a href="http://unicode.org/versions/Unicode10.0.0/">version 10.0</a> of the <a href="http://www.unicode.org/standard/standard.html">Unicode Standard</a>. In summary, <a href="">Unicode 10</a> adds 8,518 characters, for a total of 136,690 character, notably:</p>
<ul>
<li>Bitcoin sign</li>
<li>56 emoji characters</li>
<li>A set of Typicon marks and symbols</li>
</ul>
</li>
<li><p><a href="https://openjdk.java.net/jeps/331">JEP-331: Low-Overhead Heap Profiling</a>: provides a low-overhead way of sampling Java heap allocations, accessible via <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html">JVMTI</a>. The JEP brings significant improvements into troubleshooting application memory issues by capturing the call site for particular allocations.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/332">JEP-332: Transport Layer Security (TLS) 1.3</a>: implements version 1.3 of the Transport Layer Security (<a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a>) Protocol, as per <a href="https://tools.ietf.org/html/rfc8446">RFC-8446</a>.
</p> </li>
<li><p><a href="https://openjdk.java.net/jeps/329">JEP-329: ChaCha20 and Poly1305 Cryptographic Algorithms</a>: implements the ChaCha20 and ChaCha20-Poly1305 ciphers as specified in <a href="https://tools.ietf.org/html/rfc7539">RFC-7539</a>. Additionally, the ChaCha20-Poly1305 is opening a door to use <a href="https://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data_%28AEAD%29">AEAD</a>-based cipher suites along with TLS 1.3, nicely complementing <a href="https://openjdk.java.net/jeps/332">JEP-332</a>.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/181">JEP-181: Nest-Based Access Control</a>: introduces nests, an access-control context that aligns with the existing notion of nested types in the Java programming language. Nests allow classes that are logically part of the same code entity, but which are compiled to distinct class files, to access each other's private members without the need for compilers to insert accessibility-broadening bridge methods. The best way to understand the changes delivered by this JEP is to look at one of the examples.</p>
<pre class="brush:java">
public class Outer {
public static class Inner {
public void print(Outer o) {
System.out.println("Inner");
o.print();
}
}
private void print() {
System.out.println("Outer");
}
}
</pre>
<p>The nested <b>Inner</b> class, the logical part of the <b>Outer</b> class, has access to its private methods. How is it possible? The compiler would generate the bridge methods for you, visible in bytecode.</p>
<pre class="brush:java">
$ javap target/classes/com/example/Outer.class
Compiled from "Outer.java"
public class com.example.Outer {
public com.example.Outer();
public static void main(java.lang.String[]);
static void access$0(com.example.Outer);
}
</pre>
<p>Now, when you compile the same class using <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a>, the first thing you will notice is that bridge method <b>access$0</b> is gone. </p>
<pre class="brush:java">
$ javap target/classes/com/example/Outer\$Inner.class
Compiled from "Outer.java"
public class com.example.Outer$Inner {
public com.example.Outer$Inner();
public void print(com.example.Outer);
}
</pre>
<p>Besides changes in JVM and bytecode, there are a number of new methods added to <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html">Class</a> class to reflect the concept of nests and nestmates:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html#getNestHost%28%29">Class<?> getNestHost()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html#isNestmateOf%28java.lang.Class%29">boolean isNestmateOf(Class<?> c)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html#getNestMembers%28%29">Class<?>[] getNestMembers()</a></li>
</ul>
<p>For existing applications and/or libraries, these changes should come at no risk unless access bridge methods are explicitly exploited (by all means, doubtful idea in the first place).</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/321">JEP-321: HTTP Client (Standard)</a>: standardizes the incubated HTTP Client API introduced in <a href="https://jdk.java.net/java-se-ri/9">JDK-9</a>, via <a href="https://openjdk.java.net/jeps/110">JEP-110</a>, and updated in <a href="https://openjdk.java.net/projects/jdk/10/">JDK-10</a>. The API is consolidated under the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html">java.net.http</a> package and consists of:</p>
<ul>
<li>class <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html">HttpClient</a></li>
<li>class <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpHeaders.html">HttpHeaders</a></li>
<li>class <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpRequest.html">HttpRequest</a></li>
<li>class <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpRequest.BodyPublishers.html">HttpRequest.BodyPublishers</a></li>
<li>class <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpResponse.BodyHandlers.html">HttpResponse.BodyHandlers</a></li>
<li>class <a href="HttpResponse.BodySubscribers">HttpResponse.BodySubscribers</a></li>
</ul>
<p>The APIs are concise and easy to use, the snippet below is a convincing example of how intuitive it is (you probably have seen this builder style in many other HTTP clients out there).</p>
<pre class="brush:java">
final HttpClient client = HttpClient
.newBuilder()
.version(Version.HTTP_2)
.connectTimeout(Duration.ofMillis(500))
.followRedirects(Redirect.NEVER)
.build();
final HttpRequest request = HttpRequest.newBuilder()
.POST(BodyPublishers.ofString("...", StandardCharsets.UTF_8))
.uri(URI.create("https://..."))
.header("Content-Type", "application/json")
.build();
final Stream<String> lines = client
.send(request, BodyHandlers.ofLines())
.body();
// ...
</pre>
<p>Not to forget the asynchronous flavor, based on <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CompletableFuture.html">CompletableFuture</a>s.</p>
<pre class="brush:java">
final CompletableFuture<Stream<String>> lines = client
.sendAsync(request, BodyHandlers.ofLines())
.thenApply(HttpResponse::body);
// ...
</pre>
<p>And obviously, the reactive style, using the APIs introduced by <a href="https://openjdk.java.net/jeps/266">JEP-266</a> in JDK-9:</p>
<pre class="brush:java">
final Subscriber<String> subscriber = ...;
final CompletableFuture<HttpResponse<Void>> response = client
.sendAsync(request, BodyHandlers.fromLineSubscriber(subscriber));
// ...
</pre>
<p>The HTTP Client supports <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.1</a>, <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> (<a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> is the default preferred protocol, and the implementation seamlessly fallbacks to <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP/1.1</a> if necessary) and <a href="https://en.wikipedia.org/wiki/WebSocket">Websocket</a>s. The <a href="https://openjdk.java.net/groups/net/httpclient/intro.html">Introduction to the Java HTTP Client</a> is a good starting point to quickly surface the capabilities of the APIs.
</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/323">JEP-323: Local-Variable Syntax for Lambda Parameters</a>: allows <code>var</code> to be used when declaring the formal parameters of implicitly typed lambda expressions. This rather small JEP delivers a substantial convenience to <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.27.1">labmda expressions</a>.</p>
<pre class="brush:java">
final Comparator<String> comparator = (@Nonnull var s1, @Nonnull var s2) -> {
return ...;
};
</pre>
<p>A <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.27.1">lambda expression</a> may be implicitly typed, nothing new here, however when you want to decorate its parameters with annotations, it used to require providing the explicit types. With this JEP, not anymore, the <code>var</code> could be used instead. Please note that an implicitly typed <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.27.1">lambda expression</a> must use <code>var</code> for all its formal parameters or for none of them.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/328">JEP-328: Flight Recorder</a>: provides a low-overhead data collection framework for troubleshooting Java applications and the HotSpot JVM. Flight Recorder has existed for many years and was previously a commercial feature of the Oracle JDK but since <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a>, it has been open-sourced (and <a href="https://aredko.blogspot.com/2021/03/chasing-javas-release-train-from-8-to.html">backported to JDK-8</a>). The Flight Recorder command line tool, <a href="https://docs.oracle.com/en/java/javase/13/docs/specs/man/jfr.html">jfr</a>, which appeared only in <a href="https://openjdk.java.net/projects/jdk/12/">JDK-12</a>, was also <a href="https://bugs.openjdk.java.net/browse/JDK-8228669">backported to JDK-11</a> and is available as of <b>11.0.6</b> release. The Flight Recorder could be activated in a different ways:</p>
<ul>
<li>Using JVM command line arguments:
<pre>$ java -XX:StartFlightRecording=settings=profile,duration=6m,name=app-startup,dumponexit=true,filename=/var/log/jfr/app-startup.jfr ...</pre>
</li>
<li>Using <a href="https://docs.oracle.com/en/java/javase/11/tools/jcmd.html">jcmd</a> command line tool:</li>
<pre>
$ jcmd <pid> JFR.start settings=profile duration=6m name=app-startup
$ jcmd <pid> JFR.dump filename=app-startup.jfr
$ jcmd <pid> JFR.stop</pre>
<li>Using <a href="http://jdk.java.net/jmc/8/">JDK Mission Control</a></li>
</ul>
<p>The Flight Recorder recordings could be visualized in <a href="http://jdk.java.net/jmc/8/">JDK Mission Control</a> or analyzed from the command line using <a href="https://docs.oracle.com/en/java/javase/13/docs/specs/man/jfr.html">jfr</a> tool. To be fair, these days the Flight Recorder is a primary go-to tool for troubleshooting JVM applications in production.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/330">JEP-330: Launch Single-File Source-Code Programs</a>: enhances the java launcher to run a program supplied as a single file of Java source code, including usage from within a script by means of <a href="https://openjdk.java.net/jeps/330#Shebang_files">"shebang" files</a> and related techniques. Who would have thought that one day Java would replace your favorite shell scripts? Well, since <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a> you could!</p>
<ul>
<li>Launch a class declared in a source file: <pre>$ java MyScript.java</pre></li>
<li>A <a href="https://en.wikipedia.org/wiki/Shebang_%28Unix%29">shebang</a> file to invoke the Java launcher using source-file mode:
<pre>#!/path/to/java --source <version></pre>
<p>Please note that in this case the file should not be named as a Java source file (i.e. it should not be a file whose name ends with <code>.java</code>)</p>
</li>
</ul>
<p>The JEP inspired a number of innovative projects, like <a href="https://github.com/jbangdev/jbang">jbang</a> f.e., to simplify launching <code>.java</code> files from literally anywhere.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/320">JEP-320: Remove The Java EE and CORBA Modules</a>: removes the Java EE and CORBA modules from the Java SE Platform and the JDK. The following modules have been removed:</p>
<ul>
<li><b>java.xml.ws</b>: replacement <a href="https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22javax.xml.ws%22%20AND%20a%3A%22jaxws-api%22">javax.xml.ws:jaxws-api</a>, <a href="https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22javax.xml.soap%22%20AND%20a%3A%22javax.xml.soap-api%22">javax.xml.soap:javax.xml.soap-api</a>, <a href="https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22javax.xml%22%20AND%20a%3A%22webservices-api%22">javax.xml:webservices-api</a></li>
<li><b>java.xml.ws.annotation</b>: replacement <a href="https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22javax.annotation%22%20AND%20a%3A%22javax.annotation-api%22">javax.annotation:javax.annotation-api</a></li>
<li><b>java.xml.bind</b>: replacement <a href="https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22javax.xml.bind%22%20AND%20a%3A%22jaxb-api%22">javax.xml.bind:jaxb-api</a></li>
<li><b>java.activation</b>: replacement <a href="https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22javax.activation%22%20AND%20a%3A%22javax.activation-api%22">javax.activation:javax.activation-api</a></li>
<li><b>java.corba</b>: replacement <a href="https://www.jacorb.org">JacORB</a></li>
<li><b>java.transaction</b>: replacement <a href="https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22javax.transaction%22%20AND%20a%3A%22javax.transaction-api%22">javax.transaction:javax.transaction-api</a></li>
</ul>
<p>Since <a href="https://www.oracle.com/ca-en/java/technologies/java-ee-glance.html">Java EE</a> has been superseded by <a href="https://jakarta.ee/">Jakarta EE</a>, all most recent replacements could be found <a href="https://wiki.eclipse.org/Jakarta_EE_Maven_Coordinates">under new Jakarta brand</a>.</p>
</li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8250784">JDK-8250784: Shenandoah: A Low-Pause-Time Garbage Collector</a>: the <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah GC</a> has been <a href="https://bugs.openjdk.java.net/browse/JDK-8250784">backported to JDK-11</a> and is available in most distributions since <b>11.0.9</b> release.</p>
</li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8191369">JDK-8191369: NMT: Enhance thread stack tracking</a>: great improvement to native memory tracking.</p>
</li>
<li><p>Probably, the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html">String</a> class had the largest number of new API methods added:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#isBlank%28%29">boolean isBlank(</a>)</li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#lines%28%29">Stream<String> lines()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#repeat%28int%29">String repeat(int count)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#strip%28%29">String strip()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#stripLeading%28%29">String stripLeading()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#stripTrailing%28%29">String stripTrailing()</a></li>
</ul>
</li>
<li><p>The family of <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Optional.html">Optional</a> classes (<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/OptionalDouble.html">OptionalDouble</a>, <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/OptionalInt.html">OptionalInt</a>, <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/OptionalLong.html">OptionalLong</a>) got a single new method:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Optional.html#isEmpty%28%29">boolean isEmpty()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html">Pattern</a> has gotten one more method to support <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/Predicate.html">Predicate</a> for matches, exceptionally convenient:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html#asMatchPredicate%28%29">Predicate<String> asMatchPredicate()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/Predicate.html">Predicate</a> in turn could now be negated:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/Predicate.html#not%28java.util.function.Predicate%29">static <T> Predicate<T> not(Predicate<? super T> target)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/ByteArrayOutputStream.html">ByteArrayOutputStream</a> could write the complete content now:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/ByteArrayOutputStream.html#writeBytes%28byte%5B%5D%29">void writeBytes(byte[] b)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream.html">InputStream</a> got a few additions:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream.html#nullInputStream%28%29">static InputStream nullInputStream()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream.html#readNBytes%28byte%5B%5D,int,int%29">byte[] readNBytes(int len) throws IOException</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/OutputStream.html">OutputStream</a> was also not left out:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/OutputStream.html#nullOutputStream%28%29">static OutputStream nullOutputStream()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/Reader.html">Reader</a> followed the same route:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/Reader.html#nullReader%28%29">static Reader nullReader(</a>)</li>
</ul>
</li>
<li><p>As well as the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/Writer.html">Writer</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/Writer.html#nullWriter%28%29">static Writer nullWriter()</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/CharSequence.html">CharSequence</a> was enriched with lexicographical comparison:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/CharSequence.html">static int compare(CharSequence cs1, CharSequence cs2)</a></li>
</ul>
</li>
<li><p>A whole family of buffer classes got the mismatch detection support:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ByteBuffer.html">ByteBuffer</a> was added <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ByteBuffer.html#mismatch%28java.nio.ByteBuffer%29">int mismatch(ByteBuffer that)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/CharBuffer.html">CharBuffer</a> was added <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/CharBuffer.html#mismatch%28java.nio.CharBuffer%29">int mismatch(CharBuffer that)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/DoubleBuffer.html">DoubleBuffer</a> was added <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/DoubleBuffer.html#mismatch%28java.nio.DoubleBuffer%29">int mismatch(DoubleBuffer that)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/FloatBuffer.html">FloatBuffer</a> was added <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/FloatBuffer.html#mismatch%28java.nio.FloatBuffer%29">int mismatch(FloatBuffer that)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/LongBuffer.html">LongBuffer</a> was added <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/LongBuffer.html#mismatch%28java.nio.LongBuffer%29">int mismatch(LongBuffer that)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ShortBuffer.html">ShortBuffer</a> was added <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ShortBuffer.html#mismatch%28java.nio.ShortBuffer%29">int mismatch(ShortBuffer that)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/SelectionKey.html">SelectionKey</a> got a few atomic operations:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/SelectionKey.html#interestOpsOr%28int%29">int interestOpsOr(int ops)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/SelectionKey.html#interestOpsAnd%28int%29">int interestOpsAnd(int ops)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/Selector.html">Selector</a> had a number of overloaded variants introduced:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/Selector.html#select%28java.util.function.Consumer%29">int select(Consumer<SelectionKey> action) throws IOException</a></li>
<li>i<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/Selector.html#select%28java.util.function.Consumer,long%29">nt select(Consumer<SelectionKey> action, long timeout) throws IOException</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/Selector.html#selectNow%28java.util.function.Consumer%29">int selectNow(Consumer<SelectionKey> action) throws IOException</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Files.html">Files</a> utility class became even more useful:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Files.html#readString%28java.nio.file.Path%29">static String readString(Path path) throws IOException</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Files.html#readString%28java.nio.file.Path,java.nio.charset.Charset%29">static String readString(Path path, Charset cs) throws IOException</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Files.html#writeString%28java.nio.file.Path,java.lang.CharSequence,java.nio.file.OpenOption...%29">static Path writeString(Path path, CharSequence csq, OpenOption... options) throws IOException</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Files.html#writeString%28java.nio.file.Path,java.lang.CharSequence,java.nio.charset.Charset,java.nio.file.OpenOption...%29">static Path writeString(Path path, CharSequence csq, Charset cs, OpenOption... options) throws IOException</a></li>
</ul>
</li>
<li><p>In the same vein, the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Path.html">Path</a> class had a few factory methods introduced:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Path.html#of%28java.lang.String,java.lang.String...%29">static Path of(String first, String... more)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Path.html#of%28java.net.URI%29">static Path of(URI uri)</a></li>
</ul>
</li>
<li><p>A new default method was added to <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Collection.html">Collection</a> interface, complementing other favors:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Collection.html#toArray%28java.util.function.IntFunction%29">default <T> T[] toArray(IntFunction<T[]> generator)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/TimeUnit.html">TimeUnit</a> had a new conversion option:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/TimeUnit.html#convert%28java.time.Duration%29">long convert(Duration duration)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html">PriorityQueue</a> was enhanced with the implementation of:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html#forEach%28java.util.function.Consumer%29">void forEach(Consumer<? super E> action)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html#removeIf%28java.util.function.Predicate%29">boolean removeIf(Predicate<? super E> filter)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html#removeAll%28java.util.Collection%29">boolean removeAll(Collection<?> c)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html#retainAll%28java.util.Collection%29">boolean retainAll(Collection<?> c)</a></li>
</ul>
</li>
<li><p>Consequently, the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/PriorityBlockingQueue.html">PriorityBlockingQueue</a> was enhanced with the implementation of:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/PriorityBlockingQueue.html#forEach%28java.util.function.Consumer%29">void forEach(Consumer<? super E> action)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/PriorityBlockingQueue.html#removeIf%28java.util.function.Predicate%29">boolean removeIf(Predicate<? super E> filter)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/PriorityBlockingQueue.html#removeAll%28java.util.Collection%29">boolean removeAll(Collection<?> c)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/PriorityBlockingQueue.html#retainAll%28java.util.Collection%29">boolean retainAll(Collection<?> c)</a></li>
</ul>
</li>
<li><p>Multiple enhancements went into <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/ByteBuffer.html">ByteBuffer</a> support by <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Deflater.html">Deflater</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Deflater.html#setInput%28java.nio.ByteBuffer%29">void setInput(ByteBuffer input)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Deflater.html#setDictionary%28java.nio.ByteBuffer%29">void setDictionary(ByteBuffer dictionary)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Deflater.html#deflate%28java.nio.ByteBuffer%29">int deflate(ByteBuffer output)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Deflater.html#deflate%28java.nio.ByteBuffer,int%29">int deflate(ByteBuffer output, int flush)</a></li>
</ul>
</li>
<li><p>... and by <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Inflater.html">Inflater</a>:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Inflater.html#setInput%28java.nio.ByteBuffer%29">void setInput(ByteBuffer input)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Inflater.html#setDictionary%28java.nio.ByteBuffer%29">void setDictionary(ByteBuffer dictionary)</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Inflater.html#inflate%28java.nio.ByteBuffer%29">int inflate(ByteBuffer output) throws DataFormatException</a></li>
</ul>
</li>
</ul>
<p>It worth to note that <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a> had introduced two new garbage collectors, <a href="https://openjdk.java.net/jeps/333">ZGC</a> and <a href="https://openjdk.java.net/jeps/318">Epsilon</a>, both were marked as experimental. We are going to get back to those in the upcoming posts while discussing more recent JDK releases.</p>
<p>So, where are we today? The <a href="https://openjdk.java.net/projects/jdk/11/">JDK-11</a> slowly but steadily getting more adoption as more and more projects migrate off the JDK-8. Nonetheless, the <a href="https://www.jrebel.com/blog/2021-java-technology-report">majority are still on JDK-8</a> and in my opinion, there are no reasons to expect drastic changes of the balance within next couple of years. But this is another story ...</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-79319479243992469202021-03-31T15:27:00.000-07:002021-03-31T15:27:32.659-07:00Chasing Java's release train, from 8 to 16. Part 1: JDK 9, the last big banger ...<p>A lot has been said about JDK's <a href="https://mreinhold.org/blog/forward-faster">new 6 months release cadence cycle</a>, and arguably, this is one of the most profound decisions being made in the recent history of the language and JVM platform in general. But if you, like me and <a href="https://snyk.io/blog/developers-dont-want-to-leave-java-8-as-64-hold-firm-on-their-preferred-release/">other ~60%</a> of the Java developers out there, stuck with JDK 8, the new releases mean little to your day to day job. But the language changes, the standard library changes, the JVM changes and so does the tooling around it. More importantly, the ecosystem also changes by aggressively raising the compatibility baselines. Java in 2021 is not the same as it was in 2020, 2017 and all the more in 2014, when Java 8 has <a href="https://en.wikipedia.org/wiki/Java_version_history#Java_SE_8">seen the light</a>. The soon to be out <a href="https://openjdk.java.net/projects/jdk/17/">JDK 17</a>, the next LTS release, would raise the bar even higher.</p>
<p>So the challenge many of us, seasoned Java developers, are facing is how to stay up to date? The risk of your skills becoming outdated is real. Hence this series of the blog posts: to consolidate the changes in the language, JVM, standard library and tooling over each release, primary from the perspective of the developer. At the end, we should be all set to meet the <a href="https://openjdk.java.net/projects/jdk/17/">JDK 17</a> fully armed. To mention upfront, we are not going to talk about incubating or preview features, nor about come-and-go ones (looking at you, <a href="https://bugs.openjdk.java.net/browse/JDK-8255616">jaotc</a>). Whenever it makes sense, the feature would have a corresponding <a href="https://en.wikipedia.org/wiki/JDK_Enhancement_Proposal">JEP</a> link, but overall there are many changes not covered by dedicated proposals. I will try to include as much as possible but there are chances some useful features may still slip away.
</p>
<p>With that, let us open the stage with JDK 8.</p>
<h3>JDK 8</h3>
Unsurprisingly, JDK 8 does not only receive the security patches and bugfixes but gets some of the new features from the upstream. The two notable ones are:
<ul>
<li>
<p>Improved Docker container detection and resource configuration usage: since <a href="https://www.oracle.com/java/technologies/javase/8u191-relnotes.html">8u191</a>, the JVM is fully container-aware (see please <a href="https://bugs.openjdk.java.net/browse/JDK-8146115">JDK-8146115</a>). The support is only available on Linux based platforms and enabled by default.</p></li>
<li><p><a href="https://mail.openjdk.java.net/pipermail/jdk8u-dev/2020-January/011063.html">JFR backport</a>: since <a href="https://mail.openjdk.java.net/pipermail/jdk8u-dev/2020-July/012143.html">8u262</a>, JFR is supported seamlessly (see please <a href="https://bugs.openjdk.java.net/browse/JDK-8223147">JDK-8223147</a>), no need to pass <b>-XX:+UnlockCommercialFeatures</b> and alike anymore</p></li>
</ul>
<p>Although not strictly related, thanks to the <a href="https://mail.openjdk.java.net/pipermail/jdk8u-dev/2020-January/011063.html">JFR backport</a>, the latest <a href="https://adoptopenjdk.net/jmc">JDK Mission Control 8</a> release comes very handy to everyone. Hopefully, your organization does update to the recent JDK patch releases on the regular basis and these features are already available to you.</p>
<h3>JDK 9</h3>
<p>The <a href="https://jdk.java.net/java-se-ri/9">JDK 9 release</a> was just huge (90+ JEPs): not only from the perspective of the bundled features, but also from the impact on the platform and ecosystem. It was 2017 but even today the groundwork established by <a href="https://jdk.java.net/java-se-ri/9">JDK 9</a> still continues. Let us take a look at the major features which went into it.</p>
<ul>
<li><p><a href="https://openjdk.java.net/jeps/261">JEP-261: Module System</a>: this change by itself is <a href="https://www.manning.com/books/the-java-module-system">worth a book</a>. Fast track to today, the debates are still ongoing if this feature is useful outside of JDK itself. The adoption of Java modules is not that high to be fair, and besides <b>Automatic-Module-Name</b>, not that many projects decided to take the full-fledged modularization path. Along with numerous JVM and standard library changes, the <a href="https://cr.openjdk.java.net/~mr/jigsaw/spec/">JPMS</a> comes with:</p>
<ul>
<li>New <a href="https://docs.oracle.com/javase/9/tools/java.htm">java</a> command line options:
<ul>
<li><b>--add-exports</b>: to export a package, which makes its public types and members accessible</li>
<li><b>--add-opens</b>: to open a package, which makes all its types and members accessible</li>
<li><b>--patch-module</b>: adds classes to a specific module</li>
<li><b>--add-modules</b>: adds the listed modules and their transitive dependencies to the module graph</li>
<li><b>--add-reads</b>: makes one module read another</li>
<li><b>--list-modules</b>: lists the observable modules</li>
<li><b>--limit-modules</b>: specifies the limit of the universe of observable modules</li>
<li><b>--describe-module</b>: describes a specified module</li>
<li><b>--validate-modules</b>: validates all modules</li>
<li><b>--illegal-access=permit|warn|debug|deny</b>: accessing internal APIs</li>
</ul>
</li>
<li>New <a href="https://docs.oracle.com/javase/9/tools/javac.htm">javac</a> command line options:
<ul>
<li><b>--add-modules</b>: specifies root modules to resolve in addition to the initial modules</li>
<li><b>--limit-modules</b>: specifies the limit of the universe of observable modules</li>
<li><b>--module</b>: compiles only the specified module and checks time stamps</li>
<li><b>--module-path</b>: specifies where to find application modules</li>
<li><b>--module-source-path</b>: specifies where to find input source files for multiple modules</li>
<li><b>--module-version</b>: specifies the version of modules that are being compiled</li>
<li><b>--add-exports</b>: specifies a package to be considered as exported from its defining module</li>
<li><b>--add-reads</b>: specifies additional modules to be considered as required by a given module</li>
<li><b>--release</b>: compiles against the public, supported and documented API for a specific VM version</li>
</ul>
</li>
<li>New <a href="https://docs.oracle.com/javase/9/tools/jmod.htm">jmod</a> tool </li>
</ul>
</li>
<li><p><a href="">JEP-260: Encapsulate Most Internal APIs</a>: encapsulates most of the JDK's internal APIs by default so that they are inaccessible at compile time, and prepare for a future release in which they will be inaccessible at run time. As we are going to see later on, the hardening efforts kicked off in this JEP are continued in later JDK releases.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/282">JEP-282: jlink: The Java Linker</a>: a tool that can assemble and optimize a set of modules and their dependencies into a custom run-time image. It is closely related to <a href="https://openjdk.java.net/jeps/220">JEP-220: Modular Run-Time Images</a> and essentially allows to bundle a highly optimized distribution tailored to the needs of your applications. As you may expect, <a href="https://docs.oracle.com/javase/9/tools/jlink.htm">jlink</a> only works with applications which are fully modularized, otherwise you <a href="https://medium.com/azulsystems/using-jlink-to-build-java-runtimes-for-non-modular-applications-9568c5e70ef4">have to do heavy lifting</a> yourself.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/238">JEP 238: Multi-Release JAR Files</a>: extends the JAR file format to allow multiple, Java-release-specific versions of class files to coexist in a single archive. The goal of this change is to allow third party libraries and frameworks to take advantage of the language or/and API features available in newer releases but still supporting the older ones. The feature has proven to be quite useful in practice but the tooling support around it, <a href="https://maven.apache.org/">Apache Maven</a> in particular, is still falling behind: see please <a href="https://maven.apache.org/plugins/maven-compiler-plugin/multirelease.html">here</a> and <a href="https://issues.apache.org/jira/browse/SUREFIRE-1731">here</a>.</p></li>
<li><p><a href="http://openjdk.java.net/jeps/241">JEP-241: Remove the jhat Tool</a>: <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jhat.html">jhat</a> was an experimental, unsupported, and out-of-date tool, superior heap visualizers and analyzers have now been available for many years.</p></li>
<li><p><a href="http://openjdk.java.net/jeps/228">JEP-228: Add More Diagnostic Commands</a>: defines additional diagnostic commands in order to improve the diagnosability of Hotspot VM and the JDK. In scope of this feature, new diagnostic commands have been added to <a href="https://docs.oracle.com/javase/9/tools/jcmd.htm">jcmd</a> tool:</p>
<ul>
<li><b>VM.info</b></li>
<li><b>VM.class_hierarchy</b></li>
<li><b>VM.set_flag</b></li>
<li><b>JVMTI.data_dump</b></li>
</ul>
</li>
<li><p><a href="http://openjdk.java.net/jeps/224">JEP-224: HTML5 Javadoc</a>: enhances the <a href="https://docs.oracle.com/javase/9/tools/javadoc.htm">javadoc</a> tool to generate <a href="https://en.wikipedia.org/wiki/HTML5">HTML5</a> markup. The primary drivers behind this feature are to increase the semantic value of web pages and to make it easier to create accessible web pages.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/229">JEP-229: Create PKCS12 Keystores by Default</a>: transitions the default keystore type from JKS to <a href="https://en.wikipedia.org/wiki/PKCS_12">PKCS12</a>. The applications may explicitly specify the keystore type they require.
</p></li>
<li><p>
<a href="https://openjdk.java.net/jeps/244%29">JEP-244: TLS Application-Layer Protocol Negotiation Extension</a>: extends the <b>javax.net.ssl</b> package to support the TLS <a href="http://www.rfc-editor.org/rfc/rfc7301.txt">Application Layer Protocol Negotiation (ALPN) Extension</a>, which provides the means to negotiate an application protocol for a TLS connection. It is a necessary prerequisite for <a href="http://www.rfc-editor.org/info/rfc7540">HTTP/2</a> support, both for client-side and for server-side.
</p></li>
<li><p>
<a href="http://openjdk.java.net/jeps/226">JEP-226: UTF-8 Property Resource Bundles</a>: changes the default file encoding for <a href="https://docs.oracle.com/javase/9/docs/api/java/util/ResourceBundle.html">ResourceBundle</a> class to load properties files from ISO-8859-1 to UTF-8, applications no longer need to convert the properties files using the escape mechanism.
</p></li>
<li><p><a href="https://openjdk.java.net/jeps/243">JEP-243: Java-Level JVM Compiler Interface</a>: introduces a Java based JVM compiler interface (JVMCI) enabling a compiler written in Java to be used by the JVM as a dynamic compiler. It paved the path for <a href="https://github.com/oracle/graal/blob/master/compiler/README.md">Graal JIT compiler</a> integration into HotSpot VM.
</p></li>
<li><p><a href="https://openjdk.java.net/jeps/222">JEP-222: jshell: The Java Shell (Read-Eval-Print Loop)</a>: a true gem of the <a href="https://jdk.java.net/java-se-ri/9">JDK 9 release</a>, an interactive tool to evaluate declarations, statements, and expressions of the Java programming language, together with an API so that other applications can leverage this functionality. Just use it, very handsome, luckily we have <a href="https://github.com/johnpoth/jshell-maven-plugin">JShell Maven Plugin</a> to take care of the classpath manipulations.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/248">JEP-248: Make G1 the Default Garbage Collector</a>: G1 becomes the default garbage collector on 32- and 64-bit server configurations. This change is based on the assumption that limiting latency is often more important than maximizing throughput.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/271">JEP-271: Unified GC Logging</a>: reimplements GC logging in a manner that is as consistent as is reasonable with the current GC logging format. There are a lot of details baked in, I would highly recommend the series of the articles (<a href="https://foojay.io/today/introduction-to-jvm-unified-logging-jep-158-jep-271/">part 1</a>, <a href="https://foojay.io/today/embracing-jvm-unified-logging/">part 2</a>) to learn about GC logging configuration and migration hints.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/264">JEP-264: Platform Logging API and Service</a>: defines a minimal logging API which platform classes can use to log messages, together with a service interface for consumers of those messages. A library or application can provide an implementation of this service in order to route platform log messages to the logging framework of its choice. If no implementation is provided then a default implementation based upon the <b>java.util.logging</b> API is used.
</p><p>This is quite valuable feature since the applications would be able to configure the JDK to use the same logging framework as the application itself. The <a href="https://logging.apache.org/log4j/2.x/">Log4j2</a> already <a href="https://issues.apache.org/jira/browse/LOG4J2-1360">supports</a> that but <a href="http://www.slf4j.org/">SLF4J</a> is <a href="https://jira.qos.ch/browse/SLF4J-442">lagging</a> a bit. In any case, the integration is looking <a href="https://www.baeldung.com/java-9-logging-api">quite straightforward</a>.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/277">JEP-277: Enhanced Deprecation</a>: allows to communicate better the status and intended disposition of APIs in the specification. The <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Deprecated.html">@Deprecated</a> annotation acquired two new properties: <b>forRemoval</b> and <b>since</b>.</p>
<pre class="brush:java">
@Deprecated(since = "11", forRemoval = true)
</pre>
<p>With such additions it became possible to express the expectations regarding the API lifecycle in more structural and formal way.</p></li>
</p></li>
<li><p>
<a href="https://openjdk.java.net/jeps/285">JEP-285: Spin-Wait Hints</a>: defines an API to allow Java code to hint to the run-time system that a spin loop is being executed. It resulted into adding a new (empty) method to <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Thread.html">java.lang.Thread</a>: <b>onSpinWait()</b>.
</p>
<pre class="brush:java">
while ( /* condition */ ) {
Thread.onSpinWait();
}
</pre>
<p>If you would like to learn more about how this method is being implemented in the HotSpot VM, please check <a href="https://bugs.openjdk.java.net/browse/JDK-8147844">JDK-8147844</a> issue.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/287">JEP-287: SHA-3 Hash Algorithms</a>: implements the <a href="https://en.wikipedia.org/wiki/SHA-3">SHA-3</a> cryptographic hash functions, quite timely addition to the Java's security library. The <a href="https://docs.oracle.com/javase/9/docs/api/java/security/MessageDigest.html">MessageDigest</a> implementation has been enhanced with <b>SHA3-224</b>, <b>SHA3-256</b>, <b>SHA3-384</b> and <b>SHA3-512</b> algorithms.</p></li>
<pre class="brush:java">
final MessageDigest digest = MessageDigest.getInstance("SHA3-512");
...
</pre>
<li><p><a href="https://openjdk.java.net/jeps/254">JEP 254: Compact Strings</a>: adopts a more space-efficient internal representation for strings while maintaining performance in most scenarios and preserving full compatibility for all related Java and native interfaces. Practically, the internal representation has changed from a UTF-16 char array to a byte array plus an encoding-flag field, no changes to existing public APIs.</p></li>
<li><p><a href="https://openjdk.java.net/jeps/266">JEP-266: More Concurrency Updates</a>: adds an interoperable publish-subscribe framework, enhancements to the <a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html">CompletableFuture</a> API, and various other improvements.</p>
<ul>
<li><a href="https://www.reactive-streams.org/">Reactive Streams</a> publish/subscribe APIs (and supporting classes):</li>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html">java.util.concurrent.Flow</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.Publisher.html">java.util.concurrent.Flow.Publisher</a></li>
<li>j<a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.Subscriber.html">ava.util.concurrent.Flow.Subscriber</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.Processor.html">java.util.concurrent.Flow.Processor</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.Subscription.html">java.util.concurrent.Flow.Subscription</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/SubmissionPublisher.html">java.util.concurrent.SubmissionPublisher</a></li>
</ul>
<li>The <a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html">CompletableFuture</a> gained a plethora of new public API methods:</li>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#newIncompleteFuture--">CompletableFuture<T> newIncompleteFuture()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#defaultExecutor--">Executor defaultExecutor()</a> (subclassing enhancement, to allow supporting an alternative default executor)</li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#copy--">CompletableFuture<T> copy()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#minimalCompletionStage--">CompletableFuture<T> minimalCompletionStage()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#completeAsync-java.util.function.Supplier-java.util.concurrent.Executor-">CompletableFuture<T> completeAsync(Supplier<? extends T> supplier, Executor executor)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#completeAsync-java.util.function.Supplier-">CompletableFuture<T> completeAsync(Supplier<? extends T> supplier)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#orTimeout-long-java.util.concurrent.TimeUnit-">CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#completeOnTimeout-T-long-java.util.concurrent.TimeUnit-">CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#failedFuture-java.lang.Throwable-">static <U> CompletableFuture<U> failedFuture(Throwable ex)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#completedStage-U-">static <U> CompletionStage<U> completedStage(U value)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#failedStage-java.lang.Throwable-">static <U> CompletionStage<U> failedStage(Throwable ex)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#delayedExecutor-long-java.util.concurrent.TimeUnit-">static Executor delayedExecutor(long delay, TimeUnit unit)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#delayedExecutor-long-java.util.concurrent.TimeUnit-java.util.concurrent.Executor-">static Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)</a></li>
</ul>
</ul>
<p>Arguably, the inclusion of the delays and timeouts into standard library was the most awaited feature. It eliminated a number of the flaws and pain points that developers had been facing using <a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html">CompletableFuture</a> APIs for years.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/143">JEP-143: Improve Contended Locking</a>: improves the overall performance of contended Java object monitors.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/290">JEP-290: Filter Incoming Serialization Data</a>: allows incoming streams of object-serialization data (<a href="https://docs.oracle.com/javase/9/docs/api/java/io/ObjectInputStream.html">ObjectInputStream</a>) to be filtered in order to improve both security and robustness. A process-wide filter could be configured via a system property or a configuration file. The system property, if supplied, supersedes the security property value:
<ul>
<li>System property: <b>jdk.serialFilter</b></li>
<li>Security property: <b>jdk.serialFilter</b> in <b>conf/security/java.properties</b></li>
</ul></p></li>
<li><p><a href="https://openjdk.java.net/jeps/102">JEP-102: Process API Updates</a>: improves the API for controlling and managing operating-system processes.</p>
<ul>
<li>A new <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ProcessHandle.html">java.lang.ProcessHandle</a> interface has been introduced</li>
<li>The <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Process.html">java.lang.Process</a> class API has been enriched
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Process.html#supportsNormalTermination--">boolean supportsNormalTermination()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Process.html#pid--">long pid()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Process.html#onExit--">CompletableFuture<Process> onExit()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Process.html#toHandle--">ProcessHandle toHandle()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Process.html#info--">ProcessHandle.Info info()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Process.html#children--">Stream<ProcessHandle> children()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Process.html#descendants--">Stream<ProcessHandle> descendants()</a></li>
</ul>
</li>
<li>The <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ProcessBuilder.html">java.lang.ProcessBuilder</a> class API has been enriched
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/ProcessBuilder.html#startPipeline-java.util.List-">static List<Process> startPipeline(List<ProcessBuilder> builders)</a></li>
</ul>
</li>
</ul>
</li>
<li><p><a href="https://openjdk.java.net/jeps/280">JEP-280: Indify String Concatenation</a>: changes the static <b>String</b>-concatenation bytecode sequence generated by <a href="https://docs.oracle.com/javase/9/tools/javac.htm">javac</a> to use <a href="https://blogs.oracle.com/javamagazine/understanding-java-method-invocation-with-invokedynamic">invokedynamic</a> calls to JDK library functions. This will enable future optimizations of <b>String</b> concatenation without requiring further changes to the bytecode emitted by <a href="https://docs.oracle.com/javase/9/tools/javac.htm">javac</a>. This one deserves more attention since it touches upon the most painful code review subject: string concatenation using '+' in Java. Suppose we have a following code snippet.</p>
<pre class="brush:java">
public static void main(String[] args) {
final String s = "folder" + File.separator + "file";
System.out.println(s);
}
</pre>
<p>In JDK 8, when we compile it, the following byte code is going to be generated:</p>
<pre class="brush:xml">
...
public static void main(java.lang.String[]);
Code:
0: new #16 // class java/lang/StringBuilder
3: dup
4: ldc #18 // String folder
6: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
9: getstatic #23 // Field java/io/File.separator:Ljava/lang/String;
12: invokevirtual #29 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: ldc #33 // String file
17: invokevirtual #29 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
23: astore_1
24: getstatic #39 // Field java/lang/System.out:Ljava/io/PrintStream;
27: aload_1
28: invokevirtual #45 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
31: return
</pre>
<p>The <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html">StringBuilder</a> has been introduced by the compiler to handle the concatenation efficiently. But on JDK 9 (and onwards), the picture is completely different:</p>
<pre class="brush:xml">
...
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/io/File.separator:Ljava/lang/String;
3: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
8: astore_1
9: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
12: aload_1
13: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
</pre>
<p>If you would like to learn more, besides reading JEP itself, I would highly recommend to glance over this blog post: <a href="https://www.guardsquare.com/en/blog/string-concatenation-java-9-untangling-invokedynamic">part 1</a>, <a href="https://www.guardsquare.com/en/blog/string-concatenation-java-9-conversion-confusion">part 2</a>.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/269">JEP-269: Convenience Factory Methods for Collections</a>: defines the library APIs to make it convenient to create instances of collections and maps with small numbers of elements, so as to ease the pain of not having collection literals in the Java programming language. Undoubtedly, the fluent collections factories significantly reduced the amount of boilerplate (as well as <a href="https://www.baeldung.com/java-initialize-hashmap">tricks</a>) the developers had to deal with.</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/List.html#of-E...-">static <E> List<E> List.of(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Set.html#of-E...-">static <E> Set<E> Set.of(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Map.html#of--">static <K, V> Entry<K, V> Map.of(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Map.html#ofEntries-java.util.Map.Entry...-">static <K, V> Entry<K, V> Map.ofEntries(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Map.html#entry-K-V-">static <K, V> Entry<K, V> entry(K k, V v)</a> (helper factory method to construct entries)</li>
</ul>
<p>Please take a notice that each of the factory methods has many overloads, the returned collections are <b>unmodifiable</b> and are safe for concurrent access by multiple threads. <b>Null</b> elements, keys, and values are not allowed, the resulting objects are <b>serializable</b>. </p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/259">JEP-259: Stack-Walking API</a>: defines an efficient standard API for stack walking that allows easy filtering of, and lazy access to, the information in stack traces. </p>
</p>
<pre class="brush:java">
final List<Class<?>> callers = StackWalker
.getInstance(Option.RETAIN_CLASS_REFERENCE)
.walk(frames -> frames
.map(StackFrame::getDeclaringClass)
.collect(Collectors.toList()));
</pre>
<p>Before that, for many years <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Throwable.html#getStackTrace--">Throwable::getStackTrace()</a> and <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Thread.html#getStackTrace--">Thread::getStackTrace()</a> were the only viable options to capture the stack traces in Java programmatically.</p>
</li>
<li><p>
<a href="https://openjdk.java.net/jeps/193">JEP-193: Variable Handles</a>: defines a standard means to invoke the equivalents of various <b>java.util.concurrent.atomic</b> and <b>sun.misc.Unsafe</b> operations upon object fields and array elements, a standard set of fence operations for fine-grained control of memory ordering, and a standard reachability-fence operation to ensure that a referenced object remains strongly reachable. The methods to create <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html">VarHandle</a> instances are located in <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html">java.lang.invoke.MethodHandles</a>, which we are going to talk about next, but there are a few static methods available as well.
</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html#fullFence--">static void fullFence()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html#acquireFence--">static void acquireFence()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html#releaseFence--">static void releaseFence()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html#loadLoadFence--">static void loadLoadFence()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html#storeStoreFence--">static void storeStoreFence()</a></li>
</ul>
<p>For the majority of the Java developers, the occasion to use <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html">VarHandle</a>s may never pop up, but it is a must-have tool in case you need to deal with memory fencing constraints and fine grained memory control.</p>
</li>
<li><p><a href="https://openjdk.java.net/jeps/274">JEP-274: Enhanced Method Handles</a>: enhances the <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandle.html">MethodHandle</a>, <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html">MethodHandles</a>, and <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html">MethodHandles.Lookup</a> classes to ease common use cases and enable better compiler optimizations.</p>
<ul>
<li>New public methods added to <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandle.html">MethodHandle</a>:
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandle.html#asSpreader-int-java.lang.Class-int-">MethodHandle asSpreader(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandle.html#asCollector-int-java.lang.Class-int-">MethodHandle asCollector(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandle.html#withVarargs-boolean-">MethodHandle withVarargs(...)</a></li>
</ul>
</li>
<li>New public methods added to <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html">MethodHandles.Lookup</a>:
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#dropLookupMode-int-">Lookup dropLookupMode(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#defineClass-byte:A-">Class<?> defineClass(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#findClass-java.lang.String-">Class<?> findClass(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#accessClass-java.lang.Class-">Class<?> accessClass(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#findVarHandle-java.lang.Class-java.lang.String-java.lang.Class-">VarHandle findVarHandle(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#findStaticVarHandle-java.lang.Class-java.lang.String-java.lang.Class-">VarHandle findStaticVarHandle(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#unreflectVarHandle-java.lang.reflect.Field-">VarHandle unreflectVarHandle(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.Lookup.html#hasPrivateAccess--">boolean hasPrivateAccess()</a></li>
</ul>
<li>New static methods added to <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html">MethodHandles</a>:
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#privateLookupIn-java.lang.Class-java.lang.invoke.MethodHandles.Lookup-">static Lookup privateLookupIn(...)</a> (so missed in JDK 8)</li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#arrayConstructor-java.lang.Class-">static MethodHandle arrayConstructor(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#arrayLength-java.lang.Class-">static MethodHandle arrayLength(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#arrayElementVarHandle-java.lang.Class-">static VarHandle arrayElementVarHandle(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#byteArrayViewVarHandle-java.lang.Class-java.nio.ByteOrder-">static VarHandle byteArrayViewVarHandle(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#byteBufferViewVarHandle-java.lang.Class-java.nio.ByteOrder-">static VarHandle byteBufferViewVarHandle(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#varHandleExactInvoker-java.lang.invoke.VarHandle.AccessMode-java.lang.invoke.MethodType-">static MethodHandle varHandleExactInvoker(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#varHandleInvoker-java.lang.invoke.VarHandle.AccessMode-java.lang.invoke.MethodType-">static MethodHandle varHandleInvoker(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#zero-java.lang.Class-">static MethodHandle zero(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#empty-java.lang.invoke.MethodType-">static MethodHandle empty(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#dropArgumentsToMatch-java.lang.invoke.MethodHandle-int-java.util.List-int-">static MethodHandle dropArgumentsToMatch(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#foldArguments-java.lang.invoke.MethodHandle-int-java.lang.invoke.MethodHandle-">static MethodHandle foldArguments(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#loop-java.lang.invoke.MethodHandle:A...-">static MethodHandle loop(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#whileLoop-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-">static MethodHandle whileLoop(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#doWhileLoop-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-">static MethodHandle doWhileLoop(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#countedLoop-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-">static MethodHandle countedLoop(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#iteratedLoop-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-">static MethodHandle iteratedLoop(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandles.html#tryFinally-java.lang.invoke.MethodHandle-java.lang.invoke.MethodHandle-">static MethodHandle tryFinally(...</a>)</li>
</ul>
</li>
<li>New methods to <a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/atomic/package-summary.html">java.util.concurrent.atomic.AtomicXxx</a> classes family (<a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/atomic/AtomicBoolean.html">AtomicBoolean</a>, <a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/atomic/AtomicInteger.html">AtomicInteger</a>, <a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/atomic/AtomicLong.html">AtomicLong</a>, ...)
<ul>
<li><code>compareAndExchange(...)</code></li>
<li><code>compareAndExchangeAcquire(...)</code></li>
<li><code>compareAndExchangeRelease(...)</code></li>
<li><code>getAcquire()</code></li>
<li><code>getOpaque()</code></li>
<li><code>getPlain()</code></li>
<li><code>setOpaque(...)</code></li>
<li><code>setPlain(...)</code></li>
<li><code>setRelease(...)</code></li>
<li><code>weakCompareAndSetAcquire(...)</code></li>
<li><code>weakCompareAndSetPlain(...)</code></li>
<li><code>weakCompareAndSetRelease(...)</code></li>
<li><code>weakCompareAndSetVolatile(...)</code></li>
</ul>
</li>
<p>As you may noticed, a number of methods return (or accept) <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/VarHandle.html">VarHandle</a>.</p>
</ul>
</li>
<li><p><a href="https://openjdk.java.net/jeps/213">JEP-213: Milling Project Coin</a>: introduces five small amendments to the Java Programming Language. Let us take a closer look at those.</p>
<ul>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-7196160">Allow @SafeVargs on private instance methods</a>
<pre class="brush:java">
public class Testable<T> {
@SafeVarargs
private void test(T ... tests) {
// ... implementation here ...
}
}
</pre>
</li>
<li><a href="Allow%20effectively-final%20variables%20to%20be%20used%20as%20resources%20in%20the%20try-with-resources%20statement.%20">Allow effectively-final variables to be used as resources in the try-with-resources statement</a>
<pre class="brush:java">
final ByteArrayOutputStream out = new ByteArrayOutputStream();
try (out) {
// ... implementation here ...
}
</pre>
</li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8062373">Allow diamond with anonymous classes if the argument type of the inferred type is denotable</a>
<pre class="brush:java">
final PrivilegedAction<String> action = new PrivilegedAction<>() {
@Override
public String run() {
// ... implementation here ...
return ...;
}
};
</pre>
</li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8061549">Remove underscore from the set of legal identifier names</a>
<pre class="brush:java">
public static void main(String[] args) {
final int _= 0;
}
</pre>
Would result in compilation error:
<pre class="brush:xml">
... as of release 9, '_' is a keyword, and may not be used as an identifier
</pre>
</li>
<li><a href="https://bugs.openjdk.java.net/browse/JDK-8071453">Support for private methods in interfaces</a>
<pre class="brush:java">
public interface Testable {
private void test() {
// ... implementation here ...
}
}
</pre>
</li>
</ul>
</li>
<li><p><a href="https://openjdk.java.net/jeps/263">JEP-263: HiDPI Graphics on Windows and Linux</a>: implements <a href="https://wiki.archlinux.org/index.php/HiDPI">HiDPI</a> graphics on Windows and Linux.</p></li>
<li><p>Multiple <a href="https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html">Stream</a> enhancements:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html#takeWhile-java.util.function.Predicate-">Stream<T> takeWhile(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html#dropWhile-java.util.function.Predicate-">Stream<T> dropWhile(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html#ofNullable-T-">static <T> Stream<T> ofNullable(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html#iterate-T-java.util.function.Predicate-java.util.function.UnaryOperator-">static <T> Stream<T> iterate(...)</a> (enhanced favor)</li>
</ul>
</li>
<li><p>A few <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html">Optional</a> enhancements:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html#ifPresentOrElse-java.util.function.Consumer-java.lang.Runnable-">void ifPresentOrElse(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html#or-java.util.function.Supplier-">Optional<T> or(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html#stream--">Stream<T> stream()</a></li>
</ul>
</li>
<li><p>A number of <a href="https://docs.oracle.com/javase/9/docs/api/java/util/stream/Collectors.html">Collectors</a> enhancements:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/stream/Collectors.html#filtering-java.util.function.Predicate-java.util.stream.Collector-">static <T,A,R> Collector<T,?,R> filtering(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/stream/Collectors.html#flatMapping-java.util.function.Function-java.util.stream.Collector-">static <T,U,A,R> Collector<T,?,R> flatMapping(...)</a></li>
</ul>
</li>
<li><p>Better parallel <a href="https://bugs.openjdk.java.net/browse/JDK-8072773">Files.lines(...)</a> splitting implementation</p></li>
<li><p>A chunk of new methods and overloads was added to <a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Math.html">Math</a> class</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Math.html#multiplyExact-long-int-">static long multiplyExact(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Math.html#multiplyFull-int-int-">static long multiplyFull(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Math.html#multiplyHigh-long-long-">static long multiplyHigh(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Math.html#floorDiv-long-int-">static long floorDiv(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Math.html#floorMod-long-int-">static int floorMod(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/lang/Math.html#fma-double-double-double-">static double fma(...)</a></li>
</ul>
</li>
<li><p>Lexicographical array comparison using <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Arrays.html#compare-boolean:A-boolean:A-">Arrays::compare</a> (great help to <a href="https://cassandra.apache.org/">Apache Cassandra</a> and <a href="https://hbase.apache.org/">Apache HBase</a>), plus a few more additions:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Arrays.html#compareUnsigned-byte:A-byte:A-">static int compareUnsigned(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Arrays.html#equals-boolean:A-int-int-boolean:A-int-int-">static boolean equals(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Arrays.html#mismatch-boolean:A-int-int-boolean:A-int-int-">static int mismatch(...)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html">ServiceLoader</a> also got some love:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html#findFirst--">Optional<S> findFirst()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html#stream--">Stream<ServiceLoader.Provider<S>> stream()</a> (and a new generic interface <a href="https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.Provider.html">ServiceLoader.Provider<S></a>)</li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html#load-java.lang.ModuleLayer-java.lang.Class-">static <S> ServiceLoader<S> load(...)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Scanner.html">Scanner</a> class has been modernized as well:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Scanner.html#findAll-java.util.regex.Pattern-">Stream<MatchResult> findAll(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Scanner.html#tokens--">Stream<String> tokens()</a></li>
</ul>
</li>
<li><p>Certainly positive improvements were introduced into <a href="https://docs.oracle.com/javase/9/docs/api/java/util/regex/Matcher.html">Matcher</a> class:
</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/regex/Matcher.html#appendReplacement-java.lang.StringBuilder-java.lang.String-">Matcher appendReplacement(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/regex/Matcher.html#appendTail-java.lang.StringBuilder-">StringBuilder appendTail(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/regex/Matcher.html#replaceAll-java.util.function.Function-">String replaceAll(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/regex/Matcher.html#replaceFirst-java.util.function.Function-">String replaceFirst(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/regex/Matcher.html#results--">Stream<MatchResult> results()</a></li>
</ul>
</li>
<li><p><a href="https://bugs.openjdk.java.net/browse/JDK-8068730">Increase precision of Clock.systemUTC()</a>: enhances the implementation of the system clocks so that they offer at least the same precision than the underlying clock available on the system. This one may hit many by surprise.</p>
<pre class="brush:java">
/* JDK 8 */ Clock.systemUTC().instant() returns '2021-03-31T02:08:45.330Z'
/* JDK 9 */ Clock.systemUTC().instant() returns '2021-03-31T02:08:45.823907100Z'
</pre>
<p>The change in precision is explained by the fact that since JDK 9, the <a href="https://docs.oracle.com/javase/9/docs/api/java/time/Clock.html">Clock</a> will see higher precision, depending on the available clock of the operating system. The developers have the ability to change that using existing <a href="https://docs.oracle.com/javase/9/docs/api/java/time/Clock.html#tick-java.time.Clock-java.time.Duration-">Clock::tick</a> method:</p>
<pre class="brush:java">
final Clock clock = Clock.tick(Clock.systemUTC(), Duration.ofMillis(1));
</pre>
<p>Or leverage a new way, added in JDK 9, <a href="https://docs.oracle.com/javase/9/docs/api/java/time/Clock.html#tickMillis-java.time.ZoneId-">Clock:tickMillis</a>:</p>
<pre class="brush:java">
final Clock clock = Clock.tickMillis(ZoneId.systemDefault());
</pre>
<p>To learn more about the clock precision quirks, please check out this great article <a href="https://shipilev.net/blog/2014/nanotrusting-nanotime/">Nanotrusting the Nanotime</a> by <a href="https://shipilev.net">Aleksey ShipilΓ«v</a>.
</p>
</li>
<li><p>Enhanced <a href="https://docs.oracle.com/javase/9/docs/api/java/security/SecureRandom.html">SecureRandom</a> algorithms and new methods:</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/security/SecureRandom.html#getInstance-java.lang.String-">static SecureRandom getInstance(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/security/SecureRandom.html#getParameters--">SecureRandomParameters getParameters()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/security/SecureRandom.html#nextBytes-byte:A-java.security.SecureRandomParameters-">void nextBytes(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/security/SecureRandom.html#reseed--">void reseed()</a></li>
</ul>
<p>For more exhaustive overview please check out this excellent <a href="https://metebalci.com/blog/everything-about-javas-securerandom/">Everything about Java's SecureRandom</a> article.</p>
</li>
<li><p>More useful methods added to <a href="https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html">InputStream</a> (and less reasons to look for help beyond the standard library):</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html#readAllBytes--">byte[] readAllBytes()</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html#readNBytes-byte:A-int-int-">int readNBytes(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html#transferTo-java.io.OutputStream-">long transferTo(OutputStream out)</a></li>
</ul>
</li>
<li><p>More <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Objects.html">Objects</a> methods</p>
<ul>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Objects.html#requireNonNullElse-T-T-">static <T> T requireNonNullElse(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Objects.html#requireNonNullElseGet-T-java.util.function.Supplier-">static <T> T requireNonNullElseGet(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Objects.html#checkIndex-int-int-">static int checkIndex(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Objects.html#checkFromToIndex-int-int-int-">static int checkFromToIndex(...)</a></li>
<li><a href="https://docs.oracle.com/javase/9/docs/api/java/util/Objects.html#checkFromIndexSize-int-int-int-">static int checkFromIndexSize(...)</a></li>
</ul>
</li>
<li><p>The <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Enumeration.html">Enumeration</a> interface was complemented with <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Enumeration.html#asIterator--">asIterator()</a> method</p></li>
<li><p><a href="https://jcp.org/aboutJava/communityprocess/mrel/jsr221/index3.html">JDBC 4.3 API</a> baseline which includes many changes across <a href="https://docs.oracle.com/javase/9/docs/api/java/sql/package-summary.html">java.sql</a> package</p></li>
<li><p><a href="https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/ConcurrentHashMap.html#computeIfAbsent-K-java.util.function.Function-">ConcurrentHashMap::computeIfAbsent</a> locking improvements (see please <a href="https://bugs.openjdk.java.net/browse/JDK-8161372">JDK-8161372</a>)</p></li>
<li><p><a href="https://docs.oracle.com/javase/9/tools/jhsdb.htm">jhsdb</a> - the command line debugger / server / UI debugger. Could attach to a Java process or could be launched as a postmortem debugger to analyze the content of a core dump from a crashed Java Virtual Machine (JVM).</p></li>
</ul>
<p>The <a href="https://openjdk.java.net/projects/jdk9/">JDK 9 changeset was massive</a>, at the same time - this is the last big bang release. We have covered most interesting pieces of it, at least from the developer's point of view. In the upcoming posts we are going to dissect other releases, one by one, and reveal the hidden gems of each.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-5123261569838332002021-01-27T16:05:00.000-08:002021-01-27T16:05:00.146-08:00JEP-396 and You: strong encapsulation of the JDK internals is the default<p>Since the inception of the <a href="https://openjdk.java.net/projects/jigsaw/">Project Jigsaw</a>, one of its goals was to encapsulate most of the JDK internal APIs in order to give the contributors a freedom to move Java forward at faster pace. <a href="https://openjdk.java.net/jeps/260">JEP-260</a>, delivered along <a href="https://openjdk.java.net/projects/jdk9/">JDK 9</a> release was a first step in this direction. Indeed, the famous <b>WARNING</b> messages like the ones below</p>
<pre>
...
WARNING: An illegal reflective access operation has occurred
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
...
</pre>
<p>started to appear all over the place. Intended to give the developers and maintainers the time to switch to the alternative, publicly available APIs, they rather provoked the opposite: most just got used to them. Well, if nothing breaks, why bother?</p>
<p>But ... the things are going to change very soon. I think many of you have seen the code which tries to do clever things by gaining the access to the private method or fields of the classes from standard library. One of the notable examples I have seen often enough is overcoming <b>ThreadPoolExecutor</b>'s core pool size / maximum pool size semantics (if curious, please read the <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html">documentations</a> and <a href="http://www.bigsoft.co.uk/blog/2009/11/27/rules-of-a-threadpoolexecutor-pool-size">complementaty material</a>) by invoking its internal <b>addWorker</b> method. The example below is a simplified illustration of this idea (please, do not do that, ever).</p>
<pre class="brush:java">
final ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue>Runnable<());
final Runnable task = ...
executor.submit(task);
// Queue is empty, enough workers have been created
if (executor.getQueue().isEmpty()) {
return;
}
// Still have some room to go
if (executor.getActiveCount() < executor.getCorePoolSize()) {
return;
}
// Core pool is full but not maxed out, let us add more workers
if (executor.getActiveCount() < executor.getMaximumPoolSize()) {
final Method addWorker = ThreadPoolExecutor.class.getDeclaredMethod(
"addWorker", Runnable.class, Boolean.TYPE);
addWorker.setAccessible(true);
addWorker.invoke(executor, null, Boolean.FALSE);
}
</pre>
<p>To be fair, this code works now on <a href="https://wiki.openjdk.java.net/display/jdk8u/Main">JDK 8</a>, <a href="https://openjdk.java.net/projects/jdk/11/">JDK 11</a> or <a href="https://jdk.java.net/15/">JDK 15</a>: find a private method, make it accessible, good to go. But it will not going to work smoothly in soon to be out <a href="http://openjdk.java.net/projects/jdk/16/">JDK 16</a> and onwards, generating the <b>InaccessibleObjectException</b> exception at runtime upon <b>setAccessible</b> invocation.</p>
<pre>
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make private boolean java.util.concurrent.ThreadPoolExecutor.addWorker(java.lang.Runnable,boolean) accessible: module java.base does not "opens java.util.concurrent" to unnamed module @72ea2f77
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
...
</pre>
<p>So what is happening here? The new <a href="https://openjdk.java.net/jeps/396">JEP-396</a> continues the endeavours of <a href="https://openjdk.java.net/jeps/260">JEP-260</a> by strongly encapsulating JDK internals by default. It has been integrated into <a href="https://jdk.java.net/16/">JDK 16</a> and <a href="https://jdk.java.net/17/">JDK 17</a> early builds which essentially means, no abusive access is going to be allowed anymore. Arguably, this is the right move, though it is very likely to be a disruptive one. </p>
<p>Should you be worrying? It is a good question: if you do not use any internal JDK APIs directly, it is very likely one of the libraries you depend upon may not play by the rules (or may not be ready for rule changes). Hopefully by the time <a href="https://jdk.java.net/16/">JDK 16</a> is released, the ecosystem will be in a good shape. There is never good time, we were warned for years and the next milestone is about to be reached. If you could help your favorite library or framework, please do.</p>
<p>The complete list of the exported packages that will no longer be open by default was conveniently made <a href="https://cr.openjdk.java.net/~mr/jigsaw/jdk8-packages-denied-by-default">available here</a>, a couple to pay attention to:</p>
<pre>
java.beans.*
java.io
java.lang.*
java.math
java.net
java.nio.*
java.rmi.*
java.security.*
java.sql
java.text.*
java.time.*
java.util.*
</pre>
<p>Last but not least, you still could overturn the defaults using the <b>--add-opens</b> command line options, but please use it with great caution (or better, do not use it at all):</p>
<pre>
$ java --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED ...
</pre>
<p>Please be proactive and test with latest JDKs in advance, luckily the early access builds (<a href="https://jdk.java.net/16/">JDK 16</a>, <a href="https://jdk.java.net/17/">JDK 17</a>) are promptly available to everyone.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-22452029785884049702020-11-28T12:22:00.000-08:002020-11-28T12:22:18.195-08:00All Your Tests Belong to You: Maintaining Mixed JUnit 4/JUnit 5 and Testng/JUnit 5 Test Suites<p>If you are seasoned Java developer who practices <a href="https://en.wikipedia.org/wiki/Test-driven_development">test-driven development</a> (hopefully, everyone does it), it is very likely <a href="https://junit.org/junit4/">JUnit 4</a> has been your one-stop-shop testing toolbox. Personally, I truly loved it and still love: simple, minimal, non-intrusive and intuitive. Along with terrific libraries like <a href="https://joel-costigliola.github.io/assertj/">Assertj</a> and <a href="http://hamcrest.org/">Hamcrest</a> it makes writing test cases a pleasure. </p>
<p>But time passes by, Java has evolved a lot as a language, however <a href="https://junit.org/junit4/">JUnit 4</a> was not really up for a ride. Around 2015 the development of <a href="https://junit.org/junit5/">JUnit 5</a> has started with ambitious goal to become a next generation of the programmer-friendly testing framework for Java and the JVM. And, to be fair, I think this goal has been reached: many new projects adopt <a href="https://junit.org/junit5/">JUnit 5</a> from the get-go whereas the old ones are already in the process of migration (or at least are thinking about it).</p>
<p>For existing projects, the migration to <a href="https://junit.org/junit5/">JUnit 5</a> will not happen overnight and would probably take some time. In today's post we are going to talk about the ways to maintain mixed <a href="https://junit.org/junit4/">JUnit 4</a> / <a href="https://junit.org/junit5/">JUnit 5</a> and <a href="https://testng.org/doc/">TestNG</a> / <a href="https://junit.org/junit5/">JUnit 5</a> test suites with a help of <a href="https://maven.apache.org/">Apache Maven</a> and <a href="https://maven.apache.org/surefire/index.html">Apache Maven Surefire</a> plugin.</p>
<p>To have an example a bit more realistic, we are going to test a <b>UploadDestination</b> class, which basically just provides a single method which says if a particular destination scheme is supported or not: </p>
<pre class="brush:java">
import java.net.URI;
public class UploadDestination {
public boolean supports(String location) {
final String scheme = URI.create(location).getScheme();
return scheme.equals("http") || scheme.equals("s3") || scheme.equals("sftp");
}
}
</pre>
<p>The implementer was kind enough to create a suite of <a href="https://junit.org/junit4/">JUnit 4</a> unit tests to verify that all expected destination schemes are indeed supported.</p>
<pre class="brush:java">
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class JUnit4TestCase {
private UploadDestination destination;
private final String location;
public JUnit4TestCase(String location) {
this.location = location;
}
@Before
public void setUp() {
destination = new UploadDestination();
}
@Parameters(name= "{index}: location {0} is supported")
public static Object[] locations() {
return new Object[] { "s3://test", "http://host:9000", "sftp://host/tmp" };
}
@Test
public void testLocationIsSupported() {
assertTrue(destination.supports(location));
}
}
</pre>
<p>In the project build, at very least, you need to add <a href="https://junit.org/junit4/">JUnit 4</a> dependency along with <a href="https://maven.apache.org/surefire/index.html">Apache Maven Surefire</a> plugin and, optionally <a href="https://maven.apache.org/surefire/maven-surefire-report-plugin/">Apache Maven Surefire Reporter</a> plugin, to your <b>pom.xml</b>, the snippet below illustrates that.</p>
<pre class="brush:xml">
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>
</pre>
<p>No magic here, triggering <a href="https://maven.apache.org/">Apache Maven</a> build would normally run all unit test suites every time.</p>
<pre>
...
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.JUnit4TestCase
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.011 s - in com.example.JUnit4TestCase
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
...
</pre>
<p>Awesome, let us imagine at some point another teammate happens to work on the project and noticed there are no unit tests verifying the unsupported destination schemes so she adds some using <a href="https://junit.org/junit5/">JUnit 5</a>. </p>
<pre class="brush:java">
package com.example;
import static org.junit.jupiter.api.Assertions.assertFalse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class JUnit5TestCase {
private UploadDestination destination;
@BeforeEach
void setUp() {
destination = new UploadDestination();
}
@ParameterizedTest(name = "{index}: location {0} is supported")
@ValueSource(strings = { "s3a://test", "https://host:9000", "ftp://host/tmp" } )
public void testLocationIsNotSupported(String location) {
assertFalse(destination.supports(location));
}
}
</pre>
<p>Consequently, another dependency appears in the project's <b>pom.xml</b> to bring <a href="https://junit.org/junit5/">JUnit 5</a> in (since its API is not compatible with <a href="https://junit.org/junit4/">JUnit 4</a>).</p>
<pre class="brush:xml">
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</pre>
<p>Looks quite legitimate, isn't it? But there is a catch ... the test run results would surprise this time.</p>
<pre>
...
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.JUnit5TestCase
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.076 s - in com.example.JUnit5TestCase
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
...
</pre>
<p>The <a href="https://junit.org/junit4/">JUnit 4</a> test suites are gone and such a behavior is actually <a href="https://maven.apache.org/surefire/maven-surefire-plugin/examples/junit-platform.html">well documentented by Apache Maven Surefire team</a> in the <b>Provider Selection</b> section of the official documentation. So how we could get them back? There are a few possible options but the simplest one by far is to use <a href="https://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4">JUnit Vintage engine</a> in order to run <a href="https://junit.org/junit4/">JUnit 4</a> test suites using <a href="https://junit.org/junit5/">JUnit 5</a> platform.</p>
<pre class="brush:xml">
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<dependencies>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
</dependency>
</dependencies>
</plugin>
</pre>
<p>With that, both <a href="https://junit.org/junit4/">JUnit 4</a> and <a href="https://junit.org/junit5/">JUnit 5</a> test suites are going to be executed side by side.</p>
<pre>
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.JUnit5TestCase
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.079 s - in com.example.JUnit5TestCase
[INFO] Running com.example.JUnit4TestCase
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.009 s - in com.example.JUnit4TestCase
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
</pre>
<p>The lesson to learn here: please watch carefully that all your test suites are being executed (the CI/CD usually keeps track of such trends and warns you right away). Especially, be extra careful when migrating to latest <a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.4-Release-Notes#junit-5s-vintage-engine-removed-from-spring-boot-starter-test">Spring Boot</a> or <a href="https://maven.apache.org/surefire/index.html">Apache Maven Surefire</a> plugin versions.</p>
<p>Another quite common use case you may run into is mixing the <a href="https://testng.org/doc/">TestNG</a> and <a href="https://junit.org/junit5/">JUnit 5</a> test suites in the scope of one project. The symptoms are pretty much the same, you are going to wonder why only <a href="https://junit.org/junit5/">JUnit 5</a> test suites are being run. The treatment in this case is a bit different and one of the options which seems to work pretty well is to enumerate the test engine providers explicitly.</p>
<pre class="brush:xml">
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit-platform</artifactId>
<version>3.0.0-M5</version>
</dependency>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-testng</artifactId>
<version>3.0.0-M5</version>
</dependency>
</dependencies>
</plugin>
</pre>
<p>The somewhat undesired effect in this case is the fact that the test suites are run separately (there are <a href="https://maven.apache.org/surefire/maven-surefire-plugin/examples/junit-platform.html">other ways though</a> to try out), for example:</p>
<pre>
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.JUnit5TestCase
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.074 s - in com.example.JUnit5TestCase
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running TestSuite
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.315 s - in TestSuite
[INFO]
[INFO] Results:
[INFO]
INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
</pre>
<p>To be fair, I think <a href="https://junit.org/junit5/">JUnit 5</a> is a huge step forward towards having modern and concise test suites for your Java (and in general, JVM) projects. These days there are seamless integrations available with mostly any other test framework or library (<a href="https://site.mockito.org/">Mockito</a>, <a href="https://www.testcontainers.org/">TestContainers</a>, ... ) and the migration path is not that difficult in most cases. Plus, as you have seen, co-existence of <a href="https://junit.org/junit5/">JUnit 5</a> with older test engines is totally feasible.</p>
<p>As always, the complete project samples are available on Github: <a href="https://github.com/reta/junit4-junit5-with-surefire">JUnit 4/JUnit 5</a>, <a href="https://github.com/reta/testng-junit5-with-surefire">TestNG / JUnit 5</a>.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-43878825564244149282020-09-30T19:12:00.004-07:002020-09-30T19:12:46.182-07:00For gourmets and practioners: pick your flavour of the reactive stack with JAX-RS and Apache CXF <p>When <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS 2.1</a> specification was released back in 2017, one of its true novelties was the introduction of the reactive API extensions. The industry has acknowledged the importance of the modern programming paradigms and specification essentially mandated the first-class support of the asynchronous and reactive programming for the Client API.</p>
<p>But what about the server side? It was not left out, the <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS 2.1</a> asynchronous processing model has been enriched with Java 8's <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html">CompletionStage</a> support, certainly a step in a right direction. Any existing <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST</a> web APIs built on top of the <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS 2.1</a> implementation (like <a href="https://cxf.apache.org/">Apache CXF</a> for example) could benefit from such enhancements right away.</p>
<pre class="brush:java">
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@Service
@Path("/people")
public class PeopleRestService {
@GET
@Produces(MediaType.APPLICATION_JSON)
public CompletionStage<List<Person>> getPeople() {
return CompletableFuture
.supplyAsync(() -> Arrays.asList(new Person("a@b.com", "Tom", "Knocker")));
}
}
</pre>
<p>Udoubtedly, <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html">CompletionStage</a> and <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html">CompletableFuture</a> are powerful tools but not without own <a href="https://www.nurkiewicz.com/2014/12/asynchronous-timeouts-with.html">quirks</a> and limitations. The <a href="https://www.reactive-streams.org/">Reactive Streams specification</a> and a number of its implementations offer a considerably better glimpse on how asynchronous and reactive programming should look like on JVM. With that, the logical question pops up: could your <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS</a> web services and APIs take advantage of the modern reactive libraries? And if the answer is positive, what does it take?</p>
<p>If your bets are on <a href="https://cxf.apache.org/">Apache CXF</a>, you are certainly well positioned. The latest <a href="https://cxf.apache.org/">Apache CXF</a> <b>3.2.14</b> / <b>3.3.7</b> / <b>3.4.0</b> release trains bring a comprehesive support of <a href="https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-3.0">RxJava3</a>, <a href="https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-2.0">RxJava2</a> and <a href="https://projectreactor.io/">Project Reactor</a>. Along this post we are going to see how easy it is to plug your favorite reactive library in and place it at the forefront of your <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST</a> web APIs and services.</p>
<p>Since the most applications and services on the JVM are built on top of excellent <a href="https://spring.io/projects/spring-framework">Spring</a> framework and <a href="https://spring.io/projects/spring-boot">Spring Boot</a>, we will be developing the reference implementations using those as a foundation. The <a href="https://spring.io/projects/spring-boot">Spring Boot</a> starter which comes along with <a href="https://cxf.apache.org/">Apache CXF</a> distribution is taking care of most of the boring wirings you would have needed to do otherwise.</p>
<pre class="brush:xml">
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
<version>3.4.0</version>
</dependency>
</pre>
<p>The <a href="https://projectreactor.io/">Project Reactor</a> is the number one choice as the reactive foundation for <a href="https://spring.io/projects/spring-framework">Spring</a>-based applications and services, so let us just start from that.</p>
<pre class="brush:xml">
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-reactor</artifactId>
<version>3.4.0</version>
</dependency>
</pre>
<p>Great, believe it or not, we are mostly done here. In order to teach <a href="https://cxf.apache.org/">Apache CXF</a> to understand <a href="https://projectreactor.io/">Project Reactor</a> types like <a href="https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html">Mono</a> or/and <a href="https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html">Flux</a> we need to tune the configuration just a bit using <b>ReactorCustomizer</b> instance.</p>
<pre class="brush:java">
import org.apache.cxf.Bus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.reactor.server.ReactorCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
@Configuration
public class AppConfig {
@Bean
public Server rsServer(Bus bus, PeopleRestService service) {
final JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
bean.getProperties(true).put("useStreamingSubscriber", true);
bean.setBus(bus);
bean.setAddress("/");
bean.setServiceBean(service);
bean.setProvider(new JacksonJsonProvider());
new ReactorCustomizer().customize(bean);
return bean.create();
}
}
</pre>
<p>With such customization in-place, our <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS</a> web services and APIs could freely utilize <a href="https://projectreactor.io/">Project Reactor</a> primitives in a streaming fashion, for example.</p>
<pre class="brush:java">
import reactor.core.publisher.Flux;
@Service
@Path("/people")
public class PeopleRestService {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Flux<Person> getPeople() {
return Flux.just(new Person("a@b.com", "Tom", "Knocker"));
}
}
</pre>
<p>As you probably noticed, the implementation purposely does not do anything complicated. However, once the reactive types are put at work, you could unleash the full power of the library of your choice (and <a href="https://projectreactor.io/">Project Reactor</a> is really good at that).</p>
<p>Now, when you undestand the principle, it comes the turn of the <a href="https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-3.0">RxJava3</a>, the last generation of the pioneering reactive library for the JVM platform.</p>
<pre class="brush:xml">
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-rx3</artifactId>
<version>3.4.0</version>
</dependency>
</pre>
<p>The configuration tuning is mostly identical to the one we have seen with <a href="https://projectreactor.io/">Project Reactor</a>, the customizer instance, <b>ReactiveIOCustomizer</b>, is all that changes.</p>
<pre class="brush:java">
import org.apache.cxf.Bus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.rx3.server.ReactiveIOCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
@Configuration
public class AppConfig {
@Bean
public Server rsServer(Bus bus, PeopleRestService service) {
final JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
bean.getProperties(true).put("useStreamingSubscriber", true);
bean.setBus(bus);
bean.setAddress("/");
bean.setServiceBean(service);
bean.setProvider(new JacksonJsonProvider());
new ReactiveIOCustomizer().customize(bean);
return bean.create();
}
}
</pre>
<p>The list of supported types includes <a href="http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Flowable.html">Flowable</a>, <a href="http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Single.html">Single</a> and <a href="http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Observable.html">Observable</a>, the equivalent implementation in terms of <a href="https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-3.0">RxJava3</a> primitives may look like this.</p>
<pre class="brush:java">
import io.reactivex.rxjava3.core.Flowable;
@Service
@Path("/people")
public class PeopleRestService {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Flowable<Person> getPeople() {
return Flowable.just(new Person("a@b.com", "Tom", "Knocker"));
}
}
</pre>
<p>Pretty simple, isn't it? If you stuck with an older generation, <a href="https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-2.0">RxJava2</a>, nothing to worry about, <a href="https://cxf.apache.org/">Apache CXF</a> has you covered. </p>
<pre class="brush:xml">
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-rx2</artifactId>
<version>3.4.0</version>
</dependency>
</pre>
<p>The same configuration trick with applying the customizer (which may look annoying at this point to be fair) is all that is required.</p>
<pre class="brush:java">
import org.apache.cxf.Bus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.rx2.server.ReactiveIOCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
@Configuration
public class AppConfig {
@Bean
public Server rsServer(Bus bus, PeopleRestService service) {
final JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
bean.getProperties(true).put("useStreamingSubscriber", true);
bean.setBus(bus);
bean.setAddress("/");
bean.setServiceBean(service);
bean.setProvider(new JacksonJsonProvider());
new ReactiveIOCustomizer().customize(bean);
return bean.create();
}
}
</pre>
<p>And we are good to go, ready to use the familiar reactive types <a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html">Observable</a>, <a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Flowable.html">Flowable</a> and <a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Single.html">Single</a>.</p>
<pre class="brush:java">
import io.reactivex.Flowable;
import io.reactivex.Observable;
@Service
@Path("/people")
public class PeopleRestService {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Observable<Person> getPeople() {
return Flowable
.just(new Person("a@b.com", "Tom", "Knocker"))
.toObservable();
}
}
</pre>
<p>Last but not least, if you happens to be using the first generation of <a href="https://github.com/ReactiveX/RxJava#version-1x">RxJava</a>, it is also <a href="https://cxf.apache.org/docs/jax-rs-rxjava.html#JAX-RSRxJava-RxJava1rx.Observablesupport">available</a> with <a href="https://cxf.apache.org/">Apache CXF</a> but certainly not recommended for production (as it has EOLed a couple of years ago).</p>
<p>Reactive programming paradigm is steadily getting more and more traction. It is great to see that the ecosystem embraces that and frameworks like <a href="https://cxf.apache.org/">Apache CXF</a> are not an exception. If you are looking for robust foundation to build reactive and/or asynchronous <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST</a> web APIs on JVM, <a href="https://cxf.apache.org/">Apache CXF</a> is worth considering, please give it a try!</p>
<p>The complete source code is available on <a href="https://github.com/reta/jax-rs-2.1-reactive-api">Github</a>.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-55579296229276906012020-07-28T17:54:00.000-07:002020-07-28T17:55:49.970-07:00It is never enough of them: enriching Apache Avro generated classes with custom Java annotations<p><a href="https://avro.apache.org">Apache Avro</a>, along with <a href="http://thrift.apache.org/">Apache Thrift</a> and <a href="https://developers.google.com/protocol-buffers">Protocol Buffers</a>, is often being used as a platform-neutral extensible mechanism for serializing structured data. In the context of <a href="https://en.wikipedia.org/wiki/Event-driven_architecture">event-driven systems</a>, the <a href="https://avro.apache.org">Apache Avro</a>'s schemas play the role of the language-agnostic contracts, shared between loosely-coupled components of the system, not necessarily written using the same programming language.</p>
<p>Probably, the most widely adopted reference architecture for such systems circles around <a href="https://kafka.apache.org/">Apache Kafka</a> backed by <a href="https://docs.confluent.io/current/schema-registry/">Schema Registry</a> and <a href="https://avro.apache.org">Apache Avro</a>, although many other excellent options are available. Nevertheless, why <a href="https://avro.apache.org">Apache Avro</a>?</p>
<p>The <a href="https://avro.apache.org/docs/current/">official documentation page</a> summarizes pretty well the key advantages <a href="https://avro.apache.org">Apache Avro</a> has over <a href="http://thrift.apache.org/">Apache Thrift</a> and <a href="https://developers.google.com/protocol-buffers">Protocol Buffers</a>. But we are going to add another one to the list: biased (in a good sense) support of the Java and JVM platform in general.</p>
<p>Let us imagine that one of the components (or, it has to be said, <a href="https://en.wikipedia.org/wiki/Microservices">microservice</a>) takes care of the payment processing. Not every payment may succeed and to propagate such failures, the component broadcasts <b>PaymentRejectedEvent</b> whenever such unfortunate event happens. Here is its <a href="https://avro.apache.org">Apache Avro</a> schema, persisted in the <b>PaymentRejectedEvent.avsc</b> file.</p>
<pre class="brush:java">
{
"type": "record",
"name": "PaymentRejectedEvent",
"namespace": "com.example.event",
"fields": [
{
"name": "id",
"type": {
"type": "string",
"logicalType": "uuid"
}
},
{
"name": "reason",
"type": {
"type": "enum",
"name": "PaymentStatus",
"namespace": "com.example.event",
"symbols": [
"EXPIRED_CARD",
"INSUFFICIENT_FUNDS",
"DECLINED"
]
}
},
{
"name": "date",
"type": {
"type": "long",
"logicalType": "local-timestamp-millis"
}
}
]
}
</pre><p>The event is notoriously kept simple, you can safely assume that in more or less realistic system it has to have considerably more details available. To turn this event into Java class at build time, we could use <a href="https://avro.apache.org/docs/current/gettingstartedjava.html">Apache Avro Maven plugin</a>, it is as easy as it could get.</p>
<pre class="brush:xml">
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.10.0</version>
<configuration>
<stringType>String</stringType>
</configuration>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
<outputDirectory>${project.build.directory}/generated-sources/avro/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</pre>
<p>Once the build finishes, you will get <b>PaymentRejectedEvent</b> Java class generated. But a few annoyances are going to emerge right away:</p>
<pre class="brush:java">
@org.apache.avro.specific.AvroGenerated
public class PaymentRejectedEvent extends ... {
private java.lang.String id;
private com.example.event.PaymentStatus reason;
private long date;
}
</pre><p>The Java's types for <b>id</b> and <b>date</b> fields are not really what we would expect. Luckily, this is easy to fix by specifying <b>customConversions</b> plugin property, for example.</p>
<pre class="brush:xml">
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.10.0</version>
<configuration>
<stringType>String</stringType>
<customConversions>
org.apache.avro.Conversions$UUIDConversion,org.apache.avro.data.TimeConversions$LocalTimestampMillisConversion
</customConversions>
</configuration>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
<outputDirectory>${project.build.directory}/generated-sources/avro/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</pre>
<p>If we build the project this time, the plugin would generate the right types.</p>
<pre class="brush:java">
@org.apache.avro.specific.AvroGenerated
public class PaymentRejectedEvent extends ... {
private java.util.UUID id;
private com.example.event.PaymentStatus reason;
private java.time.LocalDateTime date;
}
</pre>
<p>It looks much better! But what about next challenge. In Java, annotations are commonly used to associate some additional metadata pieces with a particular language element. What if we have to add a custom, application-specific annotation to all generated event classes? It does not really matter which one, let it be <b>@javax.annotation.Generated</b>, for example. It turns out, with <a href="https://avro.apache.org">Apache Avro</a> it is not an issue, it has dedicated <b>javaAnnotation</b> property we could benefit from.</p>
<pre class="brush:java">
{
"type": "record",
"name": "PaymentRejectedEvent",
"namespace": "com.example.event",
"javaAnnotation": "javax.annotation.Generated(\"avro\")",
"fields": [
{
"name": "id",
"type": {
"type": "string",
"logicalType": "uuid"
}
},
{
"name": "reason",
"type": {
"type": "enum",
"name": "PaymentStatus",
"namespace": "com.example.event",
"symbols": [
"EXPIRED_CARD",
"INSUFFICIENT_FUNDS",
"DECLINED"
]
}
},
{
"name": "date",
"type": {
"type": "long",
"logicalType": "local-timestamp-millis"
}
}
]
}
</pre>
<p>When we rebuild the project one more time (hopefully the last one), the generated <b>PaymentRejectedEvent</b> Java class is going to be decorated with the additional custom annotation.</p>
<pre class="brush:java">
@javax.annotation.Generated("avro")
@org.apache.avro.specific.AvroGenerated
public class PaymentRejectedEvent extends ... {
private java.util.UUID id;
private com.example.event.PaymentStatus reason;
private java.time.LocalDateTime date;
}
</pre>
<p>Obviously, this property has no effect if the schema is used to produce respective constructs in other programming languages but it still feels good to see that Java has privileged support in <a href="https://avro.apache.org">Apache Avro</a>, thanks for that! As a side note, it is good to see that after some quite long inactivity time the project is expiriencing the second breath, with regular releases and new features delivered constantly.</p>
<p>The complete source code is available on <a href="https://github.com/reta/avro-custom-annotations">Github</a>.</p>
Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-46242743780491824842020-04-30T18:57:00.000-07:002020-04-30T18:57:06.350-07:00The crypto quirks using JDK's Cipher streams (and what to do about that)<p>In our day-to-day job we often run into the recurrent theme of transferring data (for example, files) from one location to another. It sounds like a really simple task but let us make it a bit more difficult by stating the fact that these files may contain confidential information and could be transferred over non-secure communication channels.</p>
<p>One of the solutions which comes to mind first is to use encryption algorithms. Since the files could be really large, hundreds of megabytes or tens of gigabytes, using the symmetric encryption scheme like <a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a> would probably make a lot of sense. Besides just encryption it would be great to make sure that the data is not tampered in transit. Fortunately, there is a thing called <a href="https://en.wikipedia.org/wiki/Authenticated_encryption">authenticated encryption</a> which simultaneously provides to us confidentiality, integrity, and authenticity guarantees. <a href="https://en.wikipedia.org/wiki/Galois/Counter_Mode">Galois/Counter Mode</a> (<a href="https://en.wikipedia.org/wiki/Galois/Counter_Mode">GCM</a>) is one of the most popular modes that supports <a href="https://en.wikipedia.org/wiki/Authenticated_encryption">authenticated encryption</a> and could be used along with <a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a>. These thoughts lead us to use <b>AES256-GCM128</b>, a sufficiently strong encryption scheme.
</p>
<p>In case you are on JVM platform, you should feel lucky since <a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a> and <a href="https://en.wikipedia.org/wiki/Galois/Counter_Mode">GCM</a> are supported by <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">Java Cryptography Architecture</a> (<a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">JCA</a>) out of the box. With that being said, let us see how far we could go.</p>
<p>The first thing we have to do is to generate a new <b>AES256</b> key. As always, <a href="https://owasp.org">OWASP</a> has a <a href="https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions">number of recommendations</a> on using <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">JCA</a>/<a href="https://en.wikipedia.org/wiki/Java_Cryptography_Extension">JCE</a> APIs properly. </p>
<pre class="brush:java">
final SecureRandom secureRandom = new SecureRandom();
final byte[] key = new byte[32];
secureRandom.nextBytes(key);
final SecretKey secretKey = new SecretKeySpec(key, "AES");
</pre>
<p>Also, to initialize <a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a>/<a href="https://en.wikipedia.org/wiki/Galois/Counter_Mode">GCM</a> cipher we need to generate random initialization vector (or shortly, IV). As per <a href="https://www.nist.gov/">NIST</a> recommendations, its length should be <b>12 bytes</b> (96 bits). </p>
<blockquote>For IVs, it is recommended that implementations restrict support to the length of 96 bits, to promote interoperability, efficiency, and simplicity of design. - <a href="https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf">Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC</a></blockquote>
<p>So here we are: </p>
<pre class="brush:java">
final byte[] iv = new byte[12];
secureRandom.nextBytes(iv);
</pre>
<p>Having the <a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a> key and IV ready, we could create a cipher instance and actually perform the encryption part. Dealing with large files assumes the reliance on streaming, therefore we use <b>BufferedInputStream</b> / <b>BufferedOutputStream</b> combined with <b>CipherOutputStream</b> for encryption.</p>
<pre class="brush:java">
public static void encrypt(SecretKey secretKey, byte[] iv, final File input,
final File output) throws Throwable {
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
try (final BufferedInputStream in = new BufferedInputStream(new FileInputStream(input))) {
try (final BufferedOutputStream out = new BufferedOutputStream(new CipherOutputStream(new FileOutputStream(output), cipher))) {
int length = 0;
byte[] bytes = new byte[16 * 1024];
while ((length = in.read(bytes)) != -1) {
out.write(bytes, 0, length);
}
}
}
}
</pre>
<p>Please note how we specify <a href="https://en.wikipedia.org/wiki/Galois/Counter_Mode">GCM</a> cipher parameters with the tag size of <b>128 bits</b> and initialize it in encryption mode (be <a href="https://crypto.stackexchange.com/questions/31793/plain-text-size-limits-for-aes-gcm-mode-just-64gb">aware of some GCM limitations</a> when dealing with files over 64Gb). The decryption part is no different besides the fact the cipher is initialized in decryption mode.</p>
<pre class="brush:java">
public static void decrypt(SecretKey secretKey, byte[] iv, final File input,
final File output) throws Throwable {
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
try (BufferedInputStream in = new BufferedInputStream(new CipherInputStream(new FileInputStream(input), cipher))) {
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(output))) {
int length = 0;
byte[] bytes = new byte[16 * 1024];
while ((length = in.read(bytes)) != -1) {
out.write(bytes, 0, length);
}
}
}
}
</pre>
<p>It seems like we are done, right? Unfortunately, not really, encrypting and decrypting the small files takes just a few moments but dealing with more or less realistic data samples gives shocking results.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf1s1JcXLmhBdnuZd7FaG863QyeLsPc6GmkrIH6huY3dSjTh_qDhJeiweE_BMOovHvKxLNSj3yYl_LEoKHp39mXFx5T0W_m_UZF1OPQxlO15I5MrdKwSMXx8b7ex21VbfApbwguNRod14w/s1600/AesJcaTest.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf1s1JcXLmhBdnuZd7FaG863QyeLsPc6GmkrIH6huY3dSjTh_qDhJeiweE_BMOovHvKxLNSj3yYl_LEoKHp39mXFx5T0W_m_UZF1OPQxlO15I5MrdKwSMXx8b7ex21VbfApbwguNRod14w/s320/AesJcaTest.png" width="320" height="57" data-original-width="439" data-original-height="78" /></a></div>
<p>Mostly 8 minutes to process a ~42Mb file (and as you may guess, larger is the file, longer it takes), the quick analysis reveals that most of that time is spent while decrypting the data (please note by no means this is a benchmark, merely a test). The search for possible culprits points out to the long-standing list of issues with <a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a>/<a href="https://en.wikipedia.org/wiki/Galois/Counter_Mode">GCM</a> and <b>CipherInputStream</b> / <b>CipherOutputStream</b> in <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">JCA</a> implementation <a href="https://bugs.openjdk.java.net/browse/JDK-8201633">here</a>, <a href="https://stackoverflow.com/questions/60575897/cipherinputstream-hangs-while-reading-data">here</a>, <a href="https://crypto.stackexchange.com/questions/20333/encryption-of-big-files-in-java-with-aes-gcm">here</a> and <a href="https://stackoverflow.com/questions/26920906/how-come-putting-the-gcm-authentication-tag-at-the-end-of-a-cipher-stream-require-internal-buffering-">here</a>.</p>
<p>So what are the alternatives? It seems like it is possible to sacrifice the <b>CipherInputStream</b> / <b>CipherOutputStream</b>, refactor the implementation to use ciphers directly and make the encryption / decryption work using <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">JCA</a> primitives. But arguably there is a better way by bringing in battle-tested <a href="https://www.bouncycastle.org/">BouncyCastle</a> library.</p>
<p>From the implementation perspective, the solutions are looking mostly identical. Indeed, although the naming conventions are unchanged, the <b>CipherOutputStream</b> / <b>CipherInputStream</b> in the snippet below are coming from <a href="https://www.bouncycastle.org/">BouncyCastle</a>.</p>
<pre class="brush:java">
public static void encrypt(SecretKey secretKey, byte[] iv, final File input,
final File output) throws Throwable {
final GCMBlockCipher cipher = new GCMBlockCipher(new AESEngine());
cipher.init(true, new AEADParameters(new KeyParameter(secretKey.getEncoded()), 128, iv));
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(input))) {
try (BufferedOutputStream out = new BufferedOutputStream(new CipherOutputStream(new FileOutputStream(output), cipher))) {
int length = 0;
byte[] bytes = new byte[16 * 1024];
while ((length = in.read(bytes)) != -1) {
out.write(bytes, 0, length);
}
}
}
}
public static void decrypt(SecretKey secretKey, byte[] iv, final File input,
final File output) throws Throwable {
final GCMBlockCipher cipher = new GCMBlockCipher(new AESEngine());
cipher.init(false, new AEADParameters(new KeyParameter(secretKey.getEncoded()), 128, iv));
try (BufferedInputStream in = new BufferedInputStream(new CipherInputStream(new FileInputStream(input), cipher))) {
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(output))) {
int length = 0;
byte[] bytes = new byte[16 * 1024];
while ((length = in.read(bytes)) != -1) {
out.write(bytes, 0, length);
}
}
}
}
</pre>
<p>Re-runing the previous encryption/decryption tests using <a href="https://www.bouncycastle.org/">BouncyCastle</a> crypto primitives yields the completely different picture.
</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0P2nK81_ysmM67rju8unpVObbWyE0yVLIaAKiJDp3EEumfEQrr3BAkFmQDeSr8PnDVGm4ep-QnY45okWZ0wcfYs9X-eqQHrd91ahdUGMQZe8Qaxt6RCkXiCb2Em7QXmc1vixUcaQt7uwn/s1600/AesBcTest.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0P2nK81_ysmM67rju8unpVObbWyE0yVLIaAKiJDp3EEumfEQrr3BAkFmQDeSr8PnDVGm4ep-QnY45okWZ0wcfYs9X-eqQHrd91ahdUGMQZe8Qaxt6RCkXiCb2Em7QXmc1vixUcaQt7uwn/s320/AesBcTest.png" width="320" height="69" data-original-width="439" data-original-height="95" /></a></div>
<p>To be fair, the file encryption / decryption on the JVM platform looked like a solved problem at first but turned out to be full of surprising discoveries. Nonetheless, thanks to <a href="https://www.bouncycastle.org/">BouncyCastle</a>, some shortcomings of <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">JCA</a> implementation are addressed in efficient and clean way.</p>
<p>Please find the complete sources available on <a href="https://github.com/reta/aes-gcm-jdk-vs-bouncycastle">Github</a>.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-51176502060912357092020-02-18T17:21:00.001-08:002020-02-18T17:21:17.119-08:00In praise of the thoughful design: how property-based testing helps me to be a better developer<p>The developer's testing toolbox is one of these things which rarely stays unchanged. For sure, some testing practices have proven to be more valuable than others but still, we are constantly looking for better, faster and more expressive ways to test our code. <a href="https://en.wikipedia.org/wiki/QuickCheck">Property-based testing</a>, <a href="https://blog.johanneslink.net/2020/01/28/why-is-pbt-not-popular-in-java/">largely unknown</a> to Java community, is yet another gem crafted by <a href="https://www.haskell.org/">Haskell</a> folks and described in <a href="https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quick.pdf">QuickCheck paper</a>.</p>
<p>The power of this testing technique has been quickly realized by <a href="https://www.scala-lang.org/">Scala</a> community (where the <a href="https://www.scalacheck.org/">ScalaCheck</a> library was born) and many others but the Java ecosystem has lacked the interest into adopting <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based testing</a> for quite some time. Luckily, since the <a href="https://jqwik.net/">jqwik</a> appearance, the things are slowly changing for better.</p>
<p>For many, it is quite difficult to grasp what <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based testing</a> is and how it could be exploited. The excellent presentation <a href="https://www.infoq.com/presentations/property-based-testing/">Property-based Testing for Better Code</a> by <a href="https://jessitron.com/">Jessica Kerr</a> and comprehensive <a href="https://fsharpforfunandprofit.com/posts/property-based-testing/">An introduction to property-based testing</a>, <a href="https://blog.ssanj.net/posts/2016-06-26-property-based-testing-patterns.html">Property-based Testing Patterns</a> series of articles are excellent sources to get you hooked, but in today's post we are going to try discovering the practical side of the <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based testing</a> for typical Java developer using <a href="https://jqwik.net/">jqwik</a>.</p>
<p>To start with, what the name <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based testing</a> actually implies? The first thought of every Java developer would be it aims to test all getters and setters (hello 100% coverage)? Not really, although for some data structures it could be useful. Instead, we should identify the <a href="https://jqwik.net/#properties">high-level characteristics</a>, if you will, of the component, data structure, or even individual function and efficiently test them by formulating the hypothesis.</p>
<p>Our first example falls into category <b>"There and back again"</b>: serialization and deserialization into <a href="https://www.json.org/json-en.html">JSON</a> representation. The class under the test is <b>User</b> <a href="https://en.wikipedia.org/wiki/Plain_old_Java_object">POJO</a>, although trivial, please notice that it has one temporal property of type <b>OffsetDateTime</b>.</p>
<pre class="brush:java">
public class User {
private String username;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS[SSS]]XXX", shape = Shape.STRING)
private OffsetDateTime created;
// ...
}
</pre>
<p>It is surprising to see how often manipulation with date/time properties are causing issues these days since everyone tries to use own representation. As you could spot, our contract is using <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO-8601</a> interchange format with optional milliseconds part. What we would like to make sure is that any valid instance of <b>User</b> could be serialized into <a href="https://www.json.org/json-en.html">JSON</a> and desearialized back into Java object without loosing any date/time precision. As an exercise, let us try to express that in pseudo code first:</p>
<pre>
For any user
Serialize user instance to JSON
Deserialize user instance back from JSON
Two user instances must be identical
</pre>
<p>Looks simple enough but here comes the surprising part: let us take a look at how this pseudo code projects into real test case using <a href="https://jqwik.net/">jqwik</a> library. It gets as close to our pseudo code as it possibly could.</p>
<pre class="brush:java">
@Property
void serdes(@ForAll("users") User user) throws JsonProcessingException {
final String json = serdes.serialize(user);
assertThat(serdes.deserialize(json))
.satisfies(other -> {
assertThat(user.getUsername()).isEqualTo(other.getUsername());
assertThat(user.getCreated().isEqual(other.getCreated())).isTrue();
});
Statistics.collect(user.getCreated().getOffset());
}
</pre>
<p>The test case reads very easy, mostly natural, but obviously, there is some background hidden behind <a href="https://jqwik.net/">jqwik</a>'s <b>@Property</b> and <b>@ForAll</b> annotations. Let us start from <b>@ForAll</b> and clear out where all these <b>User</b> instances are coming from. As you may guess, these instances must be generated, preferably in a randomized fashion.</p><p>For most of the built-in data types <a href="https://jqwik.net/">jqwik</a> has a rich set of data providers (<b>Arbitraries</b>), but since we are dealing with application-specific class, we have to supply our own generation strategy. It should be able to emit <b>User</b> class instances with the wide range of usernames and the date/time instants for different set of timezones and offsets. Let us do a sneak peek at the provider implementation first and discuss it in details right after.</p>
<pre class="brush:java">
@Provide
Arbitrary<User> users() {
final Arbitrary<String> usernames = Arbitraries.strings().alpha().ofMaxLength(64);
final Arbitrary<OffsetDateTime> dates = Arbitraries
.of(List.copyOf(ZoneId.getAvailableZoneIds()))
.flatMap(zone -> Arbitraries
.longs()
.between(1266258398000L, 1897410427000L) // ~ +/- 10 years
.unique()
.map(epochMilli -> Instant.ofEpochMilli(epochMilli))
.map(instant -> OffsetDateTime.from(instant.atZone(ZoneId.of(zone)))));
return Combinators
.combine(usernames, dates)
.as((username, created) -> new User(username).created(created));
}
</pre>
<p>The source of <b>usernames</b> is easy: just random strings. The source of <b>dates</b> basically could be any date/time between 2010 and 2030 whereas the <b>timezone</b> part (thus the offset) is randomly picked from all available region-based zone identifiers. For example, below are some samples <a href="https://jqwik.net/">jqwik</a> came up with.</p>
<pre>
{"username":"zrAazzaDZ","created":"2020-05-06T01:36:07.496496+03:00"}
{"username":"AZztZaZZWAaNaqagPLzZiz","created":"2023-03-20T00:48:22.737737+08:00"}
{"username":"aazGZZzaoAAEAGZUIzaaDEm","created":"2019-03-12T08:22:12.658658+04:00"}
{"username":"Ezw","created":"2011-10-28T08:07:33.542542Z"}
{"username":"AFaAzaOLAZOjsZqlaZZixZaZzyZzxrda","created":"2022-07-09T14:04:20.849849+02:00"}
{"username":"aaYeZzkhAzAazJ","created":"2016-07-22T22:20:25.162162+06:00"}
{"username":"BzkoNGzBcaWcrDaaazzCZAaaPd","created":"2020-08-12T22:23:56.902902+08:45"}
{"username":"MazNzaTZZAEhXoz","created":"2027-09-26T17:12:34.872872+11:00"}
{"username":"zqZzZYamO","created":"2023-01-10T03:16:41.879879-03:00"}
{"username":"GaaUazzldqGJZsqksRZuaNAqzANLAAlj","created":"2015-03-19T04:16:24.098098Z"}
...
</pre>
<p>By default, <a href="https://jqwik.net/">jqwik</a> will run the test against 1000 different sets of parameter values (randomized <b>User</b> instances). The quite helpful <b>Statistics</b> container allows to collect whatever distribution insights you are curious about. Just in case, why not to collect the distribution by zone offsets?</p>
<pre>
...
-04:00 (94) : 9.40 %
-03:00 (76) : 7.60 %
+02:00 (75) : 7.50 %
-05:00 (74) : 7.40 %
+01:00 (72) : 7.20 %
+03:00 (69) : 6.90 %
Z (62) : 6.20 %
-06:00 (54) : 5.40 %
+11:00 (42) : 4.20 %
-07:00 (39) : 3.90 %
+08:00 (37) : 3.70 %
+07:00 (34) : 3.40 %
+10:00 (34) : 3.40 %
+06:00 (26) : 2.60 %
+12:00 (23) : 2.30 %
+05:00 (23) : 2.30 %
-08:00 (20) : 2.00 %
...
</pre>
<p>Let us consider another example. Imagine at some point we decided to reimplement the equality for <b>User</b> class (which in Java means, overriding <b>equals</b> and <b>hashCode</b>) based on <b>username</b> property. With that, for any pair of <b>User</b> class instances the following invariants must hold true:
<ul>
<li>if two <b>User</b> instances have the same <b>username</b>, they are equal and must have same hash code</li>
<li>if two <b>User</b> instances have different <b>username</b>s, they are not equal (but hash code may not necessarily be different)</li>
</ul>It is the perfect fit for <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based testing</a> and <a href="https://jqwik.net/">jqwik</a> in particular makes such kind of tests trivial to write and maintain.</p>
<pre class="brush:java">
@Provide
Arbitrary<String> usernames() {
return Arbitraries.strings().alpha().ofMaxLength(64);
}
@Property
void equals(@ForAll("usernames") String username, @ForAll("usernames") String other) {
Assume.that(!username.equals(other));
assertThat(new User(username))
.isEqualTo(new User(username))
.isNotEqualTo(new User(other))
.extracting(User::hashCode)
.isEqualTo(new User(username).hashCode());
}
</pre>
<p>The assumptions expressed through <b>Assume</b> allow to put additional constraints on the generated parameters since we introduce two sources of the usernames, it could happen that both of them emit the identical username at the same run so the test would fail.</p>
<p>The question you may be holding up to now is: what is the point? It is surely possible to test serialization / deserialization or <b>equals</b>/<b>hashCode</b> without embarking on <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based testing</a> and using <a href="https://jqwik.net/">jqwik</a>, so why even bother? Fair enough, but the answer to this question basically lies deeply in how we approach the design of our software systems.</p><p>By and large, <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based testing</a> is heavily influenced by functional programming, not a first thing which comes into mind with respect to Java (at least, not yet), to say it mildly. The randomized generation of test data is not novel idea per se, however what <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based testing</a> is encouraging you to do, at least in my opinion, is to think in more abstract terms, focus not on individual operations (equals, compare, add, sort, serialize, ...) but what kind of properties, characteristics, laws and/or invariants they come with to obey. It certainly feels like an alien technique, paradigm shift if you will, encourages to spend more time on designing the right thing. It does not mean that from now on all your tests must be <a href="https://en.wikipedia.org/wiki/QuickCheck">property-based</a> but I believe it certainly deserves the place in the front row of our testing toolboxes.</p>
<p>Please find the complete project sources available on <a href="https://github.com/reta/pbt-jqwik">Github</a>.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-58209745460491241372019-11-30T12:40:00.003-08:002019-11-30T12:40:54.084-08:00Spring has you covered, again: consumer-driven contract testing for messaging continued<p>In the <a href="https://aredko.blogspot.com/2019/10/tell-us-what-you-want-and-we-will-make.html">previous post</a> we have started to talk about <a href="https://aredko.blogspot.com/2016/11/keep-your-promises-contract-based.html">consumer-driven contract testing</a> in the context of the message-based communications. In today's post, we are going to include yet another tool in our testing toolbox but before that, let me do a quick refresher on a system under the microscope. It has two services, <b>Order Service</b> and <b>Shipment Service</b>. The <b>Order Service</b> publishes the messages / events to the message queue and <b>Shipment Service</b> consumes them from there.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_lcnoPqOP5I6lI59CYMN582S1HZdTsP_GQy8lOqFTfTfF-Pk4hJ7H5rHnrhr17_SznQOn_OMjaPti5NKUiHqz3JFwXCv6xfy37zz3uGVC0yuSs3awRwZFKy0Lp0preOgUSLwkZJYJrpiO/s1600/order-shipment.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_lcnoPqOP5I6lI59CYMN582S1HZdTsP_GQy8lOqFTfTfF-Pk4hJ7H5rHnrhr17_SznQOn_OMjaPti5NKUiHqz3JFwXCv6xfy37zz3uGVC0yuSs3awRwZFKy0Lp0preOgUSLwkZJYJrpiO/s400/order-shipment.png" width="400" height="100" data-original-width="643" data-original-height="161" /></a></div>
<p>The search for the suitable test scaffolding led us to discovery of the <a href="https://docs.pact.io/">Pact</a> framework (to be precise, <a href="https://github.com/DiUS/pact-jvm">Pact JVM</a>). The <a href="https://docs.pact.io/">Pact</a> offers simple and straightforward ways to write consumer and producer tests, leaving no excuses to not doing <a href="https://aredko.blogspot.com/2016/11/keep-your-promises-contract-based.html">consumer-driven contract testing</a>. But there is another player on the field, <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a>, and this is what we are going to discuss today.</p>
<p>To start with, <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a> fits the best JVM-based projects, built on top of terrific <a href="https://spring.io/projects">Spring</a> portfolio (although you could make it work in <a href="https://spring.io/blog/2018/02/13/spring-cloud-contract-in-a-polyglot-world">polyglot scenarios</a> as well). In addition, the collaboration flow that <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a> adopts is slightly different from the one <a href="https://docs.pact.io/">Pact</a> taught us, which is not necessarily a bad thing. Let us get straight to the point.</p>
<p>Since we are scoping out to messaging only, the first thing <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a> asks us to do is to define messaging contract specification, written using convenient <a href="https://cloud.spring.io/spring-cloud-static/Greenwich.RELEASE/multi/multi_contract-dsl.html">Groovy Contract DSL</a>.</p>
<pre class="brush:groovy">
package contracts
org.springframework.cloud.contract.spec.Contract.make {
name "OrderConfirmed Event"
label 'order'
input {
triggeredBy('createOrder()')
}
outputMessage {
sentTo 'orders'
body([
orderId: $(anyUuid()),
paymentId: $(anyUuid()),
amount: $(anyDouble()),
street: $(anyNonBlankString()),
city: $(anyNonBlankString()),
state: $(regex('[A-Z]{2}')),
zip: $(regex('[0-9]{5}')),
country: $(anyOf('USA','Mexico'))
])
headers {
header('Content-Type', 'application/json')
}
}
}
</pre>
<p>It resembles a lot <a href="https://docs.pact.io/">Pact</a> specifications we are already familiar with (if you are not a big fan of <a href="https://groovy-lang.org/index.html">Groovy</a>, no real need to learn it in order to use <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a>). The interesting parts here are <b>triggeredBy</b> and <b>sentTo</b> blocks: basically, those outline how the message is being produced (or triggered) and where it should land (channel or queue name) respectively. In this case, the <b>createOrder()</b> is just a method name which we have to provide the implementation for.</p>
<pre class="brush:java">
package com.example.order;
import java.math.BigDecimal;
import java.util.UUID;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.order.event.OrderConfirmed;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMessageVerifier
public class OrderBase {
@Autowired private MessageChannel orders;
public void createOrder() {
final OrderConfirmed order = new OrderConfirmed();
order.setOrderId(UUID.randomUUID());
order.setPaymentId(UUID.randomUUID());
order.setAmount(new BigDecimal("102.32"));
order.setStreet("1203 Westmisnter Blvrd");
order.setCity("Westminster");
order.setCountry("USA");
order.setState("MI");
order.setZip("92239");
orders.send(
MessageBuilder
.withPayload(order)
.setHeader("Content-Type", "application/json")
.build());
}
}
</pre><p>There is one small detail left out though: these contracts are managed by providers (or better to say, producers), not consumers. Not only that, the producers are responsible for publishing all the stubs for consumers so they would be able to write the tests against. Certainly a different path than <a href="https://docs.pact.io/">Pact</a> takes, but on the bright side, the test suite for producers are 100% generated by <a href="https://maven.apache.org/">Apache Maven</a> / <a href="https://gradle.org/">Gradle</a> plugins.</p>
<pre class="brush:xml">
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>2.1.4.RELEASE</version>
<extensions>true</extensions>
<configuration>
<packageWithBaseClasses>com.example.order</packageWithBaseClasses>
</configuration>
</plugin>
</pre>
<p>As you may have noticed, the plugin would assume that the base test classes (the ones which have to provide <b>createOrder()</b> method implementation) are located in the <b>com.example.order</b> package, exactly where we have placed <b>OrderBase</b> class. To complete the setup, we need to add a few dependencies to our <b>pom.xml</b> file.</p>
<pre class="brush:xml">
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.10.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-verifier</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</pre>
<p>And we are done with producer side! If we run <b>mvn clean install</b> right now, two things are going to happen. First, you will notice that some tests were run and passed, although we wrote none, these were generated on our behalf.</p>
<pre>
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.example.order.OrderTest
....
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
</pre>
<p>And secondly, the stubs for consumers are going to be generate (and published) as well (in this case, bundled into <b>order-service-messaging-contract-tests-0.0.1-SNAPSHOT-stubs.jar</b>).</p>
<pre>
...
[INFO]
[INFO] --- spring-cloud-contract-maven-plugin:2.1.4.RELEASE:generateStubs (default-generateStubs) @ order-service-messaging-contract-tests ---
[INFO] Files matching this pattern will be excluded from stubs generation []
[INFO] Building jar: order-service-messaging-contract-tests-0.0.1-SNAPSHOT-stubs.jar
[INFO]
....
</pre>
<p>Awesome, so we have messaging contract specification and stubs published, the ball is on consumer's field now, the <b>Shipment Service</b>. Probably, the most tricky part for the consumer would be to configure the messaging integration library of choice. In our case, it is going to be <a href="https://spring.io/projects/spring-cloud-stream">Spring Cloud Stream</a> however <a href="https://cloud.spring.io/spring-cloud-contract/reference/html/project-features.html#features-messaging-integrations">other integrations</a> are also available.</p>
<p>The fastest way to understand how the <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a> works on cosumer side is to start from the end and to look at the complete sample test suite first.</p>
<pre class="brush:java">
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMessageVerifier
@AutoConfigureStubRunner(
ids = "com.example:order-service-messaging-contract-tests:+:stubs",
stubsMode = StubRunnerProperties.StubsMode.LOCAL
)
public class OrderMessagingContractTest {
@Autowired private MessageVerifier<Message<?>> verifier;
@Autowired private StubFinder stubFinder;
@Test
public void testOrderConfirmed() throws Exception {
stubFinder.trigger("order");
final Message<?> message = verifier.receive("orders");
assertThat(message, notNullValue());
assertThat(message.getPayload(), isJson(
allOf(List.of(
withJsonPath("$.orderId"),
withJsonPath("$.paymentId"),
withJsonPath("$.amount"),
withJsonPath("$.street"),
withJsonPath("$.city"),
withJsonPath("$.state"),
withJsonPath("$.zip"),
withJsonPath("$.country")
))));
}
}
</pre>
<p>At the top, the <b>@AutoConfigureStubRunner</b> references the stubs published by producer, effectively the ones from <b>order-service-messaging-contract-tests-0.0.1-SNAPSHOT-stubs.jar</b> archive. The <b>StubFinder</b> helps us to pick the right stub for the test case and to trigger a particular messaging contract verification flow by means of calling <b>stubFinder.trigger("order")</b>. The value <b>"order"</b> is not arbitrary, it should match the <b>label</b> assigned to the contract specification, in our case we have it defined as: </p>
<pre class="brush:groovy">
package contracts
org.springframework.cloud.contract.spec.Contract.make {
...
label 'order'
...
}
</pre>
<p>With that, the test should be looking simple and straightfoward: trigger the flow, verify that the message has been placed into the messaging channel and satisfies the consumer expectations. From the configuration standpoint, we only need to provide this messaging channel to run the tests against.</p>
<pre class="brush:java">
@SpringBootConfiguration
public class OrderMessagingConfiguration {
@Bean
PollableChannel orders() {
return MessageChannels.queue().get();
}
}
</pre>
<p>And again, the name of the bean, <b>orders</b>, is not a random pick, it has to much the destination from the contract specification:</p>
<pre class="brush:groovy">
package contracts
org.springframework.cloud.contract.spec.Contract.make {
...
outputMessage {
sentTo 'orders'
...
}
...
}
</pre>
<p>Last but not least, let us enumerate the dependencies which are required on consumer side (luckily, there is no need to use any additional <a href="https://maven.apache.org/">Apache Maven</a> or <a href="https://gradle.org/">Gradle</a> plugins).</p>
<pre class="brush:xml">
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-verifier</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
<version>2.2.1.RELEASE</version>
<type>test-jar</type>
<scope>test</scope>
<classifier>test-binder</classifier>
</dependency>
</dependencies>
</pre>
<p>A quick note here. The last dependency is quite an important piece of the puzzle, it brings the integration of the <a href="https://spring.io/projects/spring-cloud-stream">Spring Cloud Stream</a> with <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a>. With that, the consumers are all set.</p>
<pre>
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.example.order.OrderMessagingContractTest
...
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
</pre>
<p>To close the loop, we should look back to the one of the core promises of the <a href="https://aredko.blogspot.com/2016/11/keep-your-promises-contract-based.html">consumer-driven contract testing</a>: allow the producers to evolve the contracts without breaking the consumers. What that means practically is that consumers may contribute their tests back to the producers, alhough the improtance of doing that is less of the concern with <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a>. The reason is simple: the producers are the ones who write the message contract specifications first and the tests generated out of these specifications are expected to fail against any breaking change. Nonetheless, there are number of benefits for producers to know how the consumers use their messages, so please give it some thoughts.</p>
<p>Hopefuly, it was an interesting subject to discuss. <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a> brings somewhat different perspective of applying <a href="https://aredko.blogspot.com/2016/11/keep-your-promises-contract-based.html">consumer-driven contract testing</a> for messaging. It is an appealing alternative to <a href="https://github.com/DiUS/pact-jvm">Pact JVM</a>, especially if your applications and services already rely on <a href="https://spring.io/projects/">Spring projects</a>.</p>
<p>As always, the complete project sources are available on <a href="https://github.com/reta/consumer-driven-contract-spring-cloud">Github</a>.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-44645552816760439282019-10-31T19:24:00.000-07:002019-10-31T19:24:24.366-07:00Tell us what you want and we will make it so: consumer-driven contract testing for messaging<p>Quite some time ago we have talked about <a href="https://aredko.blogspot.com/2016/11/keep-your-promises-contract-based.html">consumer-driven contract testing</a> from the perspective of the <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST(ful)</a> web APIs in general and their projection into Java (<a href="https://jax-rs-spec.java.net/">JAX-RS 2.0</a> specification) in particular. It would be fair to say that <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST</a> still dominates the web API landscape, at least with respect to public APIs, however the shift towards <a href="https://en.wikipedia.org/wiki/Microservices">microservices</a> or/and service-based architecture is changing the alignment of forces very fast. One of such disrupting trends is <a href="https://en.wikipedia.org/wiki/Message_queue">messaging</a>.</p>
<p>Modern <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST(ful)</a> APIs are implemented mostly over <a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP 1.1</a> protocol and are constrained by its request/response communication style. The <a href="https://en.wikipedia.org/wiki/HTTP/2">HTTP/2</a> is <a href="https://www.mnot.net/blog/2019/10/13/h2_api_multiplexing">here to help out</a> but still, not every use case fits into this communication model. Often the job could be performed asynchronously and the fact of its completion could be broadcasted to interested parties later on. This is how most of the things work in real life and using <a href="https://en.wikipedia.org/wiki/Message_queue">messaging</a> is a perfect answer to that.</p>
<p>The <a href="https://en.wikipedia.org/wiki/Message_queue">messaging</a> space is really crowded with astonishing amount of <a href="https://en.wikipedia.org/wiki/Message_broker">message brokers</a> and brokerless options available. We are not going to talk about that instead focusing on another tricky subject: the message contracts. Once the producer emits message or event, it lands into the queue/topic/channel, ready to be consumed. It is here to stay for some time. Obviously, the producer knows what it publishes, but what about consumers? How would they know what to expect?</p>
<p>At this moment, many of us would scream: use schema-based serialization! And indeed, <a href="https://avro.apache.org/">Apache Avro</a>, <a href="https://thrift.apache.org/">Apache Thrift</a>, <a href="https://developers.google.com/protocol-buffers">Protocol Buffers</a>, <a href="https://msgpack.org/index.html">Message Pack</a>, ... are here to address that. At the end of the day, such messages and events become the part of the provider contract, along with the <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST(ful)</a> web APIs if any, and have to be communicated and evolved over time without breaking the consumers. But ... you would be surprised to know how many organizations found their nirvana in <a href="https://en.wikipedia.org/wiki/JSON">JSON</a> and use it to pass messages and events around, throwing such <a href="https://en.wikipedia.org/wiki/Character_large_object">clobs</a> at consumers, no schema whatsoever! In this post we are going to look at how consumer-driven contract testing technique could help us in such situation.</p>
<p>Let us consider a simple system with two services, <b>Order Service</b> and <b>Shipment Service</b>. The <b>Order Service</b> publishes the messages / events to the message queue and <b>Shipment Service</b> consumes them from there.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOuiYJo1lXVzZz6dtF5qxJO4_hqZW4oMhpnGV8dskAFTk6ojWFiYZ9oGnTuMl20gEiY7pwyFcLIoZg3ML4FAdeinwlIIfAid7uYc57KQZK9pXpf2jIxaBGnyX90ONdTU3zgPllh8TgBSDQ/s1600/order-shipment.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOuiYJo1lXVzZz6dtF5qxJO4_hqZW4oMhpnGV8dskAFTk6ojWFiYZ9oGnTuMl20gEiY7pwyFcLIoZg3ML4FAdeinwlIIfAid7uYc57KQZK9pXpf2jIxaBGnyX90ONdTU3zgPllh8TgBSDQ/s400/order-shipment.png" width="400" height="100" data-original-width="643" data-original-height="161" /></a></div>
<p>Since <b>Order Service</b> is implemented in Java, the events are just POJO classes, serialized into <a href="https://en.wikipedia.org/wiki/JSON">JSON</a> before arriving to the message broker using one of the numerous libraries out there. <b>OrderConfirmed</b> is one of such events.</p>
<pre class="brush:java">
public class OrderConfirmed {
private UUID orderId;
private UUID paymentId;
private BigDecimal amount;
private String street;
private String city;
private String state;
private String zip;
private String country;
}
</pre>
<p>As it often happens, the <b>Shipment Service</b> team was handed over the sample <a href="https://en.wikipedia.org/wiki/JSON">JSON</a> snippet or pointed out some documentation piece, or reference Java class, and that is basically it. How <b>Shipment Service</b> team could kickoff the integration while being sure their interpretation is correct and the message's data they need will not suddenly disappear? <a href="https://martinfowler.com/articles/consumerDrivenContracts.html">Consumer-driven contract testing</a> to the rescue!</p>
<p>The <b>Shipment Service</b> team could (and should) start off by writing the test cases against the <b>OrderConfirmed</b> message, embedding the knowledge they have, and our old friend <a href="https://docs.pact.io/">Pact</a> framework (to be precise, <a href="https://github.com/DiUS/pact-jvm">Pact JVM</a>) is the right tool for that. So how the test case may look like?</p>
<pre class="brush:java">
public class OrderConfirmedConsumerTest {
private static final String PROVIDER_ID = "Order Service";
private static final String CONSUMER_ID = "Shipment Service";
@Rule
public MessagePactProviderRule provider = new MessagePactProviderRule(this);
private byte[] message;
@Pact(provider = PROVIDER_ID, consumer = CONSUMER_ID)
public MessagePact pact(MessagePactBuilder builder) {
return builder
.given("default")
.expectsToReceive("an Order confirmation message")
.withMetadata(Map.of("Content-Type", "application/json"))
.withContent(new PactDslJsonBody()
.uuid("orderId")
.uuid("paymentId")
.decimalType("amount")
.stringType("street")
.stringType("city")
.stringType("state")
.stringType("zip")
.stringType("country"))
.toPact();
}
@Test
@PactVerification(PROVIDER_ID)
public void test() throws Exception {
Assert.assertNotNull(message);
}
public void setMessage(byte[] messageContents) {
message = messageContents;
}
}
</pre>
<p>It is exceptionally simple and straightforward, no boilerplate added. The test case is designed right from the <a href="https://en.wikipedia.org/wiki/JSON">JSON</a> representation of the <b>OrderConfirmed</b> message. But we are only half-way through, the <b>Shipment Service</b> team should somehow contribute their expectations back to the <b>Order Service</b> so the producer would keep track of who and how consumes the <b>OrderConfirmed</b> message. The <a href="https://docs.pact.io/">Pact</a> test harness takes care of that by generating the pact files (set of agreements, or pacts) out of the each <a href="https://junit.org/">JUnit</a> test cases into the <b>'target/pacts'</b> folder. Below is an example of the generated <b>Shipment Service-Order Service.json</b> pact file after running <b>OrderConfirmedConsumerTest</b> test suite.</p>
<pre class="brush:java">
{
"consumer": {
"name": "Shipment Service"
},
"provider": {
"name": "Order Service"
},
"messages": [
{
"description": "an Order confirmation message",
"metaData": {
"contentType": "application/json"
},
"contents": {
"zip": "string",
"country": "string",
"amount": 100,
"orderId": "e2490de5-5bd3-43d5-b7c4-526e33f71304",
"city": "string",
"paymentId": "e2490de5-5bd3-43d5-b7c4-526e33f71304",
"street": "string",
"state": "string"
},
"providerStates": [
{
"name": "default"
}
],
"matchingRules": {
"body": {
"$.orderId": {
"matchers": [
{
"match": "regex",
"regex": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
}
],
"combine": "AND"
},
"$.paymentId": {
"matchers": [
{
"match": "regex",
"regex": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
}
],
"combine": "AND"
},
"$.amount": {
"matchers": [
{
"match": "decimal"
}
],
"combine": "AND"
},
"$.street": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.city": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.state": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.zip": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.country": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "4.0.2"
}
}
}</pre>
<p>The next step for <b>Shipment Service</b> team is to share this pact file with <b>Order Service</b> team so these guys could run the provider-side <a href="https://docs.pact.io/">Pact</a> verifications as part of their test suites.</p>
<pre class="brush:java">
@RunWith(PactRunner.class)
@Provider(OrderServicePactsTest.PROVIDER_ID)
@PactFolder("pacts")
public class OrderServicePactsTest {
public static final String PROVIDER_ID = "Order Service";
@TestTarget
public final Target target = new AmqpTarget();
private ObjectMapper objectMapper;
@Before
public void setUp() {
objectMapper = new ObjectMapper();
}
@State("default")
public void toDefaultState() {
}
@PactVerifyProvider("an Order confirmation message")
public String verifyOrderConfirmed() throws JsonProcessingException {
final OrderConfirmed order = new OrderConfirmed();
order.setOrderId(UUID.randomUUID());
order.setPaymentId(UUID.randomUUID());
order.setAmount(new BigDecimal("102.33"));
order.setStreet("1203 Westmisnter Blvrd");
order.setCity("Westminster");
order.setCountry("USA");
order.setState("MI");
order.setZip("92239");
return objectMapper.writeValueAsString(order);
}
}
</pre>
<p>The test harness picks all the pact files from the <b>@PactFolder</b> and run the tests against the <b>@TestTarget</b>, in this case we are wiring <b>AmqpTarget</b>, provided out of the box, but your could plug your own specific target easily.</p>
<p>And this is basically it! The consumer (<b>Shipment Service</b>) have their expectations expressed in the test cases and shared with the producer (<b>Order Service</b>) in a shape of the pact files. The producers have own set of tests to make sure its model matches the consumers' view. Both sides could continue to evolve independently, and trust each other, as far as pacts are not denounced (hopefully, never).</p>
<p>To be fair, <a href="https://docs.pact.io/">Pact</a> is not the only choice for doing <a href="https://martinfowler.com/articles/consumerDrivenContracts.html">consumer-driven contract testing</a>, in the upcoming post (already in work) we are going to talk about yet another excellent option, <a href="https://spring.io/projects/spring-cloud-contract">Spring Cloud Contract</a>. </p>
<p>As for today, the complete project sources are available on <a href="https://github.com/reta/consumer-driven-contract-messaging-pact">Github</a>. </p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0tag:blogger.com,1999:blog-8745745886722714957.post-26500544680215777822019-07-18T14:23:00.000-07:002019-07-18T14:23:14.862-07:00Testing Spring Boot conditionals the sane way<p>If you are more or less experienced <a href="https://spring.io/projects/spring-boot">Spring Boot</a> user, it is very luckily that at some point you may need to run into the situation when the particular beans or configurations have to be injected <a href="https://reflectoring.io/spring-boot-conditionals/">conditionally</a>. The mechanics of it is well <a href="https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html">understood</a> but sometimes the testing such conditions (and their combinations) could get messy. In this post we are going to talk about some possible (arguably, sane) ways to approach that.</p>
<p>Since <a href="https://docs.spring.io/spring-boot/docs/1.5.21.RELEASE/reference/html/">Spring Boot 1.5.x</a> is still widely used (nonetheless it is racing towards the <a href="https://spring.io/blog/2018/07/30/spring-boot-1-x-eol-aug-1st-2019">EOL this August</a>), we would include it along with <a href="https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/">Spring Boot 2.1.x</a>, both with <a href="https://junit.org/junit4/">JUnit 4.x</a> and <a href="https://junit.org/">JUnit 5.x</a>. The techniques we are about to cover are equally applicable to the regular configuration classes as well as <a href="https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html">auto-configurations</a> classes.</p>
<p>The example we will be playing with would be related to our home-made logging. Let us assume our <a href="https://spring.io/projects/spring-boot">Spring Boot</a> application requires some bean for dedicated logger with name <b>"sample"</b>. In certain circumstances however this logger has to be disabled (or become effectively a noop), so the property <b>logging.enabled</b> serves like a kill switch here. We use <a href="https://www.slf4j.org/">Slf4j</a> and <a href="https://logback.qos.ch/">Logback</a> in this example, but it is not really important. The <b>LoggingConfiguration</b> snippet below reflects this idea.</p>
<pre class="brush:java">
@Configuration
public class LoggingConfiguration {
@Configuration
@ConditionalOnProperty(name = "logging.enabled", matchIfMissing = true)
public static class Slf4jConfiguration {
@Bean
Logger logger() {
return LoggerFactory.getLogger("sample");
}
}
@Bean
@ConditionalOnMissingBean
Logger logger() {
return new NOPLoggerFactory().getLogger("sample");
}
}
</pre>
<p>So how would we test that? <a href="https://spring.io/projects/spring-boot">Spring Boot</a> (and <a href="https://spring.io/projects/spring-framework">Spring Framework</a> in general) has always offered the <a href="https://docs.spring.io/spring-boot/docs/1.5.21.RELEASE/reference/html/boot-features-testing.html">outstanding test scaffolding support</a>. The <b>@SpringBootTest</b> and <b>@TestPropertySource</b> annotations allow to quickly bootstrap the application context with the customized properties. There is one issue though: they are applied per test class level, not a per test method. It certainly makes sense but basically requires you to create a test class per combination of conditionals.</p>
<p>If you are still with <a href="https://junit.org/junit4/">JUnit 4.x</a>, there is one trick you may found useful which exploits <b>Enclosed</b> runner, the hidden gem of the framework.</p>
<pre class="brush:java">
@RunWith(Enclosed.class)
public class LoggingConfigurationTest {
@RunWith(SpringRunner.class)
@SpringBootTest
public static class LoggerEnabledTest {
@Autowired private Logger logger;
@Test
public void loggerShouldBeSlf4j() {
assertThat(logger).isInstanceOf(ch.qos.logback.classic.Logger.class);
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = "logging.enabled=false")
public static class LoggerDisabledTest {
@Autowired private Logger logger;
@Test
public void loggerShouldBeNoop() {
assertThat(logger).isSameAs(NOPLogger.NOP_LOGGER);
}
}
}
</pre>
<p>You still have the class per condition but at least they are all in the same nest. With <a href="https://junit.org/">JUnit 5.x</a>, some things got easier but not to the level as one might expect. Unfortunately, <a href="https://docs.spring.io/spring-boot/docs/1.5.21.RELEASE/reference/html/">Spring Boot 1.5.x</a> does not support <a href="https://junit.org/">JUnit 5.x</a> natively, so we have to rely on extension provided by <a href="https://github.com/sbrannen/spring-test-junit5">spring-test-junit5</a> community module. Here are the relevant changes in <b>pom.xml</b>, please notice that <b>junit</b> is explicitly excluded from the <b>spring-boot-starter-test</b> dependencies graph.</p>
<pre class="brush:xml">
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.sbrannen</groupId>
<artifactId>spring-test-junit5</artifactId>
<version>1.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
</pre>
<p>The test case itself is not very different besides usage of the <b>@Nested</b> annotation, which comes from <a href="https://junit.org/">JUnit 5.x</a> to support tests as inner classes.</p>
<pre class="brush:java">
public class LoggingConfigurationTest {
@Nested
@ExtendWith(SpringExtension.class)
@SpringBootTest
@DisplayName("Logging is enabled, expecting Slf4j logger")
public static class LoggerEnabledTest {
@Autowired private Logger logger;
@Test
public void loggerShouldBeSlf4j() {
assertThat(logger).isInstanceOf(ch.qos.logback.classic.Logger.class);
}
}
@Nested
@ExtendWith(SpringExtension.class)
@SpringBootTest
@TestPropertySource(properties = "logging.enabled=false")
@DisplayName("Logging is disabled, expecting NOOP logger")
public static class LoggerDisabledTest {
@Autowired private Logger logger;
@Test
public void loggerShouldBeNoop() {
assertThat(logger).isSameAs(NOPLogger.NOP_LOGGER);
}
}
}
</pre><p>If you try to run the tests from the command line using <a href="https://maven.apache.org/">Apache Maven</a> and <a href="https://maven.apache.org/surefire/maven-surefire-plugin">Maven Surefire plugin</a>, you might be surprised to see that none of them were executed during the build. The issue is that ... <a href="https://maven.apache.org/surefire/maven-surefire-plugin/examples/junit-platform.html">all nested classes are excluded</a> ... so we need to put in place <a href="https://junit.org/junit5/docs/current/user-guide/#running-tests-build-maven-filter-test-class-names">another workaround</a>.</p>
<pre class="brush:xml">
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<excludes>
<exclude />
</excludes>
</configuration>
</plugin>
</pre>
<p>With that, things should be rolling smoothly. But enough about <a href="https://spring.io/blog/2018/07/30/spring-boot-1-x-eol-aug-1st-2019">legacy</a>, the <a href="https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/">Spring Boot 2.1.x</a> comes as the complete game changer. The family of the context runners, <b>ApplicationContextRunner</b>, <b>ReactiveWebApplicationContextRunner</b> and <b>WebApplicationContextRunner</b>, provide an easy and straightforward way to tailor the context on per test method level, keeping the test executions incredibly fast.</p>
<pre class="brush:java">
public class LoggingConfigurationTest {
private final ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(UserConfigurations.of(LoggingConfiguration.class));
@Test
public void loggerShouldBeSlf4j() {
runner
.run(ctx ->
assertThat(ctx.getBean(Logger.class)).isInstanceOf(Logger.class)
);
}
@Test
public void loggerShouldBeNoop() {
runner
.withPropertyValues("logging.enabled=false")
.run(ctx ->
assertThat(ctx.getBean(Logger.class)).isSameAs(NOPLogger.NOP_LOGGER)
);
}
}
</pre>
<p>It looks really great. The <a href="https://junit.org/">JUnit 5.x</a> support in <a href="https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/">Spring Boot 2.1.x</a> is much better and with the the upcoming <b>2.2<b></b></b> release, <a href="https://junit.org/">JUnit 5.x</a> will be the <a href="https://github.com/spring-projects/spring-boot/issues/14736">default engine</a> (not to worry, the old <a href="https://junit.org/junit4/">JUnit 4.x</a> will still be supported). As of now, the switch to <a href="https://junit.org/">JUnit 5.x</a> needs a bit of work on dependencies side.</p>
<pre class="brush:xml">
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</pre>
<p>As an additional step, you may need to use recent <a href="https://maven.apache.org/surefire/maven-surefire-plugin">Maven Surefire plugin</a>, <b>2.22.0</b> or above, with out-of-the box <a href="https://junit.org/">JUnit 5.x</a> support. The the snippet below illustrates that.</p>
<pre class="brush:xml">
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</pre>
<p>The sample configuration we have worked with is pretty naive, many of the real-world applications would end up with quite complex contexts built out of many conditionals. The flexibility and enormous opportunities that come out of the context runners, the invaluable addition to the <a href="https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/">Spring Boot 2.x</a> test scaffolding, are just the live savers, please keep them in mind.</p>
<p>The complete project sources are available on <a href="https://github.com/reta/spring-boot-testing-conditional">Github</a>.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com2tag:blogger.com,1999:blog-8745745886722714957.post-80289936954386638192019-05-11T08:23:00.000-07:002019-05-11T08:23:13.367-07:00When HTTP status code is not enough: tackling web APIs error reporting<p>One area of the <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">RESTful</a> web APIs design, quite frequently overlooked, is how to report errors and problems, either related to business or application. The proper usage of the <a href="https://www.restapitutorial.com/httpstatuscodes.html">HTTP status codes</a> comes to mind first, and although quite handy, often it is not informative enough. Let us take <a href="https://www.restapitutorial.com/httpstatuscodes.html">400 Bad Request</a> for example. Yes, it clearly states that the request is problematic, but what exactly is wrong?</p>
<p>The <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">RESTful</a> architectural style does not dictate what should be done in this case and so everyone is inventing its own styles, conventions and specifications. It could be as simple as including error message into the response or as shortsighted as copy/pasting long stack traces (in case of Java or .NET, to name a few cultprits). There is no shortage of ideas but luckily, we have at least some guidance available in the form of <a href="https://tools.ietf.org/html/rfc7807">RFC 7807: Problem Details for HTTP APIs</a>. Despite the fact that it is not an official specification but a draft (still), it outlines the good common principles on the problem at hand and this is what we are going to talk about in this post.</p>
<p>In the nutshell, <a href="https://tools.ietf.org/html/rfc7807">RFC 7807: Problem Details for HTTP APIs</a> just proposes the error or problem representation (in <a href="https://www.json.org/">JSON</a> or <a href="https://en.wikipedia.org/wiki/XML">XML</a> formats) which may include at least the following details:
<ul>
<li><strong>type</strong> - A URI reference that identifies the problem type</li>
<li><strong>title</strong> - A short, human-readable summary of the problem type</li>
<li><strong>status</strong> - The <a href="https://tools.ietf.org/html/rfc7231#section-6">HTTP status code</a></li>
<li><strong>detail</strong> - A human-readable explanation specific to this occurrence of the problem</li>
<li><strong>instance</strong> - A URI reference that identifies the specific occurrence of the problem</li>
</ul>
More importantly, the problem type definitions may extend the problem details object with additional members, contributing to the ones above. As you see, it looks dead simple from the implementation perspective. Even better, thanks to <a href="https://github.com/zalando">Zalando</a>, we already have the <a href="https://tools.ietf.org/html/rfc7807">RFC 7807: Problem Details for HTTP APIs</a> implementation <a href="https://github.com/zalando/problem">for Java</a> (and <a href="https://github.com/zalando/problem-spring-web">Spring Web</a> in particular). So ... let us give it a try!
</p>
<p>Our imaginary People Management web API is going to be built using the state of the art technology stack, <a href="https://spring.io/projects/spring-boot">Spring Boot</a> and <a href="https://cxf.apache.org/">Apache CXF</a>, the popular web services framework and <a href="https://jcp.org/aboutJava/communityprocess/final/jsr370/index.html">JAX-RS 2.1</a> implementation. To keep it somewhat simple, there are only two endpoints which are exposed: registration and lookup by person identifier.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqY3eA0vYdlZiFPPZYuSuR18RIjvulWOTxcfT64cY-tKgGbGykRKcgr2SQXmfCVFf50BTL4ZzgpXk0e3pnnXey0zxJ8hXsSUeUZbpN-LN0YSu9HtM6wEPcGCgJ8nEGfFrNRGQgRpzQicEk/s1600/people-management-problem-reporting.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqY3eA0vYdlZiFPPZYuSuR18RIjvulWOTxcfT64cY-tKgGbGykRKcgr2SQXmfCVFf50BTL4ZzgpXk0e3pnnXey0zxJ8hXsSUeUZbpN-LN0YSu9HtM6wEPcGCgJ8nEGfFrNRGQgRpzQicEk/s640/people-management-problem-reporting.png" width="540" height="250" data-original-width="1244" data-original-height="587" /></a></div>
<p>Sweeping aside the tons of issues and business constraints you may run into while developing the real-world services, even with this simple API a few things may go wrong. The first problem we age going to tackle is what if the person you are looking for is not registered yet? Looks like a fit for <b>404 Not Found</b>, right? Indeed, let us start with our first problem, <b>PersonNotFoundProblem</b>!</p>
<pre class="brush: java">
public class PersonNotFoundProblem extends AbstractThrowableProblem {
private static final long serialVersionUID = 7662154827584418806L;
private static final URI TYPE = URI.create("http://localhost:21020/problems/person-not-found");
public PersonNotFoundProblem(final String id, final URI instance) {
super(TYPE, "Person is not found", Status.NOT_FOUND,
"Person with identifier '" + id + "' is not found", instance,
null, Map.of("id", id));
}
}
</pre>
<p>It resembles a lot the typical Java exception, and it really is one, since <b>AbstractThrowableProblem</b> is the subclass of the <b>RuntimeException</b>. As such, we could throw it from our <a href="https://jcp.org/aboutJava/communityprocess/final/jsr370/index.html">JAX-RS</a> API. </p>
<pre class="brush: java">
@Produces({ MediaType.APPLICATION_JSON, "application/problem+json" })
@GET
@Path("{id}")
public Person findById(@PathParam("id") String id) {
return service
.findById(id)
.orElseThrow(() -> new PersonNotFoundProblem(id, uriInfo.getRequestUri()));
}
</pre>
<p>If we run the server and just try to fetch the person providing any identifier, the problem detail response is going to be returned back (since the dataset is not pre-populated), for example:</p>
<pre>
$ curl "http://localhost:21020/api/people/1" -H "Accept: */*"
HTTP/1.1 404
Content-Type: application/problem+json
{
"type" : "http://localhost:21020/problems/person-not-found",
"title" : "Person is not found",
"status" : 404,
"detail" : "Person with identifier '1' is not found",
"instance" : "http://localhost:21020/api/people/1",
"id" : "1"
}
</pre>
<p>Please notice the usage of the <b>application/problem+json</b> media type along with additional property <b>id</b> being included into the response. Although there are many things which could be improved, it is arguably better than just naked <b>404</b> (or <b>500</b> caused by <b>EntityNotFoundException</b>). Plus, the documentation section behind this <b>type</b> of the problem (in our case, <b>http://localhost:21020/problems/person-not-found</b>) could be consulted in case further clarifications may be needed.</p>
<p>So designing the problems after exceptions is just one option. You may often (and for very valid reasons) restrain from coupling you business logic with unrelated details. In this case, it is perfectly valid to return the problem details as the response payload from the <a href="https://jcp.org/aboutJava/communityprocess/final/jsr370/index.html">JAX-RS</a> resource. As an example, the registration process may raise <b>NonUniqueEmailException</b> so our web API layer could transform it into appropriate problem detail.</p>
<pre class="brush: java">
@Consumes(MediaType.APPLICATION_JSON)
@Produces({ MediaType.APPLICATION_JSON, "application/problem+json" })
@POST
public Response register(@Valid final CreatePerson payload) {
try {
final Person person = service.register(payload.getEmail(),
payload.getFirstName(), payload.getLastName());
return Response
.created(uriInfo.getRequestUriBuilder().path(person.getId()).build())
.entity(person)
.build();
} catch (final NonUniqueEmailException ex) {
return Response
.status(Response.Status.BAD_REQUEST)
.type("application/problem+json")
.entity(Problem
.builder()
.withType(URI.create("http://localhost:21020/problems/non-unique-email"))
.withInstance(uriInfo.getRequestUri())
.withStatus(Status.BAD_REQUEST)
.withTitle("The email address is not unique")
.withDetail(ex.getMessage())
.with("email", payload.getEmail())
.build())
.build();
}
}
</pre>
<p>To trigger this issue, it is enough to run the server instance and try to register the same person twice, like we have done below.</p>
<pre>
$ curl -X POST "http://localhost:21020/api/people" \
-H "Accept: */*" -H "Content-Type: application/json" \
-d '{"email":"john@smith.com", "firstName":"John", "lastName": "Smith"}'
HTTP/1.1 400
Content-Type: application/problem+json
{
"type" : "http://localhost:21020/problems/non-unique-email",
"title" : "The email address is not unique",
"status" : 400,
"detail" : "The email 'john@smith.com' is not unique and is already registered",
"instance" : "http://localhost:21020/api/people",
"email" : "john@smith.com"
}
</pre>
<p>Great, so our last example is a bit more complicated but, probably, at the same time, the most realistic one. Our web API heavily relies on <a href="https://beanvalidation.org/">Bean Validation</a> in order to make sure the input provided by the consumers of the API is valid. How would we represent the validation errors as the problem details? The most straightforward way is to supply the dedicated <b>ExceptionMapper</b> provider, which is the part of the <a href="https://jcp.org/aboutJava/communityprocess/final/jsr370/index.html">JAX-RS</a> specification. Let us introduce one.</p>
<pre class="brush: java">
@Provider
public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
@Context private UriInfo uriInfo;
@Override
public Response toResponse(final ValidationException ex) {
if (ex instanceof ConstraintViolationException) {
final ConstraintViolationException constraint = (ConstraintViolationException) ex;
final ThrowableProblem problem = Problem
.builder()
.withType(URI.create("http://localhost:21020/problems/invalid-parameters"))
.withTitle("One or more request parameters are not valid")
.withStatus(Status.BAD_REQUEST)
.withInstance(uriInfo.getRequestUri())
.with("invalid-parameters", constraint
.getConstraintViolations()
.stream()
.map(this::buildViolation)
.collect(Collectors.toList()))
.build();
return Response
.status(Response.Status.BAD_REQUEST)
.type("application/problem+json")
.entity(problem)
.build();
}
return Response
.status(Response.Status.INTERNAL_SERVER_ERROR)
.type("application/problem+json")
.entity(Problem
.builder()
.withTitle("The server is not able to process the request")
.withType(URI.create("http://localhost:21020/problems/server-error"))
.withInstance(uriInfo.getRequestUri())
.withStatus(Status.INTERNAL_SERVER_ERROR)
.withDetail(ex.getMessage())
.build())
.build();
}
protected Map<?, ?> buildViolation(ConstraintViolation<?> violation) {
return Map.of(
"bean", violation.getRootBeanClass().getName(),
"property", violation.getPropertyPath().toString(),
"reason", violation.getMessage(),
"value", Objects.requireNonNullElse(violation.getInvalidValue(), "null")
);
}
}
</pre><p>The snippet above distingushes two kind of issues: the <b>ConstraintViolationException</b>s indicate the invalid input and are mapped to <b>400 Bad Request</b>, whereas generic <b>ValidationException</b>s indicate the problem on the server side and are mapped to <b>500 Internal Server Error</b>. We only extract the basic details about violations, however even that improves the error reporting a lot. </p>
<pre>
$ curl -X POST "http://localhost:21020/api/people" \
-H "Accept: */*" -H "Content-Type: application/json" \
-d '{"email":"john.smith", "firstName":"John"}' -i
HTTP/1.1 400
Content-Type: application/problem+json
{
"type" : "http://localhost:21020/problems/invalid-parameters",
"title" : "One or more request parameters are not valid",
"status" : 400,
"instance" : "http://localhost:21020/api/people",
"invalid-parameters" : [
{
"reason" : "must not be blank",
"value" : "null",
"bean" : "com.example.problem.resource.PeopleResource",
"property" : "register.payload.lastName"
},
{
"reason" : "must be a well-formed email address",
"value" : "john.smith",
"bean" : "com.example.problem.resource.PeopleResource",
"property" : "register.payload.email"
}
]
}
</pre>
<p>This time the additional information bundled into the <b>invalid-parameters</b> member is quite verbose: we know the class (<b>PeopleResource</b>), method (<b>register</b>), the method's argument (<b>payload</b>) and the properties (<b>lastName</b> and <b>email</b>) respectively (all that extracted from the property path).</p>
<p>Meaningful error reporting is one of corner stones of the modern <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">RESTful</a> web APIs. Often it is not easy but definitely worth the efforts. The consumers (which often are just other developers) should have a clear understanding of what went wrong and what to do about it. The <a href="https://tools.ietf.org/html/rfc7807">RFC 7807: Problem Details for HTTP APIs</a> is a step into right direction and libraries like <a href="https://github.com/zalando/problem">problem</a> and <a href="https://github.com/zalando/problem-spring-web">problem-spring-web</a> are here to back you up, please make use of them.</p>
<p>The complete source code is available on <a href="https://github.com/reta/jax-rs-2.1-problem">Github</a>.</p>Andriy Redkohttp://www.blogger.com/profile/16473725484333171169noreply@blogger.com0