Contents

JDK 18 proposals: Java 18

Photo by NASA from Unsplash

Java 18 release is planned for 2021-03-22. Let’s have a look at what to expect in this release. I invite you for a quick overview of the features. Prepare yourself a cup of good coffee ☕!

Where to track JEPS for Java 18

The best place to track what JEPS are included for implementation on specific Java releases is openjdk release summary.

For Java 18 you should keep an eye on https://openjdk.java.net/projects/jdk/18/

Adoption of Java 17 is moving forward. Developers, aware that staying behind is sometimes really painful, always try to be one step ahead of the curve. Well, mentally, at least (poor souls with Java 7 on production, I’m really sorry for you… I know, I know: the circumstances).

This blog post is about some JEPs that are planned in release 18 of openjdk.

The GA (general availability) date of JDK 18 is 2022-03-22, so it is quite possible that the list of JEPS would actually lengthen with new additions.

As for today, there are eight JEPs targeted at Java 18:

Let’s have a closer look to some of them.

JEP reader notes

JEP descriptions below are just my notes made while I was reading the proposals. The proposals would probably evolve into new versions and the API proposed now may change as well.

My understanding of those JEPs is probably weak and leaky, so please don’t take anything you read for granted; rather use it as inspiration for your own (re)search.

UTF-8 by default

This JEP was created four years ago. It’s goal is to make Java programs more predicatable with regard to using charsets - in cases when there is no possibility to provide charset as API parameter it should be clear what charset is used.

For example, method and constructor references (::) in streams require single argument, so in such cases programmers cannot use additional “charset” parameter even if overloaded method/constructor version exists.

In such cases a platform-dependent charset is applied. In order to achieve predictable behavior and portable code on systems having different default charsets this JEP proposes that specific charset is used.

Default charset

The charset of choice is UTF-8. It is widely used on the web, in xml documents and on some great operating systems like Linux. It will by default be used throughout the standard API.

Default charset of current JDK can be checked using -XshowSettings:properties parameter of java -version:

1
java -XshowSettings:properties -version 2>&1 | grep file.encoding

Currently there are many APIs that use the default encoding:

  • in java.io package, constructors of InputStreamReader, FileReader, OutputStreamWriter, FileWriter, and PrintStream
  • in java.util package, Formatter and Scanner
  • in java.net package, URLEncoder and URLDecoder (in deprecated methods)

The Change

The core change is this: the specification of Charset.defaultCharset() would now say that the default charset is UTF-8 unless configured otherwise by an implementation-specific means.

In order to use something different than UTF-8, users would be able to set file.encoding to:

  • COMPAT - which will enable pre-java 18 behavior
  • UTF-8 - which will be a no-op
  • something different - the behavior is not specified

The charset of System.out and System.err will be as specified by Console.charset().

Simple web server

The goal of JEP 408 is:

Provide 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.

Commandline server

The second interesting JEP that landed in Java 18 is Java version of

1
python3 -m http.server

The server would be started using a command very similar to python3 version:

1
$ java -m jdk.httpserver

which starts a server on default 8000 port.

However, you can specify other port:

1
$ java -m jdk.httpserver -p 9000

or bind to all interfaces (note -b - bind - option):

1
$ java -m jdk.httpserver -b 0.0.0.0

By default, the server serves files from the current working directory. You can override that using -p option:

1
$ java -m jdk.httpserver -p data

Commandline options

The -h option displays a simple help message that lists all supported options:

1
2
3
4
5
6
7
8
Options:
-b, --bind-address    - Address to bind to. Default: 127.0.0.1 or ::1 (loopback).
                        For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory       - Directory to serve. Default: current directory.
-o, --output          - Output format. none|info|verbose. Default: info.
-p, --port            - Port to listen on. Default: 8000.
-h, -?, --help        - Print this help message.
To stop the server, press Ctrl + C.

Notes

  • only HTTP is supported (no HTTPS support)
  • mime tyeps are resolved automatically (.html files are served as text/html and .java files are served as text/plain) - this can be customized through java.net.URLConnection::getFileNameMap API
  • every request is logged to the console (by default the server behaves as if -o info was passed in commandline; you can use -o none to disable logging or -o verbose to have all you need)
  • server can be embedded in other applications using new API: SimpleFileServer, HttpHandler, and Request (probably in com.sun.net.httpserver package)

