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/

Friday 25 November 2022

Why do I write this blog?

I came across two Twitter feeds (is that the word?) that mirrored my thoughts on why I write this blog.

Camille Fournier I love how many blog posts I ave that are 80% complete and will probably have to be completely rewritten to actually be complete. Ugh. Writing.
Brian Goetz You've fallen for your own Jedi mind trick, which is that you are writing for others. Really, its about organizing, validating, and sharpening your thinking, and the pot of "likes" at the end of the rainbow is just motivation. In the end, it's mostly for you. So, success!

Thursday 17 November 2022

Java Beans

Back when I was young and innocent, and when I started learning about Beans. Java Beans, Java Bean Conventions, EJB Beans, and all that, I started writing Beans.

So I ended up with a lot of classes called ItemsBean, UsersBean, etc.

I visited this admittedly old code, and immediately thought, "What was I thinking?!?".

A Bean, though most veteran Java Programmers have an incling what it is, does not provide ANY meaning to people outside the Java field. Even for Java people, it doesn't explain a lot.

This is a naming scheme where a lot of people completely got it wrong.

Sure, Beans is cute and funny in combination with Java (coffee, in case I have to spell it out.)

So, paraphrasing Uncle Bob here:

“Don't be cute.”
- Uncle Bob (Robert C. Martin)

Unfortunately, we're kinda stuck with the naming scheme for now. I will just point everyone to the magical file "beans.xml" that is supposed to just exist in a certain place, to make magic happen.

Don't get me wrong. I love Java. But, admittedly, sometimes it's weird...

References

Clean Code - Chapter 2 - Meaningful Names (Robert C. Martin)
http://cleancoder.com/

Thursday 10 November 2022

J-Fall 2022 Writeup

I went to J-Fall 2022 on the 3d of November 2022.

I attended the following sessions:

Pattern Matching: Small Enhancement or Major Feature? - Hanno Embregts Peter Wessels
I enjoyed it immensely.
Design Patterns in the Light of Lambda Expressions - Venkat Subramaniam
It was pretty good. An eyeopener was replacing Autoclosable with Lambdas, that was fun.
Java Next - From Amber to Loom, from Panama to Valhalla - Nicolai Parlog
Great overview of what's coming.
Effective Developer Testing: taking it to the next level - Maurício Aniche
He had some thoughts on tests that I didn't think of before.
Modern and Lightweight Cloud Application Development with Jakarta EE 10 - Ivar Grimstad
I always like to keep up to date with Jakarta.
Keep your dependencies in check - Marit van Dijk
Dependencies, who doesn't hate them.

Things I would like to have attended, but will probably at some point look them up on the YouTube:

Supercharge your Native Image applications 🚀 - Alina Yurenko
Really interested in this, since I'm using GraalVM in my hobby projects. Unfortunately cannot go native at the moment, as lots of it is dynamic.
Threading the needle: multithreading with project Loom - Marcel Ton
Sounded interesting, but I don't do much with threads (which is the safe bet, honestly).

And that's about it.

References

NLJUG - JFall 2022
http://jfall.nl/

Thursday 3 November 2022

Maven: Where did it come from?

Just a small note.

So, I started using StringUtils in my code. It's from the apache.commons.lang3 package.

Then I noticed that I am not actually using this dependency in my pom.

So I'm using a transitive dependency, which is basically a bad habit, as this transitive dependency can suddenly change.

So I need to find which transitive dependency it is, and add it to my local pom, just to be sure.

The Maven dependency in question is:

org.apache.commons:commons-lang3:3.4

A little research and it turns out mvn dependency:tree is very nice.

You get something like:

So, as it turns out commons-lang3 is a transitive dependency of info.blii.wiki:bliki-core:jar:3.1.0

Well, that's a relief, that is.

References

StackOverflow - In Maven 2, how do I know from which dependency comes a transitive dependency?
https://stackoverflow.com/questions/34144/in-maven-2-how-do-i-know-from-which-dependency-comes-a-transitive-dependency

Thursday 27 October 2022

Entity-Relationship Diagrams

I tend to forget what the arrows mean in an entity-relationship diagram.

Example

In the diagram above, there's strong limitations on the values possible in the GuildMember table. Unfortunately, these cannot be expressed in Foreign Key Relationships.

