Thursday 29 December 2022

Installing GraalVM on Linux using Alternatives

Was looking for how to install GraalVM on Linux using the Alternatives system.

I've already looked at sdk-man on my MacBook PRO, which works fine.

I found the references below.

References

[1] GraalVM - Installation on Linux Platforms
https://www.graalvm.org/22.3/docs/getting-started/linux/
[2] How to Install GraalVM Community Edition on Linux
https://gist.github.com/ricardozanini/fa65e485251913e1467837b1c5a8ed28

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

Thursday 15 December 2022

Jakarta 10

So, yay, my homebrew hobby project is now Java 17 and Jakarta 10 and Microprofile 5 running on Payara Micro 2022.6.1.

I found a good writeup here on what actually Jakarta 10 is.

I also found something about what sort of Maven archetype to use.

I'll write a blog post on what I had to do to upgrade. It wasn't much, but I decided to make some changes unrelated to Jakarta as well.

References

Payara - Jakarta EE 10: Frequently Asked Questions (FAQs)
https://blog.payara.fish/jakarta-ee-10-java-ee-j2ee-frequently-asked-questions
Mastertheboss - A Maven starter for Jakarta EE projects
http://www.mastertheboss.com/java-ee/jakarta-ee/a-maven-starter-for-jakarta-ee-projects
Jakarta - ECLIPSE STARTER FOR JAKARTA EE
https://start.jakarta.ee/

Thursday 8 December 2022

Null handling in Kotlin

Just some notes, as I find myself forgetting the Null-safety features of Kotlin.

Actually, most of the text in this blog is basically already written in [1], so you might want to check there as well.

So, there are three possibiltiies:

// always has a value:
val name: String = "Mrbear"

// sometimes has a value:
val sometimesName: String? = null

// function in java, so no clue what it might be:
val date = LocalDate.now()
// the return value shows up in IntelliJ hints as LocalDate!

You can use if statements, of course, to check for nulls.

Sometimes this doesn't work, because it might be mutable, see [2].

Safe calls can be used, like so for example when person might be null:

val person: Person? = getPerson()
val name: String? = person?.name

Using let:

person?.name?.let{ println(it) }

Default values:

person?.name?:"Mrbear"

Throwing an exception if it's not there:

person?.name?:return null
person?.name?:throw NullPointerException("name is not there")

And then there's !!, for those who know what they're doing:

val now: LocalDate = LocalDate.now()!!

Safe casts if the cast is unsuccesfull:

val aInt: Int? = a as? Int

References

[1] Kotlin - Null safety
https://kotlinlang.org/docs/null-safety.html
[2] Smart cast to 'Type' is impossible, because 'x' is a mutable property that could have been changed by this time
http://randomthoughtsonjavaprogramming.blogspot.com/2022/09/smart-cast-to-type-is-impossible.html

Thursday 1 December 2022

Git 2.23 Adds Switch and Restore Commands

Just a little blurb from me on how to deal with branches with the "switch" command. I was still used to the "checkout" command.

# show all branches
git branch            
  
# create local branch from integration, and switch
git switch -c integration origin/integration
 
# switch back to the master
git switch master  
 
# switch to integration
git switch integration
  
# remove old local branch v0.0.1
git branch -d v0.0.1

References

InfoQ - Git 2.23 Adds Switch and Restore Commands
https://www.infoq.com/news/2019/08/git-2-23-switch-restore/