Suche
Die Macht der 8

Java EE 8: Hält es, was die lange Wartezeit versprochen hat?

Lars Röwekamp

©Shutterstock/Macrovector

Nach Jahren des Stillstands war es vor wenigen Tagen endlich so weit: Die neue Java-EE-Spezifikation, Java EE 8, wurde veröffentlicht. Die ursprünglich für den Sommer dieses Jahres geplante Version 8 des Java-Enterprise-Standards ist dabei deutlich mehr als nur Technologie. Sie ist ein Bekenntnis von Oracle zum Java Community Process.

Was lange währt, wird endlich gut. Dieses Sprichwort als Maßstab genommen, müsste die neue Version der Java-EE-Spezifikation nicht nur gut, sondern sehr gut werden. Mehr als vier Jahre sind seit dem Erscheinen des Vorgängers und damit der immer noch aktuellen Version 7 ins Land gezogen. Wer nun aber glaubt, dass Java EE 8 mit unzähligen Neuerungen und Verbesserungen aufwartet, der sei eines Besseren belehrt. Die kommende Version ist eher eine Evolution denn eine Revolution. Einige sprechen sogar von einem Maintenance-Release auf dem Weg zu Java EE 9. Trotzdem ist genau diese Version ein wichtiger Meilenstein für die Zukunft von Java EE. Aber immer der Reihe nach …

Politik, wohin man schaut

Eigentlich fing alles so schön an. Nachdem es bei der Spezifikation von Java EE 7 zu etlichen Unstimmigkeiten gekommen war und Oracle sich den Vorwurf gefallen lassen musste, ein wenig den Fokus verloren zu haben, schien man mit Java EE 8 alles besser machen zu wollen. Bereits kurz nach der finalen Veröffentlichung von Java EE 7 wurde im Rahmen der JavaOne Conference 2013 ein Panel mit dem bezeichnenden Titel „Java EE – Meet the Spec Leads“ abgehalten, in dem ausgiebig über die zukünftige Ausrichtung von Java EE diskutiert wurde.

Alle waren sich einig, dass die Community bei der Bestimmung der zukünftigen Ausrichtung eine größere Rolle spielen sollte, um späte Richtungswechsel, wie bei Java EE 7 geschehen, zu verhindern. Um das zu gewährleisten, wurde eine zweiteilige öffentliche Umfrage gestartet, an der sich ca. 5 000 Interessierte beteiligten. In Teil 1 der Umfrage, durchgeführt im Dezember 2013, ging es um sinnvolle neue APIs und eine generelle Einschätzung zu Hype-Themen wie NoSQL oder HTML5. Im Januar 2014 wurden im zweiten Teil zusätzlich die Themen Cloud und Microservices sowie Security, Logging und Deployment aufgegriffen. Das damalige Ergebnis war eindeutig. Die Community legte den Fokus auf „leichtgewichtig“ und „einfach zu verwenden“. Cloud und Co. sowie neue UI-Technologien dagegen schienen eher von geringerem Interesse zu sein. API-Kandidaten wie JSON Binding, Java EE Configuration oder JCache bekamen einen Zuspruch von 60 % bis 80 %. Gleiches galt für die Fokussierung auf ein Alignment der Verwendung von CDI innerhalb der vielen existierenden Java-EE-APIs. Vorschläge zu Cloud-nahen Themen dagegen lagen nur selten über 50 %. Die zukünftige Roadmap von Java EE schien somit klar zu sein.

Lesen Sie auch: Java EE geht an die Eclipse Foundation

