Welche Einstellung haben Sie? Anwendungskonfiguration mit MicroProfile Config

MicroProfile (https://microprofile.io/) enthält u. a. den Baustein Microprofile Config, mit dem JEE-Anwendungen aus verschiedenen Quellen heraus Konfigurationsparameter erhalten können. Neben einem API stehen auch Injektionsmöglichkeiten zur Verfügung.

Configuration API

MicroProfile Config ist durch Vorläufer-Projekte wie Apache Tamaya oder DeltaSpike Config beeinflusst und besitzt diverse Implementierungen wie SmallRye Config im WildFly. Zum portablen Zugriff auf den konkreten Provider dient die Klasse ConfigProvider und das Interface Config. Im einfachsten Fall kann ein Konfigurationswert so erfragt werden:

Config config = ConfigProvider.getConfig();
String value = config.getValue("java.version", String.class);

Config Sources

Die Konfigurationswerte werden diesen Quellen entnommen (in absteigender Priorität):

  • System Properties
  • Environment Variables
  • Classpath resource(s) META-INF/microprofile-config.properties

Nach Wunsch können die Prioritäten angepasst und zusätzliche Konfigurationsquellen ergänzt werden.

Injektion von Konfigurationswerten

Für die bequeme Nutzung in CDI Beans steht der Qualifier @ConfigProperty zur Verfügung:

@Inject
@ConfigProperty(name = "answer")
int answer;

Sollte eine solche Property-Referenz nicht befriedigt werden, d. h. sollte es im konkreten Beispiel keinen konfigurierten Wert mit dem Namen answer geben, schlägt das Deployment der Anwendung fehl. Ist das nicht erwünscht, kann als Injektionsziel ein Optional genutzt werden:

@Inject
@ConfigProperty(name = "HOME")
Optional home;

Plattformen

MicroProfile Config wird u. a. von folgenden JEE-Servern unterstützt:

  • OpenLiberty (mit aktiviertem Feature mpConfig-1.3).
  • Payara.
  • WildFly.

Die genannten Klassen befinden sich bspw. in der Maven-Dependency org.eclipse.microprofile.config:microprofile-config-api (derzeit in Version 1.3).

Demo

In https://github.com/GEDOPLAN/microprofile-demo/tree/master/microprofile-config finden Sie ein Beispielprojekt mit einem REST-Service, der API-Nutzung und Injektionsmöglichkeiten zeigt. Die Anwendung kann als WAR-File auf einen der genannten Server deployt werden. Alternativ können über vorkonfigurierte Maven-Profile Docker-Images zum direkten Ausprobieren erstellt werden.

Bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/

Werbeanzeigen

JEE – Ende mit Schrecken oder Schrecken ohne Ende?

Die Geschichte von Oracle und Jakarta EE

Im Herbst 2017 entschloss sich Oracle zur Überführung der Java EE in ein Open-Source-Projekt unter dem Dach der Eclipse Foundation. In meinem Beitrag https://javaeeblog.wordpress.com/2017/09/13/oracle-schlaegt-verlagerung-von-java-ee-in-die-eclipse-foundation-vor/ von damals war ich zuversichtlich, dass dieser Schritt Java EE aus dem starren Korsett des JCP befreien und der Plattform die Modernität und Agilität ermöglichen würde, die neben der Verlässlichkeit und Stabilität in aktuellen Projekten benötigt wird.

Erste Zweifel kamen mir bei der Nachricht, dass Oracle die Rechte am Namen „Java EE“ nicht abgeben würde. Als Folge davon müssen alle Vorkommen von „Java EE“ durch den neuen Namen „Jakarta EE“ ersetzt werden. Nun ist Textersatz nicht wirklich kompliziert, also was soll’s?

Es verbleibt aber genauso das geistige Eigentum an den bislang von Oracle geführten Spezifikationen in den Händen von Oracle, so dass sich nach meiner Beobachtung nun viele Monate lang JEE-Experten damit beschäftigen durften, Spezifikationen so umzuschreiben, dass sie zwar inhaltlich kompatibel bleiben, aber keine schützenswerte Originalformulierung mehr enthalten.

Diese Zeit hätte man sehr gut in die Weiterentwicklung der Plattform stecken können. Glücklicherweise gibt es das Projekt MicroProfile (https://microprofile.io/), das in der letzten Zeit ganz und garnicht paralysiert herumgelegen hat, sondern mit wirklich hoher Innovationsgeschwindigkeit JEE-Erweiterungen entwickelt hat, die dringend notwendig sind, um JEE in der Greifvogelvoliere von immer kleineren Services irgendwo in den Wolken flugfähig zu halten.

Nun kommt aber noch der dicke Brocken, dass man sich nicht auf eine Weiterentwicklung des Paketnamensraums javax einigen konnte: Die bisherigen Pakete dürfen nur unverändert weiter genutzt werden, Neu- und Weiterentwicklungen müssen in neuen Paketen (jakarta) geschehen. Einfacher Textersatz ist hier natürlich wiederum möglich, bricht aber die Kompatibilität bestehender Anwendungen und Bibliotheken mit der neuen, paketumbenannten Plattform.

Wer sich nun mit Häme zu Aussagen wie „JEE ist eh Sch…, habe ich doch schon lange gesagt. Mit Spring ist alles besser!“ versteigt, sollte mal nachschauen, wieviele javax er in seinem Code findet.

Mir erschließt sich nicht, welchen Wert Oracle in den zurückgehaltenen Rechten sieht. Für mich als Entwickler und Berater scheint der Wert zukünftig bei 0 $ zu liegen. Und dafür erzeugt man große Verärgerung und Kosten bei Kunden und Anwendern? Nun bin ich kein Jurist, insbesondere kein Markenrechtler. Ich muss das also nicht verstehen.

Blick nach vorne

Was kann man nun in dieser verfahrenen Situation tun? Man könnte eine Koexistenz von javax und jakarta anstreben, Neuerungen wie gefordert in jakarta einführen und javax als Basis nutzen. Basis im Sinne von Basisklasse bzw. Basisinterface bedeutet aber eine (zu) enge Kopplung an die alten Versionen: API-Änderungen müssten sich i. W. auf Erweiterungen beschränken und man würde die ganzen alten Zöpfe weiter mit sich herumschleppen.

Es wird also darauf hinauslaufen, dass betroffene Pakete von javax.xyz in jakarta.xyz umbenannt werden. Das bedeutet zwar breaking Changes, bietet aber die Möglichkeit, veraltete APIs abzustoßen. Die erste Version, die dies betrifft, ist Jakarta EE 9, denn Jakarta EE 8 ist vollständig kompatibel zu Java EE 8, nutzt also weiterhin die bestehenden javax-Pakete. Es wird derzeit diskutiert, ob die Umbenennung der Pakete in einem Big Bang geschehen soll oder inkrementell immer dann, wenn Änderungen an einer Teilspezifikation durcheführt werden sollen. Ersteres hätte den Charme, mit einem – allerdings großen – Schritt sofort Unabhängigkeit zu erreichen. Bei der inkrementellen Vorgehensweise könnte man darauf spekulieren, dass sich manche Teile der Plattform zumindest in näherer Zukunft gar nicht ändern werden und man den Aufwand in diesem Bereich also sparen könnte. Ein Festlegung der Marschrichtung sollte noch im Juni 2019 geschehen.

Die Kompatibilität von Anwendungen älterer Versionen zu neuen Laufzeitplattformen ist einer der großen Werte der JEE. Ihn gilt es auf jeden Fall zu erhalten. Ich bin mir sicher, dass die Anbieter der modernen JEE-Server wie WildFly, OpenLiberty, Payara oder TomEE Mittel finden werden, z. B. durch Bytecode-Manipulation zur Deployment-Zeit, Altanwendungen mit javax-Paketen auf Jakarta-EE-9+-Servern lauffähig zu erhalten.

Das Ganze erzeugt nicht gerade unerheblichen Aufwand. Er ist es aber wert, angenommen zu werden. Kaum eine Plattform für Enterprise-Anwendungen hat ein ähnliches Gewicht, eine ähnliche Verbreitung in Projekten bei gleichzeitiger Flexibilität hinsichtlich der Laufzeitplattform – ob klassische Server wie die oben genannten oder Micro-Frameworks wie KumuluzEE, Meecrowave oder Quarkus. Wenn nun juristische Verkrustungen entfallen und die weitere Entwicklung ohne Korsett geschehen, kann’s doch nur super werden!

Bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/

Unterhaus weiterhin einig im Nicht-entscheiden – droht jetzt der harte Pexit?

Das britische Parlament hat das Austrittsabkommen zum dritten Mal abgelehnt. Bis zum 12. April muss geklärt werden, wie es weitergehen soll. Droht jetzt auch für den beliebten JEE-Server Payara der Pexit, d. h. die Abschottung vom Rest der Welt?

Der Server wird von der Payara Services Ltd in Malvern, United Kingdom, angeboten und als Open Source Software entwickelt. Könnte bei einem Pexit ohne Deal Entwicklern aus dem restlichen Europa der freie Zugang zum Quellcode entzogen werden?

Auf der Payara-Webseite finden sich Aussagen wie „Always Open Source“, „Public open source development“ und „encouraging users’ involvement“, aber lassen sich diese vor dem Hintergrund der entscheidungsunfähigen Politik noch halten?

Payara hat neben dem Hauptsitz auch ein Office in Portugal. Vielleicht lassen sich Downloads, Contributions und Service Requests zukünftig über dieses europäische Büro abwickeln.

Die letzten Entscheidungen stehen noch aus; hoffentlich kann Payara doch noch in der Community verbleiben!

Übrigens: Wir bieten unsere JEE-Kurse auch auf Payara an und das vermittelte Wissen ist garantiert nicht vom britischen Parlament abhängig. Schauen Sie doch mal vorbei: https://gedoplan-it-training.de/.

Wie läuft’s denn so? Monitoring mit MicroProfile Metrics

In meinem Blog-Eintrag „Alles gesund? Health checking mit MicroProfile Health“ habe ich dargestellt, wie einfach man den Gesundheitszustand einer Anwendung auf Basis von MicroProfile Health veröffentlichen kann. Und wenn man nun nicht nur wissen möchte, ob ein Service läuft, sondern mehr Details über den Service-Zustand erfahren möchte? Dann unterstützt uns ein weiterer Baustein: MicroProfile Metrics.

Telemetriedaten out of the box

Über den Rest-Endpoint /metrics/base werden ohne weiteren Programmieraufwand Basis-Telemetriedaten zur Verfügung gestellt:

{
  "memory.maxHeap": 8342470656,
  "memory.usedHeap": 259349504,
  "gc.G1 Young Generation.count": 28,
  "cpu.systemLoadAverage": 0.6,
  "thread.count": 111,
  ...
}

Die Daten werden in JSON geliefert, wenn man sie mit diesem Media Type anfordert (z. B. curl -H "Accept: application/json" localhost:8080/metrics/base). Mit dem Default-Media-Type text/plain werden die Daten passend für die Verarbeitung in Prometheus geliefert.

Der Endpoint /metrics/vendor ist für herstellerspezifische Messdaten aus der genutzten Plattform vorgesehen.

Anwendungsspezifische Metriken

EE-Anwendungen können weitere Messdaten bereitstellen. So kann man bspw. Methoden von CDI Beans mit der Annotation @Gauge versehen:

  @Gauge(name = "answerToLifeUniverseAndEverything", absolute = true, unit = MetricUnits.NONE)
  public long getAnswerToLifeUniverseAndEverything() {
    return 42;
  }

Die derart zur Verfügung gestellten Werte werden im Endpoint /metrics/application veröffentlicht:

{
  "answerToLifeUniverseAndEverything": 42
}

Neben dieser Möglichkeit, eigene Messwerte zu berechnen und zu veröffentlichen, kann man auch Telemetriedaten durch das Framework aggregieren lassen:

  @GET
  @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  @Timed(name = "personList", absolute = true)
  public List getAll() {
    return this.personRepository.findAll();
  }

@Timed bewirkt hier, dass die Ausführungszeiten der annotierten Methode gemessen und zu verschiedenen statistischen Werten aggregiert werden:

{
  "personList": {
    "p50": 4922800.0,
    "p75": 5160200.0,
    "p95": 1.00322E7,
    "p98": 1.00322E7,
    "p99": 1.00322E7,
    "p999": 1.00322E7,
    "min": 3218000.0,
    "mean": 5476794.800597489,
    "max": 1.00322E7,
    "stddev": 2340795.005960796,
    "count": 5,
    "meanRate": 0.6766077577220262,
    "oneMinRate": 1.0,
    "fiveMinRate": 1.0,
    "fifteenMinRate": 1.0
  }
}

Analog könnte mit @Counted ein Aufrufzähler für eine Methode etabliert werden.

Gesamt-Metrik

Der Endpoint /metrics fasst die bislang genannten Datengruppen in ein Gesamtergebnis zusammen.

Plattformen

MicroProfile Metrics wird u. a. von folgenden JEE-Servern unterstützt:

  • OpenLiberty (mit aktivierten Features mpMetrics-1.1 und monitor-1.0).
  • Payara.
  • WildFly (Achtung: WildFly hat unterschiedliche Ports für Applikations- und Management-Zugriffe, im Default 8080 und 9990. Der o. a. REST-Endpoint /metrics steht über den Management-Port zur Verfügung).

Die genannten Klassen befinden sich bspw. in der Maven-Dependency org.eclipse.microprofile:microprofile.

Demo

In https://github.com/GEDOPLAN/metrics-demo finden Sie ein Beispielprojekt mit einem REST-Service, dessen GET– und POST-Methoden so annotiert sind, dass Zeiten bzw. Aufrufzahlen gemessen werden. Der o. a. Service, der die legendäre 42 als Ergebnis liefert, ist ebenfalls im Beispielprojekt enthalten. Die Anwendung kann als WAR-File auf einen der genannten Server deployt werden. Alternativ können über vorkonfigurierte Maven-Profile Docker-Images zum direkten Ausprobieren erstellt werden.

Bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/

WildFly 16 released!

Seit gestern steht die Version 16.0.0.Final des WildFly zum Download bereit (http://wildfly.org/downloads/).

Bei einem kurzen Blick darauf sind mir zwei interessante Neuerungen aufgefallen:

  • Der Server unterstützt nun auch Microprofile Metrics.
  • Mit Hilfe von Galleon (https://github.com/wildfly/galleon), einem neuen Provisionierungstool, können Serverinstallationen erzeugt werden, die nur die jeweils benötigten Module enthalten.

Beide Punkte zeigen, dass der Server sich immer weiter in Richtung Cloud Readiness entwickelt.

Bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/

Transaktionssteuerung mit @Transactional

Transaktionen sind nötig, um Daten atomar, konsistent, isoliert und dauerhaft bearbeiten und speichern zu können (ACID-Prinzip). Methoden von CDI-Beans können durch eine simple Annotation mit einem aktiven Transaktionskontext versehen werden.

Transaktionsinterceptor mit dem Binding @Transactional

Schon seit der Version 1.1 von CDI steht ein global aktivierter Transaktionsinterceptor zur Verfügung. Er wird mit der Annotation @Transactional einer Methode einer CDI-Bean zugeordnet:

public class SomeService {
  ...
  @Transactional
  public void doSomethingTransactionally() {
    ...
  }

Beim Aufruf der Methode ohne aktive Transaktion wird nun eine Transaktion gestartet und nach dem Verlassen der Methode wieder geschlossen. Die Regeln für das Transaktionsende sind etwas seltsam:

  • Endet die Methode mit einem Return, wird ein Commit versucht.
  • Wirft die Methode eine sog. System Exception, resultiert dies in einem Rollback. System Exceptions sind i. W. die unchecked Exceptions (ergänzt um die RemoteException, die aber explizit kaum eine Rolle spielt).
  • Wirft die Methode eine Application Exception – das sind alle anderen Exeptions – wird ein Commit (!) versucht.

Insbesondere der letzte Punkt ist (zumindest mir) unverständlich: Für mich modellieren Exceptions immer Ausnahmesituationen, die den jeweiligen Geschäftsprozess ungültig machen. Glücklicherweise kann man mit einem Parameter der Annotation auch in diesem Fall ein Rollback ansteuern:

  @Transactional(rollbackOn=Exception.class)
  public void doSomethingTransactionally() {

Transaktionsmodus

Die Art der Transaktionssteuerung durch @Transactional kann mit dem Parameter value bestimmt werden:

  • TxType.REQUIRED ist der Default, der in den allermeisten Fällen passend ist. Beim Aufruf einer davon betroffenen Methode wird geprüft, ob bereits eine aktive Transaktion vorliegt. Wenn ja, wird diese für den Methodenaufruf genutzt. Andernfalls wird eine neue Transaktion gestartet und am Methodenende wieder beendet.
  • TxType.REQUIRES_NEW startet bei Methodenbeginn stets eine neue Transaktion, die am Methodenende wieder beendet wird. Eine ggf. bereits aktive Transaktion wird zuvor suspendiert. Dieser Modus sollte dann verwendet werden, wenn die Ergebnisse des Methodenaufrufs auch dann dauerhaft abgespeichert werden sollen, wenn die umgebende Transaktoin zurückgerollt wird. Beispiel: Auditing/Protokollierung von Methodenaufrufen.
  • TxType.MANDATORY verlangt eine aktive Transaktion bei Methodenbeginn und wirft andernfalls eine TransactionRequiredException. Dieser Modus ist könnte prinzipiell für Repositories (a.k.a DAOs) interessant sein, da deren feingranulare Methoden i. A. von einem Service aufgerufen werden, der selbst transaktional ist. Leider werden Lifecycle-Methoden (@PostConstruct-annotierte Methoden etc.) nicht durch die @Transactional-Interceptoren intercepted. Häufig werden darin Repository-Methoden aufgerufen. Dann sollte für Repositories wie auch bei anderen Services TxType.REQUIRED verwendet werden.
  • Die weiteren Modi TxType.NOT_SUPPORTED, TxType.SUPPORTS und TxType.NEVER sind i. A. unbrauchbar.

Danke für’s Lesen und bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/

Alles gesund? Health checking mit MicroProfile Health

Im Bereich von Microservices und verteilten Systemen – aber nicht nur dort – ist es wichtig zu wissen, ob ein Service/System ordnungsgemäß läuft oder gestört ist. Im MicroProfile (https://microprofile.io) ist mit MicroProfile Health ein Teil enthalten, der es in Kombination mit CDI sehr einfach macht, Health Checking in EE-Anwendungen zu integrieren.

Health Check Callbacks

Die Idee von MicroProfile Health ist, dass Anwendungen auf EE-Servern oder auf Basis von MicroProfile-Frameworks ein oder mehrere Methoden zum Prüfen des Gesundheitszustandes enthalten. Der Server sammelt die Zustände bei Bedarf ein und aggregiert daraus einen Gesamtzustand, der auf dem Rest-Endpoint /health veröffentlicht wird:

GET /health

200 OK
{
  "outcome": "UP",
  "checks": [{
    "name": "Service1",
    "state": "UP",
    "data": {
      "memory": 180672240
    }
  }, {
    "name": "Service2",
    "state": "UP"
  }]
}

Die Callback-Methoden heißen – etwas uninspiriert – call und stammen aus dem Interface org.eclipse.microprofile.health.HealthCheck. Der Server sucht sich automatisch alle Klassen zusammen, die dieses Interface implementieren und mit dem CDI-Qualifier org.eclipse.microprofile.health.Health versehen sind:

@ApplicationScoped
@Health
public class HealthCheck1 implements HealthCheck {

  @Override
  public HealthCheckResponse call() {
    ...

Das Prüfergebnis können die Methoden bequem mittels Builder-Pattern zusammensetzen:

  public HealthCheckResponse call() {
    return HealthCheckResponse
        .named("Service1")
        .up()
        .withData("memory", Runtime.getRuntime().freeMemory())
        .build();

Neben up gibt es auch down sowie die Methode status, der der Gesundheitszustand als Parameter übergeben wird.

Gesamtgesundheit

Bei einem GET-Request auf den Pfad /health werden alle verfügbaren call-Methoden aus allen Anwendungen des Servers aufgerufen und zu einem Gesamtzustand kombiniert. Liefern alle Checks UP, ist das Attribut outcome im Gesamtergebnis ebenfalls UP und der Response hat den Status 200 OK. Andernfalls wird der Response mit 503 Service unavailable ausgeliefert und outcome ist darin DOWN.

Plattformen

MicroProfile Health wird u. a. von folgenden JEE-Servern unterstützt:

  • OpenLiberty (mit aktiviertem Feature mpHealth-1.0).
  • Payara.
  • WildFly (Achtung: WildFly hat unterschiedliche Ports für Applikations- und Management-Zugriffe, im Default 8080 und 9990. Der o. a. REST-Endpoint /health steht über den Management-Port zur Verfügung).

Die genannten Klassen befinden sich bspw. in der Maven-Dependency org.eclipse.microprofile:microprofile.

Demo

In https://github.com/GEDOPLAN/health-demo finden Sie ein Beispielprojekt mit zwei Health Checks, deren Ergebnis über eine Webseite beeinflusst werden kann. Die Anwendung kann als WAR-File auf einen der genannten Server deployt werden. Alternativ können über vorkonfigurierte Maven-Profile Docker-Images zum direkten Ausprobieren erstellt werden.

Ausblick

Im Beispiel wird als Zusatzinformation die Menge freien Speichers hinzugefügt, was im Kontext von Health Checking nicht grundsätzlich falsch ist, aber doch eher eine Metrik darstellt. Für deren Veröffentlichung hält MicroProfile eine weitere Teilspezifikation bereit: MicroProfile Metrics. Sie wird Gegenstand eines der nächsten Blog Posts sein – stay tuned!

Bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/