Removed and Deprecated APIs: Java EE, JavaFX, Nashorn (JEP 320, 335, 336)
Removal Timeline
| API | Deprecated In | Removed In | JEP |
|---|---|---|---|
| Java EE modules (JAXB, JAX-WS, etc.) | Java 9 | Java 11 | JEP 320 |
| JavaFX | Bundled with Java 9 | Unbundled in Java 11 | — |
| Nashorn JavaScript Engine | Java 11 | Java 15 | JEP 335 |
| Pack200 tools and API | Java 11 | Java 14 | JEP 336 |
javah tool | Java 9 | Java 10 | JEP 313 |
sun.misc.BASE64Encoder/Decoder | Java 8 | Java 11 | Encapsulation |
Thread.destroy(), Thread.stop(Throwable) | Old | Java 11 | — |
| Applet API | Java 9 | Java 17 | JEP 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
| Module | Package(s) | What It Contained |
|---|---|---|
java.xml.bind | javax.xml.bind.* | JAXB — XML-to-Java binding |
java.xml.ws | javax.xml.ws.*, javax.jws.* | JAX-WS — SOAP web services |
java.xml.ws.annotation | javax.xml.ws.spi.http, javax.annotation | Web service annotations |
java.corba | javax.activity, javax.rmi, org.omg.* | CORBA |
java.transaction | javax.transaction | JTA (Java Transaction API) |
java.activation | javax.activation | JAF (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:
- Use an external CORBA library (e.g., JacORB)
- 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
jlinkto 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.BASE64Encoder | java.util.Base64.getEncoder() |
sun.misc.BASE64Decoder | java.util.Base64.getDecoder() |
sun.reflect.Reflection.getCallerClass() | StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass() |
sun.misc.Unsafe | Still 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:
| Library | Min Version | Issue |
|---|---|---|
| ASM | 7.0 | Class file version 55 (Java 11) |
| Byte Buddy | 1.9.0 | Module-safe bytecode generation |
| cglib | 3.2.8 | Proxy generation on Java 11 |
| Javassist | 3.23.1 | Java 11 compatibility |
| Spring Framework | 5.1.x | First version with full Java 11 support |
| Hibernate | 5.3.x | Java 11 compatible |
| Mockito | 2.23.0 | Byte 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>