InspectorJ – Teil 1: MicroStream

MicroStream: Hoch performante Persistenz und Serialisierung?

Sven Ruppert

© Shutterstock / Sira Anamwong (modifiziert)

Das Java-Universum bietet viele Bibliotheken, Tools und Frameworks. In seiner neuen Serie „InspectorJ“ untersucht Java-Experte Sven Ruppert aktuelle Trends. Im ersten Teil legt er sein Augenmerk auf das Projekt MicroStream. Das Framework verspricht, hochperformante Lösungen für die Serialisierung und Persistierung von Java-Anwendungen zu bieten.

Seit einiger Zeit gibt es einen neuen Mitbewerber im Bereich der Persistenz und Serialisierung. Die Rede ist vom Projekt MicroStream. Um was handelt es sich da genau? MicroStream nimmt in Anspruch, eine hochperformante und vor allem für den Entwickler sehr einfach zu handhabende Lösung für die Herausforderungen der Serialisierung und der Persistierung zu sein. Wie einfach, schnell und komfortabel das ist, werden wir uns nun im Detail ansehen.

Vorbereitung

Wie es sich für ein ordentliches Tutorial gehört, beginnen wir an dieser Stelle auch mit einem HelloWorld. Zu allererste müssen wir dafür ein leeres Maven-Projekt erstellen. Ok, ich muss zugeben, so leer ist es nicht, da ich Abhängigkeiten wie JUnit 5 und ähnliches einmal außer Acht lasse. Um Zugriff auf die notwendigen Binaries zu bekommen, benötigen wir zunächst die Koordinaten des Maven-Repositorys.

  <repositories>
    <repository>
      <id>microstream-releases</id>
      <url>https://repo.microstream.one/repository/maven-public/</url>
    </repository>
  </repositories>

Und aus diesem Repository holen wir uns nun die Komponente, mit der wir eine lokal eingebettete Speichereinheit erhalten:

    <dependency>
      <groupId>one.microstream</groupId>
      <artifactId>storage.embedded</artifactId>
      <version>02.00.00-MS-GA</version>
    </dependency>

HelloWorld – Schreiben

Die notwendigen Elemente stehen einem nun zu Verfügung und wir können jetzt damit beginnen, ein HelloWorld zu persistieren. Hierzu erzeuge ich eine Klasse mit dem Namen HelloWorld, in der es genau ein Attribut vom Typ String gibt. Das sollte keine besondere Herausforderung für MicroStream darstellen und lässt uns das unkonfigurierte Verhalten einmal ansehen.

public class HelloWorld {
  private String value;
  public String getValue() {
    return value;
  }
  public void setValue(String value) {
    this.value = value;
  }
}

Um nun das Verhalten auszuprobieren, formuliere ich die jeweiligen Verwendungsbeispiele als JUnit-5–Tests. Ausgangspunkt für den ersten Versuch ist nachfolgender JUnit-Test.

public class HelloWorldTest {
  @Test
  void test001() {
    final HelloWorld value = new HelloWorld();
    value.setValue("HelloWorld");
    // do something with MicroStream
  }
}

MicroStream ist derart aufgebaut, dass man einen Bezugspunkt benötigt. In der Dokumentation ist dieser als StorageRoot bezeichnet. In dem Beispiel hier ist es einfach, unsere Klasse HelloWorld. Die Beschreibung lässt einen schon vermuten, dass man seine Objektbäume der Storage Engine übergibt, damit diese sie sich dann von oben bis unten vornimmt. Als erstes ist es also ein Baum mit genau einem Knoten, eben unser HelloWorld. Daraus ergibt sich nun unser erster Versuch und ist nachfolgend umgesetzt worden.

final HelloWorld value = new HelloWorld();
value.setValue("HelloWorld");
final EmbeddedStorageManager storageManager = EmbeddedStorage.start();
storageManager.setRoot(value);
storageManager.storeRoot();
storageManager.shutdown();

