Java 1.9/9 New Features

Content:

  1. Version numbers
  2. Release
  3. General
  4. Collection of
  5. SHA3
  6. StackWalker
  7. ProcessHandle
  8. Simplified version for javac and multi version jar files
  9. Module system
    1. Concept
    2. How it works
    3. Java itself
    4. Remarks
  10. Applets
  11. Interfaces private static methods
  12. Deserialization filter
  13. Nashorn and ES6
  14. JShell
  15. AOT compilation
  16. New HTTP client

Version numbers:

This version is officially known as Java SE 9, but often called Java 1.9. Unlike many previous versions it does not have a code name.

Java version Official name Code name Release date
1.0 JDK 1.0 January 23rd 1996
1.1 JDK 1.1 February 19th 1997
1.2 J2SE 1.2 Playground December 8th 1998
1.3 J2SE 1.3 Kestrel May 8th 2000
1.4 J2SE 1.4 Merlin February 6th 2002
1.5/5 Java SE 5 Tiger September 30th 2004
1.6/6 Java SE 6 Mustang December 11th 2006
1.7/7 Java SE 7 Dolphin July 7th 2011
1.8/8 Java SE 8 March 18th 2014
1.9/9 Java SE 9 September 21st 2017

Release:

Java 1.9 was released September 21st 2017.

It has previously been scheduled for release in September 2016, March 2017 and July 2017, but it took a longer time to get Jigsaw (the module system) completed and that delayed release.

The last planned release date of July 27th was moved because the JCP EC rejected the specification for the module system May 8th. A revised specification resolving some issues and concerns had to be made and it got approvced by the JCP EC June 26th.

General:

Version 1.9 has one really huge change (Jigsaw - the module system) and a bunch of relative small changes.

This makes it a weird release. The module system is the biggest change in the history of Java. But for the many users that will not utilize the module system it will look like small changes.

Collection of

Java 9 added a new easy way to initialize collections. Somewhat similar to what C# got back in C# 3.0.

Old way:

import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

public class Coll8 {
    public static void main(String[] args) {
        List<String> lst = new ArrayList<>();
        lst.add("A");
        lst.add("BB");
        lst.add("CCC");
        System.out.println(lst);
        Map<Integer,String> mp = new HashMap<>();
        mp.put(1, "A");
        mp.put(2, "BB");
        mp.put(3, "CCC");
        System.out.println(mp);
        Set<Integer> st = new HashSet<>();
        st.add(1);
        st.add(2);
        st.add(3);
        System.out.println(st);
        // since 1.6:
        List<String> lstx = Arrays.asList("A", "BB", "CCC");
        System.out.println(lstx);
    }
}

New way:

import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

public class Coll9 {
    public static void main(String[] args) {
        List<String> lst = List.of("A", "BB", "CCC");
        System.out.println(lst);
        Map<Integer,String> mp = Map.of(1, "A", 2, "BB", 3, "CCC");
        System.out.println(mp);
        Set<Integer> st = Set.of(1, 2, 3);
        System.out.println(st);
        // alternate form for map
        Map<Integer,String> mpx = Map.ofEntries(Map.entry(1, "A"), Map.entry(2, "BB"), Map.entry(3, "CCC"));
        System.out.println(mpx);
    }
}

Note that the collections created via the of method are immutable (so in that regard the new way is actually not totally equivalent to the old way).

I think these are convenient and I am sure they will be used.

SHA3

Java 9 got support for SHA3 hash functions.

SHA3 hash functions are implemented exactly like other hash functions in Java like SHA2.

SHA2 example:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.xml.bind.DatatypeConverter;

public class SHA2 {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        String s = "123 - This is a test!";
        System.out.println(DatatypeConverter.printHexBinary(md.digest(s.getBytes())));
    }
}

SHA3 example:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.xml.bind.DatatypeConverter;

public class SHA3 {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA3-256");
        String s = "123 - This is a test!";
        System.out.println(DatatypeConverter.printHexBinary(md.digest(s.getBytes())));
    }
}

Note that SHA3 is not better / more secure than SHA2. SHA3 was not invented due to known problems in SHA2. SHA3 was invented to have two independent families of hash functions to avoid disaster if someday in the future a vulnerability was found in one of them.

It is good that Java get support for SHA3, but I don't think many will actually use it - no need to change from SHA2 for now.

StackWalker

Java 9 got a nice way to get a stack trace instead of the old hack of creating an artificial Exception object..

Old hack:

public class Trace8 {
    private static void m3() {
        Exception ex = new Exception();
        for(StackTraceElement ste : ex.getStackTrace()) {
            System.out.printf("%s line %d class %s method %s\n", ste.getFileName(), ste.getLineNumber(), ste.getClassName(), ste.getMethodName());
        }
    }
    private static void m2() {
        m3();
    }
    private static void m1() {
        m2();
    }
    public static void main(String[] args) {
        m1();
    }
}

StackWalker:

import java.util.stream.Collectors;
import java.util.List;

public class Trace9 {
    private static void m3() {
        List<StackWalker.StackFrame> stack = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk(s -> s.collect(Collectors.toList()));
        for(StackWalker.StackFrame sf : stack) {
            System.out.printf("%s line %d %s method %s\n", sf.getFileName(), sf.getLineNumber(), sf.getDeclaringClass(), sf.getMethodName());
        }
    }
    private static void m2() {
        m3();
    }
    private static void m1() {
        m2();
    }
    public static void main(String[] args) {
        m1();
    }
}

I don't think this is important and it will rarely be used.

ProcessHandle

Java 9 got classes to look at OS processes.

Demo:

import java.util.Optional;

public class ProcList {
    private static void printInfo(ProcessHandle p) {
        ProcessHandle.Info info = p.info();
        System.out.printf("%d %s %s %s\n", p.pid(),
                                           info.user().orElse("*"),
                                           info.command().orElse("*"),
                                           String.join(",", info.arguments().orElse(new String[] { "*" })));
    }
    private static void printAll() {
        ProcessHandle.allProcesses().forEach(p -> printInfo(p));
    }
    private static void printSelf() {
        printInfo(ProcessHandle.current());
    }
    public static void main(String[] args) {
        printSelf();
        printAll();
    }
}

I don't think it is important - it will not be used much. You don't write this type of code in Java.

And besides - the use of streams and Optional makes the API look over engineered to me.

Simplified version for javac and multi version jar files

Java 9 has two new features for making it easier to build for old Java versions.

One feature simplifies the javac command options for older Java versions.

Previously one had to use:

javac -source 1.x -target 1.x -bootclaspath <path for 1.x> ...

Now one can use:

javac --release x ...

Another feature enables a single jar file to have different versions of the same class for different versions of Java.

It works simply by:

The jar utility has been updated to make this an easy task.

Let us see an example.

basesrc/test/Main.java:

package test;

public class Main {
    public static void main(String[] args) {
        Test o = new Test();
        o.test();
    }
}

basesrc/test/Test.java:

package test;

public class Test {
    public void test() {
        System.out.println("I am an 8");
    }
}

rel9src/test/Test.java:

package test;

public class Test {
    public void test() {
        System.out.println("I am a 9");
    }
}

Build:

javac --release 8 -d basebin basesrc/test/*java
javac --release 9 -d rel9bin rel9src/test/*java
jar --create --file test.jar -C basebin test --release 9 -C rel9bin test

The content of test.jar looks like:

     0 Fri Feb 03 22:51:12 EST 2017 META-INF/
    85 Fri Feb 03 22:51:12 EST 2017 META-INF/MANIFEST.MF
     0 Fri Feb 03 22:48:24 EST 2017 test/
   315 Fri Feb 03 22:51:12 EST 2017 test/Main.class
   391 Fri Feb 03 22:51:12 EST 2017 test/Test.class
     0 Fri Feb 03 22:48:26 EST 2017 META-INF/versions/9/test/
   390 Fri Feb 03 22:51:12 EST 2017 META-INF/versions/9/test/Test.class

Run with Java 9:

C:\Work\jdk9\mvj>java -version
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+154)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+154, mixed mode)

C:\Work\jdk9\mvj>java -cp test.jar test.Main
I am a 9

Run with Java 8:

C:\Work\jdk9\mvj>java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

C:\Work\jdk9\mvj>java -cp test.jar test.Main
I am an 8

I think this is a very useful and very impressive feature. I fear that it will not be widely used, because not everyone has the need for this feature and it is a little bit complex to use.

Module system

The new module system better known under its codename "project Jigsaw" has been underway a long time.

Quick overview of the history:

June 2005
JSR 277 created with target release Java 7
Februar 2009
Project Jigsaw start
September 2010
Jigsaw get delayed from Java 7 to Java 8
July 2012
Jigsaw get delayed from Java 8 to Java 9
November 2014
Jigsaw becomes JSR 376
September 2017
Java 9

It is the biggest change in Java ever.

Concept

Modules are a new high level grouping concept that adds another layer of grouping on top of packages.

Java -8 layers:

  1. package
  2. class (and other types)

Java 9- layers:

  1. module
  2. package
  3. class (and other types)

So a module is a collection of related packages.

But modules are not just a logical grouping for structure purpose. Modules actually impacts visibility of classes (and other types).

Public classes inside a module is only visible to other modules, if their module explicit export their package.

Java -8 visibility:

declared visibility same class other class same package other class other package
public yes yes yes
package/default yes yes no
private yes no no

Java 9- visibility:

declared visibility package exported by module same class other class same package other class other package same module other class other module
public yes yes yes yes yes
public no yes yes yes no
package/default does not matter yes yes no no
private does not matter yes no no no

To be able to use something from a package in another module the module also needs to explicit import (require) it.

This is a big change.

An important detail is that a module can export a package in two ways:

The last option is a concept similar to C++ friend concept.

The new module system also provides a way for having modules provide implementation for interfaces declarative.

An obvious question would be how the Java module system differs from OSGI. OSGI is a module system for Java that has existed for many years. It turns out that there are big differences.

OSGI modules are loaded and unloaded by an OSGI container that provides full life cycle control.

Java module system modules are groupings of packages used by the JVM.

How it works

Let us look at how it works in practice.

Modules are defined and dependencies managed via module-info.java.

The syntax is:

module name.of.this.module {
    requires name.of.package.required;
    exports name.of.package.exportet;
    exports name.of.package.exportet to name.of.module,name.of.module;
    uses name.of.interface.this.module.will.load.via.serviceloader;
    provides name.of.interface.another.module.can.load.via.serviceloader with name.of.class.in.this.module.to.be.loaded.by.serviceloader;
}

Now let us create a small example.

The example will look a bit weird, because the module system is primarily intended to help with very large applications. And that makes it difficult to create a small example that makes sense.

Module moddemo.api

This module defines an API for dumping records in any format.

srcapi/module-info.java:

module moddemo.api {
    exports moddemo.api;
}

srcapi/moddemo/api/Dumper.java:

package moddemo.api;

public interface Dumper<T> {
    public void setFieldPicker(FieldPicker<T> fp);
    public void dump(T o);
}

srcapi/moddemo/api/DumperFactory.java:

package moddemo.api;

public interface DumperFactory<T> {
    public Dumper<T> getInstance();
}

srcapi/moddemo/api/FieldPicker.java:

package moddemo.api;

public interface FieldPicker<T> {
    public int getNoFields();
    public Object getField(T o, int ix);
}

Module moddemo.csv

This module contains an implementation for dumping in CSV format.

srccsv/module-info.java:

module moddemo.csv {
    requires moddemo.api;
    exports moddemo.csv;
    exports moddemo.csv.impl to moddemo.mgr;
    provides moddemo.api.DumperFactory with moddemo.csv.CsvDumperFactory;
}

srccsv/moddemo/csv/CsvDumperFactory.java:

package moddemo.csv;

import moddemo.api.Dumper;
import moddemo.api.DumperFactory;

import moddemo.csv.impl.CsvDumper;

public class CsvDumperFactory<T> implements DumperFactory<T> {
    @Override
    public  Dumper<T> getInstance() {
        return new CsvDumper<T>();
    }
}

srccsv/moddemo/csv/impl/CsvDumper.java:

package moddemo.csv.impl;

import moddemo.api.Dumper;
import moddemo.api.FieldPicker;
import moddemo.csv.mgmt.CsvDumperTrack;

public class CsvDumper<T> implements Dumper<T> {
    private FieldPicker<T> fp = null;
    private CsvDumperTrack track = new CsvDumperTrack();
    @Override
    public void setFieldPicker(FieldPicker<T> fp) {
        this.fp = fp;
    }
    @Override
    public void dump(T o) {
        for(int i = 0; i < fp.getNoFields(); i++) {
            if(i > 0) {
                System.out.print(",");
            }
            System.out.print(fp.getField(o, i));
        }
        System.out.println();
        track.incrementCount();
    }
    public int getCount() {
        return track.getCount();
    }
}

srccsv/moddemo/csv/mgmt/CsvDumperTracker.java:

package moddemo.csv.mgmt;

public class CsvDumperTrack {
    private int count = 0;
    public void incrementCount() {
        count++;
    }
    public int getCount() {
        return count;
    }
}

Module moddemo.mgr

This module provides the ability to manage dumpers. This is a dirty module in the sense that it uses implementation specific methods to get its information.

srcmgr/module-info.java:

module moddemo.mgr {
    requires moddemo.api;
    requires moddemo.csv;
    exports moddemo.mgr;
}

srcmgr/moddemo/mgr/Manager.java:

package moddemo.mgr;

import moddemo.api.Dumper;
import moddemo.csv.impl.CsvDumper;

public class Manager {
    public static int getCount(Dumper dmp) {
        if(dmp instanceof CsvDumper) {
            return ((CsvDumper)dmp).getCount();
        } else {
            throw new RuntimeException("Oooops");
        }
    }
}

Module moddemo.cli

This module contains a demo program writing two records in CSV format.

srccli/module-info.java:

module moddemo.cli {
    requires moddemo.api;
    requires moddemo.csv;
    requires moddemo.mgr;
    uses moddemo.api.DumperFactory;
}

srccli/moddemo/cli/Data.java:

package moddemo.cli;

public class Data {
    private int f1;
    private String f2;
    public Data(int f1, String f2) {
        this.f1 = f1;
        this.f2 = f2;
    }
    public int getF1() {
        return f1;
    }
    public String getF2() {
        return f2;
    }
}

srccli/moddemo/cli/DataFieldPicker.java:

package moddemo.cli;

import moddemo.api.FieldPicker;

public class DataFieldPicker implements FieldPicker<Data> {
    @Override
    public int getNoFields() {
        return 2;
    }
    @Override
    public Object getField(Data o, int ix) {
        switch(ix) {
            case 0: return o.getF1();
            case 1: return o.getF2();
            default: throw new RuntimeException("Ooops");
        }
    }
}

srccli/moddemo/cli/Main.java:

package moddemo.cli;

import java.util.Iterator;
import java.util.ServiceLoader;

import moddemo.api.Dumper;
import moddemo.api.DumperFactory;

import moddemo.mgr.Manager;

public class Main {
    public static void main(String[] args) {
        ServiceLoader<DumperFactory> sl = ServiceLoader.load(DumperFactory.class);
        Iterator<DumperFactory> it = sl.iterator();
        while(it.hasNext()) {
            @SuppressWarnings("unchecked")
            DumperFactory<Data> df = (DumperFactory<Data>)it.next();
            Dumper<Data> d = df.getInstance();
            d.setFieldPicker(new DataFieldPicker());
            d.dump(new Data(123, "ABC"));
            d.dump(new Data(456, "DEF"));
            System.out.printf("%d records written\n", Manager.getCount(d));
        }
    }
}

Visibility and dependencies

Packages are visible as follows:

Module Package Visible to
moddemo.api moddemo.api all
moddemo.csv moddemo.csv all
modedemo.csv.impl only moddemo.mgr
modedemo.csv.mgmt none
moddemo.mgr modedemo.mgr all
moddemp.cli modedemo.cli all

Module have dependencies as follows:

Module Depends on modules
moddemo.api
moddemo.csv moddemo.api
moddemo.mgr moddemo.api + moddemo.csv
moddemo.cli moddemo.api + moddemo.csv + moddel.mgr

Even though the example is a unrealistic simple, then it should provide an idea about how the module system will allow large applications to be split into modules with tight control of dependencies.

Build and run

To build, use:

javac -d binapi srcapi/moddemo/api/*.java srcapi/*.java
jar --create --file mods/moddemo.api.jar -C binapi .
javac --module-path mods --add-modules moddemo.api -d bincsv srccsv/moddemo/csv/impl/*.java srccsv/moddemo/csv/mgmt/*.java srccsv/moddemo/csv/*.java srccsv/*.java
jar --create --file mods/moddemo.csv.jar -C bincsv .
javac --module-path mods --add-modules moddemo.api,moddemo.csv -d binmgr srcmgr/moddemo/mgr/*.java srcmgr/*.java
jar --create --file mods/moddemo.mgr.jar -C binmgr .
javac --module-path mods --add-modules moddemo.api,moddemo.csv,moddemo.mgr -d bincli srccli/moddemo/cli/*.java srccli/*.java
jar --create --file mods/moddemo.cli.jar --main-class=moddemo.cli.Main -C bincli .

To run use:

java --module-path mods -m moddemo.cli/moddemo.cli.Main

Java itself

An important aspect of the new module system is that it has been applied to Java itself. The entire Java library has been split in 27 modules (plus 8 modules for JavaFX).

That provides at least two benefits:

The documentation has also been updated to follow the new structure: module -> package -> class.

IMPORTANT: com.sun packages are no longer visible to applications. They are only exported to the Java library modules that actually needs them.

Remarks

If your application does not use modules then everything should work like it worked before in most cases.

In case of errors with missing Java class during compile then try add an approriate --add-modules switch to include the module.

If your code uses something not exported by its module and compile gives errors, then the best solution is to stop using that something and use something supported. But if that is not possible, then there is a workaround. One can force an export of the package with:

java --add-opens modulename/packagename=ALL-UNNAMED ...

or:

java --add-exports modulename/packagename=ALL-UNNAMED

I think the new module system is a really strong feature. It will be very useful for large applications. But it will not be useful for small applications. So I fear that too few Java developers will get it on their radar screen - and that would be a shame.

Applets

It has happened. Java applets are now officially deprecated.

This should not come as a surprise. Applets was dropped as a mainstream web technology 15 years ago. In this decade Java applets have been haunted by lots of security vulnerabilities. And to prevent that Oracle made using Java applets a hassle for users with multiple confirmation prompts.

Java Web Start is not deprecated and can to some extent be used as replacement for Java applets.

Interfaces private static methods

In Java 9 interfaces kan now have private static methods.

The only purpose is to support code reuse between default methods.

I think it is a nice little practical feature that will be used when people learn about it, but not that important.

Deserialization filter

It has been known for years that deserialization of untrusted input can be a security vulnerability in Java.

Now a mechanism to try control deserialization has been added.

Problem

To understand the potential problems let us look at some code.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InvalidClassException;
import java.io.IOException;
import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SerTest {
    public static class DataOne implements Serializable {
        private static final long serialVersionUID = 1L;
        private int iv;
        private String sv;
        public DataOne() {
            this(0, "");
        }
        public DataOne(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public int getIv() {
            return iv;
        }
        public void setIv(int iv) {
            this.iv = iv;
        }
        public String getSv() {
            return sv;
        }
        public void setSv(String sv) {
            this.sv = sv;
        }
    }
    public static class DataTwo implements Serializable {
        private static final long serialVersionUID = 1L;
        private byte[] a;
        public DataTwo() {
            this(0);
        }
        public DataTwo(int n) {
            this(new byte[n]);
        }
        public DataTwo(byte[] a) {
            this.a = a;
        }
        public byte[] getA() {
            return a;
        }
        public void setA(byte[] a) {
            this.a = a;
        }
    }
    public static class DataThree implements Serializable {
        private static final long serialVersionUID = 1L;
        private int v;
        public DataThree() {
            this(0);
        }
        public DataThree(int v) {
            this.v = v;
        }
        public int getV() {
            return v;
        }
        public void setV(int v) {
            this.v = v;
        }
        private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException
        {
            System.out.println("Executing DataThree.readObject");
            v = ois.readInt();
        }
        private void writeObject(ObjectOutputStream oos) throws IOException
        {
            System.out.println("Executing DataThree.writeObject");
            oos.writeInt(v);
        }
    }
    public static <T> byte[] serialize(T o) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        oos.close();
        return baos.toByteArray();
    }
    public static <T> T deserialize(byte[] b) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(b);
        ObjectInputStream ois = new ObjectInputStream(bais);
        @SuppressWarnings("unchecked")
        T o = (T)ois.readObject();
        ois.close();
        return o;
    }
    private static <T> T test(T o) throws ClassNotFoundException, IOException {
        byte[] b = serialize(o);
        byte[] dummy = new byte[50_000_000]; // simulate that serializer has more memory available then deserializer
        return deserialize(b);
    }
    private static void testOne() throws IOException, ClassNotFoundException {
        DataOne one = test(new DataOne(123, "ABC"));
        System.out.println(one.getIv() + " " + one.getSv());
    }
    private static void testTwo(int n) throws IOException, ClassNotFoundException {
        DataTwo two = test(new DataTwo(n));
        System.out.println(two.getA().length);
    }
    private static void testThree() throws IOException, ClassNotFoundException {
        DataThree three = test(new DataThree(123));
        System.out.println(three.getV());
    }
    public static void main(String[] args) throws Exception {
        testOne();
        try {
            testTwo(100);
            testTwo(25_000_000);
        } catch(InvalidClassException ice) {
            System.out.println("Data rejected by deserialization filter");
        } catch(OutOfMemoryError oome) {
            System.out.println("Crashing due to out of memory");
        }
        try {
            testThree();
        } catch(InvalidClassException ice) {
            System.out.println("Data rejected by deserialization filter");
        }
    }
}

Output:

C:\Work\j9>\SUN\64bit\jdk-9\bin\java -Xmx100m SerTest
123 ABC
100
Crashing due to out of memory
Executing DataThree.writeObject
Executing DataThree.readObject
123

And we see two potential problems:

The new deserialization filter provides a mechanism to help mitigate such problems.

Here we will look at 3 different mechanisms.

Global filter

Simply run the program with -Djdk.serialFilter=maxarray=1000;maxbytes=10000 to limit array sizes to 1000 and bytes per object to 10000:

C:\Work\j9>\SUN\64bit\jdk-9\bin\java -Xmx100m -Djdk.serialFilter=maxarray=1000;maxbytes=10000 SerTest
Sep 09, 2017 11:14:07 PM java.io.ObjectInputFilter$Config lambda$static$0
INFO: Creating serialization filter from maxarray=1000;maxbytes=10000
123 ABC
100
Data rejected by deserialization filter
Executing DataThree.writeObject
Executing DataThree.readObject
123

Stream filter

The same filter can be added to only a specific stream:

    public static <T> T deserialize(byte[] b) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(b);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter("maxarray=1000;maxbytes=10000"));
        @SuppressWarnings("unchecked")
        T o = (T)ois.readObject();
        ois.close();
        return o;
    }

Output:


C:\Work\j9>\SUN\64bit\jdk-9\bin\java -Xmx100m SerTest
123 ABC
100
Data rejected by deserialization filter
Executing DataThree.writeObject
Executing DataThree.readObject
123

Stream custom filter

To catch the readObject method a custom filter can be used:

    public static <T> T deserialize(byte[] b) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = new ByteArrayInputStream(b);
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.setObjectInputFilter(new ObjectInputFilter() {
            public Status checkInput(FilterInfo fi) {
                if(fi.arrayLength() > 1000) {
                    return Status.REJECTED;
                } else if(fi.streamBytes() > 10000) {
                    return Status.REJECTED;
                } else {
                    List<String> methods = new ArrayList<String>();
                    Class<?> clz = fi.serialClass();
                    while(clz != null) {
                        methods.addAll(Arrays.asList(clz.getDeclaredMethods()).stream().map(m -> m.getName()).collect(Collectors.toList()));
                        clz = clz.getSuperclass();
                    }
                    if(methods.contains("readObject")) {
                        return Status.REJECTED;
                    } else {
                        return Status.ALLOWED;
                    }
                }
            }
        });
        @SuppressWarnings("unchecked")
        T o = (T)ois.readObject();
        ois.close();
        return o;
    }

Output:

C:\Work\j9>\SUN\64bit\jdk-9\bin\java -Xmx100m SerTest
123 ABC
100
Data rejected by deserialization filter
Executing DataThree.writeObject
Data rejected by deserialization filter

Backport

The deserialization filter stuff has supposedly been backported to older Java versions:

but I could not get everything working on Java 8 update 141, so be careful about expectations.

Nashorn and ES6

Nashorn the embedded JavaScript interpreter in Java has been updated with a lot of ES6 (EcmaScript 6) features.

JShell

Java 9 comes with an interactive Java shell.

Example:

C:\Work\jdk9>jshell
|  Welcome to JShell -- Version 9-ea
|  For an introduction type: /help intro

jshell> int a = 2;
a ==> 2

jshell> int b = a + a;
b ==> 4

jshell> System.out.println(b);
4

jshell> CTRL/D
C:\Work\jdk9>

Honestly I can't see what I would use that for.

AOT compilation

As an experiment Oracle Java for Linux 64 bit ships with an AOT (Ahead Of Time) compiler.

Usage:

jaotc --output HelloWorld.so HelloWorld.java
java -XX:AOTLIbrary=HelloWorld.so HelloWorld

The purpose is obviously to reduce startup time. This is not relevant for Java EE, but it can be important for some Java SE applications.

If it becomes a success then it will probably show up on all platforms.

New HTTP client

Java 9 was supposed to contain a new and more flexible HTTP client API with support for HTTP 2.0 and web sockets.

There certainly is a need for it. URLConnection/HttpURLConnection/HttpsURLConnection is not a good API for writing a HTTP client. There is a reason that most people are using Apache HTTP Client.

But it did not make it into the official Java SE 9 release. Instead some experimental code was shipped with Java SE.

OpenJDK and Oracle Java are shipping with classes:

But they are put in module jdk.incubator.httpclient package jdk.incubator.http and the module is not added by default so --add-modules jdk.incubator.httpclient is required to use it.

The expectation is that the new HTTP client will make into Java 10.

But the caveats for usage in Java 9 are:

So code developed for Java 9 using this will require changes to work in Java 10.

Article history:

Version Date Description
1.0 February 4th 2017 Initial version
1.1 March 23rd 2017 Add release dates
1.2 August 25th 2017 Add JCP discussion and new release date
1.3 September 10th 2017 Add deserialization filter description and note about HTTP client
1.4 September 23rd 2017 Actual release and small change to ProcList program

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj