Suche
Kolumne

Enterprise Tales: Reactive oder nicht reactive?

Arne Limburg

©SuS_Media

Vor einiger Zeit habe ich eine Kolumne zu Reactive Programming in Java 9 und Spring 5 geschrieben und dabei auch kurz betrachtet, was passieren müsste, damit dieses Konzept Einzug in Enterprise Java halten kann. Auf technischer Ebene müsste vor allem die Bindung eines Requests an (nur) einen Thread überwunden werden. Grundsätzlich ist asynchrone Verarbeitung zwar mit Servlet 3.0 möglich, viele Java EE Specs verlassen sich aber noch darauf, dass die Verarbeitung eines Requests auch nur in einem Thread stattfindet. Ist das einmal nicht der Fall, kann es durchaus sein, dass relevanter Kontext fehlt. Soll also das Programmierparadigma der reaktiven Programmierung in den Enterprise-Java-Standard aufgenommen werden, gibt es hier noch einige offene Punkte.

Kürzlich habe ich zufällig ein altes Java Magazin aus dem Jahr 2005 in den Händen gehalten. Damals war das neueste Programmierparadigma die aspektorientierte Programmierung. Von aspektorientierter Programmierung spricht heute niemand mehr (explizit), was die Frage aufkommen lässt, ob es der reaktiven Programmierung in zehn Jahren nicht ähnlich gehen wird. Nun ist es nicht so, dass die aspektorientierte Programmierung seinerzeit Eingang in Java SE oder Java EE fand, jedenfalls nicht explizit. Deshalb ist die Frage gerechtfertigt, ob jede Technologie und jedes neue Programmierparadigma blind in den Standard übernommen werden sollten.

Ohne Praxistest läuft in Sachen Standard nichts

In Zeiten von aspektorientierter Programmierung war es noch gängiges Vorgehen, dass Enterprise Java am Reißbrett entstand (und damit fernab der Realität der meisten Projekte), was unter anderem ja auch dazu geführt hat, dass J2EE (so hieß der Enterprise-Java-Standard damals noch) in der Praxis nicht mehr verwendet wurde. Stattdessen wurden neue Projekte in der Regel mit dem Spring-Framework umgesetzt. Mittlerweile hat sich die Welt hier weitergedreht. Das Spring-Framework ist immer noch ein Innovationstreiber. In den Enterprise-Java-Standard schaffen es aber nur Dinge, die entweder schon praxiserprobt sind oder zumindest von der Community vehement gefordert werden, wie das in Java EE 8 neu hinzugekommene Security-API.

Bei Reactive Programming ist bisher beides nicht der Fall. Der gemeine Enterprise-Java-Entwickler weiß mittlerweile, wie er ausreichend performante Webapplicationen schreibt, die klassischerweise einen Thread pro Request blockieren und dadurch auch deutlich einfacher zu debuggen sind als eine vergleichbare Anwendung, die reaktiv entwickelt wurde. Zudem gibt es in JAX-RS Mittel und Wege, wenn in einzelnen Use Cases die synchrone Verarbeitung nicht ausreicht. Hier kann der Entwickler dann entweder zur AsyncResponse greifen, um einen Request in einem anderen Thread weiterzuverarbeiten (Listing 1) oder er kann, wenn es darum geht, große Datenmengen zum Client zu streamen, das Interface StreamingOutput verwendeen (Listing 2).

@Path("/customers")
public class MyResource {
  @Resource
  private ManagedExecutorService executorService;
  ...
  @POST
  public void createCustomer(
    Customer customer, @Suspended AsyncResponse response) {
    CompletableFuture
      .supplyAsync(() -> createCustomer(customer), executorService)
      .thenAccept(response::resume);
  }

  private Response createCustomer(Customer customer) {
    ...
  }
}
@Path("/customers")
public class MyResource {
  ...
  @GET
  @Path("/{customerNumber}/image")
  public SteamingOutput getImage(@PathParam("customerNumber") String number) {
    return new StreamingOutput() {
      public void write(OutputStream out) {
        out.write(imageStream());
      }
    };
  }
}

Das API zur Verwendung der AsyncResponse ist nicht gerade intuitiv. Dass eine Methode, die eine Antwort liefert, void als Rückgabewert hat, ist eher überraschend. Auch das Interface zum Streaming gewinnt keinen Schönheitspreis. Da wäre eine Unterstützung des Java-9-Flow-API in JAX-RS vom ästhetischen Aspekt her ohne Frage ein Gewinn (Listing 3). Rechtfertigt aber allein das eine Aufnahme in den Enterprise-Standard? Und was bringt ein reaktives Webinterface, wenn die dahinter liegenden APIs Reactive Programming nicht unterstützen (Stichwort JDBC und JPA)?

@Path("/customers")
public class MyResource {
  ...
  @POST
  public Response createCustomer(Flow.Publisher<Customer> customer) {
    return createCustomer(customer);
  }
  ...
  @GET
  public Flow.Publisher<Customer> findAllCustomers() {
    return customerRepository.findAll();
  }
}

Werfen wir erneut einen Blick auf die aspektorientierte Programmierung. Die Idee dort war, bestimmte Querschnittsfunktionalität automatisch in den Binärcode der Aufrufe der Fachfunktionalität einzuweben. Hierzu wurde eigens eine Begriffswelt geschaffen, um diese Stellen zu beschreiben. Ein Methodenaufruf ist dabei ein Joinpoint. Das Pattern, über das an einen solchen Jointpoint die Aspekte (die Querschnittsfunktionalität) angehängt werden, heißt Pointcut.

Aspektorientierte Programmierung wird heute noch vom Spring-Framework unterstützt, in der Regel aber nur noch unter der Haube verwendet, sodass der normale Applikationsentwickler davon nichts mitbekommt. Auch im Enterprise-Java-Standard wird Querschnittsfunktionalität benötigt und auch unterstützt. Hier wurde aber nicht das komplette Set der aspektorientierten Programmierung übernommen. In der interceptors-spec und mit einer Erweiterung in der CDI-Spec wurden vereinfachte Mechanismen standardisiert, um Querschnittsfunktionalität in Enterprise Java verwenden zu können.

Übertragen auf die reaktive Programmierung könnte das z. B. heißen, dass einzelne Enterprise-Java-Specs die neuen Java-9-Datentypen durchaus übernehmen könnten, sofern am Ende entweder eine sinnvolle Erweiterung der Funktionalität oder eine deutliche Verbesserung der APIs steht. Ein komplettes Webframework in Enterprise Java scheint aber momentan noch nicht notwendig und auch nicht angebracht, weil reaktive Programmierung in der Java-Welt noch nicht so weit verbreitet ist.

Java EE 8 bringt nur punktuelle Neuerungen

Wirft man einen Blick auf die Neuerungen von Java EE 8 im Detail, so findet man dort Dinge wie asynchrone CDI-Events, Server-sent Events, WebSocket in JSF, JSON-B, HTTP/2-Support, HTTP-Patch-Support. Alles keine Umwälzungen der Enterprise-Java-Welt, sondern eher Themen, die man sowieso im Standard erwartet hätte oder die sinnvolle Ergänzungen aktueller Standards sind. Alles in allem wirkt Java EE 8 eher wie ein Maintenance-Release, was vor allem vor dem Hintergrund interessant ist, dass dasselbe schon über Java EE 7 gesagt wurde. Es häufen sich mittlerweile gar die Stimmen, die aufgrund dieser fehlenden Innovationskraft bereits das Ende von Enterprise Java beschreien (mal wieder).

Aber die Projekterfahrung zeigt ein anderes Bild. Seit den Neuerungen von Java EE 6 ist der Standard so ausgereift und gut nutzbar, dass es keiner großen Novellen bedarf. Wichtiger sind besagte kleine Updates und Anpassungen an die Praxis. Ansonsten ist es eben diese Kontinuität, die den Enterprise-Java-Standard in der Vergangenheit so stark gemacht hat. In diesem Sinne wäre die Unterstützung des Flow-API z. B. in JAX-RS wohl nur eine weitere dieser kleinen Neuerungen, die sinnvoll sind, aber eben nicht als Überschrift für die Version 9 des Enterprise-Java-Standards taugen würden. Wirft man einen Blick in den Projektalltag, wäre wohl sowieso eine Aufnahme von Microservices-spezifischen Themen in den Standard deutlich wichtiger. Hier hat die Community ja mit MicroProfile, das wie der Enterprise-Java-Standard bei der Eclipse Foundation liegt, ja schon gute Vorarbeit geleistet.

Fazit

Bei der Weiterentwicklung von Java SE und auch der Enterprise-Version von Java (wie auch immer sie in Zukunft heißen wird) sollte sehr genau darauf geachtet werden, welche Neuerung es wert ist, in den Standard übernommen zu werden und in welchem Umfang dies geschehen soll. Das ist in der Vergangenheit nicht immer gelungen, und so gibt es manche Dinge in Enterprise Java, die der Entwickler nicht benötigt, aber aus Gründen der Abwärtskompatibilität auch nicht mehr los wird.

Die Einführung des Flow-API in Java 9 war sinnvoll, zumal wirklich nur ein rudimentäres API standardisiert wurde. Es wäre schön, wenn dieses API auch vom nächsten JAX-RS-Standard unterstützt würde, ebenso von JDBC und JPA. Notwendig ist es aber für den Erfolg der nächsten Enterprise-Java-Version nicht. Da wäre es wichtiger, APIs, die sich in der Praxis bereits zu einem De-facto-Standard entwickelt haben, in einen echten Standard zu überführen. Hier wäre z. B. das Thema Resilienz mit Hystrix zu nennen. Das erwähnte MicroProfile 1.2 macht dazu ja bereits einen Vorschlag, wie das passieren könnte. Dieses Profil in den Enterprise-Java-Standard zurückzuführen, wäre daher sicherlich die wichtigere Aufgabe und auch ein guter Aufhänger für die nächste Enterprise-Java-Version. In diesem Sinne, stay tuned.

Geschrieben von
Arne Limburg
Arne Limburg
Arne Limburg ist Softwarearchitekt bei der open knowledge GmbH in Oldenburg. Er verfügt über langjährige Erfahrung als Entwickler, Architekt und Consultant im Java-Umfeld und ist auch seit der ersten Stunde im Android-Umfeld aktiv.
Kommentare

Schreibe einen Kommentar

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