Was genau passiert hier?

* (Zeile 3) Die Storage Engine wird gestartet.
* (Zeile 4) Der Root Node wird gesetzt.
* (Zeile 5) Der Baum wird von der Engine persistiert.
* (Zeile 6) Die gesamte Engine wird heruntergefahren und beendet.

Wenn man dies nun ausführt, sieht man zuerst einmal nichts in der Konsole. Allerdings hat sich etwas in unserem Projektverzeichnis etwas verändert: es wurde ein Verzeichnis mit dem Namen storage angelegt. Darin enthalten sind Dateien und ein ein weiteres Verzeichnis mit dem Namen channel_0. Die Vermutung liegt nahe, das sich hier unser HelloWorld eingenistet hat. Der sich daraus ergebende nachfolgende Schritt besteht demnach darin, diese Identität von unserem HelloWorld wieder zum Leben zu erwecken. Dazu schreiben wir einen weiteren JUnit-5-Test, um den schreibenden und lesenden Vorgang zunächst separiert zu halten.

W-JAX 2019 Java-Dossier für Software-Architekten

Kostenlos: Java-Dossier für Software-Architekten 2019

Auf über 30 Seiten vermitteln Experten praktisches Know-how zu den neuen Valuetypes in Java 12, dem Einsatz von Service Meshes in Microservices-Projekten, der erfolgreichen Einführung von DevOps-Praktiken im Unternehmen und der nachhaltigen JavaScript-Entwicklung mit Angular und dem WebComponents-Standard.

 

HelloWorld – Lesen

Da wir beim dem Schreibvorgang keine Konfigurationsparameter gesetzt haben, werden wir zum Lesen der Entität ebenfalls keine Konfigurationsparameter setzen. Die Annahme ist hier erst einmal, dass es sich symmetrisch verhalten wird. Zu Beginn wird wieder eine Storage Engine gestartet und mit der Methode root() der Root Node geladen. Der Rückgabewert ist vom Typ Object. Leider haben wir hier nicht die Möglichkeit, mittels Generics den Typ gleich mit anzugeben. Aber eventuell kommt ja in einer der nächsten Versionen eine Methode mit der Signatur <T>root().

Diese Instanz können wir nun mittels instanceof auf ihre Verwandschaft zu unserem HelloWorld hin prüfen. Wenn das positiv verlaufen ist, werden wir einen Cast auf den Typ HelloWorld riskieren und nachfolgend den Wert mittels der Methode getValue() für einen Vergleich einsetzen. Selbstverständlich ist die letzte Anweisung an die Storage Engine auch hier wieder ein shutdown().

final EmbeddedStorageManager storageManager = EmbeddedStorage.start();
final Object                 root           = storageManager.root();
Assertions.assertTrue(root instanceof HelloWorld);
HelloWorld helloWorld = (HelloWorld) root;
Assertions.assertEquals("HelloWorld", helloWorld.getValue());
storageManager.shutdown();

Wenn man dies nun ausführt, wird man mit einem positiven Testergebnis belohnt. Soweit scheint es schon einmal zu funktionieren. Nun bin ich neugierig, ob das auch mit Kotlin funktioniert.

Unveränderliches HelloWorld

Unveränderliche Datenstrukturen erfreuen sich in der Java-Welt einer immer größeren Beliebtheit. Deswegen möchte ich als nächstes testen, ob sich das ebenfall so ohne Weiteres umsetzen lässt. Hierzu bekommt unsere Klasse HelloWorld nun einen Mitspieler mit dem Namen HelloWorldImmutable. Der Unterschied zu dem ersten Typ liegt darin, dass es weiterhin eine get-Methode für den Wert geben wird, jedoch die set-Methode durch einen Konstruktor-Parameter abgelöst wird.

public class HelloWorldImmutable {
  private String value;
  public HelloWorldImmutable(String value) {
    this.value = value;
  }
  public String getValue() {
    return value;
  }
}

Der dazugehörige JUnit-5-Test sieht dann wie folgt aus.

final HelloWorldImmutable value = new HelloWorldImmutable("HelloWorldImmutable");
final EmbeddedStorageManager storageManager = EmbeddedStorage.start();
storageManager.setRoot(value);
storageManager.storeRoot();
storageManager.shutdown();

Bitte nicht vergessen das Verzeichnis storage zu löschen bevor man diesen Test nun ebenfalls laufen lässt. Alles scheint soweit gut zu verlaufen. Nach dem Durchlauf dieser Test-Methode befindet sich wieder ein Verzeichnis mit dem Namen storage in dem Projektverzeichnis. Ebenfalls sind die weiteren Elemente in diesem Verzeichnis enthalten, wie es es im vorherigen Durchlauf der Fall gewesen ist. Es steht einem nun nichts im Wege, auch hier wieder mit dem Lesen der gerade gespeicherten Entität vom Typ HelloWorldImmutable weiterzumachen. Wir setzen zum wiederholten Mal einen JUnit-5-Test ein.

final EmbeddedStorageManager storageManager = EmbeddedStorage.start();
final Object                 root           = storageManager.root();
Assertions.assertTrue(root instanceof HelloWorldImmutable);
HelloWorldImmutable helloWorld = (HelloWorldImmutable) root;
Assertions.assertEquals("HelloWorldImmutable", helloWorld.getValue());
storageManager.shutdown();

Auch in diesem Fall wird man mit einem erfolgreichen Testdurchlauf belohnt. Zusammenfassend kann man bis zu diesem Schritt sagen, dass es also keine besonderen Anforderungen an eine klassische Java Bean gibt, um diese zu schreiben und zu laden. Zugegebenermaßen ist das natürlich auch ein Trivial-Fall gewesen. Nach diesen ersten Schritten kommt mir sofort der Gedanke, dass ein Versuch mit Kotlin lohnenswert sein kann.

Kotlin – HelloWorld

Der erste Schritt besteht in unserem Fall darin, die Klasse HelloWorld und die Klasse HelloWorldImmutable mittels Kotlin abzubilden. Hierbei ergeben sich, bedingt durch die Sprache Kotlin, verschiedene Möglichkeiten.

class HelloWorldKotlin { var value: String? = null }

class HelloWorldImmutableKotlin(val value: String)

data class HelloWorldKotlinDataclass(val value:String)

Ich möchte an dieser Stelle nicht im Detail auf die Unterschiede eingehen. Hier geht es lediglich darum, ob ein Schreiben und Laden der Entitäten ebenfalls so ohne Probleme funktioniert. Die dazugehörigen JUnit-5-Tests sind genauso aufgebaut wie vorher die Versuche mit den Java-Klassen. Das Ergebnis ist in allen Fällen grün. Es sieht demnach so aus, als würde einem Einsatz mit Kotlin nichts im Wege stehen.

Fazit

Wir haben uns die ersten Schritte mit der Storage Engine von MicroStream angesehen und eine triviale Entität gespeichert. Das hat mit Java- als auch Kotlin-Klassen funktioniert. Weiteren Experimenten steht nun nichts mehr im Wege. Der Quelltext zu diesem Artikel ist auf GitHub zu fineden.

Wer Fragen und Anmerkungen hat, meldet sich am besten per Twitter an @SvenRuppert oder direkt per Mail an sven.ruppert@gmail.com

Happy Coding!

Geschrieben von
Sven Ruppert
Sven Ruppert
Sven Ruppert arbeitet seit 1996 mit Java und ist Developer Advocate bei Vaadin. In seiner Freizeit spricht er auf internationalen und nationalen Konferenzen, schreibt für IT-Magazine und für Tech-Portale. Twitter: @SvenRuppert
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
4000
  Subscribe  
Benachrichtige mich zu: