Saturday, 21 June 2025

Git Large File Storage (LFS)

One of my hobby projects is using big files. Github complains about it:

remote: warning: File mrbear/Terrain_PureNature.asset is 58.17 MB; this is larger than GitHub's recommended maximum file size of 50.00 MB
remote: warning: GH001: Large files detected. You may want to try Git Large File Storage1 - https://git-lfs.github.com.

Luckily this is only a warning. The real error happens when trying to push 2GB or bigger files onto the github2.

So, after installing it on my system...

brew install git-lfs

And telling git I wish to use it:

git lfs install

I had to tell it which files were the primary culprits (apparently we do not want ALL files to use LFS).

git lfs track ".png"
git lfs track ".asset"
git lfs track ".fbx"
git lfs track ".unity"

Let's not forget to add those new settings to the repo.

git add Assets/.gitattributes

Apparently this will only work on new files. Existing files in the repo are not changed.

But your could try the command git-lfs-migrate (which will change your repos history) for existing files3.

Addendum

I think it can be argued that, if you need to use LFS a lot for your git repo, git might actually not be the right tool for you.

But I'll leave that argument to smarter people.

References

[1] Git Large File Storage (LFS)
https://git-lfs.github.com
[2] GitHub - About Git Large File Storage
https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-git-large-file-storage
[3] Github Blog - Git LFS 2.2.0 released
https://github.blog/open-source/git/git-lfs-2-2-0-released/

Thursday, 29 May 2025

JPA Entity Lifecycle Events

Apparently JPA Entity have a lifectcycle. See [1].

References

[1] Baeldung - JPA Entity Lifecycle Events
https://www.baeldung.com/jpa-entity-lifecycle-events

Thursday, 22 May 2025

Const in Kotlin

So, const values must be known at compile time. Hence we cannot use any kind of method to compute const values.

So, are non-const values (which we should take care to make immutable) allowed in a Companion Object? Or is an Companion Object explicitly for constants?

I think it's perfectly safe to use consts and non-consts in a companion object, as long as they're effectively immutable.

Any comments?

The interesting part is that, according to [1], Kotlin inlines the const values where they are actually used. So the definition of the constant has actually disappeared from the compiled byte code. Which is of course much faster, than having to access a constant field somewhere.

Fascinating stuff.

References

[1] Baeldung - Kotlin const, var, and val Keywords
https://www.baeldung.com/kotlin/const-var-and-val-keywords

Thursday, 8 May 2025

Releasing unused but committed Heap memory

As mentioned in the references below, with some commandline arguments to the JVM, you can force periodic Garbage Collection cycles when on low load to reclaim memory that isn't used (because it's not doing anything).

This comes in handy when using containers, where committed unused memory in the node just costs money.

The application is considered inactive, and G1 triggers a periodic garbage collection if the following conditions hold:

G1PeriodicGCInterval
number of milliseconds have passed since any previous garbage collection pause and there is no concurrent cycle in progress at this point. A value of zero indicates that periodic garbage collections to promptly reclaim memory are disabled.
G1PeriodicGCSystemLoadThreshold
The average one-minute system load value as returned by the getloadavg() call on the JVM host system (e.g. container) is below this value. A value of zero means this is ignored.

As an example:

java -server -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication -XX:+UseG1GC \
-XX:G1PeriodicGCInterval=30000 -XX:G1PeriodicGCSystemLoadThreshold=0.5 \
-Xmaxf0.3 -Xminf0.1 -Xmx2048M -Xms32M \
-jar ../releases/mrbear.war

This example would indicate that every 30 seconds, if the average system load is below 0.5, a Garbage Collection cycle may be initiated to reclaim memory.

Java Agent

In the past Virtuozzo, which I used, had to put in a java-agent in the command line to have the same functionality. So this is no longer necessary.

Progress!

References

Virtuozzo - Elastic JVM with Automatic Vertical Memory Scaling
https://www.virtuozzo.com/company/blog/elastic-jvm-vertical-scaling/
JEP 346: Promptly Return Unused Committed Memory from G1
https://openjdk.org/jeps/346

Thursday, 1 May 2025

Links related to VoxxedDays Amsterdam

I had some links to useful websites that I needed to put somewhere.

Apache Calcite

It seems to be a kind of proxy that you can put between your application and your data layer. It allows for data manipulations, for example using SQL, and your data layer can be anything (and frequently is). If there's an Adapter for it, you can connect Calcite to your data thing. If there isn't you have to write your own adapter.

https://calcite.apache.org/

Cursor - The AI Code Editor

What it says on the box.

https://www.cursor.com/

Micrometer

A system to check what your software is doing (in the field).

https://micrometer.io/

Github - Oracle - graalvm-reachability-metadata

Find out if there's support for certain frameworks in the GraalVM for Native using this list.

https://github.com/oracle/graalvm-reachability-metadata

Baeldun - Code Snippets in Java API Documentation

Besides Markdown syntax in JavaDoc in the new versions of Java, it is also possible to add code snippets to your javadoc.

I think it's interesting. It would for example be a good idea to add a code snippet in the JavaDoc that refers to a specific Class in your testset.

https://www.baeldung.com/java-doc-code-snippets

GitLab - BeyondxScratch - hexagonal-architecture-java-springboot

There was a talk about hexagonal architecture, that I seem to have seen before.

https://gitlab.com/beyondxscratch/hexagonal-architecture-java-springboot

StarWars REST API

An interesting little website that can provide REST services about the Star Wars films. If you want to quickly test some REST responses. (Their certificate seems to have expired though)

Coolify - Self-hosting with superpowers.

What is says on the box. Easily selfhosting a cloud environment.

https://coolify.io

Cheap dedicated servers, cloud & hosting from Germany

Kind of an standard hosting company, but they seems to have Server Auctions? Buy some cloud with refurbished hardware, is basically the idea. I thought that was interesting.

https://www.hetzner.com/

Thursday, 24 April 2025

Compatiblity between Kotlin and Java regarding Default Methods

In short, default methods in interfaces in Kotlin are not compatible with Java.

That's the short version.

The long version follows.

Let's have a simple Person interface written in Kotlin:

Now we wish to use it in Java.

This doesn't work. Although the interface defines a default implementation of getGender(), this default implementation is invisible when called from Java.

java: org.mrbear.kotlin.interfaces.MrBear is not abstract and does not override abstract method getGender() in org.mrbear.kotlin.interfaces.Person

Now, in the past there used to be something called @JvmDefault, but that is deprecated and doesn't work.

Instead it has been superseded by @JvmDefaultWithoutCompatibility1 and @JvmDefaultWithCompatibility2 (which, quite frankly, makes it a tad less understandable).

Also, when you use either annotation, you are required to add a command line parameter when you compile, or it won't work.

With compatibility requires -jvm-default=enable.

Without compatibility requires -jvm-default=no-compatibility.

However, it seems that the default is with compatibility that it is turned on automatically in Kotlin 2.2. Which will be released soonish hopefully.

How it works

Apparently what happens is that Kotlin automatically creates an Abstract class in the Interface that implements the methods that are default (under water). The abstract class is called Interface$DefaultImpls.

If you run Without Compatibility, it means that the DefaultImpls won't be generated but only "real Java default methods in the Interface". This means your interface in Kotlin will actually change (and is therefore not backwards compatible).

See reference [3] for more details.

References

[1] Kotlin LangRef - JvmDefaultWithoutCompatibility
https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.jvm/-jvm-default-without-compatibility/
[2] Kotlin LangRef - JvmDefaultWithCompatibility
https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.jvm/-jvm-default-with-compatibility/
[3] KT-4779 Generate default methods for implementations in interfaces
https://youtrack.jetbrains.com/issue/KT-4779/Generate-default-methods-for-implementations-in-interfaces

Thursday, 17 April 2025

How to install Javascript Engine in GraalVM - FollowUp

Things have changed compared to [3].

The GraalVM Updater was removed in GraalVM for JDK 21.[1] See issue 6855 [2].

Now we have to add Javascript using simple Maven dependencies that we add. See [4].

Also GraalVM seems to not like the Java standard scriptengine API, and support for it might go away soon.

References

[1] GraalVM Updater
https://www.graalvm.org/latest/reference-manual/graalvm-updater/
[2] [GR-46219] Remove the GraalVM Updater
https://github.com/oracle/graal/issues/6855
[3] How to install Javascript Engine in GraalVM
https://randomthoughtsonjavaprogramming.blogspot.com/2023/06/how-to-install-javascript-engine-in.html
[4] GraalVM - GraalJS
https://www.graalvm.org/latest/reference-manual/js/

Thursday, 10 April 2025

VoxxedDays Amsterdam - Roundup

How to survive as a developer in the exponential age of AI - Sander Hoogendoorn

Technical Debt

Ward Cunningham said it as follows:

The danger occurs when the debt is not repaid. Every minute spent on code that is not quite right for the programming task of the moment counts as interest on that debt.

Entire engineeering organizations can be bought to a stand-still under the debt load of an unfactored implementation, object-oriented or otherwise.

Shipping first-time code is like going into debt.

A little debt speeds development so long as it is paid back promptly with refactoring.

Have you gone MADR? - Johan Hutting

Short blurb on Javadoc. Markdown now suppported in new Java versions.

Also was very interested in the new @snippet javadoc tag.

See for more information on MADR at https://adr.github.io/madr/

MADR stands for Markdown Architectural Decision Records

Bring the Action: Using GraalVM in Production - Alina Yurenko

Nice deep dive about the different things they're working on.

There's a list of libraries that have some sort of support regarding metadata at https://github.com/oracle/graalvm-reachability-metadata.

Quotes

A code generation tool that gets you 80-90% of the way there is like a boat that takes you 80-90% of the way.

You'll need to be a strong swimmer.

Jason Gorman

If debugging is the process of removing bugs, then programming must be the process of putting them in.

Edsger W. Dijkstra

References

VoxxedDays Amsterdam
https://amsterdam.voxxeddays.com/
MyBlog - Voxxed days amsterdam 2025
https://randomthoughtsonjavaprogramming.blogspot.com/2025/04/voxxed-days-amsterdam-2025.html

Thursday, 27 March 2025

Using different JDBC drivers

So I had the problem that I needed to connect to both Oracle databases as well as Postgres database.

And just including the dependencies in my Maven was not enough.

One of the drivers gets overwritten by another driver in the service provider mechanism.

At first I tried to register them by hand (which works):

DriverManager.registerDriver(new org.postgresql.Driver());
DriverManager.registerDriver(new OracleDriver()); return DriverManager.getConnection(url, user, password);

Or, you could do the extreme magic thing1 2:

Class.forName("org.postgresql.Driver");
Class.forName("oracle.jdbc.driver.OracleDriver"); return DriverManager.getConnection(url, user, password);

Which causes both drivers to run a static block registering themselves on the DriverManager in the same fashion as my code on top.

All this, because the service provider mechanism that automatically loads the JDBC driver, only allows one Driver to be registered3.

References

[1] StackOverflow - What is the difference between "Class.forName()" and "Class.forName().newInstance()"?
https://stackoverflow.com/questions/2092659/what-is-the-difference-between-class-forname-and-class-forname-newinstanc/2093236#2093236
[2] Baeldung - Loading JDBC Drivers
https://www.baeldung.com/java-jdbc-loading-drivers
[3] Baeldung - Java Service Provider Interface
https://www.baeldung.com/java-spi

Saturday, 15 March 2025

Git: recovering a dropped commit

So I was working, and I accidentally dropped one commit too many.

Oh dear.

So I thought, would there be a possibility of getting this one back? And I was quite surprised that there was.

A quick google search later, I found the information I put in the References below.

git reflog

The reflog or "Reference logs" contain a record when the tips of branches or other references were updated in the local repository. From my imperfect knowledge, it sounds like a database transaction log.

With it you can pinpoint the exact point in the records where your change/branche/commit was still there and reset the head "hard" to this SHA value.

git reset --hard d6b66766a9a

Bear in mind that the reflog is only available locally in your git repo, and it will get cleaned of old reflog entries in due time.

References

Rewind Blog - How to Restore a Deleted Branch or Commit with Git Reflog
https://rewind.com/blog/how-to-restore-deleted-branch-commit-git-reflog/
git - git-reflog - Manage reflog information
https://git-scm.com/docs/git-reflog

Tuesday, 4 March 2025

Kotlin Operator Overloading

So my colleague mentioned operator overloading, and how much fun it is.

So I wrote a little test.

It works pretty good, but for infix functions it has a very high "syntactic sugar" and less about "real" operator overloading.

Also, the reference page in [1] indicates that for Infix functions, the precedence has fixed rules that do not (always) conform to precedence that we would assume.

My colleague told me that a number of infix functions were created for QueryDSL, so we could write a semblance of SQL in Kotlin, but the precedence tends to screw things up.

We removed these infix functions from our source code again.

So, use sparingly and only when it makes sense. For example if it makes your code a magnitude easier to read/reason about and preferable with as small a scope as possible.

References

[1] KotlinLang - Functions
https://kotlinlang.org/docs/functions.html#function-scope
Baeldung - Operator Overloading in Kotlin
https://www.baeldung.com/kotlin/operator-overloading
Baeldung - Infix Functions in Kotlin
https://www.baeldung.com/kotlin/infix-functions

Thursday, 20 February 2025

Kotlin: listOf() versus emptyList()

So I noticed that Kotlin tends to have several ways of doing the same thing1. Sometimes these are actually identical, sometimes there are subtle differences.

That makes things hard for me.

Let's take this trivial example.

There is no difference. The one thing you could mention is that emptyList() more accurately conveys what you are trying to do.

There's something similar in Java. Collections.emptyList() versus List.of(). The comments on [2] seems to be very interesting.

In Java one could argue that List.of() is new, so should be used. And we could assume that the Language Architects in Java wouldn't add a List.of() for no reason.

References

[1] Kotlinlanguage Reference - emptyList
https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/empty-list.html
[2] StackOverflow - List.of() or Collections.emptyList()
https://stackoverflow.com/questions/39400238/list-of-or-collections-emptylist

Thursday, 6 February 2025

Java - Behaviour of Equals in Collections

I just thought I'd write some things down that I already know, but sometimes it's nice to see this proven.

It's a very very beginner Java subject, but people won't fault me for blogging about it.

It might be of some use to somebody.