Thursday 22 December 2022

Upgrading to Jakarta 10

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 Persistenceimport javax.persistence.*import jakarta.persistence.*
Jakarta Securityimport 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 CDIimport 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 Servletsimport javax.servlet.http.*import jakarta.servlet.http.*
import javax.servlet.*import jakarta.servlet.*
import javax.servlet.annotation.*;import jakarta.servlet.annotation.*;
Jakarta Validationimport javax.validation.constraints.*;import jakarta.validation.constraints.*;
Jakarta Restimport 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 bindimport javax.xml.bind.annotation.*;import jakarta.xml.bind.annotation.*;
Jakarta Websocketsimport 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

No comments:

Post a Comment