Despite all of the hype Java 9 will not just be about modularity; it is targeting a large number of additional pieces of functionality that are being delivered as Java Enhancement Proposals (JEPs) implemented in OpenJDK (the reference implementation for Java SE).
In this article we will focus on some of the new JEPs that may well have the biggest impact on developers' working lives during the lifetime of Java 9. These include the new HTTP/2 support and the JShell REPL (Read-Eval-Print-Loop) that brings shell-based interactive Java development and exploration of APIs.
HTTP/2
The HTTP/2 standard is a new version of the HTTP protocol. The existing version, HTTP/1.1, dates from 1999 and has significant problems, including:
Head-of-line blocking
In HTTP/1.1 responses are received in the same order as the requests were sent. This means that, for example, when viewing a large HTML page containing several small images, the images will have to "queue up" behind the HTML and can’t be sent until the browser finishes completely downloading the HTML. This is called "Head-of-line blocking" and leads to a number of possible rendering problems.
In HTTP/2, responses can be chunked and even interleaved, to allow responses and requests to be truly multiplexed.
Restricted connections to a single site
The HTTP/1.1 standard states that: "A single-user client SHOULD NOT maintain more than 2 connections with any server". Along with the head-of-line problems, this has seriously limited page performance.
HTTP/2 tackles this by assuming that connections are persistent, and will only close either after a user navigates away, or in the event of a technical failure. With the use of multiplexing, this should help to reduce page bottlenecks.
Overhead of control headers
Existing versions of HTTP use simple, text-based headers to control communication. This has the advantage of being very simple to understand, and allows debugging simply by connecting to a port and typing some text. However, the use of a text-based protocol disproportionately inflates small responses. Additionally, a large number of HTTP responses have little or no payload (e.g. HEAD requests that simply seek to confirm that a resource has not changed). Paying the full cost of text-based headers (~700 bytes that can't be compressed in HTTP/1.1 despite being easy to squeeze) for a response consisting of essentially just a last-modified datetime is an incredibly wasteful part of the existing HTTP standard.
The alternative is to use a binary encoding for headers. This approach can greatly speed up smaller requests and uses far less bandwidth. It is the approach that HTTP/2 has chosen to follow, in the spirit of the principle that standards should prefer text-based protocols, but use binary for efficiency where compelling reasons to do so exist.
What to expect from HTTP/2
The standard was created by the IETF HTTP Working Group, which comprised representatives and engineers from Mozilla, Google, Microsoft, Apple, and others, and chaired by Mark Nottingham, a senior engineer from the content delivery network Akamai. HTTP/2 is therefore a version optimized for the needs of large, high-volume websites and places performance and bandwidth consumption above simplicity of implementation and easy debugging.
The group chair summarizes some of the key properties of HTTP/2 as follows:
- Same HTTP APIs
- Cheaper Requests
- Network- and Server-Friendliness
- Cache Pushing
- Being Able to Change Your Mind
- More Encryption
What does this mean for Java?
Java has had support for HTTP since version 1.0, but much of the code comes from a very different era. For example, Java’s support is designed around a relatively protocol-agnostic framework (the URL class), as it was not clear in the late 90s that the web would be the dominant force that it has become.
The support was based on best ideas at the time, but the world has experienced many changes, not the least of which is the fact that when Java’s original HTTP support shipped, HTTPS didn’t exist. So Java’s APIs treat HTTPS as an afterthought, with some irreducible complexity as a result.
In the modern world, HTTPS is becoming ubiquitous, with HTTP moving towards becoming a legacy technology. Even the US government has now adopted a plan to move to HTTPS-only.
HTTP support in the core JDK has not kept pace with the real web. In fact, even JDK 8 still only ships a client that supports HTTP/1.0 - and so almost all developers have switched to using a client library, such as Apache HttpComponents.
All of this means that support for HTTP/2 is a core piece of Java functionality for the next decade. It also allows us to revisit our old assumptions, rewrite the APIs and provide a "second bite of the apple". HTTP/2 will be a major API for every developer for years to come.
The new API makes a clean break with the past, by abandoning any attempt to maintain protocol independence. Instead, the API focuses solely on HTTP, but with the additional understanding that HTTP/2 is not fundamentally changing semantics. Therefore, the API can be independent of HTTP version, whilst still providing support for the new framing and connection handling parts of the protocol.
In the new API, a simple HTTP request can be created and handled like this:
HttpResponse response = HttpRequest
.create(new URI("http://www.infoq.com"))
.body(noBody())
.GET().send();
int responseCode = response.responseCode();
String responseBody = response.body(asString());
System.out.println(responseBody);
The fluent / builder style for the API should feel much more modern and comfortable to the developer than the existing legacy API.
The current codebase contains a version with the new API, but which currently supports only HTTP/1.1. This allows developers to experiment with and validate the new API while the HTTP/2 support is being completed.
The code is available from the OpenJDK sandbox repository and should be landing on the JDK 9 mainline very soon. From there it will start to be included automatically with Oracle’s binary beta builds. The HTTP/2 support is currently being worked on, and will be arriving in the next few months.
In the meantime, you can experiment with the API by checking out the source using Mercurial and then following the AdoptOpenJDK build instructions with your checked out code.
One of the first features to arrive that differs from existing capabilities is the asynchronous API. This enables a long-running request to be handed off to a background VM-managed thread via sendAsync():
HttpRequest req = HttpRequest
.create(new URI("http://www.infoq.com"))
.body(noBody())
.GET();
CompletableFuture<HttpResponse> aResp = req.sendAsync();
Thread.sleep(10);
if (!aResp.isDone()) {
aResp.cancel(true);
System.out.println("Failed to reply quickly...");
return;
}
HttpResponse response = aResp.get();
For the HTTP/1.1 implementation, this is more of a developer convenience than anything else, as HTTP/1.1 does not offer a cancellation mechanism for requests that have already been sent to the server, but HTTP/2 should allow for the client to indicate cancellation of requests that a server has already started working on.
JShell
Many languages feature interactive environments for exploratory development. In some cases (notably Clojure and other Lisps), the interactive environment is where developers spend a large amount, or even the majority, of their coding time. Other languages, such as Scala or JRuby also make extensive use of REPLs.
Of course, Java has previously had the Beanshell scripting language, but this never achieved full standardisation, and the project has become dormant in recent years. The inclusion of the Nashorn Javascript implementation in Java 8 (and the jjs REPL) opened the door to broader consideration of REPLs and the possibility of interactive development in Java.
An effort to bring a modern REPL to Java 9 has commenced as JEP 222, under Project Kulla in OpenJDK. The name Kulla refers to the ancient Babylonian god of bricks and foundations. The aim of the project is to provide as close an experience to "full Java" as possible. The project does not introduce new non-Java syntax but instead disables some features of the language that are not useful for interactive development (such as top-level access control modifiers or synchronized).
Like all REPLs, JShell provides a command line, rather than IDE-like experience. Statements and expressions are evaluated immediately, in the context of an execution state, rather than having to be packaged into classes. Methods are also free-floating, and do not have to belong to a particular class. Instead, JShell uses "snippets" of code to provide a top-level execution environment.
JShell has been developed in a separate project, like the HTTP/2 API, in order to avoid impacting the stability of mainline builds during a period of rapid development. JShell is expected to be merged into mainline during August 2015.
For now, developers can build Kulla from scratch (source available from Mercurial) using the AdoptOpenJDK instructions.
For some initial experimentation, the simplest route is probably to use a standalone "tryout" jar. These are jars that the community have built specifically for developers who don’t want to do a build from scratch.
The tryout jars can be found on the AdoptOpenJDK Cloudbees instance where CI build artifacts are published.
To use them, you will need to have a Java 9 beta (or OpenJDK 9 build) installed. Then download a jar file, rename it to kulla.jar and from a command line:
$ java -jar kulla.jar
| Welcome to JShell -- Version 0.610
| Type /help for help
->
This is the standard interface for a REPL, and as usual, commands are issued by starting them with a single / character.
JShell has a fairly complete (but still evolving) help syntax, which is easily accessed:
-> /help
Type a Java language expression, statement, or declaration.
Or type one of the following commands:
/l or /list [all] -- list the source you have typed
/seteditor <executable> -- set the external editor command to use
/e or /edit <name or id> -- edit a source entry referenced by name or id
/d or /drop <name or id> -- delete a source entry referenced by name or id
/s or /save [all|history] <file> -- save the source you have typed
/o or /open <file> -- open a file as source input
/v or /vars -- list the declared variables and their values
/m or /methods -- list the declared methods and their signatures
/c or /classes -- list the declared classes
/x or /exit -- exit the REPL
/r or /reset -- reset everything in the REPL
/f or /feedback <level> -- feedback information: off, concise, normal, verbose, default, or ?
/p or /prompt -- toggle display of a prompt
/cp or /classpath <path> -- add a path to the classpath
/h or /history -- history of what you have typed
/setstart <file> -- read file and set as the new start-up definitions
/savestart <file> -- save the default start-up definitions to the file
/? or /help -- this help message
/! -- re-run last snippet
/<n> -- re-run n-th snippet
/-<n> -- re-run n-th previous snippet
Supported shortcuts include:
-- show possible completions for the current text
Shift- -- for current method or constructor invocation, show a synopsis of the method/constructor
JShell supports tab completion, so that we can easily find println() or any other method we want to use:
-> System.out.print
print( printf( println(
Traditional expression evaluation is also easy, although Java's statically typed nature makes this a little more exacting than in dynamically typed languages. JShell automatically creates temporary variables to hold the values of expressions and ensure that they remain in scope for later use:
-> 3 * (4 + 5)
| Expression value is: 27
| assigned to temporary variable $1 of type int
-> System.out.println($1);
27
We can also view the source code of everything entered in the session so far, with the /list command:
-> /list
9 : 3 * (4 + 5)
10 : System.out.println($1);
The command /vars shows all of the variables (both explicitly defined and temporaries) and the values they are currently holding:
-> String s = "Dydh da"
| Added variable s of type String with initial value "Dydh da"
-> /vars
| int $1 = 27
| String s = "Dydh da"
As well as simple bits of code, the REPL allows the creation of classes and other user-defined types very simply. For example, classes can be created in just one line, like this (note that the starting and closing braces are required):
-> class Pet {}
| Added class Pet
-> class Cat extends Pet {}
| Added class Cat
The concise, free-flowing nature of code in JShell means that we can use the REPL to demonstrate Java language features very simply. For example, let's look at the well-known type problem that Java arrays have due to array covariance:
-> Pet[] pets = new Pet[1]
| Added variable pets of type Pet[] with initial value [LPet;@2be94b0f
-> Cat[] cats = new Cat[1]
| Added variable cats of type Cat[] with initial value [LCat;@3ac42916
-> pets = cats
| Variable pets has been assigned the value [LCat;@3ac42916
-> pets[0] = new Pet()
| java.lang.ArrayStoreException thrown: REPL.$REPL13$Pet
| at (#20:1)
This makes JShell a great teaching or exploration tool, and is closest in experience to the Scala REPL. With the /classpath switch, additional jars can be loaded in, to allow interactive exploration of APIs directly from the REPL.
Getting Involved
Major IDEs are beginning to provide builds that support early JDK 9 - including Netbeans and Eclipse Mars. IntelliJ 14.1 reportedly supports JDK 9, but it is unclear how far the support for the new modular JDK extends.
These IDE builds do not, for now, support either HTTP/2 or JShell, as these features have yet to land on the OpenJDK mainline, but developers should expect them to appear as part of the standard JDK beta builds soon, with IDE plugins following on closely behind. The APIs are still under development, and end-user use and participation is actively being sought by the project leads.
The JDK 9 Outreach programme is also underway to encourage developers to test their code and applications on JDK 9 before it arrives. HTTP/2 & JShell aren't the only new features being worked on - other new JDK 9 functionality under development as JEPs includes
- 102 Process API Updates
- 165 Compiler Control
- 227 Unicode 7.0
- 245 Validate JVM Command-Line Flag Arguments
- 248: Make G1 the Default Garbage Collector
- TLS Updates (JEP 219, 244, 249)
A complete list of all the JEPs currently under consideration (and the Java versions they are intended for) can be found here.
About the Author
Ben Evans is the CEO of jClarity, a Java/JVM performance analysis startup. In his spare time he is one of the leaders of the London Java Community and holds a seat on the Java Community Process Executive Committee. His previous projects include performance testing the Google IPO, financial trading systems, writing award-winning websites for some of the biggest films of the 90s, and others.