Interessant ist, dass im Anschluss an diese anfängliche Euphorie die weiteren Bemühungen erst einmal ins Stocken gerieten. So wurde zum Beispiel zunächst keine neue Java EE 8 Expert Group ins Leben gerufen, sondern Diskussionen über die bestehenden Kanäle der Java EE 7 Expert Group geführt. Das Ganze artete am Ende so weit aus, dass die Weiterentwicklung von Java EE nahezu zum Erliegen kam und Oracle ein gewisses Desinteresse an Java EE vorgeworfen wurde. Nicht wenige gingen sogar so weit, Java EE – mal wieder – für tot zu erklären, da Oracle sich scheinbar voll und ganz auf die eigene Cloud-Strategie konzentrierte und die Zukunft von Java EE keine Rolle mehr zu spielen schien. Das war in etwa auch der Zeitpunkt der Gründung der Java EE Guardians, einer Initiative zur Rettung von Java EE. Eine interessante Übersicht über die damals vorhandenen bzw. nicht vorhandenen Aktivitäten findet sich in dem Artikel „Java EE 8, Current Status: Case study for completed work since late 2015“.

Erst nach arger Kritik aus der Community und dem Weggang etlicher Java-EE-Experten von Oracle (einige gingen freiwillig, andere wurden gegangen), fand eine Kehrtwende statt. Oracle betonte mehrfach die strategische Bedeutung von Java EE und veröffentlichte auf der JavaOne 2016 eine korrigierte und minimierte Version der Roadmap. Java EE 8 würde keine revolutionären Neuerungen beinhalten, sondern das konsequent fortführen, was mit Java EE 5, 6 und 7 begonnen wurde: „Ease of Development“ und „API Alignment“. Themenbereiche wie Cloud und Microservices sollten bis auf wenige vorbereitende Maßnahmen außen vor bleiben und stattdessen Kernthema von Java EE 9 werden. Gleichzeitig wurde auch die Verschiebung des Release auf Sommer 2017 bekannt gegeben. Die Community wurde wenige Wochen später ein zweites Mal gebeten, über die potenziellen Kandidaten – oder sollte man besser sagen: Streichkandidaten – der zukünftigen Java-EE-Version abzustimmen. Diese Mal nahmen nur noch knapp über 1 500 Personen an der Umfrage teil, die meisten aus Europa. Die für die Weltwirtschaft durchaus relevanten BRIC-Staaten Brasilien, Russland, Indien und China haben die Umfrage indes weitestgehend ignoriert.

Das Ergebnis der Umfrage floss direkt in die finale Roadmap ein. Bereits geplante APIs, wie das Security-API oder JAX-RS, wurden in ihrem Umfang erweitert. Neue API-Kandidaten kamen hinzu: Configuration API, HealthCheck API – beide wurden aber später wieder fallen gelassen und auf Java EE 9 verschoben. Im Gegenzug fielen einige prominente APIs wie JMS 2.1, Management 2.0 oder MVC 1.0 dem Rotstift zum Opfer, da nur so der geplante Releasetermin gehalten werden konnte. Das wiederum ist wichtig, da man zukünftig deutlich kürzere Zyklen für die Java-Enterprise-Plattformen angekündigt hat und Java EE 9 – mit den Schwerpunktthemen modulare Runtime und Cloud – bereits 2018 veröffentlicht werden soll.

Technologie

So viel zur nicht ganz unkomplizierten Entstehungsgeschichte von Java EE 8 und der damit verbundenen politischen Komponente. Aber natürlich gibt es auch den einen oder anderen technologischen Aspekt, der es wert ist, genauer beleuchtet zu werden.

Java EE 8 hat sich zum Ziel gesetzt, den in Java EE 7 gesetzten Fokus auf moderne Webanwendungen konsequent weiterzuverfolgen. HTTP/2-Support inkl. Push statt Pull, eine deutlich verbesserte Unterstützung von JSON sowie ein reaktives Programmiermodell stehen hier ganz oben auf der Liste. Auch das bereits in Java EE 5 eingeführte Motto Ease of Development wird weiter vorangetrieben. Die Einführung neuer Annotationen in nahezu jedem API sowie ein verbesserter CDI-Support zur vereinfachten Programmierung sind hierfür ein klares Indiz. Zusätzlich findet bei etlichen Java-EE-APIs eine Adaption hin zu den neuen Features von Java 8, zum Beispiel Nutzung von Lambdas oder Stream-API, statt.

Brave new Web

Viele der Änderungen und Neuerungen finden sich im direkten oder indirekten Webumfeld wieder. Geht man einmal von klassischen Java-EE-Webanwendungen aus, sind hier vor allem die beiden Spezifikationen Java Servlet 4.0 (JSR 369) und JSF 2.3 (JSR 372) zu nennen.

Ein Großteil der neuen Servlet-4.0-Features betrifft den Support von HTTP/2 und dient somit vor allem der Performanceoptimierung beim Browsen von Webapplikationen. Mittels Server Push kann der Servlet-Container zukünftig proaktiv Daten bzw. Ressourcen an den Client senden. Sind zum Beispiel innerhalb einer index.html weitere Webressourcen wie Stylesheets, Images oder JavaScript-Dateien referenziert, können diese vom Servlet-Container parallel zur eigentlich angeforderten Ressource – in diesem Fall also der index.html – ausgeliefert werden. Ein weiterer Performance-Boost kann durch die Unterstützung des HTTP/2 Request-/Response Multiplexing erreicht werden. Hierbei können mehrere Requests und Responses asynchron – also non-blocking – über eine einzige TCP-Verbindung verarbeitet werden. Die meisten Entwickler von Java-basierten Webanwendungen werden allerdings kaum Berührungspunkte zu den neuen Features des Servlet-API haben, da diese sich eher transparent im Hintergrund abspielen.

Etwas handfester wird es, zumindest aus Sicht des Anwendungsentwicklers, bei der neuen JSF-Spezifikation. Nachdem man jahrelang an dem veralteten Modell der JSF Managed Bean Facility festgehalten hat, ist dieses nun endlich (endlich!) deprecated. Konkret heißt es dazu in der Spezifikation:

„As of version 2.3 of this specification, use of the managed bean facility as specified in this section is strongly discouraged. A better and more cohesively integrated solution for solving the same problem is to use Contexts and Dependency Injection (CDI), as specified in JSR-365.“

Entsprechend verbessert wurde die Integration von CDI. Zum einen können die bisher noch nicht unterstützen JSF-Komponenten Converter, Validator und Client Behaviour als Injection Target fungieren. Zum anderen stellt JSF eine ganze Reihe neuer CDI Producer zur Verfügung, um so innerhalb der eigenen Sourcen auf einfache Weise Zugriff auf wesentliche JSF-Artefakte wie zum Beispiel @SessionMap, @RequestMap oder @HeaderMap zu bekommen.

Ebenfalls neu in JSF ist der direkte WebSocket-Support. Mittels f:websocket-Tag kann innerhalb einer JSF View eine WebSocket Push Connection aufgebaut und so auf eingehende Messages reagiert werden. Das Pendant auf Seiten des Servers nennt sich PushContext und kann via CDI injectet werden. Listings 1 und 2 zeigen ein minimalistisches Beispiel.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:f="http://xmlns.jcp.org/jsf/core"
  xmlns:h="http://xmlns.jcp.org/jsf/html">
  <h:head>
    Websocket Example
  </h:head>
  <h:body>
    <f:websocket channel="helloWorldChannel" 
      onmessage="function(message){alert(message)}" />
  </h:body>
</html>
@Named
@RequestScoped
public class SomeBean {

  // WebSocket push channel with name "helloWorldChannel"
  @Inject @Push(channel = "helloWorldChannel""
  private PushContext channel;

  public void send(){
    channel.send("Hello, push world!");
  }
)

Auch im Bereich Ajax gibt es einige Verbesserungen. So können künftig beliebige Methoden auf Seiten des Servers via <h:commandScript/>-Tag asynchron angestoßen werden. Wer schon einmal mit OmniFaces gearbeitet hat, dem dürfte dieses Tag vertraut vorkommen. Zusätzlich erlaubt das <f:ajax>-Tag die Rückgabe von JavaScript-Fragmenten, die innerhalb des Partial Response als Eval-Skripte mitgegeben und nach der Abarbeitung des Requests auf Seiten des Clients ausgeführt werden können.

Interessant und seit Langem überfällig ist auch die neu eingeführte Class Level Validation. Mithilfe des <f:validateWholeBean>-Tags werden Prüfungen auf Klassenebene der Backing Bean einer JSF View möglich, was u. a. eine Multi-Field-Validierung von Eingabeformularen erlaubt.

JSF 2.3 setzt Java 8 voraus, was die Nutzung sämtlicher Java-8-Features in JSF ermöglicht. Unter anderem spiegelt sich dies in Form eines neuen Java Date and Time API (JSR 310) und verbesserter UIData- und UIRepeat-Komponenten wider.

RESTful API Design

Die mit Abstand meisten Änderungen und Neuerungen in Java EE gibt es im Umfeld derjenigen APIs, die zum Design von Anwendungsschnittstellen genutzt werden können, allen voran das API zur Implementierung von REST-konformen Schnittstellen: JAX-RS 2.1.

Für ein Minor-Release beinhaltet das JAX-RS-2.1-API eine ganze Menge neuer Features, wobei scheinbar im Endspurt zum finalen Release das eine oder andere geplante Feature auf der Strecke geblieben zu sein scheint. Wie schon zuvor beim Servlet API, stehen auch bei JAX-RS Non-Blocking I/O und Asynchronität im Fokus. So wird zum Beispiel Asynchronität in Form eines Reactive Clients auf Basis des CompletionStage-Features aus Java 8 unterstützt. Die Idee der Reactive Clients ist nicht neu. Bereits seit JAX-RS 2.0 können Clients asynchrone Anfragen an einen Server senden und auf deren Antwort via InvocationCallback oder Future<T> reagieren. Kompliziert wird es bei diesem Szenario allerdings immer dann, wenn mehrere Events ins Spiel kommen, was zu einer Verschachtelung der Callback Handler führt (auch bekannt als Pyramid of Doom). Genau an diesem Punkt setzt die auf dem Interface CompletionStage basierende Lösung an. Listing 3 zeigt ein Beispiel aus der Spezifikation, in dem ein Client zwei asynchrone Calls absetzt, deren Ergebnisse im Anschluss nach ihrem Eintreffen wieder zusammengeführt werden.

// async REST call no 1 (via RxInvoker using CompletionStage)
CompletionStage cs1 = client.target("http://one.de/api")
  .request()
  .rx()
  .get(String.class);

// async REST call no 2 (via RxInvoker using CompletionStage)
CompletionStage cs2 = client.target("http://two.de/api")
  .request()
  .rx()
  .get(String.class);

// combining result of async REST call no 1 and no 2
CompletionStageString>> list = cs1.thenCombine(cs2, list::of);

Ebenfalls neu unterstützt wird in JAX-RS 2.1 der W3C- bzw. WHATWG-Standard Server-sent Events, bei dem über eine langlebende HTTP-Verbindung Push Notifications in Form spezieller DOM Events vom Server zum Browser gesendet werden. Die Events können dabei sowohl an einen speziellen Client als auch – via Broadcasting – an eine Gruppe von Clients geschickt werden. Listing 4 zeigt den Aufbau einer SSE-Verbindung durch einen JAX-RS Client, Listing 5 den zugehörigen JAX-RS Server, der via Broadcasting Events an alle registrierten Clients verschickt.

WebTarget target = client.target("http://...");
try (SseEventSource source = SseEventSource.target(target).build()) {
  source.register(System.out::println);
  source.open();
  Thread.sleep(5000); // Consume events for just 5000 ms
} catch (InterruptedException e) {
  // do some intelligent exception handling
    ...
}
@Singleton
public class SseResource {

  @Context
  private Sse sse;

  private SseBroadcaster sseBroadcaster;

  @PostConstruct
  public init() {
    this.sseBroadcaster = sse.newBroadcaster();
  }