For example:

  • a Person can hold only one rank in a Guild.
  • a Person can only be a GuildMember in a Guild once.

The way to fascilitate this, is to put a unique index on GuildMember regarding the columns GUILDNR and PERSONNR.

References

PlantUML - Entity Relationship Diagram
https://plantuml.com/ie-diagram
LucidChart - Wat is een Entity Relationship Diagram?
https://www.lucidchart.com/pages/nl/wat-is-een-entity-relationship-diagram

Thursday 20 October 2022

Renewing LetsEncrypt SSL Certificates automatically

I found a little script (that I changed a little) to accomodate me in automating SSL certificate renewals.

First of all, the script is executed using a crontab, every 73 days. Why every 73 days? Because that is 5 times a year. Any less, and we (just) go past the 90 days validation period1. (365/4 = 91.25)

The script that basically delegates everything to either other scripts or is dependent of other scripts:

For the script that automatically imports into keystore, see [2].

References

[1] FAQ - Let's Encrypt
https://letsencrypt.org/docs/faq/
[2] LetsEncrypt with Webroot
https://randomthoughtsonjavaprogramming.blogspot.com/2021/11/letsencrypt-with-webroot.html

Thursday 13 October 2022

Why don't record classes follow the Java Bean conventions?

Records are named tuples1.

Notice that the name of the field accessors does not start with get and, therefore, does not conform to the JavaBeans conventions.

We can declare getter methods if we really want to, and let them refer to the record method, but I'd opt not to if possible. We can do it, for a little while, until frameworks catch up to the new code convention, which will be pretty quick.

As Brian Goetz wrote in an online thread2:

“. . . the language has a responsibility to look forward as well as backward, and balance the needs of existing code with the needs of new code. Taking a bad library naming convention and burning it into the language forever would have been the worse choice.”

Builders

We can create builders for Records, if we like. See [3] for instance, but I'm not a fan of automatic code generation.

FreeMarker

The reason I started on this blog, is because there's a plethora of different frameworks/tools/what-have-you available that all center around the Java Beans conventions.

In most cases, records are not a good fit for these, as those frameworks and tools operate on the notion that these are mutable data classes.

But sometimes, there are definitely simple use cases where it would come in handy.

For example with the use of Freemarker4, a templating engine that converts your templates and Java POJOs to nice html pages (or other kinds of pages, there's no limit really).

And these follow the getX() convention.

So, one way would be to create the blasted getter methods in the Record class, but then you're adding boilerplate again, and I feel uncomfortable with that.

Perhaps an idea is to create my own DefaultObjectWrapper5.

So, Freemarker will need some changes to support record classes6. Something like automatically generating a hashmap of all the methods that have no arguments, yet return something... and preferable do not start with get?

References

[1] Data Classes and Sealed Types for Java - Brian Goetz February 2019
https://openjdk.java.net/projects/amber/design-notes/records-and-sealed-classes
[2] ForumPost - Feedback for records: Accessors name() vs. getName()
https://mail.openjdk.java.net/pipermail/amber-dev/2020-August/006414.html
[3] GitHub - RecordBuilder
https://github.com/Randgalt/record-builder
[4] FreeMarker
https://freemarker.apache.org/
[5] FreeMarker - Object wrappers
https://freemarker.apache.org/docs/pgui_datamodel_objectWrapper.html#pgui_datamodel_defaultObjectWrapper
[6] Apache JIRA - FREEMARKER-183
https://issues.apache.org/jira/browse/FREEMARKER-183
JLS - 8.10. Record Classes
https://docs.oracle.com/javase/specs/jls/se16/html/jls-8.html#jls-8.10
JavaMagazine - Records Come to Java
https://blogs.oracle.com/javamagazine/post/records-come-to-java
JavaMagazine - Java records: Serialization, marshaling, and bean state validation
https://blogs.oracle.com/javamagazine/post/diving-into-java-records-serialization-marshaling-and-bean-state-validation

Wednesday 5 October 2022

Paving the on-ramp

Brian Goetz wrote an interesting article1 about what Java could do to lessen the big bulk of context needed for first-timers to write a simple Hello-World program in java. The whole "public static void main(String[] args)" conundrum.

I especially like the following quote (out of context) in de article:

“We will never be able to satisfy programmers’ insatiable appetite for typing fewer keystrokes, and we shouldn’t try, because the goal of programming is to write programs that are easy to read and are clearly correct, not programs that were easy to type.”

So I went and tried to see what's the absolute minimum that still compiles in Java. Of course, the "real" main method is hidden in the Main class. And the class below is in the nameless package.

class MyProgram extends Main {
  void main() {
    println("Hello, world.");
  }
}

The hidden "Main" class looks like this, and can be safely ignored (for now) by new programmers:

/**
 * Hello world!
 */
public class Main {

  public void println(Object x) {
    System.out.println(x);
  }

  public static void main(String[] args) {
    new MyProgram().main();
  }
}

Added benefit, you can simply "run" this class, as its parent class has a "public static void main(String[] args)".

Also, the parent class has a "println" method, so we do not need to import System.out.println as well.

Admittedly, this is all a bit "hacky", where we just hide what we don't want beginning programmers to know in another class. But hey, hiding implementation details that are not important to the current "thing", is one of the core principles of Software Design, isn't it?

Updated 2023/04/17

An example in JDK 21

The reference [1] is implemented in JDK 21, using reference [2].

A simple program can now look like this:

void main() {
    System.out.println("Hello, World!");
}

Updated 2024/02/20

References

[1] Paving the on-ramp (Brian Goetz September 2022)
https://openjdk.org/projects/amber/design-notes/on-ramp
[2] JEP 445: Unnamed Classes and Instance Main Methods (Preview)
https://openjdk.org/jeps/445

Wednesday 7 September 2022

Smart cast to 'Type' is impossible, because 'x' is a mutable property that could have been changed by this time

So I run into this problem quite often lately, most of the time it happens when I have defined a Hibernate Entity in Kotlin, and I wish to use it in my code.

It happens when there's a property that could conceivably be accessed in another thread, cause the two statements, the if-statement and the execution statement to look at different states, and cause a ClassCastException.

The following code demonstrates the message Kotlin provides when writing such a program:

Possible solutions

References

Youtube - Let, Also, Apply, Run, With - Kotlin Scope Functions
https://www.youtube.com/watch?v=Vy-dS2SVoHk

Saturday 30 July 2022

How to UPSERT (INSERT or UPDATE) rows with MERGE in Oracle Database

It's awesome! A colleague of mine uses merge for batch jobs, as it is a great deal quicker.

Unfortunately, performance seems to drop when using it to insert/update individual entries.

Friday 22 July 2022

Falling into a trap: decompiling java code

So my colleague was decompiling some Java code. We're using IntelliJ and then it happens automatically if you happen to select a Class file or something referencing a class file for which there are no sources attached.

And for the life of us, we couldn't understand why that code seemed to work.

Here's the decompiled code snippet.

protected Block createItem(String title, ItemProperties properties) {
  return this.createItem(title, properties);
}

It looked like it was going in circles, as the method was simply calling itself. This is bound to create a StackOverflowError when enough recursion happens.

However, no such problem occurred.

When looking at the source, however, we found it was not similar to the decompiled code:

protected Block createItem(String title, ItemProperties properties) {
  return createItem(title, properties, new SubItem[0]);
}

What turned out to be the issue, an empty array as the varargs at the end of a method is simply removed in the decompiled class version.

Hence our confusion.

It took us several minutes to find this out.

References

Baeldung - Varargs
https://www.baeldung.com/java-varargs

Monday 9 May 2022

Hibernate Entities in Kotlin

Did a little research what kind of Kotlin classes I should use when creating Entities for the Hibernate framework.

Data classes

Just a short blurp on the requirements that data classes need from [1]:

  • the primary constructor needs to have at least one parameter
  • all primary constructor parameters need to be marked as either val or var
  • data classes cannot be abstract, open, sealed or inner

Hibernate Entities

Just a short blurp on the JPA requirements of entities from [2]

  • The entity class must have a public or protected no-argument constructor.
  • The entity class must be a top-level class.
  • The entity class must not be final. No methods or persistent instance variables of the entity class may be final.
  • The persistent state of an entity is represented by instance variables, which may correspond to JavaBean-style properties. An instance variable must be directly accessed only from within the methods of the entity by the entity instance itself. The state of the entity is available to clients only through the entity’s accessor methods (getter/setter methods) or other business methods.

Conclusion

  • we need to define constructor parameters with var - as they should be mutable
  • we need to specify defaults for every constructor parameter, in order to generate the no-argument constructor
  • Hibernate entities should be "open" in Kotlin (so not final) in order for Hibernate to generate proxies. (this is fixed with the allopen addon in Maven)

So data classes are not a good fit, but a "normal" Kotlin class with just a constructor with annotated vars parameters with default values will work just fine.

References

[1] Kotlin - Data Classes
https://kotlinlang.org/docs/data-classes.html
[2] Hiberante ORM 5.5.9. Final User Guide
https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#entity
Medium.com - Defining JPA/Hibernate Entities in Kotlin
https://medium.com/swlh/defining-jpa-hibernate-entities-in-kotlin-1ff8ee470805

Thursday 17 March 2022

Join in SQL

There are lots of nice diagrams explaining the difference between the different joins in SQL.

References

Code Project for those who Code - Visual Representation of SQL Joins
https://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins

Thursday 10 March 2022

For-loops and Streams

So, I'm almost always trying to use Streams in Java.

There are only a handful of times when Streams are not a perfect fit. And most of the times, this is when there is more than one list, and you need to traverse them in sync.

So, we have different ways of doing the same thing. Let's count 'em down.

We'll use the following test setup, using the new Java Money and Currency reference implementation1 2.

Use an ordinary for-loop

This was way back when we didn't have anything better.

And let's be honest, sometimes this is all you need and it is still surprisingly readable.

Use a for-each loop

This is one of my, let us say, less perfect attempts.

It's nice that I've used the for-each construct, but too bad I've hacked an additional index to it to do what I want.

Streams!

So, my colleague at work provided me with this solution, to get away from the whole for-loop (at least, superficially, when you look deep deep down into it, it's just a for loop written as a stream.)

I has to use a reduce in the example, because one of the disadvantages here, is that it's not allowed to reassign variables inside a lambda. Variables should be effectively final.

This disadvantage is not present in for-loops.

But I've also seen some truly horrendous hacks where people started using things like AtomicInteger as a "wrapper" workaround, so they could manipulate the inside of the wrapper inside the lambda.

References

[1] Baeldung - Java Money and the Currency API
https://www.baeldung.com/java-money-and-currency
[2] JSR 354: Money and Currency API
https://jcp.org/en/jsr/detail?id=354
Medium - Experienced Developers, Use These Quirks to Create Better Java Lambdas
https://medium.com/javarevisited/experienced-developers-use-these-quirks-to-create-better-java-lambdas-4ae656148274

Thursday 10 February 2022

Being Glue

Found the following blog on my twitter feeds, and I thought it was important and insightful.

Making a note of it here for future reference.

References

No Idea Blog - Being Glue
https://noidea.dog/glue

Thursday 3 February 2022

Hibernate Proxies and the Visitor Pattern

In short, I had a superclass, and two subclasses, each with their own discriminator.

And for each superclass Hibernate returned, it was very important to verify (because business logic) which one was loaded.

But Hibernate creates proxies of the superclass, so the only way to tell is by making sure the business logic is put into the entity itself.

But what if you cannot do that, or do not want to do that, because dependencies and libraries and all that joy.

It can be done by means of the Visitor pattern1.

Performance

It does take a bit of a performance hit, because the Entity needs to be loaded (during the execution of the Visitor pattern) in order to determine the Discriminator.

In our case this wasn't an issue, as the problem occurred in a many-to-one relationships.

In other cases, the query just loads the specific subclass at once.

References

[1] JBoss Community Archive - Proxy Visitor Pattern
https://developer.jboss.org/docs/DOC-13931

Thursday 27 January 2022

RAID Mirrorring with MDADM

So I posted about my RAID solution plenty of times, but I have some additional information.

So here're my current harddrives:

# lsscsi
[0:0:0:0]    disk    ATA      Samsung SSD 860  1B6Q  /dev/sda 
[4:0:0:0]    disk    ATA      WDC WD4003FZEX-0 1A01  /dev/sdb 
[5:0:0:0]    disk    ATA      WDC WD5000AAKS-0 3B01  /dev/sdc 
[5:0:1:0]    disk    ATA      ST2000DM001-1CH1 CC29  /dev/sdd 
[10:0:0:0]   disk    WD       Ext HDD 1021     2021  /dev/sde 
[11:0:0:0]   disk    WD       Ext HDD 1021     2021  /dev/sdf 

Three harddrives of which I use for my RAID 1. One internal SATA drive and two external USB drives.

/dev/sdd - harddrive 2 TB
./dev/sdd1        2048 3907024895 3907022848  1.8T fd Linux raid autodetect

/dev/sde - external WD harddrive 2 TB
/dev/sde1        2048 3907024895 3907022848  1.8T fd Linux raid autodetect

/dev/sdf - external WD harddrive 2 TB
/dev/sdf1        2048 3907024895 3907022848  1.8T fd Linux raid autodetect

And then I had a problem.

# mdadm --detail /dev/md127
/dev/md127:
           Version : 1.2
     Creation Time : Wed Mar  6 22:16:05 2013
        Raid Level : raid1
        Array Size : 1953380160 (1862.89 GiB 2000.26 GB)
     Used Dev Size : 1953380160 (1862.89 GiB 2000.26 GB)
      Raid Devices : 3
     Total Devices : 2
       Persistence : Superblock is persistent

       Update Time : Sat Nov 27 20:57:13 2021
             State : clean, degraded 
    Active Devices : 2
   Working Devices : 2
    Failed Devices : 0
     Spare Devices : 0

Consistency Policy : resync

              Name : micemouse:0
              UUID : ed4531c4:59c132b2:a6bfc3d1:6da3b928
            Events : 6205

    Number   Major   Minor   RaidDevice State
       2       8       65        0      active sync   /dev/sde1
       -       0        0        1      removed
       3       8       49        2      active sync   /dev/sdd1

For some reason, we're missing /dev/sdf with partition /dev/sdf1.

I am fairly concerned here.

So I reattached the device, and then had to rebuild, etc, etc and it took a few days.

mdadm /dev/md127 -a /dev/sdf1

A better solution would have been to prevent the automounting of raid arrays, until I've attached said external drives.

They are not attached by default.

Turning off auto raid detection

The reason I wanted to turn off automatic raid detection, is because I have two disks that can be attached via USB that are part of the RAID array.

When they are not attached, and the raid is autodetected, this time it started up with one device removed.

And it takes a dickens of a time to reattach the devices to the raid.

Sometimes the configuration is simply not there (autodetects the superblock on drives, and works it out from there).

If it is there, it should be either in:

/etc/mdadm/mdadm.conf
for some Linux, for example Ubuntu.
/etc/mdadm.conf
for some Linux, for example Fedora.

Simply adding the following should be enough:

echo "AUTO -all" >> /etc/mdadm.conf

A better way would probably be to have stopped the raid, and re-assemble it again.

Will try this next time if it happens.

Stopping an array

sudo umount /mnt/md127
sudo mdadm --stop --scan

Starting an Array

For simple setups:

sudo mdadm --assemble --scan

For specific setups:

sudo mdadm --assemble /dev/md127 /dev/sdd /dev/sde /dev/sdf

For machine/harddrive independent setup (he gathers the appropriate components itself):

mdadm --scan --assemble --uuid=a26bf396:31389f83:0df1722d:f404fe4c

And then you can do:

sudo mount /dev/md127 /mnt/raid

References

DigitalOcean - How To Manage RAID Arrays with mdadm on Ubuntu 16.04
https://www.digitalocean.com/community/tutorials/how-to-manage-raid-arrays-with-mdadm-on-ubuntu-16-04
Re: turn off auto assembly
https://www.spinics.net/lists/raid/msg30997.html

Thursday 20 January 2022

Are we really engineers?

So, read a (long) blogpost on this, and thought I'd put it out here.

References

Hillel Wayne - Are we really engineers?
https://www.hillelwayne.com/post/are-we-really-engineers/
Hillel Wayne - We are not special
https://www.hillelwayne.com/post/we-are-not-special/
Hillel Wayne - What engineering can teach (and learn from) us
https://www.hillelwayne.com/post/what-we-can-learn/