Wednesday, January 11, 2023

Hidden gems in Java 19, Part 2: The real hidden stuff


Compared to previous Java releases, the scope of changes in Java 19 has decreased significantly by targeting only seven implemented JEPs—most of which are new or improved incubator or preview features. As far as affecting your production code, you can take a little breather. However, Java 19 contains thousands of performance, security, and stability updates that aren’t in a JEP—and they are worthy of being adopted.

Oracle Java Tutorial and Material, Oracle Java Prep, Java Preparation, Java Guides, Java Learning, Java Certification

Even if you don’t use any of the preview or incubator features in the JEPs (read all about them in “Hidden gems in Java 19, Part 1: The not-so-hidden JEPs”), you should consider moving to Java 19.

This article outlines the updates buried deep in the release notes, and you and your team should be aware of them. For example, some new features include support for Unicode 14.0, additional date-time formats, new Transport Layer Security (TLS) signature schemes, and defense against Return Oriented Programming (ROP) attacks via PAC-RET protection on AArch64 systems.

I am using the Java 19.0.1 jshell tool to demonstrate the code in this article. If you want to test the features, download JDK 19, fire up your terminal, check your version, and run jshell, as follows. Note that you might see a newer dot-release version of the JDK, but nothing else should change.

[mtaman]:~ java -version
 java version "19.0.1" 2022-10-18
 Java(TM) SE Runtime Environment (build 19.0.1+10-21)
 Java HotSpot(TM) 64-Bit Server VM (build 19.0.1+10-21, mixed mode, sharing)

[mtaman]:~ jshell --enable-preview
|  Welcome to JShell -- Version 19.0.1
|  For an introduction type: /help intro

jshell>

The enhancements


This section describes some additions and enhancements in Java 19.

Support for Unicode 14.0. Java 19 provides a small but significant addition for internationalization; it provides upgrades to Unicode 14.0. The java.lang.Character class now supports Level 14 of the Unicode Character Database (UCD), which adds 838 new characters, 5 new scripts, and 37 new emoji characters.

New system properties for System.out and System.err. If you run an existing application with Java 19, you may see question marks on the console instead of special characters. This is because, as of Java 19, the operating system’s default encoding is used for printing to System.out and System.err.

For example, cp1252 encoding is the default on Windows. If that’s not what you want and you’d prefer to see output in UTF-8, add the following JVM options when calling the application:

-Dstdout.encoding=utf8 -Dstderr.encoding=utf8

Your platform determines what these system properties’ default settings are. When the platform doesn’t have console streams, the values default to the native.encoding property’s value. When necessary, the launcher’s command-line option -D can override the properties and set them to UTF-8.

If you don’t want to do this each time the software launches, you can also define the following environment variable (it starts with an underscore) to set these parameters globally:

_JAVA_OPTIONS="-Dstdout.encoding=utf8 -Dstderr.encoding=utf8"

New methods to create preallocated hash maps and hashsets. You might wonder why you would need new methods to create preallocated hash maps and hashsets. Here’s an example to clarify that more: If you want to create an ArrayList of 180 elements, you could write the following code:

List<String> list = new ArrayList<>(180);

The underlying array, the ArrayList, is allocated directly for 180 elements and does not have to be enlarged several times as you insert the 180 elements.

Similarly, you might try to create a HashMap with 180 preallocated mappings as follows:

Map<String, Integer> map = new HashMap<>(180);

Intuitively, you would think that this new HashMap offers space for 180 mappings. However, it does not! This happens because the HashMap has a default load factor of 0.75 when it is initialized. This indicates that the HashMap gets rebuilt (also called rehashed) with double the size as soon as it is 75% filled. Thus, the new HashMap is initialized with a capacity of 180 and can hold only 135 (180 × 0.75) mappings without being rehashed.

Therefore, to create a HashMap for 180 mappings, calculate the capacity by dividing the number of mappings by the load factor: 180 ÷ 0.75 = 240. So, a HashMap for 180 mappings would be created as follows:

// for 180 mappings: 180 / 0.75 = 240
Map<String, Integer> map = new HashMap<>(240);

Java 19 makes it easier to create a HashMap that has the required mappings without fiddling with load factors by using the new static factory method newHashMap(int).

Map<String, Integer> map = HashMap.newHashMap(180);

Look at the source code to see how it works.

public static <K, V> HashMap<K, V> newHashMap(int numMappings) {
    return new HashMap<>(calculateHashMapCapacity(numMappings));
}

static final float DEFAULT_LOAD_FACTOR = 0.75f;

static int calculateHashMapCapacity(int numMappings) {
    return (int) Math.ceil(numMappings / (double) DEFAULT_LOAD_FACTOR);
}

Similar labor-saving static factory methods have been created in Java 19. Here’s the complete set.

◉ HashMap.newHashMap
◉ LinkedHashMap.newLinkedHashMap
◉ WeakHashMap.newWeakHashMap
◉ HashSet.newHashSet
◉ LinkedHashSet.newLinkedHashSet

TLS signature schemes. Applications now can alter the signature schemes used in specific TLS or Datagram Transport Layer Security (DTLS) connections using two new Java SE methods, setSignatureSchemes() and getSignatureSchemes(), which are found in the class javax.net.ssl.SSLParameters.

The underlying provider may set the default signature schemes for each TLS or DTLS connection. Applications can also alter the provider-specific default signature schemes by using the jdk.tls.server.SignatureSchemes and jdk.tls.client.SignatureSchemes system attributes. The setSignatureSchemes() method overrides the default signature schemes for the specified TLS or DTLS connections if the signature schemes parameter is not null.

It is recommended that when third-party vendors add support for Java 19 or later releases, they also add support for these methods. The JDK SunJSSE provider supports this technique. However, you should be aware that a provider might not have received an update to support the new APIs, in which case the provider might disregard the established signature schemes.

Support for PAC-RET protection on Linux/AArch64. To defend against Return Oriented Programming (ROP) attacks (documentation here), OpenJDK uses hardware features from the ARM v8.3 Pointer Authentication Code (PAC) extension but only when they are enabled.

To use this functionality, OpenJDK must first be compiled using GCC 9.1.0+ or LLVM 10+ with the configuration flag --enable-branch-protection. Then, if the system supports it and the Java binary was compiled with branch protection enabled, the runtime flag -XX:UseBranchProtection=standard will enable PAC-RET protection; otherwise, the flag is quietly ignored. A warning will be printed to the console if the system does not support PAC-RET protection or if the Java binary was not built with branch protection enabled. As an alternative, -XX:UseBranchProtection=pac-ret also enables PAC-RET protection.

Additional date-time formats. Java 19 brings new formats to the java.time.format.DateTimeFormatter and DateTimeFormatterBuilder classes. In prior releases, only four predefined styles were available: FormatStyle.FULL, FormatStyle.LONG, FormatStyle.MEDIUM, and FormatStyle.SHORT. Now you can specify a flexible style with the new DateTimeFormatter.ofLocalizedPattern(String requestedTemplate) method.

For example, the following creates a formatter that may format a date according to a locale, for example, “Feb 2022” in the US locale and “2022年2月” in the Japanese locale:

DateTimeFormatter.ofLocalizedPattern("yMMM")

There’s also a new supporting function: DateTimeFormatterBuilder.appendLocalized(String requestedTemplate).

Automatic generation of the class data sharing archive. With Java 19, the JVM option -XX:+AutoCreateSharedArchive automatically creates or updates an application’s class data sharing (CDS) archive, for example

java -XX:+AutoCreateSharedArchive -XX:SharedArchiveFile=app.jsa -cp application.jar App

The specified CDS archive will be written if it does not exist or if a different version of the JDK generated it.

Javadoc search enhancements. Java 19 can create a standalone search page for the API documentation produced by Javadoc, and the search syntax has been improved to support multiple search terms.

Highlighting of deprecated elements, variables, and keywords. The Java Shell tool (jshell) now marks deprecated elements and highlights deprecated variables and keywords in the console.

Specified stack size no longer rounded up. Historically, the actual Java thread stack size might differ from the value provided by the -Xss command-line option; it might be rounded up to a multiple of the system page size when that’s required by the operating system. That’s been fixed in Java 19, so the stack size specified is what you get.

Larger default key sizes for cryptographic algorithms. What happens if the caller does not specify a key size when using a KeyPairGenerator or KeyGenerator object to generate a key pair or secret key? In such cases, JDK providers use provider-specific default values.

Java 19 increases the default key sizes for various cryptographic algorithms as follows:

◉ Elliptic Curve Cryptography (ECC): increased from 256 to 384 bits
◉ Rivest-Shamir-Adleman (RSA), RSASSA-PSS, and Diffie-Hellman (DH): increased from 2,048 to 3,072 bits
◉ Advanced Encryption Standard (AES): increased from 128 to 256 bits, if permitted by the cryptographic policy; otherwise, it falls back to 128

In addition, the default digest method used by the jarsigner tool has changed from SHA-256 to SHA-384. The jarsigner tool’s default signature algorithm has also been modified to reflect this. Except for more-extended key sizes whose security strength matches SHA-512, SHA-384 is used instead of SHA-256.

Note that jarsigner will keep using SHA256withDSA as the default signature algorithm for Digital Signature Algorithm (DSA) keys to help with interoperability with earlier Java editions.

Linux cpu.shares argument no longer misinterpreted. The Linux cgroups argument cpu.shares was improperly interpreted by earlier JDK editions. When the JVM was run inside a container, this could result in the JVM using fewer CPUs than were available, underutilizing CPU resources.

With Java 19, the JVM will no longer, by default, take cpu.shares into account when determining how many threads to allocate to the various thread pools.

To return to the old behavior, use the command-line option -XX:+UseContainerCpuShares, but be aware that this option is deprecated and might be eliminated in a subsequent JDK release.

Upgraded support for locale data. Locale data based on the Unicode Common Locale Data Repository (CLDR) has been upgraded to version 41. Refer to the Unicode Consortium’s CLDR release notes for the list of changes.

Bug fixes and changes


This section describes some of the bug fixes and changes in Java 19.

Source- and binary-incompatible changes to java.lang.Thread. With the preview introduction of virtual threads in Java 19’s JEP 425, some source and binary changes have been made to the class java.lang.Thread that may impact your code if you extend the class. More details are in the documentation. Be aware of the following changes:

◉ Three new final methods have been added: Thread.isVirtual(), Thread.threadId(), and Thread.join(Duration). Suppose there is existing compiled code that extends Thread, and the subclass declares a method with the same name, parameters, and return type as any of these methods. In such a case, IncompatibleClassChangeError will be thrown at runtime if the subclass is loaded.
◉ The Thread class defines several new methods. If one of your source code files extends Thread and a method in the subclass conflicts with any of the new Thread methods, the file will not compile without being changed.
◉ Thread.Builder is added as a nested interface. If one of your source code files extends Thread and imports a class named Builder, and the code in the subclass references Builder as a simple name, the file will not compile without being changed.

Indify string concatenation changes to the order of operations. In Java 19, the process of concatenating strings now evaluates each parameter and eagerly creates a string from left to right. This fixes a bug in Java 9’s JEP 280, which introduced string concatenation techniques based on invokedynamic. (By the way, the word indify is short for using invokedynamic.)

For example, the following code now prints zoozoobar not zoobarzoobar:

StringBuilder builder = new StringBuilder("zoo");
System.out.println("" + builder + builder.append("bar"));

Lambda deserialization for object method references on interfaces. Deserialization of serialized method references to Object methods, which used an interface as the type on which the method is invoked, can now be deserialized again.

Keep in mind that the class files must be recompiled to support deserialization.

POSIX file access attributes copied to the target on a foreign file system. When two files are linked to different file system providers, such as when you copy a file from the default file system to a zip file system, the Java 19 function java.nio.file.Files.copy(Path, Path) copies Portable Operating System Interface (POSIX) file attributes from the source file to the destination file.

The POSIX file attribute view must be supported by both the source and target file systems. The owner and group owner of the file are not copied; the POSIX attributes copied are restricted to the file access rights.

Methods of InputStream and FilterInputStream no longer synchronized. The mark and reset functions of the java.io.InputStream and java.io.FilterInputStream classes no longer use the keyword synchronized. Since the other methods in these classes do not synchronize, this keyword is useless and has been removed in Java 19.

Some returned strings slightly different. In Java 19, the specification of the Double.toString(double) and Float.toString(float) methods is now tighter than in earlier releases, and the new implementation fully adheres to the specification.

The result of this change is that some returned strings are now shorter than when earlier Java releases are used, and inputs at the extremes of the subnormal ranges near zero might look different. However, the number of cases where there’s a difference in output is relatively small compared to the sheer number of possible double and float inputs.

For example, the double subnormal range is Double.toString(1e-323), which now returns 9.9E-324, as mandated by the new specification. Another example: Double.toString(2e23) now returns 2.0E23; in earlier releases, it returns 1.9999999999999998E23.

User’s home directory set to $HOME if invalid. The user.home system property on Linux and macOS systems is set to the operating system’s specified home directory. The value of the environment variable $HOME is used in place of the directory name if the variable is empty or contains only one character.

Typically, $HOME has a valid value and the same directory name. Except in systems such as system on Linux or when running in a container such as Docker, the default to $HOME is unusual and unlikely to happen.

Java 19 was changed to use the correct user home directory.

Deprecation


This section describes the features, options, and APIs deprecated in Java 19.

Deprecation of Locale class constructors. In Java 19, the public constructors of the Locale class were marked as deprecated. You should use the new static factory method Locale.of() to ensure only one instance per Locale configuration.

The following example shows the use of the factory method compared to the old constructor:

Locale japanese = new Locale("ja"); // deprecated
Locale japan    = new Locale("ja", "JP"); // deprecated

Locale japanese1 = Locale.of("ja");
Locale japan1    = Locale.of("ja", "JP");

System.out.println("japanese  == Locale.JAPANESE = " + (japanese  == Locale.JAPANESE));
System.out.println("japan     == Locale.JAPAN    = " + (japan     == Locale.JAPAN));
System.out.println("japanese1 == Locale.JAPANESE = " + (japanese1 == Locale.JAPANESE));
System.out.println("japan1    == Locale.JAPAN    = " + (japan1    == Locale.JAPAN));

When you run this code, you will see that the objects supplied via the factory method are identical to the Locale constants, whereas those created with constructs logically are not.

Several java.lang.ThreadGroup methods degraded. In Java 14 and Java 16, many Thread and ThreadGroup methods were marked as deprecated for removal. Now, the following methods have been decommissioned in Java 19:

◉ ThreadGroup.destroy() invocations will be ignored.
◉ ThreadGroup.isDestroyed() always returns false.
◉ ThreadGroup.setDaemon() sets the daemon flag, but this has no effect.
◉ ThreadGroup.getDaemon() returns the value of the unused daemon flags.
◉ ThreadGroup.suspend(), resume(), and stop() throw an UnsupportedOperationException.

Removed items


This section describes the old features, options, and APIs removed in Java 19.

TLS cipher suites using 3DES removed from the default enabled list. The default list of allowed cipher suites no longer includes the following TLS cipher suites that employ the outdated Triple Data Encryption (3DES) algorithm:

◉ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
◉ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
◉ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
◉ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
◉ SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
◉ SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
◉ SSL_RSA_WITH_3DES_EDE_CBC_SHA

Note that cipher suites using 3DES are already disabled by default in the jdk.tls.disabledAlgorithms security property. To turn them back on, you can remove 3DES_EDE_CBC from the jdk.tls.disabledAlgorithms security parameter and re-enable the suites using the setEnabledCipherSuites() function of the SSLSocket, SSLServerSocket, or SSLEngine classes. While you are free to use these suites, you do so at your own risk; these were removed for a reason!

Alternately, the https.cipherSuites system property can be used to re-enable the suites if an application is using the HttpsURLConnection class.

Removal of the GCParallelVerificationEnabled diagnostic flag. Disabling parallel heap verification has never been used other than with its default value because there are no known benefits to doing so. Additionally, for a very long time, with no problems, this default value permitted multithreaded verification. Therefore, the GCParallelVerificationEnabled diagnostic flag was removed.

SSLSocketImpl finalizer implementation removed. Because the Socket implementation now handles the underlying native resource releases, the finalizer implementation of SSLSocket has been abandoned. With Java 19, if SSLSocket is not explicitly closed, TLS close_notify messages won’t be sent.

If you fail to correctly close sockets, you might see a runtime error. Applications should never rely on garbage collection and should always permanently close sockets.

Alternate ThreadLocal implementation of the Subject::current and Subject::callAs APIs removed. The jdk.security.auth.subject.useTL system property and the alternate ThreadLocal implementation of the Subject::current and Subject::callAs APIs have been removed. The default implementation of these APIs is still supported.

Source: oracle.com

Friday, January 6, 2023

Hidden gems in Java 19, Part 1: The not-so-hidden JEPs

Core Java, Oracle Java, Java Prep, Java Preparation, Java Tutorial and Materials, Java Skills, Java Jobs


Java 19 has seven main JEPs, which is a lower count than the nine JEPs in Java 18, the 14 JEPs in Java 17, the 17 JEPs in Java 16, the 14 JEPs in Java 15, and the 16 JEPs in Java 14. However, focusing on quantity doesn’t tell the story of Java 19, which contains extremely important JEPs for the future-looking Panama, Amber, and Loom projects, as well as porting the JDK to the Linux/RISC-V instruction set.

I am using the Java 19.0.1 jshell tool to demonstrate the code in this article. If you want to test the features, download JDK 19, fire up your terminal, check your version, and run jshell, as follows. Note that you might see a newer dot-release version of the JDK, but nothing else should change.

[mtaman]:~ java -version
 java version "19.0.1" 2022-10-18
 Java(TM) SE Runtime Environment (build 19.0.1+10-21)
 Java HotSpot(TM) 64-Bit Server VM (build 19.0.1+10-21, mixed mode, sharing)

[mtaman]:~ jshell --enable-preview
|  Welcome to JShell -- Version 19.0.1
|  For an introduction type: /help intro

jshell>

Be aware of two important notes.

◉ Two of the JEPs covered in this article are published as incubator modules to solicit developer feedback. An incubator module’s API could be altered or disappear entirely, so don’t count on it being in a future Java release. You should play with incubator modules but not use them in production code. To use incubator modules, use the --add-modules JVM switch.

◉ If a JEP is a preview feature, it is fully specified and implemented but is not finalized. Thus, it should not be used in production code. Use the switch --enable-preview to use those features.

The following JEPs are in Java 19:

Project Loom

◉ JEP 425: Virtual threads (first preview)
◉ JEP 428: Structured concurrency (first incubator)

Project Amber

◉ JEP 405: Record patterns (first preview)
◉ JEP 427: Pattern matching for switch (third preview)

Project Panama

◉ JEP 424: Foreign Function and Memory API (first preview)
◉ JEP 426: Vector API (fourth incubator)

In addition, there’s the following hardware port JEP:

◉ JEP 422: Linux/RISC-V port

Project Loom JEPs


Project Loom is designed to deliver new JVM features and APIs to support easy-to-use, high-throughput, lightweight concurrency as well as a new programming model, which is called structured concurrency.

Virtual threads. In JEP 425, Java 19 introduces virtual threads to the Java platform as a first preview. It is one of the most significant updates to Java in a very long time, but it is also a change that is hardly noticeable. Even though there are many excellent articles regarding virtual threads, such as Nicolai Parlog’s “Coming to Java 19: Virtual threads and platform threads,” I cannot discuss other impending features without first giving a brief overview of virtual threads.

Virtual threads fundamentally redefine the interaction between the Java runtime and the underlying operating system, removing significant barriers to scalability. Still, they don’t dramatically change how you create and maintain concurrent programs. Virtual threads behave almost identically to the threads you are familiar with, and there is barely any additional API.

Let’s look at them from a different view by asking the following question: Why do developers need virtual threads?

Anyone who has ever worked on a back-end application under high load is aware that threads are frequently the bottleneck. A thread is required for each incoming request to be processed. One Java thread corresponds to one operating system thread, consuming many resources. It’s best to start with a few hundred threads; otherwise, the entire system’s stability is jeopardized.

However, in real life, more than a few hundred threads are often required, especially if processing a request takes longer due to the need to wait for blocking data structures such as queues, locks, or external services such as databases, microservices, or cloud APIs.

For example, if a request takes two seconds and the thread pool is limited to 100 threads, the application could serve up to 50 requests per second. Even if several threads are served per CPU core, the CPU would be underutilized because it would spend most of its time waiting for responses from external services. So, you really need thousands of threads—or maybe tens of thousands. However, you’re not going to get that from your hardware.

One solution has been to use the reactive programming model with frameworks such as Project Reactor and RxJava.

Sadly, reactive code is often more complex than sequential code, and it can be hard to maintain. Here’s an example.

public DeferredResult<ResponseEntity<?>> createOrder(
    CreateOrderRequest createOrderRequest, Long sessionId, HttpServletRequest context) {
  
  DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();

  Observable.just(createOrderRequest)
      .doOnNext(this::validateRequest)
      .flatMap(
          request ->
              sessionService
                  .getSessionContainer(request.getClientId(), sessionId)
                  .toObservable()
                  .map(ResponseEntity::getBody))
      .map(
          sessionContainer ->
              enrichCreateOrderRequest(createOrderRequest, sessionContainer, context))
      .flatMap(
          enrichedRequest ->
              orderPersistenceService.persistOrder(enrichedRequest).toObservable())
      .subscribeOn(Schedulers.io())
      .subscribe(
          success -> deferredResult.setResult(ResponseEntity.noContent()),
          error -> deferredResult.setErrorResult(error));

  return deferredResult;
}

In the reactive world, all the above code merely defines the reactive flow but doesn’t execute it; the code is executed only after the call to subscribe() (at the end of the method) in a separate thread pool. For this reason, it doesn’t make any sense to set a breakpoint at any line of the code above. Therefore, this code is hardly readable and is also tough to debug.

Additionally, the database and external services drivers’ maintainer must support the reactive model, and you’re not going to see that very often.

Core Java, Oracle Java, Java Prep, Java Preparation, Java Tutorial and Materials, Java Skills, Java Jobs
Virtual threads are a better solution because they allow you to write code that is quickly readable and maintainable without having to jump through hoops. That’s because virtual threads are like normal threads from a Java code perspective, but they are not mapped 1:1 to operating system threads.

Instead, there is a pool of so-called carrier threads onto which a virtual thread is temporarily mapped. The carrier thread can execute another virtual thread (a new thread or a previously blocked thread). As soon as the virtual thread encounters a blocking operation, the virtual thread is removed from the carrier threads.

Thus, blocking operations no longer block the executing thread; this lets the JVM process many requests in parallel with a small pool of carrier threads, allowing you to reimplement the reactive example above quite simply as the following:

public void createOrder(
    CreateOrderRequest createOrderRequest, Long sessionId, HttpServletRequest context) {
  
  validateRequest(createOrderRequest);

  SessionContainer sessionContainer =
      sessionService
          .getSessionContainer(createOrderRequest.getClientId(), sessionId)
          .execute()
          .getBody();

  EnrichedCreateOrderRequest enrichedCreateOrderRequest =
      enrichCreateOrderRequest(createOrderRequest, sessionContainer, context);

  orderPersistenceService.persistOrder(enrichedCreateOrderRequest);
}

As you can see, such code is easier to read and write, just as any sequential code is, and it’s also easier to debug by conventional means.

I believe that once you start using virtual threads, you will never switch back to reactive programming. Even better, you can continue to use your code unchanged with virtual threads because (thanks to this new JEP) it is part of the JDK. Well, it’s there as a preview; in a future Java version, virtual threads will be a standard feature.

Structured concurrency. In JEP 428, structured concurrency, which is an incubator module in Java 19, helps to simplify error management and subtask cancellation. Structured concurrency treats concurrent tasks operating in distinct threads as a single unit of work, improving observability and dependability.

Suppose a function contains several invoice-creating subtasks that need to be done in parallel, such as getting data from a database with getOrderBy(orderId), calling a remote API with getCustomerBy(customerId), and loading and reading data from a file with getTemplateFor(language). You could use the Java executable framework, for example, as in the following:

private final ExecutorService executor = Executors.newCachedThreadPool();

public Invoice createInvoice(int orderId, int customerId, String language) 
    throws InterruptedException, ExecutionException {
  
    Future<Customer> customerFuture =
        executor.submit(() -> customerService.getCustomerBy(customerId));

    Future<Order> orderFuture =
        executor.submit(() -> orderService.getOrderBy(orderId));

    Future<String> invoiceTemplateFuture =
        executor.submit(() -> invoiceTemplateService.getTemplateFor(language));

    
    Customer customer = customerFuture.get();
    Order order = orderFuture.get();
    String template = invoiceTemplateFuture.get();

    return invoice.generate(customer, order, template);
}

You can pass the three subtasks to the executor and wait for the partial results. It is easy to implement the basic task quickly, but consider these possible issues.

◉ How can you cancel other subtasks if an error occurs in one subtask?
◉ How can you cancel the subtasks if the invoice is no longer needed?
◉ How can you handle and recover from exceptions?

All these possible issues can be addressed, but the solution would require complex and difficult-to-maintain code.

And, more importantly, what if you want to debug this code? You can generate a thread dump, but it would give you a bunch of threads named pool-X-thread-Y. And you wouldn’t know which pool thread belongs to which calling threads since all calling threads share the executor’s thread pool.

The new structured concurrency API improves the implementation, readability, and maintainability of code for requirements of this type. Using the StructuredTaskScope class, you can rewrite the previous code as follows:

Invoice createInvoice(int orderId, int customerId, String language)
    throws ExecutionException, InterruptedException {
  
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

        Future<Customer> customerFuture = 
          scope.fork(() -> customerService.getCustomerBy(customerId));

        Future<Order> orderFuture = 
          scope.fork(() -> orderService.getOrderBy(orderId));


        Future<String> invoiceTemplateFuture = 
          scope.fork(() -> invoiceTemplateService.getTemplateFor(language));

        
        scope.join();              // Join all forks
        scope.throwIfFailed();     // ... and propagate errors


        Customer customer = customerFuture.resultNow();
        Order order = orderFuture.resultNow();
        String template = invoiceTemplateFuture.resultNow();


        // Here, both forks have succeeded, so compose their results
        return invoice.generate(customer, order, template);
    }
}

There’s no need for the ExecutorService in the scope of the class, so I replaced it with a StructuredTaskScope located in the method’s scope. Similarly, I replaced executor.submit() with scope.fork().

By using the scope.join() method, you can wait for all tasks to be completed—or for at least one to fail or be canceled. In the latter two cases, the subsequent throwIfFailed() throws an ExecutionException or a CancellationException.

The new approach brings several improvements over the old one.

◉ When you run the task, the subtasks form a self-contained unit of work in the code; you no longer need ExecutorService in a higher scope. The threads do not come from a thread pool; each subtask is executed in a new virtual thread.
◉ As soon as an error occurs in one of the subtasks, all other subtasks get canceled.
◉ When the calling thread is canceled, the subtasks are also canceled.
◉ The call hierarchy between the calling thread and the subtask-executing threads is visible in the thread dump.

To try the example yourself, you must explicitly add the incubator module to the module path and also enable preview features in Java 19. For example, if you have saved the code in a file named JDK19StructuredConcurrency.java, you can compile and run it as follows:

$ javac --enable-preview -source 19 --add-modules jdk.incubator.concurrent JDK19StructuredConcurrency.java

$ java --enable-preview --add-modules jdk.incubator.concurrent JDK19StructuredConcurrency

Project Amber JEPs


JEP 405 and JEP 427 are part of Project Amber, which focuses on smaller Java language features that can improve developers’ everyday productivity.

Pattern matching for switch. This is a feature that has already gone through two rounds of previews. First appearing in Java 17, pattern matching for switch allows you to write code like the following:

switch (obj) {
  case String s && s.length() > 8 -> System.out.println(s.toUpperCase());
  case String s                   -> System.out.println(s.toLowerCase());

  case Integer i                  -> System.out.println(i * i);

  default -> {}
}

You can use pattern matching to check if an object within a switch statement is an instance of a particular type and if it has additional characteristics. In the Java 17–compatible example above, the goal is to find strings longer than eight characters.

To improve the readability of this feature, Java 19 changed the syntax. In Java 17 and Java 18, the syntax was to write String s && s.length() > 0; now, in Java 19, instead of &&, you must use the easier-to-read keyword when.

Therefore, the previous example would be written in Java 19 as the following:

switch (obj) {
  case String s when s.length() > 8 -> System.out.println(s.toUpperCase());
  case String s                     -> System.out.println(s.toLowerCase());

  case Integer i                    -> System.out.println(i * i);

  default -> {}
}

What’s also new is that the keyword when is a contextual keyword; therefore, it has a meaning only within a case label. If you have variables or methods with the name when in your code, you don’t need to change them. This change won’t break any of your other code.

Record patterns. I am still discussing the topic of pattern matching here because JEP 405 is related to it. If the subject of records is new to you, “Records come to Java” by Ben Evans should help.

A record pattern comprises three components.

◉ A type
◉ A list of record component pattern matches
◉ An optional identifier

Record and type patterns can be nested to allow for robust, declarative, and modular data processing. It is better to explain this with an example, so let me clarify what a record pattern is. Assume you have defined the following Point record:

public record Point(int x, int y) {}

You also have a print() method that can print any object, including positions.

private void print(Object object) {
  
  if (object instanceof Point point) {
    System.out.println("object is a point, x = " + point.x() 
                                      + ", y = " + point.y());
  }
  // else ...
}

You might have seen this notation before; it was introduced in Java 16 as pattern matching for instanceof.

Record pattern for instanceof. As of Java 19, JEP 405 allows you to use a new feature called a record pattern. This new addition allows you to write the previous code as follows:

private void print(Object object) {
  if (object instanceof Point(int x, int y)) {
    System.out.println("object is a point, x = " + x + ", y = " + y);
  } 
  // else ...
}

Instead of needing to match on Point point and access point fields with the whole object, as in the previous code, you now can match on Point(int x, int y) and can then access their x and y fields directly.

Record pattern with switch. Previously with Java 17, you could also write the original example as a switch statement.

private void print(Object object) {
  switch (object) {
    case Point point
        -> System.out.println("object is a point, x = " + point.x() 
                                             + ", y = " + point.y());
    // other cases ...
  }
}

You can now also use a record pattern in the switch statement.

private void print(Object object) {
  switch (object) {
    case Point(int x, int y) 
        -> System.out.println("object is a point, x = " + x + ", y = " + y);

    // other cases ...
  }
}

Nested record patterns. It is now possible to match nested records. Here’s another example that defines a second record, Line, with a start point and a destination point, as follows:

public record Line(Point from, Point to) {}

The print() method can now use a record pattern to print all the path’s x and y coordinates easily.

private void print(Object object) {
  if (object instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
    System.out.println("object is a Line, x1 = " + x1 + ", y1 = " + y1 
                                     + ", x2 = " + x2 + ", y2 = " + y2);
  }
  // else ...
}

Alternatively, you can write the code as a switch statement.

private void print(Object object) {
  switch (object) {
    case Line(Point(int x1, int y1), Point(int x2, int y2))
        -> System.out.println("object is a Line, x1 = " + x1 + ", y1 = " + y1 
                                            + ", x2 = " + x2 + ", y2 = " + y2);
    // other cases ...
  }
}

Thus, record patterns provide an elegant way to access a record’s elements after a type check.

Project Panama JEPs


The Project Panama initiative, which includes JEP 424 and JEP 426, focuses on interoperability between the JVM and well-defined foreign (non-Java) APIs. These APIs often include interfaces that are used in C libraries.

Foreign functions and foreign memory. In Project Panama, a replacement for the error-prone, cumbersome, and slow Java Native Interface (JNI) has been in the works for a long time.

The Foreign Linker API and the Foreign Memory Access API were already introduced in Java 14 and Java 16, respectively, as incubator modules. In Java 17, these APIs were combined to form the single Foreign Function and Memory API, which remained in the incubator stage in Java 18.

Java 19’s JEP 424 has promoted the new API from incubator to preview stage, which means that only minor changes and bug fixes will be made. So, it’s time to introduce the new API.

The Foreign Function and Memory API enables access to native memory (that is, memory outside the Java heap) and access to native code (usually C libraries) directly from Java.

The following examples store a string in off-heap memory, followed by a call to the C standard library’s strlen function to return the string length.

public class ForeignFunctionAndMemoryTest {
  public static void main(String[] args) throws Throwable {
    // 1. Get a lookup object for commonly used libraries
    SymbolLookup stdlib = Linker.nativeLinker().defaultLookup();

    // 2. Get a handle on the strlen function in the C standard library
    MethodHandle strlen = Linker.nativeLinker().downcallHandle(
        stdlib.lookup("strlen").orElseThrow(), 
        FunctionDescriptor.of(JAVA_LONG, ADDRESS));

    // 3. Convert Java String to C string and store it in off-heap memory
    MemorySegment str = implicitAllocator().allocateUtf8String("Happy Coding!");

    // 4. Invoke the foreign function
    long len = (long) strlen.invoke(str);

    System.out.println("len = " + len);
  }
}

The FunctionDescriptor expects the foreign function’s return type as the first parameter, with the function’s arguments coming in as extra parameters. The FunctionDescriptor handles accurate conversion of all Java types to C types and vice versa.

Since the Foreign Function and Memory API is still in the preview stage, you must specify a few parameters to compile and run the code.

$ javac --enable-preview -source 19 ForeignFunctionAndMemoryTest.java

$ java --enable-preview ForeignFunctionAndMemoryTest

As a developer who has worked with JNI—and remembers how much Java and C boilerplate code I had to write and keep in sync—I am delighted that the effort required to call the native function has been reduced by orders of magnitude.

Vector math. I’ll start by dispelling a possible point of confusion: The new Vector API has nothing to do with the java.util.Vector class. Instead, this is a new API for mathematical vector computation and mapping to modern Single-Instruction-Multiple-Data (SIMD) CPUs.

The Vector API attempts to make it easier for native code and JVM code to communicate with one another. The Vector API is also the fourth incubation of the API that defines vector computations that successfully compile at runtime to optimal vector instructions on supported CPU architectures, outperforming equivalent scalar computations.

With the help of the user model in the API, developers can use the HotSpot JVM’s autovectorizer to design sophisticated vector algorithms in Java that are more reliable and predictable.

The Vector API has been a part of the JDK since Java 16 as an incubator module, and in Java 17 and Java 18 it underwent significant development. The Foreign Function and Memory API preview defines improvements to loading and storing vectors to and from memory segments as part of the API proposed for JDK 19.

Along with the complementing vector mask compress operation, Java 19’s new JEP 426 adds the cross-lane vector operations of compress and expand. The compress operation maps the lanes of a source vector—which are chosen by a mask—to a destination vector in lane order. The compress procedure improves the query result filtering. The expand operation does the opposite.

You can also expand bitwise integral lane-wise operations, including counting the number of one bits, reversing the order of bits, and compressing and expanding bits.

The API’s objectives include being unambiguous, being platform-neutral, and having dependable runtime and compilation performance on the x64 and AArch64 architectures.

The hardware port


RISC-V is a free, open source instruction set architecture that’s becoming increasingly popular, and now there’s a Linux JDK for that architecture in JEP 422. A wide range of language toolchains already supports this hardware instruction set.

Currently, the Linux/RISC-V port will support only one general-purpose 64-bit instruction set architecture with vector instructions: an RV64GV configuration of RISC-V. More may be supported in the future.

The HotSpot JVM subsystems that are supported with this new Java 19 feature are

◉ C1 (client) just-in-time (JIT) compiler
◉ C2 (server) JIT compiler
◉ Template interpreter
◉ All mainline garbage collectors, including ZGC and Shenandoah

Source: oracle.com

Wednesday, January 4, 2023

Quiz yourself: The three-argument overload of the Stream API’s reduce method

Oracle Java, Oracle Java Exam, Oracle Java Tutorial and Materials, Oracle Java Certification, Oracle Java Guides

There are three reduce overloads; you should know what they do.

Imagine you have the following Person record:

record Person(String name, Integer experience) {}

Your colleague wrote the following code to calculate the total experience of all the people in the stream:

public static Integer calculateTotalExperience(Stream<Person> stream) {

  return stream.reduce(Integer.valueOf(0),

    (sum, p) -> sum += p.experience, // line n1

    (v1, v2) -> v1 * v2);     // line n2

}

To test the code, your colleague used the following test case, which produced a total experience of 15 years:

Person p1 = new Person("P1", 3);

Person p2 = new Person("P2", 3);

Person p3 = new Person("P3", 4);

Person p4 = new Person("P4", 5);

List<Person> list = List.of(p1, p2, p3, p4);

Integer totalAge = calculateTotalExperience(list.stream());

Which statement is correct? Choose one.

A. Line n1 contains an error.

B. Line n2 contains an error.

C. Both lines n1 and n2 contain errors.

D. The code is properly constructed for calculating the total experience.

Answer. This question investigates the three-argument overload of the reduce method in the Stream API.

In the Stream API, the reduce method creates a single result by taking the elements of the stream one at a time and updating an intermediate result. When all the stream data has been used, that intermediate result is considered final.

There are three reduce overloads, and it’s helpful to discuss all of them, since they introduce the key concepts sequentially.

The first overload takes a single argument that’s a BinaryOperator. That operator combines pairs of items of the stream data type into one item of the same type. Then the next stream item is combined with that intermediate result, and this is done repeatedly until all the stream data has been used. If the stream is empty, there can’t be a result in the normal way. Because of that, this overload returns an Optional that either contains the result of a nonempty stream or is itself empty to indicate no result.

The two-argument overload of reduce also takes a value of the result type. This is called the identity value and must have a couple of properties. First, it represents the result value if the stream is empty. Second, it should be possible to incorporate this value into the binary operator’s calculations any number of times without changing the final result. So, for simple addition, this identity value would be zero. For multiplication, it would be one. Because the identity value is provided, this overload does not need to return an Optional, and instead it returns a value of the stream type under all normal circumstances.

The third overload, which takes three arguments, is the topic of this question. This overload is used when the result is not of the same type as the stream data. In other APIs, an equivalent method might go by another name, perhaps involving the word fold or aggregate.

The operation of this three-argument reduction takes an identity value of the result type, rather than the stream type. It also takes a BiFunction operation that combines a value of the result type with a value of the stream type and produces a new value of the result type. This works well but has a problem in a parallel configuration of the stream.

In parallel mode, each of the separate threads that work on the reduction produce a partial result derived from just some of the stream’s data. To get to a final result, these partial results must be combined. This is the purpose of the third argument, which is a BinaryOperator of the result type. The signature of this method is as follows:

<U> U reduce(U identity,

   BiFunction<U, ? super T, U> accumulator,

   BinaryOperator<U> combiner);

In the general case of a stream running in sequential mode, there won’t be multiple partial results across multiple threads. Consequently, the combiner operation won’t be needed in a stream running in sequential mode. This fact turns out to be important to answering this question.

In the code presented here, identity is an Integer containing zero. This is the correct value for the identity value of an addition operation.

The accumulator operation on line n1 is provided by the following lambda:

(sum, p) -> sum += p.experience

This code uses the += assignment operator to add the current stream item’s experience field to the sum so far. This might look suspect, for two reasons.

◉ First, the sum is an Integer object, and that type is immutable. However, in Java, functions and lambda formal parameters are mutable by default, and the expression actually modifies the value of the sum to refer to a newly created Integer object. So this concern is unfounded.

◉ The second potential concern is that the lambda must implement a BiFunction that returns an Integer object, but this lambda lacks an obvious value to return. Of course, in Java, assignment operators have value. So the value of the expression sum += p.experience is actually the value assigned to the sum. That’s the correct value, so this lambda is correct both syntactically and semantically. Therefore, there’s no error on line n1, and options A and C are both incorrect.

Next, consider the combiner provided on line n2. This has the job of adding up the intermediate sums that might be created in separate threads if the stream were executed in parallel mode. However, it should be calculating a sum, not a multiplication, so that’s clearly a logical error. This tells you that option B is correct and, consequently, that option D is incorrect. Even though the code produces the right answer, it is not correctly written.

As a side note, the Java documentation for the Collector interface mentions a similar situation to the one described in this quiz.

A sequential implementation of a reduction using a collector would create a single result container using the supplier function and invoke the accumulator function once for each input element. A parallel implementation would partition the input, create a result container for each partition, accumulate the contents of each partition into a sub-result for that partition, and then use the combiner function to merge the subresults into a combined result.

There doesn’t seem to be an equivalent statement for the reduce operation, but clearly the expectation is that the combiners will typically not be invoked when a stream runs sequentially. This also explains how the code generated the correct result when your colleague ran the test.

Of course, although there’s no obvious reason why it would be useful, there does not appear to be any guarantee that the combiner must not be used in a sequential mode. So a developer must not assume that the combiner will be unused. By simply changing the stream in this example to parallel mode, you should expect to get incorrect results.

Conclusion. The correct answer is option B.

Source: oracle.com

Monday, January 2, 2023

Java for the enterprise: What to expect in Jakarta EE 10


Last year, Java EE completed its transfer to the Eclipse Foundation and adopted a new name, Jakarta EE. While this is a great achievement in and of itself, perhaps the most interesting part of that is that it’s now finally time to start looking forward.

As a quick recap, Table 1 shows key historic and future Jakarta EE dates, some of which are tentative. There are some changes from the version of the table I presented in an article in February 2020.

Oracle Java, Jakarta EE 10, Oracle Java Certification, Oracle Java Certification, Java Prep, Java Preparation, Oracle Java Tutorial and Materials

Table 1. The history and latest release projections for Java EE and Jakarta EE

Comparing the table shown in the previous article to this one, you can see that the JDK 11 compatibility theme moved from Jakarta EE 9 to Jakarta EE 9.1, which is still to be released this year.

While this obviously takes some time away from Jakarta EE 10, planning for that latter release has started to some degree nevertheless, and some of the individual specifications and API projects have started their discussions. Note that everything presented in this article is preliminary and represents the current state of what is thought to be the direction in which Jakarta EE 10 will be heading, but it provides no guarantees that any of this will actually end up in Jakarta EE 10.

It’s all about CDI alignment


One of the topics that is likely to be adopted for the Jakarta EE 10 overall theme might be “Contexts and Dependency Injection (CDI) alignment,” that is, closing the gap between Enterprise JavaBeans (EJB) and CDI. From roadmaps, to discussions among vendors, to wishes from the community, this often comes out on top.

Historically Jakarta EE has used different component models for many of its constituent specifications. Java Server Faces (JSF), now called Jakarta Server Faces, had its own managed beans as did, for example, the REST (JAX-RS), Java Servlet, and EJB specifications. For vendors this meant implementing similar things multiple times over, every time in a slightly different way, while for developers it meant learning similar things multiple times over—and especially wondering why certain things can’t be combined in their applications.

For instance, an interceptor can’t be applied to a Servlet method, while @RolesAllowed doesn’t work on either a Servlet method or a JSF-managed bean. To fix these issues, a single platform-wide component model was introduced in Java EE 6: CDI. The CDI API fully focuses on being a standalone component model with several well-defined services such as interceptors and decorators.

Jakarta Transactions (JTA) was one of the first APIs to start this alignment process by providing a CDI-compatible interceptor, @Transactional, and scope, @TransactionScope, in Java EE 7.

JSF followed right away by introducing new scopes such as @FlowScoped and a CDI version of the existing @ViewScoped in Java EE 7. Quite a few other things such as @Asynchronous, @Lock, @Startup/@DependsOn, and @Schedule were, unfortunately, not included as CDI versions in Java EE 7. Sadly, those didn’t even appear in Java EE 8, though that version did introduce Java EE Security (now Jakarta Security), which is built on top of CDI. That release also delivered JSF 2.3, which provided CDI-based injection and expression language lookup of a large number of artifacts. Additionally, JSF 2.3 officially deprecated its own managed bean system in favor of using CDI beans.

Jakarta EE is expected to pick up the pace again, providing CDI versions of those enterprise beans and common annotations, as well as upgrading and enhancing the existing CDI support in several Jakarta APIs.

Here are several changes you should expect in the Jakarta EE 10 specs.

Jakarta Server Faces


The next version of JSF will be JSF 4.0. Its own major theme will be removing legacy functionality that has already been deprecated. Plus, legacy features that haven’t been deprecated before will be deprecated and likely removed in a future release.

For example, the native expression language that JSF still includes will be removed. This was deprecated over 15 years ago but is still there. That expression language shows up in a number of API types, for example, here in ActionSource:

public interface ActionSource {
    MethodBinding getAction();
    void setAction(MethodBinding action);
    // other methods omitted for brevity
}

All methods referencing types from the native expression language, such as MethodBinding, will be removed.

Support for Jakarta Server Pages (JSP) as a view declaration language will be removed as well, meaning Facelets will remain as the only default view language. Corresponding with the potential overall Jakarta EE 10 theme, the native managed bean system will be completely removed, making CDI beans the designated bean type for JSF.

Finally, some of the extension tags will be removed, such as composite:extension. These were related to the big plans JSF designers once had for visual editors, such as those that existed for Microsoft Visual Basic. These plans never came to fruition, and despite some attempts, most of it was withdrawn. Some remnants of these plans, however, remained in JSF and will now finally be removed.

You can expect some new small features and refinements in the API, for instance, default methods in the PhaseListener interface, use of suppliers in several places, adding generics that were still not present, and small utility methods helpful for component libraries. One example: There will be a release() method on FacesContext as part of PrimeFaces.

As for bigger features, a prototype is currently in the works to add a simple REST lifecycle to JSF. This is not intended as a full-featured REST framework, but instead it is to simplify the use case where JSF applications now use a view action in combination with an empty page. This looks as follows:

@RequestScoped
public class RestBean {

   @Inject FacesContext context;
   
   @RestPath("/helloWorld")
   public String helloWorld() {
        return "Hello World! Postback is " + context.isPostBack();
   }
}

Another feature being looked at is supporting extensionless URLs by default or by using a single configuration option. JSF 2.3 provided basic support for this by officially supporting exact mapping, and JSF 4.0 may expand on this support. Thus, a URL such as http://localhost:8080/foo.xhtml (the current default) will be accessible via http://localhost:8080/foo as well.

Scopes have always played an important role in JSF, and one of the things the team is looking forward to is adding a new scope, @ClientWindowScoped, which builds on the Client Id feature that was introduced in JSF 2.2 as a base facility but was not expanded upon in JSF 2.3.

The Jakarta Security API


Jakarta Security was a new API in Java EE 8. It came out of the box with three authentication mechanisms: Basic, Form, and a variant on Form that’s best for working with JSF.

For the version in Jakarta EE 10, the plan is to add new authentication mechanisms. High on the list are at least Client-Cert and Digest, to make Jakarta Security a full replacement for authentication mechanisms provided by Java Servlet, and to add new methods to support OpenID, OAuth, and JSON Web Token (JWT). The latter is an especially interesting case, because during the Java EE transfer, JWT had already been added to MicroProfile. It’s an open question how to deal with this.

Supporting the CDI-alignment theme, the Jakarta Security wish list includes CDI-based alternatives for the common annotations @RolesAllowed and @RunAs, including, perhaps, supporting the existing annotations. Currently in Jakarta EE, @RolesAllowed is supported only by EJB, where it throws an exception if access is denied to a bean method. However, in MicroProfile or, more precisely in JWT, it’s implied that @RolesAllowed triggers a mandatory authentication mechanism invocation when access is initially denied to a Jakarta REST resource method. Jakarta Security should cover both cases and define those well.

A major new feature being considered for Jakarta Security is that of user-friendly authentication modules, thereby enabling custom authorization rules. There’s some history here. One of the main interfaces in Jakarta Security is the HttpAuthenticationMechanism, which is effectively an HTTP-specific and CDI-enabled ease-of-use layer on top of the lower-level ServerAuthModule from Jakarta Authentication.

By the way, there is a Jakarta Authorization feature that provides low-level portable authorization modules. However, due to the way modules must be created and installed, modules are not really suitable for use in ordinary applications. Let’s hope Jakarta Security provides a similar CDI-enabled ease-of-use layer.

A prototype for this functionality was developed all the way back in 2016, but it was not incorporated in Jakarta Security 1.0 due to lack of time to properly evaluate it. For instance, bridging role checking to an external service instead of assigning all roles when a caller is authenticated would look like the following:

@ApplicationScoped
public class MyAuthorizationModule {

    @Inject
    SecurityConstraints securityConstraints

    @Inject
    MyService service;
   
    @PostAuthenticate
    @PreAuthorize
    @ByRole
    public Boolean myLogic(
        Caller caller, Permission requestedPermission) {
        
        return securityConstraints.getRequiredRoles(requestedPermission)
                .stream()
                .anyMatch(role -> service.isInRole(caller, role));
    }
   
}

The authorization module is called by the container to check whether a caller can access a protected URL such as https://localhost:8080/myapp/admin/foo, or in response to HttpServletRequest.isCallerInRole(), or following a @RolesAllowed annotation.

As part of Jakarta Security, the lower-level Jakarta Authentication and Jakarta Authorization APIs may get some smaller updates as well. These APIs (technically service provider interfaces, or SPIs) are not directly aimed at application developers; the goal is to extend them somewhat and adding clarifications to help higher-levels APIs. For Jakarta Authorization, an important new feature planned is to allow low-level authorization modules to be installed per application—and allow that to be done by the application. Currently this can be done only at the server level.

The Jakarta Servlet API


Jakarta Servlet is the quintessential API in Jakarta EE. Over time it has been adapted to support the somewhat lesser known Jakarta Managed Beans 2.0 specification, meaning that in Jakarta EE, a servlet is a managed bean. In practice this means some CDI features are supported, such as @Inject, but for instance scopes or CDI-style interceptor bindings are not supported.

To align Jakarta Servlet further with CDI is difficult. More than most other APIs in Jakarta EE, Jakarta Servlet has a huge active user base that uses it separately from Jakarta EE, and there are several vendors that exclusively focus on this user base.

Oracle Java, Jakarta EE 10, Oracle Java Certification, Oracle Java Certification, Java Prep, Java Preparation, Oracle Java Tutorial and Materials
So far, the proposals for further alignment vary between multiple options. One is to include a Jakarta EE–specific chapter in the Jakarta Servlet specification that says that in a Jakarta EE environment, servlets should be full CDI beans. This would require no API changes, which is a plus, but it would still require the traditional Servlet base class to be extended, which by default delegates all HTTP methods to a single service() method. This, for instance, is not ideal for security interceptors.

A potential solution is to make all the methods from the Servlet base interface into default methods, so that in a Jakarta EE environment you could write something like the following:

@RequestScoped
@WebServlet("/foo/bar")
public class MyBean implements Servlet {
      
    public void doGet(HttpServletRequest req, HttpServletResponse res) {
        // ...
    }
}

Another proposal is to change nothing in the API but to specify that if a servlet is treated as a CDI bean, and the container detects (for example) that the service() method has not been overridden, the doGet() methods are called directly. Such a CDI bean would then almost look like a regular servlet:

@RequestScoped
@WebServlet("/foo/bar")
public class MyBean extends HttpServlet {
      
    public void doGet(HttpServletRequest req, HttpServletResponse res) {
        // ...
    }
}

Another CDI-alignment issue concerns the additional built-in beans for HttpServletRequest, HttpSession, and ServletContext, which are now defined by the CDI specification. Conceptually those don’t belong in the CDI spec, and for this reason alone it would be better if they were moved to the Jakarta EE part of the Jakarta Servlet spec. Practically, the injected HttpServletRequest is the most troublesome because it doesn’t define which HttpServletRequest is injected. ServerAuthModules and Filters can wrap it and after forwarding to another servlet, there’s another version of the request coming into view. Most implementations today inject HttpServletRequest in the state in which it entered the request pipeline, and this is often not what applications expect. A Jakarta Servlet native version of HttpServletRequest could provide the actual current request.

At the other end of the spectrum of alignment, there’s the issue in Jakarta EE that Jakarta REST, which listens to HTTP requests as well, technically does not depend on Jakarta Servlet. In a Jakarta EE environment, it always practically depends on Jakarta Servlet, but in other environments this doesn’t need to be the case. To align these two, the idea has been expressed to extract from Jakarta Servlet a low-level flexible HTTP API, on which both Jakarta Servlet and Jakarta REST could be based in Jakarta EE and, potentially, in other frameworks. In practice, this separation already takes place. For example, in GlassFish this is implemented by Grizzly, and in Tomcat there’s Coyote.

Besides these alignment issues, there are a number of more native features in the pipeline, with the most important one being RFC 6265: state-management cookies with SameSite behavior.

Other small enhancements to Jakarta Servlet include distinguishing between the query string and POST body parameters, as well as gaining an easier-to-use HttpServletRequestWrapper such that only a minimal amount of work has to be done to override the URL.

The Jakarta REST API


Like JSF, Jakarta REST has its own native managed bean system. Because Jakarta REST was introduced together with CDI in Java EE 6, it had some alignment facilities from the get-go, but nevertheless Jakarta REST uses its own injection annotations (specifically @Context) and its own rules around these.

Just like JSF 4.0, Jakarta REST 4.0 will drop its own managed bean system and its own injection annotations. This means that moving forward, Jakarta REST resources will be only CDI beans. An intermediate version, Jakarta REST 3.1, is planned, which will formally deprecate this managed bean system and will allow at least class-level injection of the artifacts currently injected using @Context via @Inject. This release will likely also deprecate the use of Java Architecture for XML Binding (JAXB) in the API, specifically by deprecating Link.JaxbLink and Link.JaxbAdapter.

In addition to the switch over to CDI, there will be a number of smaller features introduced. For instance, parameters annotated with @CookieParam, @FormParam, @HeaderParam, @MatrixParam, and @QueryParam can now also have an array type. In earlier versions of Jakarta EE, they could use only a type of List, Set, or SortedSet. For instance, now you can code the following:

@Path("/users")
public class UserResource {
    @GET
    public Response getUsers(@QueryParam("orderBy") String[] orderBy) {
        return …
    }
}

Another addition is a default exception mapper that implements ExceptionMapper<Throwable> and sets the response to status 500 unless the exception is a WebApplicationException. In that case, the mapper sends the embedded response and its own status code.

The Jakarta Concurrency API


The Java EE Concurrency API was first created in 2003, but it was then stalled for many years, only to be released in Java EE 7, seemingly under some time constraints.

The API shows its age a little by still strongly adhering to the container-managed principle. This practically means that the configuration of the concurrency resources is supposed to be done in a proprietary way using specific tools of the Jakarta EE server (for instance, an admin GUI, a CLI, or an XML file inside a server folder).

While this principle may have been the norm in 2003, the world moved on in the years that the Concurrency API lay dormant. More common, concurrency evolved to a hybrid model where resources can be defined and configured by either the server or the application. Therefore, a long overdue addition to the Jakarta Concurrency API is a @ManagedExecutorServiceDefinition—just like @LdapIdentityStoreDefinition and @DataSourceDefinition—which allows applications to define and configure their own managed executor.

By the way, the Jakarta Concurrency API is very important for the CDI-alignment story, because nearly all the things that are still very useful and available only in EJB are related to concurrency. This concerns specifically the following annotations:

◉ @Asynchronous
◉ @Lock and @AccessTimeout
◉ @Schedule and @Timeout
◉ @Stateless

@Asynchronous in EJB is pretty basic, so a newer version could go a little beyond those basics. One proposal is to optionally allow a managed thread pool to be specified on which the annotated method will be executed. That way with two such pools, you can avoid a certain type of deadlock for cooperating asynchronous methods. As with JWT for Jakarta Security, here too a potential difficulty is that MicroProfile has already introduced a CDI-based @Asynchronous (in the Fault Tolerance API, which is a little unexpected perhaps).

@Stateless itself will not be directly transferred into the Jakarta Concurrency API. One implied aspect is that @Stateless beans are pooled, and a single-bean instance is defined to handle only a single call at the same time. Together, these two beans would form a natural way to throttle concurrency. Discussions around this led to a proposed @Pooled or @MaxConcurrency annotation for the new version of the Jakarta Concurrency API.

A particular problem when doing concurrent programming in Jakarta EE is when, for example, an initial request thread holds a lot of contextual information, such as the current application for which the request is needed (for proper Java Naming and Directory Interface lookups), the authenticated identity, or the current active CDI scopes. When work starts in a new thread, some or all of that context needs to be transferred (propagated).

When the Jakarta Concurrency API was revived from initial work done in early 2000, the designers didn’t take CDI into account. This has been a major hindrance ever since because nothing concerning scopes propagates now in a portable way. To solve this problem, an explicit context propagation API is in the works. This API has been prototyped under MicroProfile, with a stated goal that it is to be included in the Jakarta Concurrency API.

Variants of CDI


With Jakarta EE likely having CDI alignment as one of its main themes, the main new feature that is being planned for CDI itself is another variant of CDI. The specification has already been split into three parts: Core CDI, CDI in Java SE, and CDI in Jakarta EE. The new variant, called CDI-Lite, will focus on build-time concerns, specifically detecting beans during build-time and providing a new kind of extension that can run during build-time.

There’s some interesting history here, because this is how EJB 1.0 actually worked; there was no reflection, and skeletons, stubs, and proxies were all generated using tools at build-time. Because this was seen as a lot of hassle, newer versions of EJB built those automatically at runtime using reflection, an approach later adopted by CDI. With CDI now explicitly supporting build-time, it’s gone full circle.

Plans for CDI-Lite are still greatly in flux, and it hasn’t even been decided yet whether CDI-Lite will be a proper subset of its higher layer, but potentially the stack could look approximately like the following:

1. Jakarta CDI: A small set of key annotations, shared with Guice, HK2, and Spring, including @Inject, @Named, @Qualifier, and @Scope
2. Jakarta CDI Lite: Beans, qualifiers (behavior), scopes (behavior), stereotypes, and build-time portable extensions
3. Jakarta CDI Core: Alternatives, decorators, runtime portable extensions (potentially, the build-time extensions are excluded)
4. Jakarta CDI EE: Rules for EJB beans and servlet components, bean names, and scope in expression language, specifically including JSF and JSP, built-in beans for Jakarta Transaction, Jakarta Security, and Jakarta Servlet

While most focus has been on CDI-Lite until now, some features for the main CDI functionality are planned as well. Many of those are specifically for the overall CDI-alignment theme, meaning that they are intended to make it easier for other APIs in Jakarta EE to integrate with CDI.

One such proposal concerns the introduction of executable methods, which effectively lets arbitrary business methods in beans use parameter injection. (Note that CDI already supports this for some callback methods.) An example would be the following:

@RequestScoped
public class MyBean {
      String hello(@ConfigOption("foo") String foo) {
   }
}

A framework such as Jakarta REST or JSF, but of course also application code itself, could then execute this method in some way. Perhaps something like:

beanManager.execute(bean, method);

Some APIs building on CDI struggle because they have fewer options to define or use certain things than CDI itself has, making them a second class citizens. Two examples concern bean-defining annotations and built-in beans.

At the moment, only CDI itself defines which annotations are bean defining. To truly integrate other APIs, they should also be able to create bean-defining annotations. This is something the next version of CDI will likely take a look at.

As discussed above, the CDI spec defines several built-in beans, and so do APIs such as Jakarta Security, JSF and, soon, Jakarta REST. The way this is typically done is via a CDI extension, which programmatically adds a Bean<T> instance. These are low-level types, so they have to find their own decorators and generate a proxy to apply them.

Unfortunately, there’s no portable API in CDI to find decorators and generate proxies, so many implementations of Jakarta APIs don’t actually do this. The result is that such built-in beans are not decoratable and also can’t be specialized, which can be quite problematic.

Built-in beans might also need the ability to obtain the current InjectionPoint. There’s currently no well-defined portable way to obtain such an InjectionPoint from within a Bean<T> instance. Making this possible is proposed for the next version of CDI.

Another proposed feature gives interceptors in CDI access to their actual (nonbinding) annotation parameters. Currently there’s no portable way to achieve this, so interceptors resort to looking at their target class and inspecting that. This works for interceptor annotations that are physically present on those classes, but it does not work for interceptors that have been dynamically added.

There are a few other CDI proposals that have been discussed less but are nevertheless worth mentioning.

The first is the ability to easily apply interceptors to built-in beans. Interceptors are easy to apply to your own code, but they are more troublesome to add to existing types. Using a producer that’s an @Alternative can use the InterceptionFactory, but then you need to get ahold of the type that the @Alternative overrides. This can be done using BeanManager#getBeans and some filtering, but it’s quite verbose. It would be much easier if this overridden instance (the instance that would have been selected for a type if you didn’t provide your alternative producer) could be injected directly.

The second issue concerns the programmatic API for obtaining bean instances. This API should provide the same expressive power to select instances that the declarative (injection) API has. At the moment, this is not the case for beans where the beans’ producer or Bean<T> makes use of an InjectionPoint. As a contrived example, consider the MicroProfile Config API, where a combination of the ConfigProperty qualifier and the name of the injected field is used to obtain the right configuration value. Via injection, this works as follows:

@Inject
@ConfigProperty
String foo;

The inputs to the selection mechanism here are string, ConfigProperty, and foo. The last part is something that can’t be provided to the programmatic selection mechanism today. In a proposed feature for CDI, this would be possible and would look something like the following:

CDI.current()
      .select(
           String.class, 
           new ConfigProperty.Literal(),
           injectionPoint().withMemberName("foo"))
       .get();

Other Jakarta EE 10 APIs


Several other Jakarta EE APIs have pending new features that have not been actively discussed as candidates for inclusion in Jakarta EE 10.

For example, Jakarta Persistence has ideas around adding support for transforming Java Persistence Query Language queries to the Criteria API and the other way around, adding higher-level pagination support (the well-known filtering, sorting, and paging paradigm), adding support for specifying which data a fetch graph should not fetch (as opposed to specifying what it should fetch), and supporting some smaller things such as allowing empty collections as a parameter in an in(…) clause.

Likewise, Jakarta Messaging has a lot of pending new features. During the Java EE 8 cycle, a number of them had actually been worked on quite a bit for what was to become Messaging 2.1 (which was never released). Specifically features for the CDI-alignment story have been proposed, such as CDI Message Consumers (having a CDI bean listen to incoming messages) and replacing the string-based activationConfig, which is in practice a rather thin layer on top of the original XML format used to configure message-driven beans. Smaller features include being able to easily send JSON- or XML-based messages.

There are also various new APIs in the works, for instance, NoSQL and model-view-controller, which may target Jakarta EE 10. For years now, there has also been talk about including a caching and a configuration API in Jakarta EE. A configuration API actually came to fruition but was developed in MicroProfile after an attempt for Jakarta EE was aborted during the Java EE 8 cycle.

Development of a temporary caching API started as early as 2001, in JSR 107: JCACHE. This was a candidate to include in Jakarta EE multiple times, but it never happened. Whether JCACHE will be transferred to Eclipse and finally be included in Jakarta EE 10 is a big question, and at this point, I don’t know the answer.

Source: oracle.com