  @GET
  @Path("register")
  @Produces(MediaType.SERVER_SENT_EVENTS)
  public void register(@Context SseEventSink eventSink) {
    eventSink.send(sse.newEvent("welcome!"));
    sseBroadcaster.register(eventSink);
  }

  @POST
  @Path("broadcast")
  @Consumes(MediaType.MULTIPART_FORM_DATA)
  public void broadcast(@FormParam("event") String event) {
    sseBroadcaster.broadcast(sse.newEvent(event));
  }
}

Neben den bisher aufgeführten Features können natürlich in einer Java-EE-8-Umgebung auch die Möglichkeiten des neuen JSON-B-API innerhalb von JAX-RS genutzt werden.

Das längst überfällige JSON-B-1.0-API (JSR 367) bietet out of the Box Support für die Transformation von Java-Objekten hin zu JSON-Objekten und vice versa. Mittels Annotationen lässt sich das gewünschte Mapping-Verhalten beliebig anpassen. So können Felder via @JsonbTransient ausgeblendet oder die Attributnamen via @JsonProperty individuell bestimmt werden. Mithilfe von @PropertyNamingStrategy lässt sich festlegen, welche generelle Namensstrategie gewählt werden soll (mit Unterstrich, ohne Unterstrich, CamelCase usw.). Die Reihenfolge der Attribute innerhalb des generierten JSON kann via @PropertyOrderStrategy bestimmt werden. Auch für die gezielte Behandlung von Null-Values gibt es mit @JsonbNillable eine eigene Annotation. Reichen die genannten Annotationen nicht aus, können Adapter oder Serializer bzw. Deserializer programmiert und für die individuell zu transformierende Klasse verwendet werden.

Lesen Sie auch: Java EE 8 ist da! Das sind die wichtigsten Änderungen und Features

Die Tatsache, dass es für die JSON-Transformation in Java EE 8 nun endlich ein eigenes API gibt und nicht mehr auf die Krücke JAX-B oder proprietäre APIs zurückgegriffen werden muss, unterstreicht die zunehmende Bedeutung von JSON im Umfeld moderner, verteilter (Web-)Anwendungen. Das wird zusätzlich durch die Erweiterungen innerhalb des API zum Parsen von JSON-Objekten deutlich (JSON-P-1.1.-API – JSR 374). Neben einigen kleineren Änderungen, zum Beispiel der Unterstützung des Java-8-Stream-API und der damit einhergehenden Einführung der Helper-Klasse JsonCollectors, stehen vor allem Anpassungen zur Unterstützung aufstrebender JSON-Standards im Fokus. Dank neu eingeführter JSON-Pointer-Klasse kann innerhalb einer JSON-Struktur gezielt ein Teilobjekt referenziert und bei Bedarf manipuliert werden (Listing 6).

// original json object 
JsonObject jsonObject = (JsonObject) JsonUtil.toJson(
  " {                               " +
  "   'name': 'lars',                " +
  "   'hobbies': [                  " +
  "        'swimming', 'running'    " +
  "   ]                             " +
  " }                               " +
// extract name from JSON structure
JsonPointer getPointer = new JsonPointer("/name");
JsonValue value = pointer.getValue(jsonObject)

// add nickname to JSON structure
String pointerStr = ("{'pointer': '/nickname', 'value': 'mobileLarson'}"
JsonObject operation = (JsonObject) JsonUtil.toJson(pointerStr);
JsonPointer pointer = new JsonPointer(operation.getString("pointer"));
JsonObject modifiedJson = (JsonObject) pointer.add(targetJson, operation.get("value"));

Alternativ zum JSON Pointer kann auch JSON Patch zur Manipulation von JSON-Objekten verwendet werden. JSON Patch bietet sich immer dann an, wenn mehrere Operationen – add, remove, replace, move, copy und test – parallel erfolgen sollen. Die gewünschten Operationen werden als eigene JSON-Array-Struktur deklariert (Listing 7) oder alternativ über einen JsonPatchBuilder angegeben.

// original json object
JsonObject targetObject = (JsonObject) JsonUtil.toJson(
  " {                               " +
  "   'name': 'lars',                " +
  "   'nickname': 'mobileLarson',  " +
  "   'hobbies': [                  " +
  "        'swimming', 'running'    " +
  "   ]                             " +
  " }                               " ); 

// patch operations to apply
JsonArray patch = (JsonAray) JsonUtil.toJson(
  " [                                                            " +
  "    { 'op': 'copy', 'path': '/nickname', 'from': '/name' },   " +
  "    { 'op': 'remove', 'path': '/hobbies/1},                   " +
  "    { 'op': 'add', 'path': '/hobbies/-', 'value': 'cycling' } " +
  "]                                                             " );
JsonPatch patch = new JsonPatch(patch)

// apply patch will result in:
// {
//    'name' : 'lars'
//    'nickname' : 'lars'
//    'hobiies' : [
//       'swimming', 'cycling'
//    ]
// }
JsonStructure result = patch.apply(target)

In eine ähnliche Richtung wie JSON Patch geht auch das neu eingeführte JSON MergePatch. Allerdings werden bei einem Merge Patch – anders als beim eben vorgestellten Patch – nicht die Patch-Operationen direkt angegeben, sondern stattdessen ein zu verschmelzendes Objekt, aus dem dann die notwendigen Operationen automatisch errechnet werden.

Alte Bekannte aufpoliert

Neben den bisher beschriebenen Neuerungen im Java-EE-Umfeld gibt es zwei weitere APIs, die grundlegend erweitert wurden und daher einen Major-Release-Sprung hingelegt haben: CDI 2.0 (JSR 365) und Bean Validation 2.0 (JSR 380).

Die neue Version der CDI-Spezifikation setzt gleich mehrere Akzente. Wie die meisten anderen Java-EE-APIs, erlaubt CDI künftig die Verwendung der wichtigsten Java-8-Features (Streams, Lambdas, Repeating Qualifiers). Für mehr Transparenz zur Laufzeit sorgt die Möglichkeit, die Abarbeitungsreihenfolge von Observern durch die Angabe von Prioritäten – via @Priority aus Commons Annotation – zu bestimmen. Mithilfe der neuen InterceptionFactory lassen sich künftig Interceptors programmatisch an Producers binden und so zum Beispiel on the fly Qualifier-Annotationen beim Produzieren eines Objekts hinzufügen. Möchte man zur Laufzeit kontextsensitiv Annotationen, wie zum Beispiel fallbezogene Qualifier, erzeugen, stehen einem dafür künftig eine ganze Reihe vordefinierter Annotation Literals zur Verfügung. Das garantiert zusätzliche Typsicherheit und verhindert somit unnötige Laufzeitfehler.

Eine wesentliche Neuerung der CDI-2.0-Spezifikation ist die Unterstützung von Java SE als Laufzeitumgebung inklusive standardisiertem Bootstraping. Um CDI zukünftig auch außerhalb von Java EE sinnvoll und standardisiert nutzen zu können, wurde die Spezifikation in drei Teile untergliedert:

  • Core CDI
  • CDI in Java SE
  • CDI in Java EE

Der Bereich „CDI in Java SE“ definiert, wie ein CDI-Container (SeContainer) innerhalb von Java SE initialisiert, gestartet, verwaltet und am Ende sauber heruntergefahren werden kann. Listing 8 zeigt ein minimalistisches Beispiel, in dem genau diese Schritte nachvollzogen werden. Mithilfe eines Container Initializers (SeContainerInitializer) kann das Bootstrapping des Containers gesteuert werden. So kann zum Beispiel zwischen automatischer Bean Discovery via Annotationen (Default) und manueller Discovery umgeschaltet werden.

public static void main(String... args) {
  SeContainerInitializer containerInit = SeContainerInitializer.newInstance();
  SeContainer container = containerInit.initialize();
  // retrieve a bean and do work with it
  MyWorkerBean myBean = container.select(MyWorkerBean.class).get();
  myBean.doSomeWork();
  // when done
  container.close();
}

Neben der eben vorgestellten Unterstützung von Java SE ist die zweite wichtige Neuerung in CDI 2.0 die Einführung von asynchronen Events – ein Feature, für das man bisher auf EJBs zurückgreifen musste. Hierzu wurde das Event-Interface um zwei fireAsync-Methoden erweitert, die beide ein Objekt vom Typ CompletionStage<T> (aus Java 8) zurückgeben. Die Verwendung ist denkbar einfach, wie die Listings 9 und 10 zeigen. In Listing 9 wird ein asynchrones Event gemäß „Fire and Forget“-Pattern erzeugt, dessen Verarbeitung im Anschluss in Listing 10 durch einen entsprechenden asynchronen Observer erfolgt.

@ApplicationScoped
public class AsyncEventFireBean {

  @Inject 
  Event event;


  public void someAsyncBusinessTriggerMethod() {
    // fire (and forget) asynchronous event
    event.fireAsync(new SomePayload());
    // do some additional work in main thread
    ...
  }
}
@ApplicationScoped
public class MyAsyncObserverBean {

  public void callMe(@ObservesAsync SomePayload payload) {

    // BTW: this is another thread
  } 
}

Wie bereits erwähnt, hat auch das Bean-Validation-API eine neue Major-Version (2.0) erhalten. Wesentlicher Grund für den Versionssprung ist die Adaption hin zu Java 8. So unterstützt Bean Validation künftig sowohl die beiden Java-8-Typen LocalTime und Optional als auch etliche der neuen Sprachfeatures (Lambdas, Repeating Qualifiers, Type Annotations). Zusätzlich werden neue Constraints (z. B. @NotBlank, @NotEmpty, @Email, @Past, @Future, @negative, @Positive …) zur Verfügung gestellt.

Sicher ist sicher

Nach gefühlten hundert Jahren nimmt sich Java EE 8 mit dem Security API 1.0 (JSR 375) endlich auch eines Themas an, das bisher im Standard vernachlässigt wurde. Ziel des neu geschaffenen API ist es, die über die letzten Jahre gewachsenen Lücken, die bisher durch herstellerspezifische Lösungen überbrückt wurden, auf einfache und moderne Art und Weise zu schließen. Eine Portabilität der verschiedenen Securityaspekte ist besonders in Hinblick auf die Zielplattform Cloud ein essenzielles Feature.

Die Security Expert Group hat sich bei dem Design des API zu einer wesentlichen Aufgabe gemacht, bestehende Security-Mechanismen zu zentralisieren und sinnvoll zu ergänzen, ohne sie aber zwingend auch zu ersetzen. So sollen z. B. innerhalb der web.xml angegebene Securitydeklarationen weiterhin Bestand haben. Gleiches gilt für bereits existierende Securityannotationen (z. B. @RolesAllowed). Auch sollte ein Wechsel zwischen herstellerspezifischer und herstellerneutraler Lösung einfach möglich sein. Zu diesem Zweck bildet die aktuelle Spezifikation drei grundlegende Komponenten ab:

  • Authentication Mechanism
  • Identity Storage
  • Security Context

Die erste Komponente, der Authentication-Mechanismus, stellt den Einstiegspunkt für die Authentifizierung dar. Mithilfe der durch den Aufrufer an den Mechanismus übergebenen Credentials (z. B. Username und Passwort) erfolgt eine Authentifizierung gegen einen der angebundenen Identity Stores. Im Erfolgsfall wird der zugehörigen User inklusive seiner Attribute (Principals) als Authenticated Subject bereitgestellt. Im Fehlerfall dagegen signalisiert der Mechanismus einen Anmeldefehler, und der Aufrufer ist nicht angemeldet. Java-EE-Container müssen mindestens LDAP und Database als Identity-Store-Typ zur Verfügung stellen.

Die dritte und letzte Komponente, der Security Context, ist die programmatische Schnittstelle des Security-API und stellt somit das Bindeglied zum Container dar. Listing 11 zeigt eine Backing Bean einer typischen Log-in-Seite (JSF View), mit deren Hilfe sich ein User innerhalb einer Webanwendung unter Verwendung des HttpAuthenticationMechanism anmelden kann. Der SecurityContext wird in diesem Beispiel zur Authentifizierung und somit für den Zugriff auf den angebundenen Identity Store genutzt. Alternativ dazu hätte man auch einen aktiven Identity Store injecten und im Anschluss dessen validate-Methode mit den Credentials aufrufen können.

@Named
@RequestScoped
public class LoginBean {

  @NotNull
  private String username;

  @NotNull
  private String password;

  @Inject
  private SecurityContext securityContext;

  @Inject
  private FacesContext facesContext;

  public void login() {

    Credential credential =
      new UsernamePasswordCredential(username, new Password(password));

    AuthenticationStatus status = securityContext.authenticate(
      getRequest(facesContext),
      getResponse(facesContext),
      withParams()
        .credential(credential));   

    if (status.equals(IN_PROGRESS)) {
      facesContext.responseComplete();
    } else if (status.equals(FAILURE)) {
      addError(facesContext, "Authentication failed");
    }
  }
}

Fazit

Auch wenn Java EE 8 am Ende aus technologischer Sicht vielleicht weniger bietet, als von dem einen oder anderen erhofft, ist es doch ein wichtiger Meilenstein auf dem Weg in die Zukunft des Java-Enterprise-Standards. Zum einen stellt Java EE 8 ein klares Bekenntnis von Oracle bezüglich des Fortbestehens des Umbrella JSR dar – eine Tatsache, an der viele Entwickler aus der Community vor wenigen Monaten noch arg gezweifelt haben. Zum anderen bietet Java EE 8, dank der Fokussierung auf API Alignement und Ease of Development, eine solide Grundlage für den zukünftigen Ausbau der Plattform in Richtung Cloud, Microservices und Reactive Programing.

Positiv anzumerken ist auf jeden Fall, dass mehr und mehr zentrale Themen wie Transaction Handling (Java EE 7) oder Security (Java EE 8) in eigene APIs ausgelagert werden und somit nicht mehr impliziter, proprietärer Bestandteil anderer APIs (z. B. EJB) sind. Das erlaubt dem Java-EE-Entwickler eine deutlich höhere Flexibilität bei der Wahl der Mittel und stellt einen ersten, wichtigen Schritt hin zu einer modularen Java EE Runtime dar, wie wir sie aktuell nur außerhalb des Standards kennen.

Abzuwarten bleibt allerdings, ob es nach Java EE 8 tatsächlich so weitergeht wie geplant und die für Java EE 9 angekündigte Roadmap gehalten werden kann. Das wäre ein extrem wichtiges Signal für alle und würde endlich für ein wenig mehr Ruhe in der Community sorgen.

Verwandte Themen:

Geschrieben von
Lars Röwekamp
Lars Röwekamp
Lars Röwekamp ist Gründer des IT-Beratungs- und Entwicklungsunternehmens open knowledge GmbH, beschäftigt sich im Rahmen seiner Tätigkeit als „CIO New Technologies“ mit der eingehenden Analyse und Bewertung neuer Software- und Technologietrends. Ein besonderer Schwerpunkt seiner Arbeit liegt derzeit in den Bereichen Enterprise und Mobile Computing, wobei neben Design- und Architekturfragen insbesondere die Real-Life-Aspekte im Fokus seiner Betrachtung stehen. Lars Röwekamp, Autor mehrerer Fachartikel und -bücher, beschäftigt sich seit der Geburtsstunde von Java mit dieser Programmiersprache, wobei er einen Großteil seiner praktischen Erfahrungen im Rahmen großer internationaler Projekte sammeln konnte.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.