Booting from NVMe on ASRock FM2A88X Pro+

TL;DR

The ASRock FM2A88X Pro+ mainboard (from 2015) supports booting an operating system from NVMe M.2 SSD connected through a PCIe-to-M.2 adapter. This requires no hacking. Only the latest available BIOS/UEFI update (from 2016) and changing couple of BIOS/UEFI settings are needed. I’ve verified it myself using Linux Mint 22 installation in UEFI mode.

The whole story

Why using NVMe M.2 SSD?

Because it is a lot faster than SSD using SATA interface. In my setup NVMe SSD running in a PC system supporting PCIe 2.0 was more than 3x faster than SSDs connected through SATA 3 (read time).

Why M.2 interface? Just because NVMe SSD variants using M.2 are much less expensive and more common than variants with PCIe connector. Moreover M.2 variants are smaller and they seem to be more future proof, as modern motherboards usually provide on-board M.2 slots. You can put it into a newer laptop as well.

How to connect NVMe M.2 SSD to an older mainboard?

The ASRock FM2A88X Pro+ mainboard is quite old now (it’s at least 10 years old now, it has FM2+ socket) and, as most such mainboards, it doesn’t have M.2 slot (rare exception here), so there is no direct means of connecting NVMe SSD with M.2 interface, like Goodram PX600 SSD PCIe 4.0 NVMe (M key):

However almost all mainboards have a PCIe x16 slot (a longer PCIe slot) or even two such slots, as in case of the ASRock FM2A88X Pro+. And there are inexpensive PCIe-to-M.2 adapters on the market like Ugreen PCIe Gen4 x16 to M.2 Expansion Card (CM465):


On the product web page there are even claims this adapter fits in shorter PCIe slots (x8 and x4) as well, but it won’t fit into PCIe x1 slot.

So the way to go is to get both: NVMe M.2 SSD and such PCIe adapter. You connect NVMe to the adapter and then you put the adapter into the PCIe slot on your mainboard.

That way you should get NVMe SSD available in your PC no matter what operating system you have – no drivers are needed. Please note that I can confirm this only for the abovementioned ASRock FM2A88X Pro+ mainboard with BIOS/UEFI v3.40. This is the latest available version (v3.40) of BIOS/UEFI update for this mainboard and it was published in January 2016.

How to put things together?

First you install NVMe SSD into the PCIe adapter. You can see the procedure on this YouTube video from “ExplainingComputers”. Then you put the PCIe adapter into a free PCIe slot on your mainboard.

How to boot from NVMe M.2 SSD?

To be able to boot from a NVMe SSD you need to open BIOS/UEFI settings by pressing F2 key or DELETE key just after a computer is turned on, then go to the “Boot” section:

Now go to “Hard Drive BBS Priorities” menu:

Now you can move NVMe SDD to the top of the list there. In my case NVMe SSD was presented as “SSDPR-PX600-250-80”. Then go back (ESC button) to the previous screen, click on “Boot Option #1” and select NVMe SDD here.

Installing an operating system

Now it’s time to install some operating system on the newly installed NVMe SSD. I used Linux Mint 22 for this. After the OS installation it’s time to try booting from NVMe SSD. My first approach failed.

So I tried reinstalling Linux Mint 22, but this time when booting from the installation media (a USB pendrive) I’ve selected a menu item with “UEFI” mentioned in the BIOS/UEFI boot menu (on this mainboard it appears after pressing [F11] for many times just after turning on the computer):

It made a difference! Most likely the boot menu item without the “UEFI” word activates booting in a BIOS-compatibility mode (CSM?) and the boot menu item starting with “UEFI” activates modern UEFI functionality. It looks that when Linux Mint is installed in UEFI mode then UEFI mode is enabled later each time we’re booting from such installation.

I restarted the computer after installation completed and this time it booted correctly from NVMe SSD! 😎

Performance

Below I’ve collected my disk read benchmark results for following data storage devices:

  1. HDD with 7200 rpm, SATA 3, EXT3 file system
  2. SSD on SATA 3, EXT4 file system
  3. SSD on SATA 3, F2FS file system
  4. NVMe SSD on PCIe 2.0 (M.2 interface), EXT4 file system

I have used the GNOME Disks program that is present in Linux Mint 22 Cinnamon for benchmarking.

HDD with 7200 rpm, SATA 3, EXT2 file system

My old hard disk drive, Seagate Barracuda (7200 rpm) using EXT3 (or maybe EXT2) filesystem got a result of little more than 100 MB/s of average disk read and mean access time of almost 10 ms.

SSD on SATA 3, EXT4 file system

My older SATA SSD, Intel SSD 300 Series 60 GB using EXT4 filesystem got a result of 464 MB/s of average disk read and mean access time of 0,2 ms.

SSD on SATA 3, F2FS file system

My newer SATA SSD, Western Digital 500 GB using F2FS filesystem got a result of 496 MB/s of average disk read and 0,19 ms of a mean access time.

NVMe SSD on PCIe 2.0 (M.2 interface), EXT4 file system

Finally the most interesting result! Goodram PX600 SSD PCIe 4.0 NVMe connected through Ugreen PCIe Gen4 x16 to M.2 Expansion Card and using EXT4 filesystem got a result of 1500 MB/s of average disk read speed and just 0,05 ms of a mean access time. Incredible! It’s 15x faster than a classing hard disk drive and more than 3x times faster than both SSDs connected through SATA cables!

Summary

In my opinion adding an NVMe SSD drive is the best way to speed up your computer, assuming your operating system will be there and your applications are there. NVMe SDD drives with M.2 connector are quite affordable (the Goodram product I’ve used is available now for some 35 EUR). If your motherboard doesn’t have an M.2 socket you can buy a PCIe adapter having such a socket and these are quite cheap (like 8 EUR). In this article I’ve presented how a PC from 2015 can be equipped with such parts, prologing its lifetime, while spending reasonable money.

Posted in BIOS, hardware, Linux | Tagged , , , , | Leave a comment

Don’t use @UtilityClass from Lombok

In short

Never use @UtilityClass annotation from Lombok in long term Java projects developed by many programmers (as is usually the case for an enterprise software). It’s just not worth as from time to time it will cause hours wasted on analysis why the project compilation started failing mysteriously with plenty of “cannot find symbol” errors like if Lombok was not on the classpath.

Full story

In case of enterprise Java projects where Lombok is used there is always a chance that someone will use @UtilityClass annotation. At first glance it doesn’t look dangerous, so it may easily pass a code review, so it stays. Later even some programmers may start to think this a “proper way” of making a utility Java class. So it sticks.

In fact using @UtilityClass annotation is a pure mannerism. It gives nothing handy. I’m not getting why people would like to make a Java class with public static methods by adding this annotation instead of using static keyword? Why to limit all possible use cases of such a utility class by artificially blocking ability to create an object of such a class (the annotation is causing a generation of a private default constructor)? But mannerism, no matter how irritating, is not the reason I’m strongly against @UtilityClass annotation.

Let’s have an example:

@UtilityClass
public class MathUtil {
  public int squared(int x) {
    return x * x;
  }
}

Somewhere else in the source code this method is used like below:

int result = MathUtil.squared(5);

And everything works just fine. But time passes, the project grows, teams are changing. At some point there will be a need to use a static import, which is an obvious way to make expressions shorter so they fit within a reasonable line width:

static import some.company.MathUtil.squared;

...

int result = squared(5);

Usually one doesn’t compile a project after introducing suth a pitty thing like a static import. So you continue coding until you get a desired part of your program completed. Then you compile.

And suddenly it appears there are plenty of “cannot find a symbol” compile errors! All in places where your code is depending on Lombok. A complete mystery! You start checking everything, you are commeting parts of your code trying to pin point the cause which seems ridiculous. Your colleagues have no advices, hours are passing…

Explanation

It turns out to be a permanent bug in Lombok handling of the @UtilityClass annotation. You cannot use a static import for a static method created with help of this annotation. It’s even mentioned in Lombok’s documentation, however it’s not explicit enough:

[…] currently non-star static imports cannot be used to import stuff from @UtilityClasses; don’t static import, or use star imports.

And another:

Current status: negative – Currently we feel this feature cannot move out of experimental status due to fundamental issues with non-star static imports.

See for yourself: https://projectlombok.org/features/experimental/UtilityClass

WTF?!? They should make a big, bold explicit statment saying this feature is buggy! Shame on you, Lombok developers!

There is even an issue reported in 2017 on GitHub: https://github.com/projectlombok/lombok/issues/1382 – and it’s still not fixed (as of 2025)… 🤦

So be a good team player as a programmer and don’t use Lombok’s @UtilityClass annotation. 🙂

Solution

Lombok’s documentation has a good advice for this annotation – just add following to Lombok configuration files in your projects:

lombok.utilityClass.flagUsage=error

And the abovementioned utility class should look just like this:

public class MathUtil {
  public static int squared(int x) {
    return x * x;
  }
}
Posted in Java | Tagged , , | Leave a comment

Running Java application as a Linux service

This post is a continuation of my previous one, “Turning a laptop into a Java app server in 2025”. In this part we’re going to setup a Java application as a service in Linux. We will use Systemd for this so a service will be automatically started on system boot and it will be run again if it crashed. All this was verified by myself on Ubuntu Server 24.04.1 LTS.

1. Service account

As we don’t want to run a service with root privileges we need to create a separate user account for it. And a separate user group. This way we start with no risk of some unwanted privileges being included. Let’s call the group “apps”:

sudo groupadd apps

Now let’s create a user. I will use “APPUSER” word below, so it’s easier to replace it by your user name of choice:

sudo useradd -c "Some service account" -d /home/APPUSER -m -g apps -s /bin/false APPUSER

There are couple things to notice. We’re giving a description (“Some service account” – you can change it to whatever), so it will be helpful in the future to figure out what it is. We specified the home directory (/home/APPUSER) and it will be created thanks to -m switch. We assigned the account to “apps” group. And we established program “false” as a shell. Thanks to this no one can log in using this account.

2. Java application

The Java application you’re going to use as a service should be prepared as a fat-JAR (uber-JAR). That way all Java dependencies will be provided. This is a standard when using Spring Boot framework, more precisely when using it with spring-boot-maven-plugin.

Just copy the application to the server machine into the home folder of APPUSER. Can be done with scp from another machine in the local network. First step is to copy the JAR file to your user home directory (this command is executed on your another computer, not on the server):

scp myapp.jar myuser@mylinuxserver:~/

Then login by SSH on the server and use sudo to move the file to the APPUSER home directory:

sudo cp myapp.jar /home/APPUSER/

We don’t need to change owner and group of the copied file, as all what is needed is a read-access and it is present by default (file belongs to root with others able to read it).

3. Systemd unit

This is the final step: creation of a Systemd unit for our service. This is defined by a file in the /etc/systemd/system directory. Its filename must have .service extension and its name will be recognized by Linux (or rather by Systemd) as a service name. Example: my-java-app.service.

Below is my compilation of different examples that I’ve found with my studies of Systemd documentation, to create a Systemd unit of type service for a Java application with following features:

  • It runs with privileges of a given user and group.
  • It is stopped by sending SIGTERM signal and then waiting, so allowing a graceful shutdown of a Java application (timeout 15 means it waits up to 15 seconds before SIGKILL signal is sent).
  • Return code from JVM stopped by SIGTERM (143) is recognized as a success. See explanation.
  • If application crashes (JVM crash?) then it is started again after 5 seconds
  • If there is a problem with starting the application it stops restarting after 5 times within 5 minutes.

This is a correct Systemd unit service definition for a Java service working with Systemd v255:

[Unit]
Description=My Java service
After=syslog.target network.target
StartLimitIntervalSec=5m
StartLimitBurst=5

[Service]
Type=exec
User=APPUSER
Group=apps
Environment="SOME_VARIABLE=foo"
Environment="OTHER_VARIABLE=bar"
WorkingDirectory=/home/APPUSER
ExecStart=<<FULL-PATH-TO-JAVA-HOME>>/bin/java -jar javaapp.jar
ExecStop=kill -TERM $MAINPID
ExecStop=timeout 15 tail --pid=${MAINPID} -f /dev/null
SuccessExitStatus=143
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

NOTE: replace <<FULL-PATH-TO-JAVA-HOME>> by a full path to a desired Java home directory. Unfortunately environment variable expansion doesn’t work here. To check what is Java home folder you can run this command:

java -XshowSettings:properties -version |& grep java.home

The above content should be put into a file placed in a system folder /etc/systemd/system, so basically you can edit it using this command:

sudo nano /etc/systemd/system/my-java-app.service

After editing and saving the file run following commands:

sudo systemctl daemon-reload
sudo systemctl enable my-java-app

And now you can run it and see its status:

sudo systemctl start my-java-app
systemctl status my-java-app

The output should look similar to this:

● my-service.service - My Java service
     Loaded: loaded (/etc/systemd/system/my-service.service; enabled; preset: enabled)
     Active: active (running) since Tue 2025-01-14 09:05:30 UTC; 20s ago
   Main PID: 2588 (java)
      Tasks: 34 (limit: 3442)
     Memory: 140.2M (peak: 140.4M)
        CPU: 21.478s
     CGroup: /system.slice/my-service.service
             └─2588 /usr/lib/jvm/java-21-openjdk-amd64/bin/java -jar javaapp.jar

It’s done! 🙂

Logs

Output of the service is automatically recorded by Linux (Systemd?) logging functionality. So you can see these logs by using journalctl, as for any other Linux logs:

journalctl -o cat -u my-service --since "1h ago"
journalctl -o cat -u my-service -f

The first form shows logs of your service from last 1 hour, but lines are cut to fit (no line wrapping). The second form shows some last logs with line wrapping and it continuously prints new log entries as they are appended.

Please notice the “-o cat” part, as it is responsible for cutting off extra information added to log messages by Systemd (another timestamp, hostname, program name, PID). Using journalctl is another story. Here is some introduction.

Posted in Java, Linux | Tagged , , , | 2 Comments

Turning a laptop into a Java app server in 2025

Disclaimer: This is my own checklist for making a hobbyist private server out of an old laptop. It is supposed to run in a local network and behind a firewall. Its purpose is to run a single Java service, hence the “Java app server” in the title. This is not related at all to full-blown Java application servers like JBoss or Glassfish. And this is not about containerized workloads.

In 2016 I’ve published an article “Turning a laptop into a Java application server in 10 steps”. It was almost 9 years ago! Things changed a little bit since then. In particular:

  • No more Apache Tomcat nor any other servlet container nor application server. It’s an era of standalone Java services/microservices now!Note: it’s not bad to use a servlet container or Java application server, if you really need it.
  • No more relational database like PostgreSQL. Nowadays we choose a right tool for a given job. We can persist data in many ways and a relational database is not the only proper solution.

Thanks to this there are fewer steps, but I’ve added some new steps as well. As a result we have now:

  1. BIOS
  2. Linux
  3. Network
  4. SSH
  5. Running with the lid closed
  6. Screensaver
  7. Updating the system
  8. Java

1. BIOS

Reboot your machine and go to BIOS. Look for power management settings and turn on the option to start the machine after a power failure, if such option is present. Turn off unused devices, like Bluetooth adapter, COM and LPT ports, etc. Set minimal available amount of memory for a video card, as Linux sever is going to work in a text mode only.

2. Linux

We’re going to use Linux as an operating system as this is most likely the best operating system in the world and it’s free. There are more reasons to choose Linux:

  • There are many actively maintained Linux distributions, so we will have updates, including security updates.
  • Most likely Linux has the best hardware support comparing to other free operating systems.
  • It will support latest LTS version of Java. This is not obvious for other free operating systems.

Which Linux distribution?

The advice is simple: use the one which you know best. If you’re new in the Linux world, use Ubuntu Server. If you’re familiar with Linux Mint or Ubuntu, use Ubuntu Server. Next advice: choose long term support release (LTS). My Linux of choice is Ubuntu Server 24.04.1 LTS (current LTS release for Ubuntu Server).

UPDATE: After ca. 3 months of running my home server under Ubuntu Server 24.04.1 LTS without bigger issues it turned out the CPU got under almost a constant load of 3% to 8% by kworker…-events process. As I could not find a quick solution for this I can no longer recommend using Ubuntu Server 24.04.1 LTS. There is too much happening under the hood and without explicit setup. After this experience I plan to try Debian or Alpine Linux.

Linux installation hints

Install your selected Linux distribution.

  • Create at least 3 partitions if you have one disk in the system: 1st partition for the root filesystem (Linux system) – 20 GB should be enough, 2nd partition for the swap space – as many GBs as you have RAM in the system, 3rd partition for home folders (user data) – use all the left disk space. That way you will be able to reinstall the whole system and keep user data.
  • Select ext4 filesystem for system partition and user data partition.
  • Check mark to install SSH server during Linux installation.
  • Give the meaningful host name when asked during Linux installation. Thanks to Avahi mDNS you will be able to use this host name in your local network like a domain name.

3. Network

If you connect your machine to the router by a cable, then you’re done. If you want to connect to a WiFi network, then follow this article: “How to connect Ubuntu Server to WiFi?”

4. SSH

Check the status of the SSH service with this command:

service ssh status

If it is not installed, install it now. It should be enough to use this command:

sudo apt install openssh-server

If the SSH service is not enable, use the below command to enable it (this will make it starting automatically on system start):

sudo systemctl enable ssh

If the SSH service is not started, start it with this command:

sudo systemctl start ssh

After all this the output of the service ssh status command should look like this:

● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2025-01-11 16:22:30 CET; 6h ago
       Docs: man:sshd(8)
             man:sshd_config(5)
    Process: 892 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
   Main PID: 897 (sshd)
      Tasks: 1 (limit: 8580)
     Memory: 1.5M
     CGroup: /system.slice/ssh.service
             └─897 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

It is important to see “Loaded: loaded”, “enabled” and “Active: active (running)”.

5. Running with the lid closed

By default closing the laptop lid is handled by Linux by suspending the system. We want it to run even when the lid is closed. So run the command:

sudo nano /etc/systemd/logind.conf

Then edit the file so it has following line uncommented:

HandleLidSwitch=ignore

Then exit the nano editor and run this command:

sudo service systemd-logind restart

6. Screensaver

It seems that by default Ubuntu Server is not blanking the screen when no keys pressed for some longer time. As a result the screen backlight is always on. Even when the lid is closed. So to save some energy let’s configure a simple screensaver using setterm that will be executed as a Systemd Unit on start:

sudo nano /etc/systemd/system/enable-console-blanking.service

Now in the editor paste this text:

[Unit]
Description=Enable console blanking

[Service]
Type=oneshot
Environment=TERM=linux
StandardOutput=tty
TTYPath=/dev/console
ExecStart=/usr/bin/setterm --blank 1

[Install]
WantedBy=multi-user.target

Save the file and close the editor. Then run:

sudo systemctl daemon-reload
sudo systemctl enable console-blanking
sudo systemctl start console-blanking
systemctl status console-blanking

You should see output like this after the last command:

○ console-blanking.service - Enable console blanking
     Loaded: loaded (/etc/systemd/system/console-blanking.service; enabled; preset: enabled)
     Active: inactive (dead) since Sat 2025-01-11 23:29:32 UTC; 2s ago
    Process: 1876 ExecStart=/usr/bin/setterm -blank 1 (code=exited, status=0/SUCCESS)
   Main PID: 1876 (code=exited, status=0/SUCCESS)
        CPU: 84ms

Jan 11 23:29:32 ubuntu systemd[1]: Starting console-blanking.service - Enable console blanking...
Jan 11 23:29:32 ubuntu systemd[1]: console-blanking.service: Deactivated successfully.
Jan 11 23:29:32 ubuntu systemd[1]: Finished console-blanking.service - Enable console blanking.

This is OK, that there is “Active: inactive (dead)”, as this is a one-shot service. Log lines show that it was run and that’s it. Your laptop screen should turn black after 1 minute of no keyboard activity after this.

7. Updating the system

This should be done from time to time to update your system components to latest versions:

sudo apt update -y &amp;amp;&amp;amp; sudo apt upgrade -y

If one of the last lines printed is “Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting.” then you should reboot:

sudo reboot

8. Java

Run following commands to install Java 21 runtime environment (JRE) headless variant from OpenJDK project:

sudo apt install openjdk-21-jre-headless

Please note, that I’ve selected current long term support version of Java. This is my recommendation.

Verification

At this point let’s reboot the system and close the lid after it started (login screen presented). Then test logging by ssh to your laptop-server from some other computer in the local network. Execute following command to verify Java:

java -version

The output should be something like this:

openjdk version "21.0.5" 2024-10-15
OpenJDK Runtime Environment (build 21.0.5+11-Ubuntu-1ubuntu124.04)
OpenJDK 64-Bit Server VM (build 21.0.5+11-Ubuntu-1ubuntu124.04, mixed mode, sharing)

Now we are ready to the next part: running Java application as a Linux service! 🙂

Posted in hardware, Java, Linux | Tagged , , , , , | Leave a comment

How to enable all Java recommended warnings in a Maven project?

By default Maven runs Java compilation without showing warnings that can be detected by javac built-in linter. But according to Java 21 documentation:

“In this release, enabling all available warnings is recommended.”

So this is a good idea to see these warnings. The javac option to enable all available warnings is -Xlint. But we need a little bit more in our pom.xml, more precisely we need to turn on showing warnings in Maven as well:


<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
        <configuration>
          <compilerArgs>
            <arg>-Xlint</arg>
          </compilerArgs>
          <showWarnings>true</showWarnings>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

Thanks to this we now can see warnings like below, which are not visible at all by default in a Maven project:

[WARNING] /home/.../k_tomaszewski/eternaldb/Database.java:[71,62] possible 'this' escape before subclass is fully initialized

Please note that these warnings will be presented for a given Java file only when it is actually compiled. So to see all warnings for sure just clean your project before compiling:


mvn clean compile

Voila! 🙂

P.S.

I think this configuration should be used with every Java & Maven project.

Posted in Java | Tagged , , | Leave a comment

How to use Git at home?

In most software development companies/workplaces Git is used as a version control system with a central repository (trust me, I’m a software developer!). Moreover, very popular web sites like GitHub popularize using Git the same way. This is the very same way the one used by older version control systems like CVS or SVN. However Git can do more, as this is a distributed version control system. In particular this means you can use it without a dedicated server!

Imagine following scenario: you have a couple of computers at home (pretty common for programmers), one time you work on a laptop, another time you work on a desktop computer – basically you use them all for whatever reasons. All these machines are running Linux. There is a local network and all these machines are connected to this network (cable or WiFi). You have working on one of these computers for some time on a project (software development project, for example) and you have created a local Git repository. It can be a cloned repository or created with a command like git init.

Problem

How you can copy your project to another computer at home? How you can propagate work increments made on this project to other computers at home? How to synchronize them later?

Solution

The solution is to use Git and SSH. Having this you need no central repository like GitHub to copy your project to your other computers at home.

First you need to have SSH service installed and running on a computer where you have created this project. Use following command (for sure correct for Linux Mint) to check it:

service ssh status

It should show you a multi-line output saying the SSH service is active and running. Otherwise most likely you need to install SSH server.

Let’s assume that following is true for a computer where you have your project now:

  • The hostname is: host1
  • Your username is: user1
  • Your project root path is: /home/user1/projects/project1

Then, on a computer where you want to copy your project to, you need to open a console (terminal), go to a directory, which is going to be a root directory for your projects, and execute the command:

git clone ssh://user1@host1/~/projects/project1

Git will ask you for a password. You need to enter a password for “user1” on the first computer.

That’s all! Now you can do things like git pull or git push (or other) to synchronize. Voila! 🙂

BTW, in the above command following protocols have the same meaning: “ssh”, “ssh+git”, “git+ssh”. See for yourself.

P.S.

You might have noticed I didn’t use IP address like in many other examples in the Internet. This is thanks to a service installed by default on Linux Mint, and probably some other Linux distributions, called Avahi, or more precisely thanks to Avahi nss-mdns. In short words, this little thing works like a DNS in your local network, so you can use host names instead of IP addresses. To see if Avahi is working on your computer, execute following command:

service avahi-daemon status

Posted in Linux | Tagged , , , , | Leave a comment

Custom ZonedDateTime formatting with Jackson

In the world of enterprise systems developed in Java the Jackson library is the most popular solution for JSON serialization. In Java the standard class for representing a full date (with time zone) and time is ZonedDateTime. In this article I’m referring to Jackson v2.17 and I’m going to present how to customize JSON format for ZonedDateTime without use of annotations and without implementing own serialization and deserialization classes.

How to make Jackson support ZonedDateTime?

Let’s start with a ZonedDateTime:


System.out.println(ZonedDateTime.now());

This prints:
2024-12-16T13:27:41.057351192+01:00[Europe/Warsaw]
There is date, time with nanoseconds precision, time offset and time zone name.

It turns out Jackson doesn’t handle “new” Java time-related classes, like ZonedDateTime, by default. One needs to register JavaTimeModule:


ObjectMapper om1 = new ObjectMapper()
    .registerModule(new JavaTimeModule());

System.out.println(om1.writeValueAsString(ZonedDateTime.now()));

It will produce a JSON looking like this:
1734349856.258791666
Quite compact (20 characters) but this numeric format is kind of against human readability JSON should have.

How to make the format human readable?

To make serialization of ZonedDateTime values being human readable we need some Jackson’s ObjectMapper configuration:


ObjectMapper om2 = new ObjectMapper()
    .registerModule(new JavaTimeModule())
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

System.out.println(om2.writeValueAsString(ZonedDateTime.now()));

Now the output looks like this:
"2024-12-16T13:05:33.48182159+01:00"
It’s nice, but it misses a time zone name (it has a time offset) and its length is now 34 characters (not counting quote characters). If someone needs to have a time zone serialized as well (not only time offset as above) then we need more ObjectMapper configuration:

ObjectMapper om3 = new ObjectMapper()
    .registerModule(new JavaTimeModule())
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
    .enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);

System.out.println(om3.writeValueAsString(ZonedDateTime.now()));

Now the output is:
"2024-12-16T13:13:30.842589636+01:00[Europe/Warsaw]"
This is 50 characters (not counting quote characters) for some time zones! It’s quite human readable, but Jackson will not preserve this time zone in deserialization. Instead by default UTC time zone is used. So to have time zone deserialized we add another piece of configuration:

ObjectMapper om4 = new ObjectMapper()
    .registerModule(new JavaTimeModule())
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
    .enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID)
    .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

String json = om4.writeValueAsString(ZonedDateTime.now());
System.out.println(json);
System.out.println(om4.readValue(json, ZonedDateTime.class));

The above code shows that date, time and time zone all were fully reconstructed on deserialization:
"2024-12-16T13:27:41.057351192+01:00[Europe/Warsaw]"
2024-12-16T13:27:41.057351192+01:00[Europe/Warsaw]
So we are able to serialize ZonedDateTime object into JSON using a human readable variant and we are able to deserialize it while preserving all attributes of ZonedDateTime. This is great!

How to make it shorter?

To make the JSON format of ZonedDateTime shorter and still human readable we need to use a custom serializer/deserializer. Here I will show you how to do it without implementing custom Jackson serializer and deserializer classes. We will use only classes provided by JDK and Jackson library. But let’s add just one assumption: we don’t need full nanoseconds resolution (unless you work for CERN and measure time for events in LHC), so we will leave 2 digits of a second fraction:


var pattern = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss.SS z");
var timeModule = new JavaTimeModule()
    .addSerializer(new ZonedDateTimeSerializer(pattern))
    .addDeserializer(ZonedDateTime.class,
        new InstantDeserializer(InstantDeserializer.ZONED_DATE_TIME, pattern) {});
ObjectMapper om5 = new ObjectMapper().registerModule(timeModule)
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
    .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

String json = om5.writeValueAsString(ZonedDateTime.now());
System.out.println(json);
System.out.println(om5.readValue(json, ZonedDateTime.class));

Please note that the above configuration intentionally doesn’t set the feature WRITE_DATES_WITH_ZONE_ID. Now the output is as follows:
"20241216 13:27:41.05 CET"
2024-12-16T13:27:41.050+01:00[Europe/Warsaw]
It’s human readable and it’s relatively short – only 24 characters (not counting quotes)! You can adjust it easily to your needs by just changing DateTimeFormatter pattern.

Please note that this approach doesn’t use annotations and thanks to this one can affect serialization of all classes done by given instance of an ObjectMapper. Voila! 🙂

Posted in Java | Tagged , , , | Leave a comment

Proper Linux driver for AMD Kaveri integrated GPU

Last time I was installing Linux Mint on a PC having AMD A8-7600 CPU, which despite its age (released in 2014) is quite capable and more than enough for average needs and playing some older video games like GRID Autosport (2014) or LEGO Jurassic World (2015) – both games working well on Linux using Steam/Proton.

The AMD A8-7600 is APU as it has an integrated GPU, which is Radeon R7 series. It belongs to the “Steamroller” microarchitecture and uses the “Kaveri” codename. There are more AMD APUs belonging to Steamroller/Kaveri family (all using FM2+ socket) and this article should be helpful if you have any of them.

Problem

With some older Linux kernels the driver for this integrated Radeon R7 series GPU seems to not make use of a full hardware acceleration, which is revealed by the command glxinfo -B. In case of Linux Mint 21.3, which has kernel 5.15, the output of this command included line:

Accelerated: no

For sure I had problems running above-mentioned games.

There is a very simple solution for this: just install Linux Mint 22 which has kernel 6.8. Then the output of the command glxinfo -B contains following lines:

Accelerated: yes
OpenGL renderer string: KAVERI (radeonsi, , LLVM 17.0.6, ...
OpenGL core profile version string: 4.5 ...

This looks better and I was able to run above-mentioned games, but I could not run the DiRT Rally video game. Please note that the Radeon R7 series GPU supports OpenGL 4.6 but above we see only OpenGL 4.5.

Information found in the Internet suggested that I should use amdgpu driver instead of radeon driver. To see which driver is used check the command inxi -G. On a fresh install of Linux Mint 21.3 or 22 the first lines printed by this command are:

Graphics:
  Device-1: AMD Kaveri [Radeon R7 Graphics] driver: radeon v: kernel

This means we’re using radeon driver. Linux selected this driver automatically (some defaults?), but we can change it.

Solution: amdgpu

The solution is to use a newer Linux kernel, like kernel 6.8 in Linux Mint 22, and to configure Linux to use amdgpu driver which is perfect for Radeon R7 Graphics. All below configuration steps are meant to add new Linux kernel parameters:

  1. Execute command sudo nano /etc/default/grub.
  2. Find line starting with GRUB_CMDLINE_LINUX_DEFAULT.
  3. Add radeon.cik_support=0 amdgpu.cik_support=1 radeon.si_support=0 amdgpu.si_support=1 iommu=pt inside quotation marks so it looks like this:
    GRUB_CMDLINE_LINUX_DEFAULT="quiet splash radeon.cik_support=0 amdgpu.cik_support=1 radeon.si_support=0 amdgpu.si_support=1 iommu=pt"

    NOTE: iommu=pt is additional solution to some problems with amdgpu driver. It’s an optional part of this solution.

  4. Save the file and exit the Nano editor.
  5. Execute command sudo update-grub
  6. Reboot

Now we can verify. First using command glxinfo -B. The output contains lines:

Accelerated: yes
OpenGL renderer string: AMD Radeon R7 Graphics (radeonsi, kaveri, LLVM 17.0.6, ...
OpenGL core profile version string: 4.6 ...

So we should be able to make use of OpenGL 4.6. The output of the command inxi -G now starts with:

Graphics:
  Device-1: AMD Kaveri [Radeon R7 Graphics] driver: amdgpu v: kernel

And there was some information about Vulkan API as well – which is good (some sources claim amdgpu driver is supporting Vulcan contrary to radeon driver). Anyway, after these steps I was able to run DiRT Rally video game. Of course video details needed to be “medium” or lower, but it was playable. Voila! 🙂

P.S.

Here are some extra information about amdgpu: https://wiki.archlinux.org/title/AMDGPU

Posted in hardware, Linux | Tagged , , , , | Leave a comment

How to connect Ubuntu Server to WiFi?

Ubuntu Server is a Linux variant dedicated for server and servers are usually connected to a computer network by a cable using Ethernet controller/card. This is quite obvious and reasonable, but not in case of a home server (a hobby system or some home assistant installation). Here I’m sharing my experience with Ubuntu Server 24.04.1 LTS on a computer having wireless network controller.

NOTE: Ubuntu Server by default installs without a GUI (desktop environment), so we’re going to use commands only.

Preliminary check

First, let’s make sure a wireless network controller is recognized by the system. You can use some of below commands to check it:


lspci -v
ls -l /sys/class/net
ip a

In proper case all of these commands should reveal you a presence of a wireless network controller. The first command has an output that should contain some product names that you may recognize, like:
Network controller: Broadcom Inc. and subsidiaries BCM4321 802.11a/b/g/n (rev 03) Subsystem: Dell Wireless 1500 Draft 802.11n WLAN Mini-card

The second command has the simplest output. It will show pseudo-files representing network interfaces. Wireless network interface should a file starting with “w”, like wlan0.

The last command is showing status information for network interfaces in the system. Our goal is to see here the wireless interface with status “UP” together with “LOWER_UP” and state “UP”, like:
...
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
...

But now you see no “LOWER_UP” and the “DOWN” state here…

NOTE: It’s out of scope of this article to show how to solve problems at this stage. For example for abovementioned Broadcom BCM4321 wireless controller one needs to install proprietary firmware (sudo apt-get install firmware-b43-installer and reboot). For the rest of this article I assume a wireless controller is supported and the only problem is to make it connected to WiFi, so to change its state from “DOWN” to “UP” and to have “LOWER_UP” status presented.

WiFi configuration

It turns out current Ubuntu Server is using a solution called “Netplan”. Thanks to this one configures a WiFi access in a YAML file that resides in the directory /etc/netplan. In case of a fresh install of Ubuntu Server 24.04.1 LTS the file was /etc/netplan/50-cloud-init.yaml. Edit this file using command:


sudo nano /etc/netplan/50-cloud-init.yaml

You need to add a key “wifis” just below the key “network”. Just under “wifis” you add a key with your wireless interface name and under this key you put the relevant setup. Example:

network:
  ethernets:
    eth0:
      dhcp4: true
  wifis:
    wlan0:
      optional: true
      dhcp4: true
      dhcp6: true
      access-points:
        "WIFI_SSID":
          password: "WIFI_PASSWORD"

I recommend using “optional: true“, like above, to speed up booting the system. More “Netplan” configuration examples here.

If your WiFi network is hidden you just need to add “hidden: true” on the same level as the “password” key.

Save the file, exit from the editor and execute command:


sudo netplan apply

wpasupplicant

Then you need to install “wpasupplicant” (apparently, a software to handle WPA – Wi-Fi Protected Access) which is missing on a fresh install of Ubuntu Server:


sudo apt-get update
sudo apt-get install wpasupplicant

Then reboot.

Now your Ubuntu Server should be connected to your WiFi network. You can check again with the command:


ip a

The output should show status “LOWER_UP” and the “UP” state for your wireless network controller. Please note, that with this configuration Ubuntu automatically connects to the wireless network. Voila! 🙂

P.S.

To see more details about your wireless connection you can use a program called “iw”. However it is not supporting all wireless adapters. It seems to not support Realtek devices. To give it a try you need to install it first:


sudo apt-get install iw

Then you can execute:


iw wlan0 link

Among others this will show you the bitrate (speed) used by your wireless connection.

Posted in Linux | Tagged , , | Leave a comment

GitHub CI for Java 21 and Maven

If you want to have a continuous integration process working on GitHub for your project and your project uses Java 21 and Maven then it’s enough to add a file with .yml extension (like “ci.yml”) into a directory .github/workflows of your project root with the following content:

name: Java CI with Maven
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
          cache: maven
      - name: Build with Maven
        run: mvn -B verify

This is using so called GitHub Actions. You can see a working example here: https://github.com/k-tomaszewski/notifications

NOTE: please verify if “branches” attribute in the above YAML is matching your branch naming, as older projects used to have a “master” branch instead of a “main” branch.

P.S.

This is just an update of the solution being part of my previous post “Hints on how to publish a Java code” which covers more topics around creating a repository on GitHub.
Posted in Java | Tagged , , , , | Leave a comment