Part 14 of 16

Removed and Deprecated APIs: Java EE, JavaFX, Nashorn (JEP 320, 335, 336)

Removal Timeline

APIDeprecated InRemoved InJEP
Java EE modules (JAXB, JAX-WS, etc.)Java 9Java 11JEP 320
JavaFXBundled with Java 9Unbundled in Java 11
Nashorn JavaScript EngineJava 11Java 15JEP 335
Pack200 tools and APIJava 11Java 14JEP 336
javah toolJava 9Java 10JEP 313
sun.misc.BASE64Encoder/DecoderJava 8Java 11Encapsulation
Thread.destroy(), Thread.stop(Throwable)OldJava 11
Applet APIJava 9Java 17JEP 289

Java EE Modules Removed (JEP 320)

The six Java EE modules that shipped with Java SE 6–10 were removed from Java 11. These modules were originally included for convenience but are maintained separately by the Jakarta EE project.

What was removed

ModulePackage(s)What It Contained
java.xml.bindjavax.xml.bind.*JAXB — XML-to-Java binding
java.xml.wsjavax.xml.ws.*, javax.jws.*JAX-WS — SOAP web services
java.xml.ws.annotationjavax.xml.ws.spi.http, javax.annotationWeb service annotations
java.corbajavax.activity, javax.rmi, org.omg.*CORBA
java.transactionjavax.transactionJTA (Java Transaction API)
java.activationjavax.activationJAF (JavaBeans Activation Framework)

Fixing JAXB (most common)

JAXB is the most frequently used of the removed modules — it is used any time you marshal/unmarshal XML with @XmlRootElement, @XmlElement, etc.

Detection:

# Check if your app uses JAXB
jdeps --multi-release 11 --class-path 'libs/*' myapp.jar | grep "javax.xml.bind"

Fix — Maven:

<!-- Add to pom.xml -->
<dependency>
  <groupId>jakarta.xml.bind</groupId>
  <artifactId>jakarta.xml.bind-api</artifactId>
  <version>2.3.3</version>
</dependency>
<dependency>
  <groupId>com.sun.xml.bind</groupId>
  <artifactId>jaxb-impl</artifactId>
  <version>2.3.3</version>
  <scope>runtime</scope>
</dependency>

Note on package names: The Jakarta EE 2.x JAXB API still uses the javax.xml.bind package name, so existing code does not need to change. Jakarta EE 3.x uses jakarta.xml.bind — if you migrate to those versions, package imports must be updated.

Fixing JAX-WS (SOAP clients)

<dependency>
  <groupId>jakarta.xml.ws</groupId>
  <artifactId>jakarta.xml.ws-api</artifactId>
  <version>2.3.3</version>
</dependency>
<dependency>
  <groupId>com.sun.xml.ws</groupId>
  <artifactId>rt</artifactId>
  <version>2.3.3</version>
  <scope>runtime</scope>
</dependency>

Fixing javax.annotation (common conflict)

javax.annotation.@PostConstruct, @PreDestroy, @Resource were in java.xml.ws.annotation. Add:

<dependency>
  <groupId>jakarta.annotation</groupId>
  <artifactId>jakarta.annotation-api</artifactId>
  <version>1.3.5</version>
</dependency>

CORBA

CORBA (org.omg.*) was completely removed with no replacement. If your application uses CORBA, you have two options:

  1. Use an external CORBA library (e.g., JacORB)
  2. Migrate to a modern RPC mechanism (gRPC, REST, or messaging)

CORBA is rarely used in modern systems. Most teams encountering this issue have inherited legacy code.


JavaFX Unbundled

JavaFX was bundled with Oracle JDK through Java 10. Starting with Java 11, it is a separate, independently-versioned project at https://openjfx.io/.

Why it was separated

JavaFX is a UI toolkit — not a core platform feature. Separating it allows independent release cycles and lets teams that do not use JavaFX exclude it from their runtime.

Adding JavaFX as a dependency

Maven:

<dependency>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-controls</artifactId>
  <version>21.0.2</version>
</dependency>
<dependency>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-fxml</artifactId>
  <version>21.0.2</version>
</dependency>

Add the Maven plugin to include native libs on module path:

<plugin>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-maven-plugin</artifactId>
  <version>0.0.8</version>
  <configuration>
    <mainClass>com.example.MainApp</mainClass>
  </configuration>
</plugin>

module-info.java:

module com.example.app {
    requires javafx.controls;
    requires javafx.fxml;
    opens com.example.app to javafx.fxml;
    exports com.example.app;
}

Nashorn JavaScript Engine Deprecated (JEP 335)

Nashorn, Java’s embedded JavaScript engine, was deprecated in Java 11 and removed in Java 15. The reason: maintaining a full JavaScript engine in the JDK is costly, and GraalVM’s JavaScript engine (GraalJS) is a much more capable, spec-compliant replacement.

Detection

grep -r "ScriptEngine\|NashornScriptEngine\|javax.script" src/
grep -r "ScriptEngineManager" src/

Migration to GraalVM JavaScript

Add GraalVM JS as a standalone dependency (works on any JDK 11+):

<dependency>
  <groupId>org.graalvm.js</groupId>
  <artifactId>js</artifactId>
  <version>23.1.2</version>
</dependency>
<dependency>
  <groupId>org.graalvm.js</groupId>
  <artifactId>js-scriptengine</artifactId>
  <version>23.1.2</version>
</dependency>

Existing ScriptEngine code still works with the GraalVM JS engine:

// Same API — just change the engine name or let it auto-discover
ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal.js");
engine.eval("print('Hello from GraalJS')");

Migration: embed JavaScript in Java (GraalVM Polyglot API)

import org.graalvm.polyglot.*;

try (var context = Context.create()) {
    context.eval("js", "print('Hello from GraalVM JS')");

    Value result = context.eval("js", "1 + 2 + 3");
    int sum = result.asInt();  // 6
}

When to avoid embedded JavaScript

Consider whether embedding JavaScript is actually needed:

  • Configuration/scripting: use Groovy, Kotlin script, or a purpose-built DSL
  • Data transformation: use Java stream operations directly
  • Business rules: use Drools or a rules engine

Pack200 Deprecated (JEP 336)

Pack200 was a JAR compression format designed in 2004 for slow network connections. JAR files compressed with Pack200 were about 60% smaller than the original JAR.

Pack200 was deprecated in Java 11 and removed in Java 14.

Who is affected

Applications that use Pack200 for JAR distribution (rare in modern systems) or build tools that produced .pack.gz files. Most Maven and Gradle builds do not use Pack200.

Migration

Simply switch to standard ZIP/JAR distribution. Modern JAR files are already ZIP-compressed. For additional size reduction:

  • Use jlink to create a minimal runtime image
  • Use ProGuard or R8 to shrink and obfuscate
  • Use GraalVM native image for native binaries

Internal API Restrictions and sun.* Access

Java 9 introduced strong encapsulation via the module system — many sun.* and com.sun.* internal APIs became inaccessible. Java 11 tightened this further.

Commonly broken internal APIs and replacements

Internal API (Java 8)Java 11 Replacement
sun.misc.BASE64Encoderjava.util.Base64.getEncoder()
sun.misc.BASE64Decoderjava.util.Base64.getDecoder()
sun.reflect.Reflection.getCallerClass()StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass()
sun.misc.UnsafeStill accessible, but requires --add-opens
com.sun.net.httpserver.*Still available as com.sun.net.httpserver

Base64 migration (very common)

// Java 8 — sun.misc (removed in Java 11)
sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
String encoded = encoder.encode(bytes);

sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
byte[] decoded = decoder.decodeBuffer(encodedString);

// Java 8+ (use this instead — java.util.Base64 was available in Java 8)
String encoded = Base64.getEncoder().encodeToString(bytes);
byte[] decoded = Base64.getDecoder().decode(encodedString);

Stack reflection migration

// Java 8 — sun.reflect
Class<?> caller = sun.reflect.Reflection.getCallerClass();

// Java 9+
Class<?> caller = StackWalker
    .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
    .getCallerClass();

Library version requirements

Libraries that use internal JDK APIs must be updated. Minimum versions for Java 11 compatibility:

LibraryMin VersionIssue
ASM7.0Class file version 55 (Java 11)
Byte Buddy1.9.0Module-safe bytecode generation
cglib3.2.8Proxy generation on Java 11
Javassist3.23.1Java 11 compatibility
Spring Framework5.1.xFirst version with full Java 11 support
Hibernate5.3.xJava 11 compatible
Mockito2.23.0Byte Buddy upgrade for Java 11

–add-opens and –add-exports Workarounds

When third-party libraries access inaccessible internals and cannot be updated immediately, use these JVM flags as temporary workarounds:

# Allow Hibernate/Spring to reflectively access java.lang internals
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED

# Allow Jackson to access private fields
--add-opens java.base/java.lang=com.fasterxml.jackson.databind

# Allow cglib
--add-opens java.base/java.lang=net.sf.cglib.proxy

# Allow JPA providers
--add-opens java.base/java.io=ALL-UNNAMED

Add these to your application startup script. In Maven Surefire (tests):

<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>3.2.5</version>
  <configuration>
    <argLine>
      --add-opens java.base/java.lang=ALL-UNNAMED
      --add-opens java.base/java.util=ALL-UNNAMED
    </argLine>
  </configuration>
</plugin>

What’s Next

Next: Migrating to Java 11: From Java 8 — Step by Step