SimpleHttpServer and API

New class SimpleHttpServer allows to create a server, define a hander and create a Filter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.sun.net.httpserver;

public final class SimpleFileServer {
    public static HttpServer createFileServer(InetSocketAddress addr,
                                              Path rootDirectory,
                                              OutputLevel outputLevel) {...}
    public static HttpHandler createFileHandler(Path rootDirectory) {...}
    public static Filter createOutputFilter(OutputStream out,
                                            OutputLevel outputLevel) {...}
    ...
}

Create server programatically

This is how you can create simple server programatically.

1
2
3
4
5
var server = SimpleFileServer.createFileServer(
  new InetSocketAddress(8080),
  Path.of("/my/data/dir"), 
  OutputLevel.VERBOSE);
server.start()

This server:

  • serves files from direcotory /my/data/dir
  • logs with verbose level
  • listens to connections on port 8080

Another option is to use create method which was added to two existing classes: HttpServer and HttpsServer.

1
2
3
4
5
public static HttpServer create(InetSocketAddress addr,
                                int backlog,
                                String root,
                                HttpHandler handler,
                                Filter... filters) throws IOException {...}

And now you can use it to create a customized server.

SimpleHttpServer customizations

What can be customized? For example, you can use different handlers for different contexts, one of them being created by new SimpleFileServer API.

How to use two handlers with two contetxts?

Have a look here:

  • at context “/store/” there is a SomePutHandler registered which will handle the requests
  • context “/browse/” would be handled by a FileHandler created by using new API SimpleFileServer.createFileHandler() which would serve files from “/my/data/dir”
1
2
3
4
5
6
7
8
 var server = HtpServer.create(
  new InetSocketAddress(8080),
  10, 
  "/store/", new SomePutHandler());
var handler = SimpleFileServer.createFileHandler(
  Path.of("/my/data/dir"));
server.createContext("/browse/", handler);
server.start();

How to create an output filter?

Another example woud create a server that has an output filter defined; such filter would (probably) cause the SomePutHandler to have the output of the response redirected to standard output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var filter = SimpleFileServer.createOutputFilter(
   System.out,
   OutputLevel.INFO);
var server = HttpServer.create(
    new InetSocketAddress(8080),
    10, 
    "/store/", 
    new SomePutHandler(), 
    filter);
server.start();

Request handling enhancements

New class HttpHandlers has two static methods for handler creation.

1
2
3
4
5
6
7
8
9
package com.sun.net.httpserver;

public final class HttpHandlers {
    public static HttpHandler handleOrElse(Predicate<Request> handlerTest,
                                           HttpHandler handler,
                                           HttpHandler fallbackHandler) {...}
    public static HttpHandler of(int statusCode, Headers headers, String body) {...}
    {...}
}

Filter abstract class allows the creation of a filter by changing (adapting) a request in some way:

1
2
3
4
5
public abstract class Filter {
    public static Filter adaptRequest(String description,
                                      UnaryOperator<Request> requestOperator) {...}
    {...}
}

They can be used in following way:

  • HttpHandlers.handleOrElse to delegate processing to different handlers based on the state of the request
  • HttpHandlers.of to create a handler that responds in the same way
  • Request.with() to modify/add a header to all incoming requests:

Request as immutable HttpExchange view

What’s interesting is that Filters use HttpExchange class which is a full, mutable state of the reqest whereas enhanced Filter’s adaptRequest method uses limited view of the state represented by new interface request:

1
2
3
4
5
6
7
8
9


public interface Request {
    URI getRequestURI();
    String getRequestMethod();
    Headers getRequestHeaders();
    default Request with(String headerName, List<String> headerValues)
    {...}
}

Customization example

Here is the code that creates customized server. It has following capabilities:

  • dispatches handling to different handlers: SomePutHandler or SomeHandler based on the type of request method; if the method is PUT, SomePutHandler is called
  • adds Foo header with value Bar
  • listens on 8080 port
  • will refuse new incomming connections if there are more that 10 already running
  • will run on “/” context
1
2
3
4
5
6
jshell> var h = HttpHandlers.handleOrElse(r -> r.getRequestMethod().equals("PUT"),
   ...> new SomePutHandler(), new SomeHandler());
jshell> var f = Filter.adaptRequest("Add Foo header", r -> r.with("Foo", List.of("Bar")));
jshell> var s = HttpServer.create(new InetSocketAddress(8080),
   ...> 10, "/", h, f);
jshell> s.start();

Code Snippets

Writing solid, usable and informative code documentation requires both skills and tools. The standard tool is javadoc which by default uses Standard Doclet.

What is javadoc and doclet

Doclet is a program in Java that uses Javadoc API to render java language comments to a HTML format. You can write your own doclets using Doclet API (also called javadoc API) and use it in javadoc tool by giving -doclet option, for example:

1
    javadoc -doclet <your Doclet subclass> -sourcepath <source-location> java.util

However, most programmers are not interested in writing doclets. They write code and want to document it and they just want the documentation to be automatically rendered to html. So they simply use javadoc with standard doclet and focus on writing doc comments using special tags (in a form @tag).

{@snippet …} tag

Java 18 will bring a new javadoc tag - @snippet - which will simplify adding example source code to API documentation.

Current approach is to include example code in <pre> {@code ….} </pre> blocks, however:

  • tools cannot reliably apply syntax highlighting because there is no way to inform the tools about the kind of content
  • comments /* … */ cannot be included in such blocks
  • fragments cannot contain html markup
  • fragments cannot use comment tags

New @snippet tag:

  • may define inline snippet (included in code) or external snippet (read from external file)
  • may define key-value pairs, so called attributes:
    • id to define snippet’s identifier and create a link to it,
    • lang that defines programming language included in the snippet,
    • file defines location of external snippet,
    • region that declares the name of a region which will be defined using @start and @end tags within snippet’s inline comments
  • may include java code, properties files content, source code in other languages or plain text
  • inline tag may contain markup tags which indicate specific regions/lines and how to present them

Two restrictions of all tags apply also to @snippet tag:

  • inline tag cannot use /* … */ comment because */ would terminate encosing documentation comment
  • inline tag can only contain balanced pairs of curly braces so that the end of @snippet block can be identifed

Example of inline @snippet

1
2
3
4
5
6
7
8
/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet :
 * if (v.isPresent()) {
 *     System.out.println("v: " + v.get());
 * }
 * }
 */

Example of exernal snippet with a region

The external snippet uses:

  • file attribute to point to the external file name and
  • region attribute named example that together with @start and @end tags marks the boundaries or the region of the code that will be included as the content of the snippet in javadoc :
1
2
3
4
/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet file="ShowOptional.java" region="example"}
 */

where ShowOptional.java is a file containing:

1
2
3
4
5
6
7
8
9
public class ShowOptional {
    void show(Optional<String> v) {
        // @start region="example"
        if (v.isPresent()) {
            System.out.println("v: " + v.get());
        }
        // @end
    }
}

Highlighting

For highlighting @highlight can be used followed by arguments:

  • // @highlight substring="println" - extracts substring and highlights it
  • // @highlight region regex = "\barg\b" followed by @end - marks a region where highlighting is applied to all text matched by regex.
  • type parameter can be set to bold, italic or highlighted which will be converted to css classes of same names and which can be defined in system or user-defined stylesheet
  • @replace regex='".*"' replacement="..." - replaces all strings with ellipsis

Linking

The snippet can also contain links to external documentation, for example this code would generate a javadoc with a link to System.out

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * A simple program.
 * {@snippet :
 * class HelloWorld {
 *     public static void main(String... args) {
 *         System.out.println("Hello World!");  // @link substring="System.out" target="System#out"
 *     }
 * }
 * }
 */

Using snippet with properties

An example of a property snippet:

1
2
3
4
5
6
7
8
9
/**
 * Here are some example properties:
 * {@snippet lang=properties :
 *   local.timezone=PST
 *   # @highlight regex="[0-9]+" :
 *   local.zip=94123
 *   local.area-code=415
 *   }
 */

Validation

The JEP proposal authors give a promise that Compiler Tree API will be extended to support @snippet tag. This will allow other tools to validate the content of the snippets:

  • external snippets may be compiled in some contexts
  • internal snippets can be wrapped so that they form a correct compilation unit, and also can be compiled or checked

Reimplement reflection with Method Handles

This is an implementation change in java.lang.reflect but API remains the same.

This JEP aims at reducing maintanance cost of java.lang.reflect and java.lang.invoke APIs.

Its goal is to use method handles as the underlying mechanism of reflection, where currently also two other methods exist:

  • calls to native code in HotSpot VM (only at the beginning of a program to enable fast startup): this type of reflection will still be there in early VM startup
  • however, dynamic generation of bytecode stubs for Method::invoke, Constructor::newInstance, Field::get and Field::set - this will be replaced with java.lang.invoke API introduced in Java 7.

Compatibility

You can enable the old implementation using a flag: -Djdk.reflect.useDirectMethodHandle=false

Vector API

This is third incubator phase and includes further fixes and enhancements as a result of received feedback.

The JEP focuses on providing new “vector API” that is platform independent. At runtime the code behind the API should reliaby compile to the optimal bytecode that uses vector instructions on supported CPUs.

Vector API has two implementations:

  • pure java implementation (“funcitonal but not optimal”)
  • vector operations for HotSpot C2 runtime compiler, where the computations are done om appropriate vector registers and using vector-related processor instructions.

A type that would be used o represent vector is Vector where E (e.g. Integer) is a boxed primitive (e.g. int). Vector and its subclasses are value classes; however with Project Valhalla the primitive types could be primitive classes.

The JEP mentions that currently only Linux and Windows will be supported, where MacOS support will come later.

Internet-Address Resolution SPI

The JEP prepares a service provider interface for host name and address resolution so that java.net.InetAdress can use other resolvers that the default (and too simple) platform resolver.

The default resolver uses hosts file and DNS. However:

  • now address resolution is blocking a thread on system call; this is problematic for Project Loom which wants to use virtual threads and needs the address resolution operation to be non-blocking so that it serve other virtual threads while waiting for resolution operation to complete
  • there are other protocols (like TLS or HTTPS) which could be used
  • frameworks would have finer control over resolution
  • it will simplify prototyping and testing, allowing fore example to writie a mock for network components that use InetAddress API

Foreign Function & Memory API

This is second incubator. The focus is to provide new API with which Java programs can use code and data outside of Java runtime:

  • invoke foreign functions
  • safely access memory not managed by JVM
  • call native libraries
  • process native data

The API allows client code in libraries and applications do the following (the new Java classes defining FFM API are given in braces):

  • allocate foreign memory (MemorySegment, MemoryAddress, and SegmentAllocator)
  • access and manipulate the memory (MemoryLayout, VarHandle)
  • manage lifecycle of foreign resources (ResourceScope)
  • call foreign functions (SymbolLookup, CLinker, and NativeSymbol)

Pattern matching for switch

This is second preview with some minor enhancements since the first preview in JEP 406:

  • constant case label must appear before a guarded pattern of the same type example
  • exhaustiveness checks with sealed hierarhies is more precise example

Summary

Majority of JEPS planned for Java 18 are enhancements to existing code. Top three JEPS actually introduce something new from the perspective of an average application developers:

  • utf-8
  • code snippets
  • simple http server

These are the JEPS I focused the most, mainly because I think I understand them.

Others - like vector API or FFI - seem to me rather complex and I’m not an expert in those areas. In fact, I have rather basic understanding of what are the usecases for them.

For example, I have never written any JNI code so I won’t appreciate the abstraction and usefulness of FFI API. Likewise, I don’t create and process computer graphics pipelines nor implement neural networks where - I imagine - vector API would fit well. However, looking at the evolution of JEPs and seeing how Java platform expands to new areas gives me a very warm feeling that Java is not dead this is the platform where I want to stay and invest my time and energy.


Ten wpis jest częścią serii java.

Wszystkie wpisy w tej serii: