OpenJDK wird von IntelliJ IDEA nicht erkannt

Nach der Installation von OpenJDK über Homebrew unter macOS mittels:

brew install openjdk

wollte ich das JDK unter IntelliJ IDEA nutzen. Allerdings wurde es dort nicht erkannt. Nachdem ich eine Neuinstallation über Homebrew gestartet habe, fiel mir eine entsprechende Meldung auf:

==> Caveats
For the system Java wrappers to find this JDK, symlink it with
  sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk

Nach der Ausführung des entsprechenden Befehls:

sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk

funktionierte die entsprechende Erkennung in der IDE wieder und das OpenJDK konnte genutzt werden.

Minecraft auf dem Steam Deck installieren

Da Minecraft nicht über Steam installiert werden kann, läuft es im ersten Moment nicht auf dem Steam Deck. Allerdings kann hier schnell Abhilfe geschaffen werden. Im ersten Schritt muss hierzu in den Desktop-Modus gewechselt werden. Dieser wird erreicht in dem im Steam Deck-Menü der Punkt Ein/Aus ausgewählt wird. Dort findet sich dann der Punkt Zum Desktop wechseln.

Die Installation des GDLauncher

Im Desktop-Modus angekommen, sollte die Softwareverwaltung (Discover) gestartet werden und dort nach der Applikation GDLauncher gesucht werden. Wird im Desktop-Modus eine Tastatur benötigt, so kann diese über einen Druck auf die Steam-Taste in Verbindung mit dem X-Button aktiviert werden. Anschließend sollte die Applikation installiert werden.

Nach einigen Minuten ist der Launcher installiert und kann gestartet werden. Im ersten Schritt möchte der Launcher Java installieren. Hier empfiehlt es sich Automatic Setup auszuwählen. Anschließend werden die benötigten Java-Versionen heruntergeladen und installiert. Danach kann sich über den GDLauncher in den Account eingeloggt werden. Bei bereits umgestellten Konten sollte hier auf Sign in with Microsoft geklickt werden. Nachdem Nutzername und Passwort eingegeben wurde und die App autorisiert wurde, kann über den GDLauncher die passende Minecraft-Version installiert werden.

Dazu müssen einige einführende Worte weggeklickt und anschließend über den Plus-Button eine neue Minecraft-Version installiert werden. Der Download der entsprechenden Version sollte nach einigen Minuten abgeschlossen sein. Nach der Installation sollte das Menü im Desktop-Modus wieder aufgerufen werden und dort nach GDLauncher gesucht werden. Nach einem rechten Mausklick auf das Symbol kann dort Add to Steam ausgewählt werden. Mit der Verknüpfung Return to Gaming Modus, welche sich direkt auf dem Desktop befindet, kann wieder in den normalen Standardmodus des Steam Deck zurückgekehrt werden.

Die Controllereinstellungen müssen für Minecraft sinnvoll definiert werden

Nachdem GDLauncher, als Icon hinterlegt wurde, muss im nächsten Schritt eine sinnvolle Controllereinstellung definiert werden. In meinem Fall habe ich das Community-Layout Minecraft Xbox Style von Rasin Bar genutzt. Anschließend kann der GDLauncher gestartet werden und dort dann die gewünschte Minecraft-Version ausgewählt und gestartet werden. In Minecraft selbst können entsprechende Einstellungen wie die gewünschte Auflösung vorgenommen werden.

Minecraft auf dem Steam Deck

Direkt auf dem Steam Deck sollte diese immer 1280 x 800 Pixel betragen und kann somit direkt über die Einstellungen des GDLauncher vorgenommen werden. Dies ist auch die sinnvollere Variante um Letterbox-Effekte zu verhindern. Da Minecraft von sich aus keine sinnvolle Gamepad-Unterstützung mitbringt; ist die Nutzung über die Dockingstation und ein separates Gamepad ohne entsprechende Mods nicht sinnvoll. Der Chat hingegen kann in der Theorie über die Bildschirmtastatur genutzt werden.

JSON mit Schlüsselwörtern unter Java deserialisieren

Mittels GSON von Google, können JSONs unter Java einfach in Objekte deserialisiert werden. Allerdings können bestimmte JSON-Objekte zu Problemen führen. So zum Beispiel folgende JSON-Datei:

{
   "default":"ABC",
   "value":"DEF"
}

Das entsprechende Java-Objekt könnte in diesem Fall wie folgt aussehen:

class Data {

  public String default;
  public String value;
}

Damit wäre GSON in der Lage die entsprechenden Felder aus dem JSON, den Felder in der Klasse zuzuordnen. Allerdings kann obige Klasse nicht kompiliert werden. Hintergrund hierfür ist das Feld default. Unter Java ist dies ein Schlüsselwort und kann somit nicht als Feldname genutzt werden. Unter Sprachen wie C# könnte hier mit einer Verbatim-Zeichenkette gearbeitet werden:

public String @default;

Allerdings bietet Java diese Möglichkeit nicht an, sodass sich hier anders beholfen werden muss. Die Lösung ist die Annotation SerializedName. Diese gibt den Namen des Schlüssels im JSON an, welcher in das entsprechende Feld deserialisiert werden soll:

class Data {

  @SerializedName("default")
  public String defaultValue;
  public String value;
}

Damit kann GSON die Serialisierung wieder vornehmen und das obige JSON kann in eine entsprechende Java-Struktur abgebildet werden.

Klassenname für Logger unter Java automatisch ermitteln

Vor einiger Zeit schrieb ich einen Artikel darüber, wie der Klassenname für einen Logger ermittelt werden kann. Im Endergebnis sah die damalige Lösung, unter Nutzung der Simple Logging Facade for Java kurz SLF4J, wie folgt aus:

Logger log = LoggerFactory.getLogger(new Exception().fillInStackTrace().getStackTrace()[0].getClassName());

Damit wird der gesamte Stacktrace zusammengesammelt und entsprechend der Klassenname extrahiert. Das Problem an dieser Variante ist das der gesamte Stack dafür ausgewertet wird und für jede Nutzung eines Logs eine relativ unintuitive und lange Zeile von A nach B kopiert werden muss. Anders sieht es mit folgender Lösung aus:

package net.seeseekey.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility class to get logger
 */
public final class Logging {

    private Logging() {
    }

    public static Logger getLogger() {

        StackWalker.StackFrame frame = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
                .walk(stream -> stream.skip(1)
                        .findFirst()
                        .orElse(null));

        if (frame == null) {
            return LoggerFactory.getLogger("Common");
        }

        return LoggerFactory.getLogger(frame.getClassName());
    }
}

Bei dieser Utility-Klasse wird der entsprechende Klassenname dynamisch über einen StackWalker ermittelt. Zurückgegeben wird hierbei der Klassenname des Aufrufers. Konnte kein Klassenname ermittelt werden, so wird stattdessen ein Logger mit dem Namen Common zurückgegeben. Damit kann der eigentliche Logger nun wie folgt angelegt werden:

Logger log = Logging.getLogger();

Der StackWalker ist ab Java 9 verfügbar und kann somit in neueren Projekten problemlos genutzt werden. Im Gegensatz zu den bisherigen Methoden Teile des Stacktrace zu erhalten, ist der StackWalker aus Performancesicht zu bevorzugen. Definiert wurde diese API in der JEP 259.

Probleme beim Deployen zu Maven Central

Im Rahmen einiger Wartungsarbeiten wollte ich eine neue Version einer Java-Bibliothek zu Maven Central deployen. Stattdessen wurde ich von der Meldung:

Execution injected-nexus-deploy of goal org.sonatype.plugins:nexus-staging-maven-plugin:1.6.7:deploy failed: An API incompatibility was encountered while executing org.sonatype.plugins:nexus-staging-maven-plugin:1.6.7:deploy: java.lang.ExceptionInInitializerError: null

überrascht. Am dahinterliegenden Problem wird bereits gearbeitet. Um das Deployment trotzdem durchführen zu können, eignet sich folgender Workaround:

export MAVEN_OPTS="--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED"
mvn clean deploy

Anschließend wird das entsprechende Deployment durchgeführt, was je nach Auslastung einige Minuten dauern kann.