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/

Werbeanzeigen

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/

Schlanke JEE-Services mit Apache Meecrowave

Das Programmmodell von JEE (aka Java EE / Jakarta EE) ermöglicht schon seit vielen Jahren – namentlich seit der Version 5 – eine effiziente Entwicklung von schlanken Geschäftsanwendungen. Als Ablaufumgebungen stehen ausgereifte und leistungsfähige JEE-Server wie WildFly oder Open Liberty zur Verfügung, die als eine Art Allrounder den Anwendungen recht umfassende Infrastruktur anbieten und sogar mehrere Anwendungen gleichzeitig betreiben können.

Gerade der letzte Punkt, der Parallelbetrieb mehrere Anwendungen, wird allerdings nur selten genutzt, um Störeffekte zwischen Anwendungen zu minimieren. Es gibt also typischerweise genau eine Anwendung pro Server, der ggf. sogar selbst in einem dedizierten Rechner – meist einer virtuelle Maschine – läuft.

Wenn man nun noch einen nur eingeschränkten Teil der angebotenen Infrastruktur benötigt, bspw. JPA, CDI und JAX-RS, also den klassischen Microservice-Stack, kann man sich die Frage stellen, ob tatsächlich ein kompletter JEE-Server gebraucht wird, oder ob man nicht einfach die benötigten Dienste in die Anwendung einbetten könnte.

An dieser Stelle setzt Apache Meecrowave an. Dieses Apache-Projekt verknüpft den JPA-Provider EclipseLink, den CDI-Container OpenWebBeans und die REST-Implementierung CXF zu einem ganz schlanken Server – oder eigentlich zu einer Server-Bibliothek, mit deren Hilfe man den Server einfach als Main-Programm startet.

Zur Einbindung von Meecrowave reicht es aus, die folgende Dependency in den Classpath zu holen:

  
    org.apache.meecrowave
    meecrowave-core
    1.2.3
  

Der in der Anwendung eingebettete Server wird dann z. B. so gestartet:

  public static void main(String[] args) {
    try (Meecrowave meecrowave = new Meecrowave().bake()) {
      // Do work here ...
    }

Beim Eintritt in den try-Block wird der Server gestartet, beim Verlassen wieder gestoppt. Sämtliche aus CDI bekannte Mechanismen für den Bootstrap einer Anwendung können zur Aktivierung von Services etc. genutzt werden, z. B. Observer für den Container Lifecycle:

  void startSomething(@Observes @Initialized(ApplicationScoped.class) Object obj) {
    // Gets called on server start ...
  }

Ein einfacher REST-Service ist dann nur noch ein Ding von wenigen Zeilen:

@Path("hello")
@ApplicationScoped
public class HelloResource {

  @GET
  @Produces(MediaType.TEXT_PLAIN)
  public String getHello() {
    return "Hello!";
  }
}

So ist also dieser – zugegebenermaßen winzige – Microservice mit nur ganz wenig Code schon fertig. Und beeindruckt mit Startzeiten im Sekundenbruchteil-Bereich sowie einem Memory Footprint von nur ca. 12 MB.

Ein einfaches Demoprojekt kann von GitHub heruntergeladen werden: https://github.com/GEDOPLAN/meecrowave-demo.

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

Using GlassFish / Payara 5 with Eclipse Oxygen

If you want to try GlassFish or Payara 5 with one of the current Eclipse versions (Oxygen, which includes JBoss Developer Studio), you run into the problem that the GlassFish Tools plugin checks the GlassFish version and refuses to work with the current release.

As a workaround edit the manifest of glassfish-api.jar located in the modules directory of your GlassFish installation and change the Bundle-Version to 4.0.0.

After this modification you can connect GlassFish 5 to Eclipse via GlassFish Tools, run the server and deploy applications as usual.

See you in our Java EE trainings in Berlin, Bielefeld or at your site: gedoplan-it-training.de/


 

Identity and Equality of JPA Entities

JPA entities contain an id field (or property) annotaded with @Id. For this post I will assume a single field, but compound ids are possible as well.

Entity objects are business objects, so it is generally accepted that we will have to supply equals and hashCode for entity classes. Let me point out here, that it’s very unlikely for our business code to call any of these methods directly, because we don’t normally need explicit comparisons of entity objects in our business code, but we use collections which in turn may call equals and hashcode for looking up or adding entries.

We have several expectations regarding the semantics of equals and collection operations expecially when looking at entity objects:
We have several expectations regarding the semantics of equals and collection operations expecially when looking at entity objects:

  1. Objects based on the same database entry should be the same regarding equals and objects from different db rows should be different

a) including objects from different entity managers,
b) including detached objects,
c) including new (transient) objects,
d) even if some non-id attribute has been changed (they will end up in ‚their‘ db records!).

  1. Hashcode-based collections (e. g. HashSet) should behave friendly, i. e.

a) adding multiple objects should be possible – even for new (transient) objects,
b) the collection should remain intact even if contained objects get persisted into the db.

If the entity has a business id, i. e. the id attribute has some business meaning and is set by the constructor, the expectations can easily be met by using exactly the id attribute in equals and hashCode.

If you choose to compare all fields instead of just the id, your implementation breaks expectation 1.d). If you choose to not implement equals and hashCode at all and rather use the methods derived from Object, your implementation breaks expectations 1.a) and 1.b).

So, as an intermediate conclusion, implement equals and hashCode in your entity classes and base them on just the id attribute(s):


@Entity
public class Foo {

@Id
private String id;

...

public Foo(String id, ...) {
this.id = id;
...
}

public int hashCode() {
return (this.id == null) ? 0 : this.id.hashCode();
}

public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Foo other = (Foo) obj;
if (this.id == null) {
return other.id == null;
}
return this.id.equals(other.id);
}

Things get more complicated, if you want to use generated ids. JPA supports you in this with the annotation @GeneratedValue which has to be placed on the id attribute in addition to @Id. The generator works for integral number fields and it’s best to abstain from primitive types in order to have the additional value null for unset ids. So you would use Integer, Long or BigInteger depending on the amount of data entries expected:

@Entity
public class Foo {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

The problems with generated ids looking at identity and equality arise from the fact, that the entity manager will set the ids late – they may be populated not until the commit of the inserting transaction.

To begin with you will use the id field for comparison in equals and in hashCode as you did for business ids before. But if you do that in exactly the way shown above, you break expectation 2.a), because equals will evaluate all new (transient) objects as equal, as their ids are all null.

You can fix this by modifying equals such that it returns false if any of the compared ids are still unset:

public boolean equals(Object obj) {
...
if (this.id == null) {
return false;
}
return this.id.equals(other.id);
}

While this now meets expectation 2.a), it still breaks expectation 2.b): If you add some new entities to a HashSet and persist them afterwards, the collection will be corrupt due to the changed hash codes.

And bad news: There is no way to get around this, if using generated ids! In many application this still is no issue, because the program sequence „Create multiple objects“, „add them to a hash-based collection“, „persist the objects“, „use the collection“ is not very common and can be circumvented by persisting (and flushing) the objects before they get added to the collection.

Please take into account, that the usage of a hash-based collection may not be directly visible and instead be hidden as association collection in some other entity class.

What are your options, if you want to use generated ids and your business logic suffers from the hash-based collection problem discussed above? Well, the solution is to use an id populated early, i. e. in the constructor, which can be generated easily and – most advisable for performance – without hitting the database. java.util.UUID offeres the desired functionalty. It supplies 128 bit integers commonly expressed as 36 character string (32 hex digits and 4 dashes). java.util.UUID uses random numbers as base and offers a distribution which makes duplicates very unlikely. Other implementations exist, which use MAC addresses and random numbers.

@Entity
public class Foo {

@Id
private String id;

private String description;

public Foo(String description) {
this.id = UUID.randomUUID().toString();
...

By using uuids you cherry-pick the advantages from having early set ids while still having generated values.

The size of uuids may be painful. They are roughly four times the size of an integer, but storage requirements depend on your database product. Remember that they are ids, so every foreign key in the db has the same size. If that is a problem, you can resort to having an early set uuid for supporting equals and hashCode, which is not annotated with @Id. Instead you add another integer field as JPA generated id, i. a. annotated with @Id @GeneratedValue. Now all tables contain a number as primary key as well as a non-primary uuid column, but all foreign keys are just numbers.

So, the subject of JPA ids, which seems easy at first glance, is far from that in reality. You may use the following recipe when designing entity classes:

  • If your entity contains some identifying business attribute, take this as id. Let equals and hashCode use exactly this attribute. All expectations expressed above will be met
    (-> ChemicalElement in showcase).
  • If you don’t find a suitable business id, and …
    • if the hash-based collection problem discussed above is no problem for your application, use a @Id @GeneratedValue annotated integer id. Let equals and hashCode use exactly this attribute, but modify equals such that unset ids render a false return value. All expectations expressed above will be met with the exception of 2.b)
      (-> LabExperiment3NonNullIdEquality in showcase).
    • you want / have to use early set ids, use uuids.
      • If db size does not really matter or your model contains just a few associations, use the uuids directly as JPA ids. Let equals and hashCode use exactly this attribute. All expectations expressed above will be met
        (-> LabExperiment4UUIdEquality in showcase).
      • If you care about the storage consumption of foreign keys in your database, use the uuid just for equals and hashCode and add a separate @Id @GeneratedValue id to your class. Let equals and hashCode use exactly the uuid attribute. All expectations expressed above will be met
        (-> LabExperiment5AddIdEquality in showcase).

There is a showcase on https://github.com/GEDOPLAN/jpa-equals-demo demonstrating the various options. You can find the referenced classes there.

See you in our trainings at Berlin, Bielefeld, Cologne or your site!
http://gedoplan-it-training.de/


 

New and noteworthy in Java EE 8

Am 18.09.2017 wurde nach langer Zeit des Wartens und auch der Unsicherheit über die Zukunft der Plattform die Version 8 der Java Enterprise Edition veröffentlicht. Diese im JSR 366 beschriebene Umbrella Specification umfasst diverse Änderungen an den in ihr enthaltenen Einzelspezifikationen, von den im Folgenden einige – die subjektiv wichtigsten – beschrieben werden:

Java API for JSON Binding 1.0 (JSON-B, JSR 367)

Durch diese neue Spezifikation enthält die Plattform erstmals die Möglichkeit der Serialisierung und Deserialisierung von Java-Objekten in bzw. aus JSON analog zu JAXB. Im Sinne von Zero Configuration gibt es Defaults für die Konvertierung von Attributen, natürlich inklusive der mit Java 8 eingeführten Datums- und Zeit-Typen. Von den Defaults kann abgewichen werden, indem Klassen und Attribute mit Annotationen versehen werden (z. B. @JsonbTransient zum Ausschluss von Attributen) oder indem eine global angepasste Konfiguration genutzt wird.

Java Servlet 4.0 (JSR 369)

Der Fokus der neuen Servlet-Spezifikation liegt auf der Unterstützung von HTTP/2. Der Datenaustausch zwischen Client (Browser) und Server kann dann mittels Header Compression, Request Multiplexing und Server Push effizienter gestaltet werden. Die API-Änderungen sind nur marginal, so dass Anwendungen in den Genuß der Optimierung einfach dadurch kommen, dass sie diese Version der Spezifikation nutzen. Die überwiegende Anzahl der Browser sind bereits HTTP/2-fähig.

JavaServer Faces 2.3 (JSF, JCP 372)

Das Paket javax.faces.bean ist nun endlich deprecated. Statt der JSF-eigenen Manage Beans setzt man nun vollständig auf CDI, was sich auch in der Injizierbarkeit von CDI Beans in JSF-Artefakte wie bspw. Konverter ausdrückt. Weitere Änderungen sind die Unterstützung von Java 8 Date/Time, Websocket Integration und die Möglichkeit feldübergreifender Validierung.

Contexts and Dependency Injection for Java 2.0 (CDI, JSR 365)

Ein CDI Container lässt sich nun auch im SE-Kontext betreiben. Dies drückt sich einerseits dadurch aus, dass die Spezifikation nun getrennte Abschnitte für SE und EE vorsieht. Andererseits ist mit Hilfe der Klasse SeContainer ein implementierungsneutraler Start und Stopp eines Containers möglich. Änderungen gibt es weiterhin bei der Verarbeitung von Events. Observer können nun eine Verarbeitungsreihenfolge erhalten oder asynchron bedient werden.

Bean Validation 2.0 (JSR 380)

Die Validierugsmöglichkeiten für Java Beans wurden um diverse neue vordefinierte Contraints ergänzt (bspw. @Email oder @Positive) und können nun auch auf Elemente von Collections angewendet werden. Schließlich werden nun auch Java-8-Klassen wie LocalDate oder Optional unterstützt.

Java API for RESTful Web Services 2.1 (JAX-RS, JSR 375)

Asynchronität ist in REST-Services schon seit der Vorversion ein Thema, das nun u. a. durch die Nutzung von CompletionStage weiter ausgebaut und resilienzfähig wird. Neu sind zudem der Support für PATCH Requests sowie Server Side Events.

Neben dieser willkürlichen Auswahl haben auch weitere Spezifikationen Änderungen erfahren.

Zu den genannten Punkten werden wir in lockerer Folge weitere Posts in diesem Blog machen. In diesem Sinne: Stay tuned!

Oracle schlägt Verlagerung von Java EE in die Eclipse Foundation vor

Seit nunmehr fast 20 Jahren ist die Java EE eine verlässliche Bank für den Aufbau und Betrieb von Enterprise-Anwendungen. Oracle – zuvor Sun – hat am Erfolg der Plattform zweifellos einen wesentlichen Anteil, zusammen mit den vielen Firmen, Einzelpersonen und der Community, die sich für Standards und deren Weiterentwicklung engagiert haben.

Nun hat sich die Welt aber weiter gedreht – manchmal scheint es, mit stetig wachsender Geschwindigkeit – und neue Anforderungen (Cloud, Microservices, … – you name it) verlangen nach einer Anpassung und Weiterentwicklung der Plattform in einer Taktung, die nicht mehr so recht zu einem in gewisser Weise zentralistischen Modell passen will.

Vor diesem Hintergrund erscheint die Ankündigung von Oracle, Java EE in die Eclipse Foundation zu verschieben, sehr sinnvoll. Sie ist vielleicht sogar überfällig, wenn man die Unsicherheit und Querelen der vergangenen Monate und Jahre betrachet.

Ich bin sehr zuversichtlich, dass dieser Schritt der richtige ist, um Java EE weiter verlässlich, stabil und zukunftsorientiert auszurichten.