Writing some notes here on what I had to do to get things running on Jakarta 10.
I've started upgrading as my application server (Payara) came out with a version that is Jakarta 10 compatible (release 6). See [1].
I'm using the Payara Micro to deploy my apps.
Things done
Change imports
Here's a list of all the imports I had to change. It's likely not interesting, but sometimes I just like to write stuff down.
Type |
Javax import |
Jakarta import |
Jakarta Persistence | import javax.persistence.* | import jakarta.persistence.* |
Jakarta Security | import javax.security.enterprise.credential.* | import jakarta.security.enterprise.credential.* |
| import javax.security.enterprise.* | import jakarta.security.enterprise.* |
| import javax.security.enterprise.authentication.mechanism.http.* | import jakarta.security.enterprise.authentication.mechanism.http.* |
| import javax.security.enterprise.credential.* | import jakarta.security.enterprise.credential.* |
| import javax.security.enterprise.identitystore.* | import jakarta.security.enterprise.identitystore.* |
| import javax.annotation.security.* | import jakarta.annotation.security.* |
Jakarta CDI | import javax.enterprise.context.* | import jakarta.enterprise.context.* |
| import javax.inject.* | import jakarta.inject.* |
| import javax.annotation.* | import jakarta.annotation.* |
| import javax.ejb.Singleton; | import jakarta.inject.Singleton; |
Jakarta Servlets | import javax.servlet.http.* | import jakarta.servlet.http.* |
| import javax.servlet.* | import jakarta.servlet.* |
| import javax.servlet.annotation.*; | import jakarta.servlet.annotation.*; |
Jakarta Validation | import javax.validation.constraints.*; | import jakarta.validation.constraints.*; |
Jakarta Rest | import javax.ws.rs.*; | import jakarta.ws.rs.*; |
| import javax.ws.rs.core.*; | import jakarta.ws.rs.core.*; |
| import javax.ws.rs.ext.*; | import jakarta.ws.rs.ext.*; |
Jakarta XML bind | import javax.xml.bind.annotation.*; | import jakarta.xml.bind.annotation.*; |
Jakarta Websockets | import javax.websocket.*; | import jakarta.websocket.*; |
The most changes are due to persistence as it kind of spreads throughout a lot of the code, mainly annotations on entities.
Changed beans.xml
The old:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="annotated">
</beans>
The new:
<?xml version="1.0" encoding="UTF-8"?>
<beans version="3.0" bean-discovery-mode="all"
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_3_0.xsd">
</beans>
Changed web.xml
The old:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee webapp_4_0.xsd"
version="4.0">
<display-name>Mrbear Website</display-name>
</web-app>
The new:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="false"
>
<display-name>Mrbear Website</display-name>
</web-app>
Changed persistence.xml
The old:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="mrbearPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/mrbear</jta-data-source>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="none"/>
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.logging.level" value="INFO"/>
<!-- line below is for logging of sql, set to FINE if you need to see it-->
<property name="eclipselink.logging.level.sql" value="INFO"/>
<property name="eclipselink.target-database" value="MySQL4"/>
</properties>
</persistence-unit>
</persistence>
The new:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="3.0" xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
>
<persistence-unit name="mrbearPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/mrbear</jta-data-source>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="jakarta.persistence.schema-generation.database.action" value="none"/>
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.logging.level" value="INFO"/>
<!-- line below is for logging of sql, set to FINE if you need to see it-->
<property name="eclipselink.logging.level.sql" value="INFO"/>
<property name="eclipselink.target-database" value="MySQL4"/>
</properties>
</persistence-unit>
</persistence>
Maven POM files
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.encoding>UTF-8</project.encoding>
<jakartaee.version>10.0.0</jakartaee.version>
<!-- XmlRootElement and all that stuff -->
<jakarta-xml-bind.version>4.0.0</jakarta-xml-bind.version>
<microprofile.version>5.0</microprofile.version>
<!-- used for EclipseLink extentions, for example Customizer/QueryHints -->
<eclipselink.version>4.0.0</eclipselink.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>${microprofile.version}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<!-- used for EclipseLink extentions, for example Customizer/QueryHints -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>${eclipselink.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>${jakartaee.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>${jakarta-xml-bind.version}</version>
</dependency>
</dependencies>
Also upgraded the java compiler plugin to use Java 17:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
Removed things like XmlRootElement and XmlTransient
I've replaced them with POJOs that I just create using JsonbBuilder.create().fromJson(json, clazz) and jsonify using JsonbBuilder.create().toJson(object). Not everywhere yet, but I'm getting there.
Javax import |
import javax.xml.bind.annotation.XmlRootElement; |
import javax.xml.bind.annotation.XmlTransient; |
Also means I could get rid of this in my pom files:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
Removed EJB
I've decided to get rid of all the EJB stuff.
This isn't related to the Jakarta 10 upgrade per se, but thought I'd mention it.
Javax import |
import javax.ejb.LocalBean; |
import javax.ejb.Stateless; |
import javax.ejb.Singleton; |
import javax.security.enterprise.SecurityContext; |
import javax.ejb.EJB; |
import javax.ejb.EJBAccessException; |
import javax.ejb.SessionContext; |
|
Replaced javax.security.enterprise.SecurityContext and javax.ejb.SessionContext with rs.core.SecurityContext. So using Rest functionality, instead of EJB functionality.
Problems
Problems with cookies
JASPIC: http msg authentication fail
java.lang.IllegalArgumentException: rfc6265CookieProcessor.invalidCharInValue 44
at org.glassfish.grizzly.http.util.CookieHeaderGenerator.validateCookieValue(CookieHeaderGenerator.java:176)
at org.glassfish.grizzly.http.util.CookieHeaderGenerator.generateHeader(CookieHeaderGenerator.java:85)
Apparently, the cookie spec no longer allowed special characters, like commas.
Problems with JavaScript
Exception in thread "main" java.lang.IllegalArgumentException: A language with id 'js' is not installed. Installed languages are: [].
at org.graalvm.truffle/com.oracle.truffle.polyglot.PolyglotEngineException.illegalArgument(PolyglotEngineException.java:131)
After some research, I found that GraalVM does not ship automatically with JavaScript since version 22. A separate GraalVM Component needs to be installed, like so:
% gu install js
Downloading: Component catalog from www.graalvm.org
Processing Component: Graal.js
Downloading: Component js: Graal.js from github.com
Installing new component: Graal.js (org.graalvm.js, version 22.3.0)
Public methods are unreachable
org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (sendMessage) on mmud.scripting.entities.Person failed due to: Unknown identifier: sendMessage
Fixed by allowing access to public methods.
Also, direct access to properties using the .<name> convention no longer works, so for instance person.room must be replaced with person.getRoom().
Unable to load class named io.jsonwebtoken.impl.DefaultJwtBuilder
UnknownClassException: Unable to load class named [io.jsonwebtoken.impl.DefaultJwtBuilder] from the thread context, current, or system/application ClassLoaders. All heuristics have been exhausted. Class could not be found. Have you remembered to include the jjwt-impl.jar in your runtime classpath?
Key is too small.
io.jsonwebtoken.security.WeakKeyException: The specified key byte array is 64 bits which is not secure enough for any JWT HMAC-SHA algorithm. The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a size >= 256 bits (the key size must be greater than or equal to the hash output size). Consider using the io.jsonwebtoken.security.Keys#secretKeyFor(SignatureAlgorithm) method to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm. See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.
So, yeah, let's make the key bigger, ey?
Now what?
UnknownClassException: Unable to find an implementation for interface io.jsonwebtoken.io.Serializer using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations.
So I didn't want to import an implementation of Gson or Jackson, as Microprofile already has an implementation, (Yasson) and who cares about implementations, eh? Just use the API!
Luckily I found an article on the internet, on how to make your own Serializer and Deserializer.
IllegalStateException: Illegal invocation type for EJB Context : SERVLET_INVOCATION
Servlet.service() for servlet mmud.MudApplication threw exception
java.lang.IllegalStateException: Illegal invocation type for EJB Context : SERVLET_INVOCATION
EJB Circular references
One other thing I encountered, was when removing/replacing the EJB Beans with just @Inject, it is no longer allowed to create circular dependencies.
I didn't mind, as creating circular dependencies is a good thing to entirely avoid, so I was pleased with the error message.
Some moving around, and things worked properly.
References
- [1] Payara Micro - Downloads
- https://www.payara.fish/downloads/payara-platform-community-edition/
- YouTube - IntelliJ IDEA by Netbrains - Migrating from javax to jakarta namespace
- https://youtu.be/mukr2Q_zBm4
- ProgrammerSought - Possible issues with cookies after upgrading to Tomcat 8
- https://www.programmersought.net/article/334704011.html
- GraalVM - Migration Guide from Nashorn to GraalVM JavaScript
- https://www.graalvm.org/22.2/reference-manual/js/NashornMigrationGuide/
- GraalVM - GraalVM JavaScript Implementation
- https://www.graalvm.org/22.3/reference-manual/js/
- Using JJWT with the Jakarta JSON Binding API
- https://blog.bmarwell.de/2022/05/12/using-jjwt-with-jakarta-jsonb.html