JPA + REST, Jackson Resolver


JAX-RS macht das Erstellen von Webservices leicht. Innerhalb von Minuten ist die Businesslogik und JPA Entitäten über REST im JSON Format verfügbar. Aber Moment! LazyLoadException? Circular Dependencies? Schnell stellen wir fest das unsere komplexen JPA Entitäten eben nicht out of the box in ein JSON Format übertragen werden können wenn Relationen und Bidirektionale Verbindungen im Spiel sind. Die Verarbeitung solche Relationen ist dann oftmals mühsam und mit viel manuellen Aufwand verbunden wenn der Umweg über DTOs gegangen wird. Jackson (JSON Parser, Standardimplementierung z.B. im Wildfly) biete hier eine interessante Möglichkeit: JsonIdentityInfo mit eigenem Resolver.

Folgendes einfaches Datenmodel führt ohne Eingriff bereits zu besagten Problemen:

 

@Entity
public class Talk {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @NotNull
    @Size(min = 5)
    private String title;

    @ManyToMany(fetch = FetchType.EAGER)
    private List speakers;
}

@Entity
public class Speaker {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    private String firstname;

    private String lastname;

    @ManyToMany(mappedBy = "speakers", fetch = FetchType.EAGER)
    private List talks;

JSON kennt keine Referenzen und würde beim Verarbeiten dieser Entitäten in eine Endlosschleife münden (Talk > Speaker > Talk > Speaker…). Um dies zu verhindern gibt es eine ganze Reihe von Möglichleiten, z.B. die Verwendung von @JSONIgnore (Referenzen beim JSON erzeugen ignorieren) oder die Entwicklung von einem DTO. Auch Denkbar ist die Verwendung von Jacksons @JsonIdentityInfo Annotation:

    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonIdentityReference(alwaysAsId = true)
    @ManyToMany(fetch = FetchType.EAGER)
    private List speakers = new ArrayList();

Dies führt dazu das Jackson diese Relation immer lediglich mit dem Feld „ID“ im JSON versieht:

{
„id“: 1,
„title“: „Power Workshop Java EE“,
„speakers“: [
1,3
],
„talkType“: „WORKSHOP“,
„duration“: 480
},

Diese Varianten haben bisher einen entscheidenden Nachteil: ein einfaches Ändern der Relationen ist ohne manuelle Schritte nicht möglich:

  • @JSONIgnore: würde sogar dazu führen das die Referenzen auf Speaker entfernt werden, da sie nicht im JSON geliefert werden und bei einem „merge“ = „null“ sind
  • DTO: manueller Aufbau der Entität und „merge“

Auch @JsonIdentityInfo hilft in diesem Fall noch nicht. Optional kann dieser Annotation aber eine „resolver“ übergeben werden, der für die Umwandlung in/von JSON sorgt und der in unserem Fall die entsprechende Entitäts-Referenz aus der Datenbank holt und diese korrekt zuweist:

    @JsonIdentityInfo(
         resolver = JPAResolver.class, 
         generator = ObjectIdGenerators.PropertyGenerator.class, 
         scope = Speaker.class, 
         property = "id"
     )

Der Resolver:

public class JPAResolver extends SimpleObjectIdResolver {

    private EntityManager em;

    public JPAResolver() {
        this.em = CDI.current().select(EntityManager.class).get();
    }

    @Override
    public void bindItem(IdKey id, Object pojo) {
        super.bindItem(id, pojo);
    }

    @Override
    public Object resolveId(IdKey id) {
        Object resolved = super.resolveId(id);
        if (resolved == null) {
            resolved = loadFromDatabase(id);
            bindItem(id, resolved);
        }

        return resolved;
    }

    private Object loadFromDatabase(IdKey idKey) {
        return this.em.getReference(idKey.scope, idKey.key);
    }

    @Override
    public ObjectIdResolver newForDeserialization(Object context) {
        return new JPAResolver();
    }

    @Override
    public boolean canUseFor(ObjectIdResolver resolverType) {
        return resolverType.getClass() == JPAResolver.class;
    }
}

Dieser muss noch (zugegeben etwas umständlich) dem Context hinzugefügt werden. Dies kann z.B. mittels JaxRS Provider geschehen mittels HandlerInstantiator . Die Registrierung ist im GitHub hier: de/gedoplan/jackson/system/JacksonJPAResolverProvider.javazu finden

Das ist charmant! Rest-Clients können nun nicht nur die Entität selber (Talk) verändern, sondern in einem Abwasch auch die Referenz (>Speaker) auf Basis der ID manipulieren. Da der Resolver über das scope-Attribut mit der Referenz-Entity-Klasse versehen ist kann er in dieser Form generisch für alle Referenzen verwendet werden. Cool.

Im nächsten Artikel werfen wir einen dann einen Blick auf JSON-B, dem neuen Standard für JSON in Java EE 8

 

Live. In Farbe. Zum ausprobieren. Auf GitHub.

 

Advertisements

Angular 5 Konverter

Ein sehr häufig vorkommendes Szenario bei der Arbeit mit Fomularen ist die Umwandlung von Werten, also die Konvertierung zwischen Formular- und Daten-Repräsentation. In Angular ist dies mit einer Direktive möglich die das Interface ControlValueAccessor implementiert.

Als Beispiel dient ein sehr simples Beispiel. In unserem Datenmodel werden Namen als separates Objekt mit den Attributen „firstname“ und „lastname“ abgelegt. Innerhalb unsere Formulares sollen diese Werte jedoch gemeinsam (mit Leerzeichen getrennt) eingegeben werden:

2018-06-12 13_49_09-AngularParserFormatter

Der Schlüsselpunkt ist das Interface ControlValueAccessor mit dessen Hilfe wir unsere eigene Zugriffsmethode auf Eingabefelder realisieren können (in diesem Beispiel verwenden wir es als Konverter, dieses Interface würde aber z.B. auch bei der Erstellung einer völlig eigenen Eingabekomponente verwendet werden).

@Directive({
  selector: '[nameConverter]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NameConverterDirective),
      multi: true
    }
  ]
})
export class NameConverterDirective implements ControlValueAccessor {...}

Hier deklarieren wir eine entsprechende Directive, die wir an die Liste der Angular zur Verfügung stehenden Value-Accessoren anhängen.

Zur Implememtierung müssen wir nun noch die entsprechenden Methoden implementieren, die im Kern diese beiden sind:

  • writeValue(value: any), vom Model in die View
  • HostListener, um auf Änderungen in der View zu reagieren
  writeValue(value: any) {
    let val = '';
    if (value && value.firstname && value.lastname) {
      const name: Name = value;
      val = name.firstname + ' ' + name.lastname;
    }
    this._renderer.setProperty(this._elementRef.nativeElement, 'value', val);
  }

  @HostListener('input', ['$event'])
  onInput(event: any) {
    const namesp = event.target.value.split(' ', 2);
    const targetValue = new Name(namesp[0], namesp.length == 1 ? '' : namesp[1]);
    this.onChangeMethod(targetValue);
  }

Live. In Farbe. Demo auf Github

Software-Architektur: Domain-Driven Design – ein Einstieg

Das Domain-Driven Design (DDD) wurde von E. Evans in seinem Buch Domain-Driven Design: Tackling Complexity in the Heart of Software eingeführt. Für mich hat die Beschäftigung mit dem DDD-Ansatz vor allen Dingen eine (Rück-) Besinnung auf die Konzepte der objektorientierten Programmierung zur Folge gehabt. Diese Aussage mag überraschend klingen, wenn man sich doch über viele Jahre mit der Programmiersprache Java und der Java EE Plattform beschäftigt hat. Wo liegt also das Problem, zumal Java doch eine objektorientierte Programmiersprache ist?

Zunächst rückt DDD mit drei Begriffen die eigentliche Fachlichkeit von Software in das Zentrum der Betrachtung.

Da ist zu aller erst die Domäne selbst. Dies ist der Ausschnitt der realen Welt, der Geschäftsbereich, das Aufgabenfeld usw. wofür ein Software-System realisiert wird. Als Beispiel mag ein Flight Information System (FIS) dienen. Hierin gibt es Aircrafts die viele Flights auf bestimmten Routes ausführen. Auf der anderen Seite gibt es Customer, die eine Booking für einen bestimmten Flight durchführen. Eine von einem Customer getätigte Booking kann aus mehreren BookingPositions je Passenger bestehen und wird durch eine Invoice abgerechnet. Offensichtlich liegt also einer Domäne in der Regel ein Datenmodell zugrunde. Darüber hinaus umfasst eine Domäne aber auch Verhalten, Prozessabläufe, Geschäftsregeln oder Ereignisse.

Um eine solche Domäne für die Software-Entwicklung greifbar zu machen, wird sie durch ein Domänen-Modell in einer abstrahierten Form beschrieben. Zur Beschreibung von Domänen-Modellen sind grafische Software-Spezifikationssprachen wie insbesondere die Unified Modeling Language (UML) oder spezielle textuelle Software-Spezifikationssprachen in Form einer Domain Specific Language (DSL) geeignet. Das Domänen-Modell spezifiziert die Gesamtheit von Daten, Verhalten, Prozessen, Regeln, Ereignissen usw., welche die Domäne ausmachen. Somit beinhaltet ein UML-Domänen-Modell nicht nur Klassen-Diagramme für das Datenmodell, sondern beispielsweise auch ein Zustands-Diagramm für den BookingState oder ein Aktivitäts-Diagramm für den Geschäftsprozess cancel flight.

Die implementierte Anwendung ist eine exakte Repräsentation des Domänen-Modells in Form von Source-Code. Damit dies gelingt, müssen für die Erstellung des Domänen-Modells und dessen Implementierung auf der einen Seite die Software-Entwickler und auf der anderen Seite die fachlichen Experten sehr eng und permanent zusammen arbeiten. Im Zuge dieser Zusammenarbeit entsteht nach und nach die Ubiquitous Language für die Domäne. Dies ist die allgegenwärtige fachliche Sprache, welche alle für die betrachtete Domäne relevanten Begriffe eindeutig definiert. Die Ubiquitous Language für das Flight Information System beinhaltet somit Begriffe wie order, departure time, reserve seat, offered price usw. Für ein Projekt-Team von Software-Entwicklern und fachlichen Experten gilt, dass die Ubiquitous Language „von jedem gesprochen“ und „von jedem eindeutig verstanden“ wird.

Das Resultat einer erfolgreichen Umsetzung des DDD-Ansatzes ist ein Software-System, welches das spezifizierte Domänen-Modell mit den Begriffen der Ubiquitous Language implementiert. Sowohl Modell als auch Implementierung der Domäne nutzen ausschließlich das Vokabular der Ubiquitous Language, deren Begriffe somit in UML-Diagrammen, DSL-Spezifikatinen, Test-Beschreibungen, Source-Code, Wiki-Dokumentation usw. einheitlich verwendet werden.

Wie sieht nun aber die konkrete Vorgehensweise des DDD für die Implementierung aus und warum spielt hierbei die Objektorientierung eine so wichtige Rolle?

Zur Klärung dieser Frage begebe ich mich auf eine Zeitreise zurück auf die Anfänge meines Programmierer-Daseins. Nach einem kurzen Intermezzo mit BASIC war Pascal die erste Programmiersprache, mit der ich mich ernsthaft auseinender gesetzt habe. Ein kleines Pascal-Beispiel deuten die folgenden Code-Ausschnitte an, nämlich die typische Vorgehensweise bei der prozeduralen Programmierung mit der Definition von Datenstrukturen und der Implementierung von Algorithmen darauf:


PROGRAM myapp;

TYPE
person = RECORD
name: STRING;
home: address;
END;
address = RECORD
...
END;

PROCEDURE foo;
VAR p: person;
a: address;
BEGIN
...
END {foo};

BEGIN
...
END {myapp}.

Nach vielen Jahren modularer Programmierung mit Modula-2 folgte dann der Übergang zur Objektorientierung mit Smalltalk. Ich lernte Sätze wie „alles ist ein Objekt“ und „ein Objekt kapselt Daten und das darauf definierte Verhalten ein“.

Mit dem Einsatz von Java und der Java EE Plattform im aktuellen Jahrtausend gelangen wir nun wieder in die Gegenwart. Wie dort ein leichtgewichtiges Java EE Programm, welches mit Maven gebaut wird, oftmals aussieht deuten die folgenden Code-Ausschnitte an:


<project ...>

<artifactId>myapp</artifactId>
<packaging>war</packaging>

@Entity
public class Person {

private String name;
private Address home;
----
@Embeddable
public class Address {
...

@RequestScoped
public class PersonService {

public void foo(Person p) {
Address a = p.getHome();

Vergleicht man nun die Beispiele in Pascal und Java EE, dann kann man sich fragen, was aus dem objektorientierten Konzept der Einkapselung von Daten und Verhalten geworden ist: Die anämische (blutleere) JPA-Entity-Klasse Person kapselt nur die Datenstruktur für Personen ein, d. h. außer Dingen wie einem Konstruktor, den standardmäßigen Getter-/Setter-Methoden oder trivialen Methoden wie z. B. toString() findet man dort kein fachliches Verhalten vor. Dieses wird stattdessen in einer separaten CDI-Service-Klasse PersonService implementiert, deren Methoden (= Algorithmen) auf den Personen-Objekten (= Datenstruktur) operieren.

Diese Art der Programmierung fühlt sich für mich inzwischen problematisch an.

Das DDD definiert nun eine Reihe von Building Blocks als die konstituierenden Elemente eines Software-Systems. Im Sinne der Objektorientierung kann man sich jeden Building Block als ein Entwurfsmuster für eine bestimmte Art von Klassen mit einer festgelegten Aufgabenstellung vorstellen. Innerhalb der Menge der Building Blocks spielt dabei das Entity eine zentrale Rolle.

Ein Entity repräsentiert ein Geschäftsobjekt im klassischen Sinn der Objektorientierung: Das Entity kapselt die benötigten Geschäftsdaten zusammen mit den darauf operierenden Methoden ein. Jedes Entity besitzt eine eindeutige und unveränderliche Objekt-Identifikation. Schließlich sind Entities im allgemeinen Verständnis persistente Objekte.

Die letztgenannte Eigenschaft legt die Implementierung von Entities im Sinne des DDD als JPA-Entity-Klassen nahe. Um jedoch auch reichhaltige Funktionalität in Form von Methoden in einer JPA-Entity-Klasse realisieren zu können, fehlt solchen Klassen die Möglichkeit, sich per CDI beliebige andere „Dienste“ in Form von entsprechenden Objekten injizieren zu lassen. Diese Injektions-Fähigkeit lässt sich jedoch durch die folgende Utility-Klasse leicht nachrüsten, wobei nur die Möglichkeiten von Standard Java EE (CDI 1.1) ausgenutzt werden (vereinfachte Darstellung):


public abstract class Injector {

public static void injectFields(Object entity) {

BeanManager beanManager = CDI.current().getBeanManager();
AnnotatedType annotatedType = beanManager.createAnnotatedType(entity.getClass());
InjectionTarget injectionTarget = beanManager.createInjectionTarget(annotatedType);
CreationalContext creationalContext = beanManager.createCreationalContext(null);
injectionTarget.inject(entity, creationalContext);

Das folgende Beispiel deutet eine Anwendung an, indem in der Entity Booking des Flight Information System ein „Dienst“ vom Typ BookingPositionRepository injiziert wird. Im Konstruktor der JPA-Entity-Klasse wird diese Injektion aufgelöst, so dass etwa innerhalb einer Methode der Klasse Booking eine zugeordnete BookingPosition persistiert werden kann:

@Entity
public class Booking {

@Inject
@Transient
BookingPositionRepository repositoryBookingPosition;

protected Booking() {
Injector.injectFields(this);
}

public void addPassenger(...) {
...
BookingPosition bookingPosition = this.factoryBookingPosition.create(...);
...
this.repositoryBookingPosition.persist(bookingPosition);
...

Durch diese Erweiterung erhält man die so genannten Rich Entities mit den folgenden Eigenschaften:

  • Einkapselung der persistenten Daten eines Geschäftsobjektes
  • Implementierung aller Methoden, die ausschließlich auf den eingekapselten Daten operieren
  • Implementierung von reichhaltigen Methoden vermöge von injizierten, beliebigen „Diensten“

Da nun vielfältige Geschäftslogik direkt in den Entities implementiert werden kann, erübrigt sich die Notwendigkeit, dass solche Funktionalität in mehr oder weniger willkürlich gewählten Service-Klassen implementiert werden muss, weil nur dort Injektionen mit @Inject möglich sind. Somit wird die Geschäftslogik ganz überwiegend direkt an den Geschäftsobjekten implementiert, wodurch die Geschäftslogik im gesamten Software-System einfach und schnell lokalisierbar bleibt.

Soweit ein erster Überblick über wesentliche Konzepte des Domain-Driven Design und die Realisierung von Rich Entities im Sinne der Objektorientierung. Die weiteren Building Blocks von DDD und den wichtigen Begriff des Bounded Context schauen wir uns in der Fortsetzung dieses Blogs zur Software-Architektur an.

Angular Animations – Frohe Weihnachten

ng-xmas-tree

Live und in Farbe unter: github/GEDOPLAN

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!